commit c9dc4bc68ef81f382a23f0ce29b0bada6dacc9c1 from: Omar Polo date: Tue Sep 13 21:29:14 2022 UTC scaffold the gemtext -> html conversion commit - ea8874f15243d7c4d6a28836a26ce3724aa1e037 commit + c9dc4bc68ef81f382a23f0ce29b0bada6dacc9c1 blob - ef100583d3b7bd999204b147ad690a6d65a212a5 blob + 5876ccc00df7b8ed5658abe9d9b655d5d7260bb0 --- fcgi.c +++ fcgi.c @@ -590,6 +590,18 @@ clt_write(struct client *clt, const uint8_t *buf, size } return (0); +} + +int +clt_putc(struct client *clt, char ch) +{ + return (clt_write(clt, &ch, 1)); +} + +int +clt_puts(struct client *clt, const char *str) +{ + return (clt_write(clt, str, strlen(str))); } int blob - fc08077639750af2413c0d69504d7e94fe50fb01 blob + 7507ff23526f5708d749d5b7341b5e50181f38ab --- galileo.h +++ galileo.h @@ -65,6 +65,8 @@ struct client { struct tls *clt_ctx; struct bufferevent *clt_bev; int clt_headersdone; + int clt_translate; + int clt_inpre; char clt_buf[1024]; size_t clt_buflen; @@ -140,6 +142,8 @@ void fcgi_accept(int, short, void *); void fcgi_read(struct bufferevent *, void *); void fcgi_write(struct bufferevent *, void *); void fcgi_error(struct bufferevent *, short error, void *); +int clt_putc(struct client *, char); +int clt_puts(struct client *, const char *); int clt_write_bufferevent(struct client *, struct bufferevent *); int clt_flush(struct client *); int clt_write(struct client *, const uint8_t *, size_t); blob - 52a332a32bb1b4a8aee7973e0430318390c3945e blob + c4d84f709240345cf2bdbc495effb636c7eaa297 --- proxy.c +++ proxy.c @@ -53,6 +53,7 @@ void proxy_init(struct privsep *, struct privsep_proc int proxy_launch(struct galileo *); void proxy_inflight_dec(const char *); int proxy_dispatch_parent(int, struct privsep_proc *, struct imsg *); +void proxy_translate_gemtext(struct client *); void proxy_resolved(struct asr_result *, void *); void proxy_connect(int, short, void *); void proxy_read(struct bufferevent *, void *); @@ -146,11 +147,174 @@ proxy_dispatch_parent(int fd, struct privsep_proc *p, default: log_warnx("unknown message %d", imsg->hdr.type); return (-1); + } + + return (0); +} + +static inline int +printurl(struct client *clt, const char *str) +{ + for (; *str; ++str) { + switch (*str) { + case ' ': + case '\t': + case '\'': + case '\\': + if (clt_printf(clt, "%2X", (unsigned char)*str) == -1) + return (-1); + break; + default: + if (clt_putc(clt, *str) == -1) + return (-1); + break; + } + } + + return (0); +} + +static inline int +htmlescape(struct client *clt, const char *str) +{ + int r; + + for (; *str; ++str) { + switch (*str) { + case '<': + r = clt_puts(clt, "<"); + break; + case '>': + r = clt_puts(clt, ">"); + break; + case '&': + r = clt_puts(clt, "&"); + break; + default: + r = clt_putc(clt, *str); + break; + } + + if (r == -1) + return (-1); + } + + return (0); +} + +static int +gemtext_translate_line(struct client *clt, char *line) +{ + /* preformatted line / closing */ + if (clt->clt_inpre) { + if (!strncmp(line, "```", 3)) { + clt->clt_inpre = 0; + return (clt_puts(clt, "")); + } + + if (htmlescape(clt, line) == -1) + return (-1); + return (clt_putc(clt, '\n')); + } + + /* pre opening */ + if (!strncmp(line, "```", 3)) { + clt->clt_inpre = 1; + return (clt_puts(clt, "
"));
+	}
+
+	/* citation block */
+	if (*line == '>') {
+		if (clt_puts(clt, "
") == -1 || + htmlescape(clt, line + 1) == -1 || + clt_puts(clt, "
") == -1) + return (-1); + return (0); + } + + /* headings */ + if (!strncmp(line, "###", 3)) { + if (clt_puts(clt, "

") == -1 || + htmlescape(clt, line + 3) == -1 || + clt_puts(clt, "

") == -1) + return (-1); + return (0); + } + if (!strncmp(line, "##", 2)) { + if (clt_puts(clt, "

") == -1 || + htmlescape(clt, line + 2) == -1 || + clt_puts(clt, "

") == -1) + return (-1); + return (0); } + if (!strncmp(line, "#", 1)) { + if (clt_puts(clt, "

") == -1 || + htmlescape(clt, line + 1) == -1 || + clt_puts(clt, "

") == -1) + return (-1); + return (0); + } + /* bullet -- XXX: group */ + if (!strncmp(line, "* ", 2)) { + if (clt_puts(clt, "") == -1) + return (-1); + return (0); + } + + /* link -- XXX: group */ + if (!strncmp(line, "=>", 2)) { + char *label; + + line += 2; + line += strspn(line, " \t"); + + label = line + strcspn(line, " \t"); + if (*label == '\0') + label = line; + else + *label++ = '\0'; + + if (clt_puts(clt, "

") == -1 || + htmlescape(clt, label) == -1 || + clt_puts(clt, "

") == -1) + return (-1); + return (0); + } + + /* paragraph */ + if (clt_puts(clt, "

") == -1 || + htmlescape(clt, line) == -1 || + clt_puts(clt, "

") == -1) + return (-1); return (0); } +void +proxy_translate_gemtext(struct client *clt) +{ + struct bufferevent *bev = clt->clt_bev; + struct evbuffer *src = EVBUFFER_INPUT(bev); + char *line; + size_t len; + int r; + + for (;;) { + line = evbuffer_readln(src, &len, EVBUFFER_EOL_ANY); + if (line == NULL) + return; + + r = gemtext_translate_line(clt, line); + free(line); + if (r == -1) + return; + } +} + static struct proxy_config * proxy_server_match(struct galileo *env, struct client *clt) { @@ -346,13 +510,17 @@ proxy_read(struct bufferevent *bev, void *d) { struct client *clt = d; struct evbuffer *src = EVBUFFER_INPUT(bev); + const char *ctype; char *hdr; size_t len; int code; if (clt->clt_headersdone) { copy: - clt_write_bufferevent(clt, bev); + if (clt->clt_translate) + proxy_translate_gemtext(clt); + else + clt_write_bufferevent(clt, bev); return; } @@ -381,12 +549,25 @@ proxy_read(struct bufferevent *bev, void *d) return; } - if (clt_printf(clt, "Content-Type: %s\r\n", &hdr[4]) == -1) + if (!strncmp(&hdr[3], "text/gemini", 11)) { + ctype = "text/html; charset=utf8"; + clt->clt_translate = 1; + } else + ctype = &hdr[3]; + + if (clt_printf(clt, "Content-Type: %s\r\n", ctype) == -1) return; if (clt_printf(clt, "\r\n") == -1) return; clt->clt_headersdone = 1; + + if (clt->clt_translate) { + if (clt_puts(clt, "" + "") == -1) + return; + } + goto copy; } @@ -412,6 +593,9 @@ proxy_error(struct bufferevent *bev, short err, void * return; if (clt_printf(clt, "Proxy error\n") == -1) return; + } else if (status == 0) { + if (clt_puts(clt, "") == -1) + return; } fcgi_end_request(clt, status);