Commit Diff


commit - 5d0feb4b6d3aad62434dfb509ca754ab1739a19c
commit + 984245ce20826f010570cf6fe030c19a1b566311
blob - b32cdff12949df5c020c20d882438672582db9a4
blob + 924d67934989339cea0250fdf5099d6d7d67e9f5
--- ChangeLog
+++ ChangeLog
@@ -1,5 +1,7 @@
 2021-06-23  Omar Polo  <op@omarpolo.com>
 
+	* parse.y (add_proxy): add proxy support (e.g. `proxy http via gemini://localhost:1965' for the duckling proxy)
+
 	* cmd.c (cmd_previous_button): stay on the line in next-button if there is no next link, and the same for previous-button
 
 	* telescope.c (handle_imsg_buf): "humanize" byte progress (i.e. trasform XYZ bytes to something readable)
blob - 3293bf279abb101b19418b65a4c9ab51ce428dd5
blob + f3fe5d8816cc4f553ec98a5bd13504932890f7cf
--- gemini.c
+++ gemini.c
@@ -73,7 +73,7 @@ static void		 read_reply(int, short, void*);
 static void		 parse_reply(struct req*);
 static void		 copy_body(int, short, void*);
 
-static void		 handle_get(struct imsg*, size_t);
+static void		 handle_get_raw(struct imsg *, size_t);
 static void		 handle_cert_status(struct imsg*, size_t);
 static void		 handle_proceed(struct imsg*, size_t);
 static void		 handle_stop(struct imsg*, size_t);
@@ -84,7 +84,7 @@ static void		 handle_dispatch_imsg(int, short, void*);
 struct timeval timeout_for_handshake = { 5, 0 };
 
 static imsg_handlerfn *handlers[] = {
-	[IMSG_GET]		= handle_get,
+	[IMSG_GET_RAW]		= handle_get_raw,
 	[IMSG_CERT_STATUS]	= handle_cert_status,
 	[IMSG_PROCEED]		= handle_proceed,
 	[IMSG_STOP]		= handle_stop,
@@ -373,16 +373,10 @@ write_request(int fd, short ev, void *d)
 	struct req	*req = d;
 	ssize_t		 r;
 	size_t		 len;
-	char		 buf[1027]; /* URL + \r\n\0 */
-
-	if (!phos_serialize_uri(&req->url, buf, sizeof(buf)))
-		die();
+
+	len = strlen(req->buf);
 
-	len = strlcat(buf, "\r\n", sizeof(buf));
-
-	assert(len <= sizeof(buf));
-
-	switch (r = tls_write(req->ctx, buf, len)) {
+	switch (r = tls_write(req->ctx, req->buf, len)) {
 	case -1:
 		close_with_errf(req, "tls_write: %s", tls_error(req->ctx));
 		break;
@@ -507,14 +501,14 @@ copy_body(int fd, short ev, void *d)
 }
 
 static void
-handle_get(struct imsg *imsg, size_t datalen)
+handle_get_raw(struct imsg *imsg, size_t datalen)
 {
 	struct req	*req;
-	char		*data;
+	struct get_req	*r;
 
-	data = imsg->data;
+	r = imsg->data;
 
-	if (data[datalen-1] != '\0')
+	if (datalen != sizeof(*r))
 		die();
 
 	if ((req = calloc(1, sizeof(*req))) == NULL)
@@ -523,10 +517,9 @@ handle_get(struct imsg *imsg, size_t datalen)
 	req->id = imsg->hdr.peerid;
 	TAILQ_INSERT_HEAD(&reqhead, req, reqs);
 
-        if (!phos_parse_absolute_uri(data, &req->url)) {
-		close_with_err(req, "Can't parse URI");
-		return;
-	}
+	strlcpy(req->url.host, r->host, sizeof(req->url.host));
+	strlcpy(req->url.port, r->port, sizeof(req->url.port));
+	strlcpy(req->buf, r->req, sizeof(req->buf));
 
 #if HAVE_ASR_RUN
         async_conn_towards(req);
blob - 7765898b8e268892fecf2b6068d06d3990da70ef
blob + c5cb9c10470e8b0c2b35adbc056382bc570e58ee
--- parse.y
+++ parse.y
@@ -22,6 +22,8 @@
 
 #include "telescope.h"
 
+#include <phos/phos.h>
+
 #include <assert.h>
 #include <ctype.h>
 #include <limits.h>
@@ -59,12 +61,14 @@ static int colorname(const char *);
 static void setcolor(const char *, const char *, const char *);
 static int attrname(char *);
 static void setattr(char *, char *, char *);
+static void add_proxy(char *, char *);
 
 %}
 
 %token TSET
 %token TSTYLE TPRFX TCONT TBG TFG TATTR
 %token TBIND TUNBIND
+%token TPROXY TVIA
 
 %token <str> TSTRING
 %token <num> TNUMBER
@@ -84,6 +88,7 @@ rule		: set
 		}
 		| bind
 		| unbind
+		| proxy
 		;
 
 set		: TSET TSTRING '=' TSTRING	{ setvars($2, $4); }
@@ -121,6 +126,9 @@ bind		: TBIND TSTRING TSTRING TSTRING	{ printf("TODO: 
 unbind		: TUNBIND TSTRING TSTRING	{ printf("TODO: unbind %s %s\n", $2, $3); }
 		;
 
+proxy		: TPROXY TSTRING TVIA TSTRING { add_proxy($2, $4); free($4); }
+		;
+
 %%
 
 void
@@ -140,15 +148,17 @@ static struct keyword {
 	const char *word;
 	int token;
 } keywords[] = {
-	{ "set", TSET },
-	{ "style", TSTYLE },
-	{ "prefix", TPRFX },
-	{ "cont", TCONT },
-	{ "bg", TBG },
-	{ "fg", TFG },
 	{ "attr", TATTR },
+	{ "bg", TBG },
 	{ "bind", TBIND },
+	{ "cont", TCONT },
+	{ "fg", TFG },
+	{ "prefix", TPRFX },
+	{ "proxy", TPROXY },
+	{ "set", TSET },
+	{ "style", TSTYLE },
 	{ "unbind", TUNBIND },
+	{ "via", TVIA },
 };
 
 int
@@ -433,6 +443,41 @@ setattr(char *prfx, char *line, char *trail)
 		yyerror("invalid style %s", current_style);
 }
 
+static void
+add_proxy(char *proto, char *proxy)
+{
+	struct proxy *p;
+	struct phos_uri uri;
+
+	if (!phos_parse_absolute_uri(proxy, &uri)) {
+		yyerror("can't parse URL: %s", proxy);
+		return;
+	}
+
+	if (*uri.path != '\0' || *uri.query != '\0' || *uri.fragment != '\0') {
+		yyerror("proxy url can't have path, query or fragments");
+		return;
+	}
+
+	if (strcmp(uri.scheme, "gemini")) {
+		yyerror("disallowed proxy protocol %s", uri.scheme);
+		return;
+	}
+
+	if ((p = calloc(1, sizeof(*p))) == NULL)
+		err(1, "calloc");
+
+	p->match_proto = proto;
+
+	if ((p->host = strdup(uri.host)) == NULL)
+		err(1, "strdup");
+
+	if ((p->port = strdup(uri.port)) == NULL)
+		err(1, "strdup");
+
+	TAILQ_INSERT_HEAD(&proxies, p, proxies);
+}
+
 void
 parseconfig(const char *filename, int fonf)
 {
blob - 0e48481d190342f3a93f0b211012c997dd225863
blob + 281e57e54e838ad6ab4613166a9fe860cc696959
--- telescope.1
+++ telescope.1
@@ -499,6 +499,14 @@ Enable
 if non zero.
 By default is 0.
 .El
+.It Ic proxy Ar proto Ic via Ar url
+Use
+.Ar url
+as proxy for all URLs with
+protocol
+.Ar proto Ns .
+.Ar url
+must be a Gemini URI without path, query and fragment component.
 .It Ic style Ar name Ar option
 Change the styling of the element identified by
 .Ar name .
@@ -611,6 +619,16 @@ style line.link {
 style line.quote {
 	prefix " ┃ "
 }
+.Ed
+.Pp
+It's possible to browse
+.Dq the small web
+.Pq i.e. simple websites
+using programs like the duckling-proxy by defining a proxy in
+.Pa ~/.telescope/config Ns :
+.Bd -literal -offset indent
+proxy http via "gemini://localhost:1965"
+proxy https via "gemini://localhost:1965"
 .Ed
 .Sh AUTHORS
 .An -nosplit
blob - 8fbb6fe92c489712e4e4f0a01e9542bcb16d995b
blob + 8bc89bfa10d7876b108f85b77bf468c796837c69
--- telescope.c
+++ telescope.c
@@ -11,6 +11,7 @@
 
 struct event		 netev, fsev;
 struct tabshead		 tabshead;
+struct proxylist	 proxies;
 
 /* the first is also the fallback one */
 static struct proto protos[] = {
@@ -96,7 +97,7 @@ handle_imsg_err(struct imsg *imsg, size_t datalen)
 static void
 handle_imsg_check_cert(struct imsg *imsg, size_t datalen)
 {
-	const char		*hash;
+	const char		*hash, *host, *port;
 	int			 tofu_res;
 	struct tofu_entry	*e;
 	struct tab		*tab;
@@ -107,7 +108,15 @@ handle_imsg_check_cert(struct imsg *imsg, size_t datal
 
 	tab = tab_by_id(imsg->hdr.peerid);
 
-	if ((e = tofu_lookup(&certs, tab->uri.host, tab->uri.port)) == NULL) {
+	if (tab->proxy != NULL) {
+		host = tab->proxy->host;
+		port = tab->proxy->port;
+	} else {
+		host = tab->uri.host;
+		port = tab->uri.port;
+	}
+
+	if ((e = tofu_lookup(&certs, host, port)) == NULL) {
 		/* TODO: an update in libressl/libretls changed
 		 * significantly.  Find a better approach at storing
 		 * the certs! */
@@ -117,10 +126,10 @@ handle_imsg_check_cert(struct imsg *imsg, size_t datal
 		tofu_res = 1;	/* trust on first use */
 		if ((e = calloc(1, sizeof(*e))) == NULL)
 			abort();
-		strlcpy(e->domain, tab->uri.host, sizeof(e->domain));
-		if (*tab->uri.port != '\0' && strcmp(tab->uri.port, "1965")) {
+		strlcpy(e->domain, host, sizeof(e->domain));
+		if (*port != '\0' && strcmp(port, "1965")) {
 			strlcat(e->domain, ":", sizeof(e->domain));
-			strlcat(e->domain, tab->uri.port, sizeof(e->domain));
+			strlcat(e->domain, port, sizeof(e->domain));
 		}
 		strlcpy(e->hash, hash, sizeof(e->hash));
 		tofu_add(&certs, e);
@@ -170,19 +179,28 @@ handle_maybe_save_new_cert(int accept, unsigned int ta
 {
 	struct tab *tab;
 	struct tofu_entry *e;
+	const char *host, *port;
 
 	tab = tab_by_id(tabid);
 
+	if (tab->proxy != NULL) {
+		host = tab->proxy->host;
+		port = tab->proxy->port;
+	} else {
+		host = tab->uri.host;
+		port = tab->uri.port;
+	}
+
 	if (!accept)
 		goto end;
 
 	if ((e = calloc(1, sizeof(*e))) == NULL)
 		die();
 
-	strlcpy(e->domain, tab->uri.host, sizeof(e->domain));
-	if (*tab->uri.port != '\0' && strcmp(tab->uri.port, "1965")) {
+	strlcpy(e->domain, host, sizeof(e->domain));
+	if (*port != '\0' && strcmp(port, "1965")) {
 		strlcat(e->domain, ":", sizeof(e->domain));
-		strlcat(e->domain, tab->uri.port, sizeof(e->domain));
+		strlcat(e->domain, port, sizeof(e->domain));
 	}
 	strlcpy(e->hash, tab->cert, sizeof(e->hash));
 	imsg_compose(fsibuf, IMSG_UPDATE_CERT, 0, 0, -1, e, sizeof(*e));
@@ -473,23 +491,54 @@ load_about_url(struct tab *tab, const char *url)
 void
 load_gemini_url(struct tab *tab, const char *url)
 {
-	size_t		 len;
+	struct get_req	 req;
 
 	stop_tab(tab);
 	tab->id = tab_new_id();
 
-	len = sizeof(tab->hist_cur->h);
-	imsg_compose(netibuf, IMSG_GET, tab->id, 0, -1,
-	    tab->hist_cur->h, len);
+	memset(&req, 0, sizeof(req));
+	strlcpy(req.host, tab->uri.host, sizeof(req.host));
+	strlcpy(req.port, tab->uri.port, sizeof(req.host));
+
+	strlcpy(req.req, tab->hist_cur->h, sizeof(req.req));
+	strlcat(req.req, "\r\n", sizeof(req.req));
+
+	req.proto = PROTO_GEMINI;
+
+	imsg_compose(netibuf, IMSG_GET_RAW, tab->id, 0, -1,
+	    &req, sizeof(req));
 	imsg_flush(netibuf);
-	return;
 }
 
+void
+load_via_proxy(struct tab *tab, const char *url, struct proxy *p)
+{
+	struct get_req req;
+
+	stop_tab(tab);
+	tab->id = tab_new_id();
+	tab->proxy = p;
+
+	memset(&req, 0, sizeof(req));
+	strlcpy(req.host, p->host, sizeof(req.host));
+	strlcpy(req.port, p->port, sizeof(req.host));
+
+	strlcpy(req.req, tab->hist_cur->h, sizeof(req.req));
+	strlcat(req.req, "\r\n", sizeof(req.req));
+
+	req.proto = p->proto;
+
+	imsg_compose(netibuf, IMSG_GET_RAW, tab->id, 0, -1,
+	    &req, sizeof(req));
+	imsg_flush(netibuf);
+}
+
 static void
 do_load_url(struct tab *tab, const char *url)
 {
 	struct phos_uri	 uri;
 	struct proto	*p;
+	struct proxy	*proxy;
 	char		*t;
 
 	if (tab->fd != -1) {
@@ -522,6 +571,13 @@ do_load_url(struct tab *tab, const char *url)
 		}
 	}
 
+	TAILQ_FOREACH(proxy, &proxies, proxies) {
+		if (!strcmp(tab->uri.scheme, proxy->match_proto)) {
+			load_via_proxy(tab, url, proxy);
+			return;
+		}
+	}
+
 	protos[0].loadfn(tab, url);
 }
 
@@ -535,6 +591,9 @@ load_url(struct tab *tab, const char *url)
 		event_loopbreak();
 		return;
 	}
+
+	tab->proxy = NULL;
+
 	hist_push(&tab->hist, tab->hist_cur);
 	do_load_url(tab, url);
 	empty_vlist(&tab->buffer);
@@ -661,6 +720,7 @@ main(int argc, char * const *argv)
 	load_certs(&certs);
 
 	TAILQ_INIT(&tabshead);
+	TAILQ_INIT(&proxies);
 
 	event_init();
 
blob - 2f88e09f60ec774b7b5cfcf00b7bd34fc4f796b6
blob + 9c6425cb8c959dafe09935df78e7dea67a5192c4
--- telescope.h
+++ telescope.h
@@ -31,6 +31,7 @@
 enum imsg_type {
 	/* ui <-> client/fs */
 	IMSG_GET,		/* data is URL, peerid the tab id */
+	IMSG_GET_RAW,		/* get but with an explicit req str */
 	IMSG_ERR,
 	IMSG_CHECK_CERT,
 	IMSG_CERT_STATUS,
@@ -212,6 +213,7 @@ struct tab {
 
 	char			*cert;
 	enum trust_state	 trust;
+	struct proxy		*proxy;
 	struct phos_uri		 uri;
 	struct histhead		 hist;
 	struct hist		*hist_cur;
@@ -242,8 +244,31 @@ struct proto {
 	 * human-friendly URL.
 	 */
 	void		 (*loadfn)(struct tab*, const char*);
+};
+
+extern TAILQ_HEAD(proxylist, proxy) proxies;
+struct proxy {
+	char	*match_proto;
+
+	char	*host;
+	char	*port;
+	int	 proto;
+
+	TAILQ_ENTRY(proxy) proxies;
+};
+
+enum {
+	PROTO_GEMINI,
+	/* ... */
 };
 
+struct get_req {
+	int		proto;
+	char		host[254];
+	char		port[16];
+	char		req[1027];
+};
+
 struct kmap {
 	TAILQ_HEAD(map, keymap)	m;
 	void			(*unhandled_input)(void);
@@ -319,6 +344,7 @@ void		 sandbox_fs_process(void);
 /* telescope.c */
 void		 load_about_url(struct tab*, const char*);
 void		 load_gemini_url(struct tab*, const char*);
+void		 load_via_proxy(struct tab *, const char *, struct proxy *);
 void		 load_url(struct tab*, const char*);
 int		 load_previous_page(struct tab*);
 int		 load_next_page(struct tab*);