commit - ea8874f15243d7c4d6a28836a26ce3724aa1e037
commit + c9dc4bc68ef81f382a23f0ce29b0bada6dacc9c1
blob - ef100583d3b7bd999204b147ad690a6d65a212a5
blob + 5876ccc00df7b8ed5658abe9d9b655d5d7260bb0
--- fcgi.c
+++ fcgi.c
}
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
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;
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
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 *);
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, "</pre>"));
+ }
+
+ 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, "<pre>"));
+ }
+
+ /* citation block */
+ if (*line == '>') {
+ if (clt_puts(clt, "<blockquote>") == -1 ||
+ htmlescape(clt, line + 1) == -1 ||
+ clt_puts(clt, "</blockquote>") == -1)
+ return (-1);
+ return (0);
+ }
+
+ /* headings */
+ if (!strncmp(line, "###", 3)) {
+ if (clt_puts(clt, "<h3>") == -1 ||
+ htmlescape(clt, line + 3) == -1 ||
+ clt_puts(clt, "</h3>") == -1)
+ return (-1);
+ return (0);
+ }
+ if (!strncmp(line, "##", 2)) {
+ if (clt_puts(clt, "<h2>") == -1 ||
+ htmlescape(clt, line + 2) == -1 ||
+ clt_puts(clt, "</h2>") == -1)
+ return (-1);
+ return (0);
}
+ if (!strncmp(line, "#", 1)) {
+ if (clt_puts(clt, "<h1>") == -1 ||
+ htmlescape(clt, line + 1) == -1 ||
+ clt_puts(clt, "</h1>") == -1)
+ return (-1);
+ return (0);
+ }
+ /* bullet -- XXX: group */
+ if (!strncmp(line, "* ", 2)) {
+ if (clt_puts(clt, "<ul><li>") == -1 ||
+ htmlescape(clt, line + 2) == -1 ||
+ clt_puts(clt, "</li></ul>") == -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, "<p><a href='") == -1 ||
+ printurl(clt, line) == -1 ||
+ clt_puts(clt, "'>") == -1 ||
+ htmlescape(clt, label) == -1 ||
+ clt_puts(clt, "</a></p>") == -1)
+ return (-1);
+ return (0);
+ }
+
+ /* paragraph */
+ if (clt_puts(clt, "<p>") == -1 ||
+ htmlescape(clt, line) == -1 ||
+ clt_puts(clt, "</p>") == -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)
{
{
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;
}
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, "<!doctype html>"
+ "<html><head></head><body>") == -1)
+ return;
+ }
+
goto copy;
}
return;
if (clt_printf(clt, "Proxy error\n") == -1)
return;
+ } else if (status == 0) {
+ if (clt_puts(clt, "</body></html>") == -1)
+ return;
}
fcgi_end_request(clt, status);