it* gopherproxy-c customized Err codevoid.de 70 i Err codevoid.de 70 hgit clone git://git.codevoid.de/gopherproxy-c-sdk URL:git://git.codevoid.de/gopherproxy-c-sdk codevoid.de 70 1Log /git/gopherproxy-c-sdk/log.gph codevoid.de 70 1Files /git/gopherproxy-c-sdk/files.gph codevoid.de 70 1Refs /git/gopherproxy-c-sdk/refs.gph codevoid.de 70 1README /git/gopherproxy-c-sdk/file/README.gph codevoid.de 70 1LICENSE /git/gopherproxy-c-sdk/file/LICENSE.gph codevoid.de 70 i--- Err codevoid.de 70 1commit 40a6ccd6cfb99c2849dff4501a54bc7752b63620 /git/gopherproxy-c-sdk/commit/40a6ccd6cfb99c2849dff4501a54bc7752b63620.gph codevoid.de 70 hAuthor: Hiltjo Posthuma URL:mailto:hiltjo@codemadness.org codevoid.de 70 iDate: Sun, 12 Aug 2018 18:14:09 +0200 Err codevoid.de 70 i Err codevoid.de 70 iinitial repo Err codevoid.de 70 i Err codevoid.de 70 iDiffstat: Err codevoid.de 70 i A .gitignore | 2 ++ Err codevoid.de 70 i A LICENSE | 15 +++++++++++++++ Err codevoid.de 70 i A Makefile | 17 +++++++++++++++++ Err codevoid.de 70 i A README | 17 +++++++++++++++++ Err codevoid.de 70 i A gopherproxy.c | 586 ++++++++++++++++++++++++++++++ Err codevoid.de 70 i Err codevoid.de 70 i5 files changed, 637 insertions(+), 0 deletions(-) Err codevoid.de 70 i--- Err codevoid.de 70 1diff --git a/.gitignore b/.gitignore /git/gopherproxy-c-sdk/file/.gitignore.gph codevoid.de 70 it@@ -0,0 +1,2 @@ Err codevoid.de 70 i+gopherproxy Err codevoid.de 70 i+*.o Err codevoid.de 70 1diff --git a/LICENSE b/LICENSE /git/gopherproxy-c-sdk/file/LICENSE.gph codevoid.de 70 it@@ -0,0 +1,15 @@ Err codevoid.de 70 i+ISC License Err codevoid.de 70 i+ Err codevoid.de 70 i+Copyright (c) 2018 Hiltjo Posthuma Err codevoid.de 70 i+ Err codevoid.de 70 i+Permission to use, copy, modify, and/or distribute this software for any Err codevoid.de 70 i+purpose with or without fee is hereby granted, provided that the above Err codevoid.de 70 i+copyright notice and this permission notice appear in all copies. Err codevoid.de 70 i+ Err codevoid.de 70 i+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES Err codevoid.de 70 i+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF Err codevoid.de 70 i+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR Err codevoid.de 70 i+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES Err codevoid.de 70 i+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN Err codevoid.de 70 i+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF Err codevoid.de 70 i+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Err codevoid.de 70 1diff --git a/Makefile b/Makefile /git/gopherproxy-c-sdk/file/Makefile.gph codevoid.de 70 it@@ -0,0 +1,17 @@ Err codevoid.de 70 i+.POSIX: Err codevoid.de 70 i+ Err codevoid.de 70 i+BIN = gopherproxy Err codevoid.de 70 i+OBJ = $(BIN:=.o) Err codevoid.de 70 i+ Err codevoid.de 70 i+# OpenBSD: use pledge(2). Err codevoid.de 70 i+#CFLAGS += -DUSE_PLEDGE Err codevoid.de 70 i+# build static: useful in www chroot. Err codevoid.de 70 i+#LDFLAGS += -static Err codevoid.de 70 i+ Err codevoid.de 70 i+all: $(BIN) Err codevoid.de 70 i+ Err codevoid.de 70 i+$(BIN): $(OBJ) Err codevoid.de 70 i+ $(CC) $(OBJ) $(LDFLAGS) -o $@ Err codevoid.de 70 i+ Err codevoid.de 70 i+clean: Err codevoid.de 70 i+ rm -f $(BIN) $(OBJ) Err codevoid.de 70 1diff --git a/README b/README /git/gopherproxy-c-sdk/file/README.gph codevoid.de 70 it@@ -0,0 +1,17 @@ Err codevoid.de 70 i+gopherproxy Err codevoid.de 70 i+=========== Err codevoid.de 70 i+ Err codevoid.de 70 i+Build dependencies: Err codevoid.de 70 i+- C compiler. Err codevoid.de 70 i+- libc Err codevoid.de 70 i+- POSIX system. Err codevoid.de 70 i+- make (optional). Err codevoid.de 70 i+ Err codevoid.de 70 i+ Err codevoid.de 70 i+Features: Err codevoid.de 70 i+- Works in older browsers such as links, lynx, w3m, dillo, etc. Err codevoid.de 70 i+- No Javascript or CSS required. Err codevoid.de 70 i+ Err codevoid.de 70 i+ Err codevoid.de 70 i+Cons: Err codevoid.de 70 i+- Not all gopher types are supported. Err codevoid.de 70 1diff --git a/gopherproxy.c b/gopherproxy.c /git/gopherproxy-c-sdk/file/gopherproxy.c.gph codevoid.de 70 it@@ -0,0 +1,586 @@ Err codevoid.de 70 i+#include Err codevoid.de 70 i+#include Err codevoid.de 70 i+ Err codevoid.de 70 i+#include Err codevoid.de 70 i+#include Err codevoid.de 70 i+#include Err codevoid.de 70 i+#include Err codevoid.de 70 i+#include Err codevoid.de 70 i+#include Err codevoid.de 70 i+#include Err codevoid.de 70 i+#include Err codevoid.de 70 i+#include Err codevoid.de 70 i+ Err codevoid.de 70 i+#define MAX_RESPONSETIMEOUT 10 /* timeout in seconds */ Err codevoid.de 70 i+#define MAX_RESPONSESIZ 4000000 /* max download size in bytes */ Err codevoid.de 70 i+ Err codevoid.de 70 i+#ifndef USE_PLEDGE Err codevoid.de 70 i+#define pledge(a,b) 0 Err codevoid.de 70 i+#endif Err codevoid.de 70 i+ Err codevoid.de 70 i+struct uri { Err codevoid.de 70 i+ char proto[16]; Err codevoid.de 70 i+ char host[256]; Err codevoid.de 70 i+ char port[8]; Err codevoid.de 70 i+ char path[1024]; Err codevoid.de 70 i+}; Err codevoid.de 70 i+ Err codevoid.de 70 i+struct visited { Err codevoid.de 70 i+ int _type; Err codevoid.de 70 i+ char username[1024]; Err codevoid.de 70 i+ char path[1024]; Err codevoid.de 70 i+ char server[256]; Err codevoid.de 70 i+ char port[8]; Err codevoid.de 70 i+}; Err codevoid.de 70 i+ Err codevoid.de 70 i+int headerset = 0; Err codevoid.de 70 i+ Err codevoid.de 70 i+void Err codevoid.de 70 i+die(int code, const char *fmt, ...) Err codevoid.de 70 i+{ Err codevoid.de 70 i+ va_list ap; Err codevoid.de 70 i+ Err codevoid.de 70 i+ if (!headerset) { Err codevoid.de 70 i+ switch (code) { Err codevoid.de 70 i+ case 400: Err codevoid.de 70 i+ fputs("Status: 400 Bad Request\r\n", stdout); Err codevoid.de 70 i+ break; Err codevoid.de 70 i+ case 403: Err codevoid.de 70 i+ fputs("Status: 403 Permission Denied\r\n", stdout); Err codevoid.de 70 i+ break; Err codevoid.de 70 i+ default: Err codevoid.de 70 i+ fputs("Status: 500 Internal Server Error\r\n", stdout); Err codevoid.de 70 i+ break; Err codevoid.de 70 i+ } Err codevoid.de 70 i+ fputs("Content-Type: text/plain; charset=utf-8\r\n\r\n", stdout); Err codevoid.de 70 i+ } Err codevoid.de 70 i+ Err codevoid.de 70 i+ va_start(ap, fmt); Err codevoid.de 70 i+ vfprintf(stderr, fmt, ap); Err codevoid.de 70 i+ va_end(ap); Err codevoid.de 70 i+ Err codevoid.de 70 i+ va_start(ap, fmt); Err codevoid.de 70 i+ vfprintf(stdout, fmt, ap); Err codevoid.de 70 i+ va_end(ap); Err codevoid.de 70 i+ Err codevoid.de 70 i+ exit(1); Err codevoid.de 70 i+} Err codevoid.de 70 i+ Err codevoid.de 70 i+/* Escape characters below as HTML 2.0 / XML 1.0. */ Err codevoid.de 70 i+void Err codevoid.de 70 i+xmlencode(const char *s) Err codevoid.de 70 i+{ Err codevoid.de 70 i+ for (; *s; s++) { Err codevoid.de 70 i+ switch(*s) { Err codevoid.de 70 i+ case '<': fputs("<", stdout); break; Err codevoid.de 70 i+ case '>': fputs(">", stdout); break; Err codevoid.de 70 i+ case '\'': fputs("'", stdout); break; Err codevoid.de 70 i+ case '&': fputs("&", stdout); break; Err codevoid.de 70 i+ case '"': fputs(""", stdout); break; Err codevoid.de 70 i+ default: putchar(*s); Err codevoid.de 70 i+ } Err codevoid.de 70 i+ } Err codevoid.de 70 i+} Err codevoid.de 70 i+ Err codevoid.de 70 i+int Err codevoid.de 70 i+dial(const char *host, const char *port) Err codevoid.de 70 i+{ Err codevoid.de 70 i+ struct addrinfo hints, *res, *res0; Err codevoid.de 70 i+ int error, save_errno, s; Err codevoid.de 70 i+ const char *cause = NULL; Err codevoid.de 70 i+ struct timeval timeout = { Err codevoid.de 70 i+ .tv_sec = MAX_RESPONSETIMEOUT, Err codevoid.de 70 i+ .tv_usec = 0, Err codevoid.de 70 i+ }; Err codevoid.de 70 i+ Err codevoid.de 70 i+ memset(&hints, 0, sizeof(hints)); Err codevoid.de 70 i+ hints.ai_family = AF_UNSPEC; Err codevoid.de 70 i+ hints.ai_socktype = SOCK_STREAM; Err codevoid.de 70 i+ hints.ai_flags = AI_NUMERICSERV; /* numeric port only */ Err codevoid.de 70 i+ if ((error = getaddrinfo(host, port, &hints, &res0))) Err codevoid.de 70 i+ die(500, "%s: %s: %s:%s", __func__, gai_strerror(error), host, port); Err codevoid.de 70 i+ s = -1; Err codevoid.de 70 i+ for (res = res0; res; res = res->ai_next) { Err codevoid.de 70 i+ s = socket(res->ai_family, res->ai_socktype, Err codevoid.de 70 i+ res->ai_protocol); Err codevoid.de 70 i+ if (s == -1) { Err codevoid.de 70 i+ cause = "socket"; Err codevoid.de 70 i+ continue; Err codevoid.de 70 i+ } Err codevoid.de 70 i+ Err codevoid.de 70 i+ if (setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) == -1) Err codevoid.de 70 i+ die(500, "%s: setsockopt: %s\n", __func__, strerror(errno)); Err codevoid.de 70 i+ if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) Err codevoid.de 70 i+ die(500, "%s: setsockopt: %s\n", __func__, strerror(errno)); Err codevoid.de 70 i+ Err codevoid.de 70 i+ if (connect(s, res->ai_addr, res->ai_addrlen) == -1) { Err codevoid.de 70 i+ cause = "connect"; Err codevoid.de 70 i+ save_errno = errno; Err codevoid.de 70 i+ close(s); Err codevoid.de 70 i+ errno = save_errno; Err codevoid.de 70 i+ s = -1; Err codevoid.de 70 i+ continue; Err codevoid.de 70 i+ } Err codevoid.de 70 i+ break; Err codevoid.de 70 i+ } Err codevoid.de 70 i+ if (s == -1) Err codevoid.de 70 i+ die(500, "%s: %s: %s:%s\n", __func__, cause, host, port); Err codevoid.de 70 i+ freeaddrinfo(res0); Err codevoid.de 70 i+ Err codevoid.de 70 i+ return s; Err codevoid.de 70 i+} Err codevoid.de 70 i+ Err codevoid.de 70 i+int Err codevoid.de 70 i+isblacklisted(const char *host, const char *port, const char *path) Err codevoid.de 70 i+{ Err codevoid.de 70 i+ char *p; Err codevoid.de 70 i+ Err codevoid.de 70 i+ if ((p = strstr(host, ".onion")) && strlen(p) == strlen(".onion")) Err codevoid.de 70 i+ return 1; Err codevoid.de 70 i+ return 0; Err codevoid.de 70 i+} Err codevoid.de 70 i+ Err codevoid.de 70 i+char * Err codevoid.de 70 i+typestr(int c) Err codevoid.de 70 i+{ Err codevoid.de 70 i+ switch (c) { Err codevoid.de 70 i+ case '0': return " TEXT"; Err codevoid.de 70 i+ case '1': return " DIR"; Err codevoid.de 70 i+ case '7': return "SEARCH"; Err codevoid.de 70 i+ case '9': return " BIN"; Err codevoid.de 70 i+ case 'g': return " GIF"; Err codevoid.de 70 i+ case 'h': return " HTML"; /* non-standard */ Err codevoid.de 70 i+ case 's': return " SND"; /* non-standard */ Err codevoid.de 70 i+ case 'A': return " AUDIO"; /* non-standard */ Err codevoid.de 70 i+ case 'I': return " IMG"; Err codevoid.de 70 i+ default: return " "; Err codevoid.de 70 i+ } Err codevoid.de 70 i+} Err codevoid.de 70 i+ Err codevoid.de 70 i+void Err codevoid.de 70 i+servefile(const char *server, const char *port, const char *path) Err codevoid.de 70 i+{ Err codevoid.de 70 i+ char buf[1024]; Err codevoid.de 70 i+ int r, w, fd; Err codevoid.de 70 i+ size_t totalsiz = 0; Err codevoid.de 70 i+ Err codevoid.de 70 i+ fd = dial(server, port); Err codevoid.de 70 i+ Err codevoid.de 70 i+ if (pledge("stdio", NULL) == -1) Err codevoid.de 70 i+ die(500, "pledge: %s\n", strerror(errno)); Err codevoid.de 70 i+ Err codevoid.de 70 i+ w = dprintf(fd, "%s\r\n", path); Err codevoid.de 70 i+ if (w == -1) Err codevoid.de 70 i+ die(500, "dprintf: %s\n", strerror(errno)); Err codevoid.de 70 i+ Err codevoid.de 70 i+ while ((r = read(fd, buf, sizeof(buf))) > 0) { Err codevoid.de 70 i+ /* too big total response */ Err codevoid.de 70 i+ totalsiz += r; Err codevoid.de 70 i+ if (totalsiz > MAX_RESPONSESIZ) { Err codevoid.de 70 i+ dprintf(1, "--- transfer too big, truncated ---\n"); Err codevoid.de 70 i+ break; Err codevoid.de 70 i+ } Err codevoid.de 70 i+ Err codevoid.de 70 i+ if ((w = write(1, buf, r)) == -1) Err codevoid.de 70 i+ die(500, "write: %s\n", strerror(errno)); Err codevoid.de 70 i+ } Err codevoid.de 70 i+ if (r == -1) Err codevoid.de 70 i+ die(500, "read: %s\n", strerror(errno)); Err codevoid.de 70 i+ close(fd); Err codevoid.de 70 i+} Err codevoid.de 70 i+ Err codevoid.de 70 i+void Err codevoid.de 70 i+servedir(const char *server, const char *port, const char *path, const char *param) Err codevoid.de 70 i+{ Err codevoid.de 70 i+ struct visited v; Err codevoid.de 70 i+ FILE *fp; Err codevoid.de 70 i+ char line[1024], uri[1024]; Err codevoid.de 70 i+ size_t totalsiz, linenr; Err codevoid.de 70 i+ ssize_t n; Err codevoid.de 70 i+ int fd, r, i, len; Err codevoid.de 70 i+ Err codevoid.de 70 i+ fd = dial(server, port); Err codevoid.de 70 i+ Err codevoid.de 70 i+ if (pledge("stdio", NULL) == -1) Err codevoid.de 70 i+ die(500, "pledge: %s\n", strerror(errno)); Err codevoid.de 70 i+ Err codevoid.de 70 i+ if (param[0]) Err codevoid.de 70 i+ r = dprintf(fd, "%s\t%s\r\n", path, param); Err codevoid.de 70 i+ else Err codevoid.de 70 i+ r = dprintf(fd, "%s\r\n", path); Err codevoid.de 70 i+ if (r == -1) Err codevoid.de 70 i+ die(500, "write: %s\n", strerror(errno)); Err codevoid.de 70 i+ Err codevoid.de 70 i+ if (!(fp = fdopen(fd, "rb+"))) Err codevoid.de 70 i+ die(500, "fdopen: %s\n", strerror(errno)); Err codevoid.de 70 i+ Err codevoid.de 70 i+ totalsiz = 0; Err codevoid.de 70 i+ for (linenr = 1; fgets(line, sizeof(line), fp); linenr++) { Err codevoid.de 70 i+ n = strcspn(line, "\n"); Err codevoid.de 70 i+ if (line[n] != '\n') Err codevoid.de 70 i+ die(500, "%s:%s %s:%d: line too long\n", Err codevoid.de 70 i+ server, port, path, linenr); Err codevoid.de 70 i+ if (n && line[n] == '\n') Err codevoid.de 70 i+ line[n] = '\0'; Err codevoid.de 70 i+ if (n && line[n - 1] == '\r') Err codevoid.de 70 i+ line[--n] = '\0'; Err codevoid.de 70 i+ if (n == 1 && line[0] == '.') Err codevoid.de 70 i+ break; Err codevoid.de 70 i+ Err codevoid.de 70 i+ /* too big total response */ Err codevoid.de 70 i+ totalsiz += n; Err codevoid.de 70 i+ if (totalsiz > MAX_RESPONSESIZ) { Err codevoid.de 70 i+ dprintf(1, "--- transfer too big, truncated ---\n"); Err codevoid.de 70 i+ break; Err codevoid.de 70 i+ } Err codevoid.de 70 i+ Err codevoid.de 70 i+ memset(&v, 0, sizeof(v)); Err codevoid.de 70 i+ Err codevoid.de 70 i+ v._type = line[0]; Err codevoid.de 70 i+ Err codevoid.de 70 i+ /* "username" */ Err codevoid.de 70 i+ i = 1; Err codevoid.de 70 i+ len = strcspn(line + i, "\t"); Err codevoid.de 70 i+ if (len + 1 < sizeof(v.username)) { Err codevoid.de 70 i+ memcpy(v.username, line + i, len); Err codevoid.de 70 i+ v.username[len] = '\0'; Err codevoid.de 70 i+ } else { Err codevoid.de 70 i+ die(500, "%s:%s %s:%d: username field too long\n", Err codevoid.de 70 i+ server, port, path, linenr); Err codevoid.de 70 i+ } Err codevoid.de 70 i+ if (line[i + len] == '\t') Err codevoid.de 70 i+ i += len + 1; Err codevoid.de 70 i+ else Err codevoid.de 70 i+ die(500, "%s:%s %s:%d: invalid line / field count\n", Err codevoid.de 70 i+ server, port, path, linenr); Err codevoid.de 70 i+ Err codevoid.de 70 i+ /* selector / path */ Err codevoid.de 70 i+ len = strcspn(line + i, "\t"); Err codevoid.de 70 i+ if (len + 1 < sizeof(v.path)) { Err codevoid.de 70 i+ memcpy(v.path, line + i, len); Err codevoid.de 70 i+ v.path[len] = '\0'; Err codevoid.de 70 i+ } else { Err codevoid.de 70 i+ die(500, "%s:%s %s:%d: path field too long\n", Err codevoid.de 70 i+ server, port, path, linenr); Err codevoid.de 70 i+ } Err codevoid.de 70 i+ if (line[i + len] == '\t') Err codevoid.de 70 i+ i += len + 1; Err codevoid.de 70 i+ else Err codevoid.de 70 i+ die(500, "%s:%s %s:%d: invalid line / field count\n", Err codevoid.de 70 i+ server, port, path, linenr); Err codevoid.de 70 i+ Err codevoid.de 70 i+ /* server */ Err codevoid.de 70 i+ len = strcspn(line + i, "\t"); Err codevoid.de 70 i+ if (len + 1 < sizeof(v.server)) { Err codevoid.de 70 i+ memcpy(v.server, line + i, len); Err codevoid.de 70 i+ v.server[len] = '\0'; Err codevoid.de 70 i+ } else { Err codevoid.de 70 i+ die(500, "%s:%s %s:%d: server field too long\n", Err codevoid.de 70 i+ server, port, path, linenr); Err codevoid.de 70 i+ } Err codevoid.de 70 i+ if (line[i + len] == '\t') Err codevoid.de 70 i+ i += len + 1; Err codevoid.de 70 i+ else Err codevoid.de 70 i+ die(500, "%s:%s %s:%d: invalid line / field count\n", Err codevoid.de 70 i+ server, port, path, linenr); Err codevoid.de 70 i+ Err codevoid.de 70 i+ /* port */ Err codevoid.de 70 i+ len = strcspn(line + i, "\t"); Err codevoid.de 70 i+ if (len + 1 < sizeof(v.port)) { Err codevoid.de 70 i+ memcpy(v.port, line + i, len); Err codevoid.de 70 i+ v.port[len] = '\0'; Err codevoid.de 70 i+ } else { Err codevoid.de 70 i+ die(500, "%s:%s %s:%d: port field too long\n", Err codevoid.de 70 i+ server, port, path, linenr); Err codevoid.de 70 i+ } Err codevoid.de 70 i+ Err codevoid.de 70 i+ uri[0] = '\0'; Err codevoid.de 70 i+ switch (line[0]) { Err codevoid.de 70 i+ case '7': Err codevoid.de 70 i+ snprintf(uri, sizeof(uri), "gopher://%s:%s/%c%s", Err codevoid.de 70 i+ v.server, v.port, v._type, v.path); Err codevoid.de 70 i+ break; Err codevoid.de 70 i+ case 'h': Err codevoid.de 70 i+ if (!strncmp(v.path, "URL:", sizeof("URL:") - 1)) Err codevoid.de 70 i+ snprintf(uri, sizeof(uri), "%s", v.path + sizeof("URL:") - 1); Err codevoid.de 70 i+ else Err codevoid.de 70 i+ snprintf(uri, sizeof(uri), "gopher://%s:%s/%c%s", Err codevoid.de 70 i+ v.server, v.port, v._type, v.path); Err codevoid.de 70 i+ break; Err codevoid.de 70 i+ case 'i': /* info */ Err codevoid.de 70 i+ case '3': /* error */ Err codevoid.de 70 i+ break; Err codevoid.de 70 i+ default: Err codevoid.de 70 i+ snprintf(uri, sizeof(uri), "?q=gopher://%s:%s/%c%s", Err codevoid.de 70 i+ v.server, v.port, v._type, v.path); Err codevoid.de 70 i+ } Err codevoid.de 70 i+ Err codevoid.de 70 i+ /* search */ Err codevoid.de 70 i+ if (v._type == '7') { Err codevoid.de 70 i+ fputs("
", stdout);	Err	codevoid.de	70
i+                        fputs(typestr(v._type), stdout);	Err	codevoid.de	70
i+                        fputs(" "	Err	codevoid.de	70
i+                                "
", stdout);	Err	codevoid.de	70
i+                } else {	Err	codevoid.de	70
i+                        fputs(typestr(v._type), stdout);	Err	codevoid.de	70
i+                        if (uri[0]) {	Err	codevoid.de	70
i+                                fputs(" ", stdout);	Err	codevoid.de	70
i+                                xmlencode(v.username);	Err	codevoid.de	70
i+                                fputs("", stdout);	Err	codevoid.de	70
i+                        } else {	Err	codevoid.de	70
i+                                fputs(" ", stdout);	Err	codevoid.de	70
i+                                xmlencode(v.username);	Err	codevoid.de	70
i+                        }	Err	codevoid.de	70
i+                }	Err	codevoid.de	70
i+                putchar('\n');	Err	codevoid.de	70
i+        }	Err	codevoid.de	70
i+        if (ferror(fp))	Err	codevoid.de	70
i+                die(500, "fgets: %s\n", strerror(errno));	Err	codevoid.de	70
i+        fclose(fp);	Err	codevoid.de	70
i+}	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+int	Err	codevoid.de	70
i+hexdigit(int c)	Err	codevoid.de	70
i+{	Err	codevoid.de	70
i+        if (c >= '0' && c <= '9')	Err	codevoid.de	70
i+                return c - '0';	Err	codevoid.de	70
i+        else if (c >= 'A' && c <= 'F')	Err	codevoid.de	70
i+                return c - 'A' + 10;	Err	codevoid.de	70
i+        else if (c >= 'a' && c <= 'f')	Err	codevoid.de	70
i+                return c - 'a' + 10;	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        return 0;	Err	codevoid.de	70
i+}	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+/* decode until NUL separator or end of "key". */	Err	codevoid.de	70
i+int	Err	codevoid.de	70
i+decodeparam(char *buf, size_t bufsiz, const char *s)	Err	codevoid.de	70
i+{	Err	codevoid.de	70
i+        size_t i;	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        if (!bufsiz)	Err	codevoid.de	70
i+                return -1;	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        for (i = 0; *s && *s != '&'; s++) {	Err	codevoid.de	70
i+                if (i + 3 >= bufsiz)	Err	codevoid.de	70
i+                        return -1;	Err	codevoid.de	70
i+                switch (*s) {	Err	codevoid.de	70
i+                case '%':	Err	codevoid.de	70
i+                        if (!isxdigit(*(s+1)) || !isxdigit(*(s+2)))	Err	codevoid.de	70
i+                                return -1;	Err	codevoid.de	70
i+                        buf[i++] = hexdigit(*(s+1)) * 16 + hexdigit(*(s+2));	Err	codevoid.de	70
i+                        s += 2;	Err	codevoid.de	70
i+                        break;	Err	codevoid.de	70
i+                case '+':	Err	codevoid.de	70
i+                        buf[i++] = ' ';	Err	codevoid.de	70
i+                        break;	Err	codevoid.de	70
i+                default:	Err	codevoid.de	70
i+                        buf[i++] = *s;	Err	codevoid.de	70
i+                        break;	Err	codevoid.de	70
i+                }	Err	codevoid.de	70
i+        }	Err	codevoid.de	70
i+        buf[i] = '\0';	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        return i;	Err	codevoid.de	70
i+}	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+char *	Err	codevoid.de	70
i+getparam(const char *query, const char *s)	Err	codevoid.de	70
i+{	Err	codevoid.de	70
i+        const char *p;	Err	codevoid.de	70
i+        size_t len;	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        len = strlen(s);	Err	codevoid.de	70
i+        for (p = query; (p = strstr(p, s)); p += len) {	Err	codevoid.de	70
i+                if (p[len] == '=' && (p == query || p[-1] == '&'))	Err	codevoid.de	70
i+                        return (char *)p + len + 1;	Err	codevoid.de	70
i+        }	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        return NULL;	Err	codevoid.de	70
i+}	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+int	Err	codevoid.de	70
i+checkparam(const char *s)	Err	codevoid.de	70
i+{	Err	codevoid.de	70
i+        for (; *s; s++)	Err	codevoid.de	70
i+                if (iscntrl(*s))	Err	codevoid.de	70
i+                        return 0;	Err	codevoid.de	70
i+        return 1;	Err	codevoid.de	70
i+}	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+int	Err	codevoid.de	70
i+parseuri(const char *str, struct uri *u)	Err	codevoid.de	70
i+{	Err	codevoid.de	70
i+        const char *s, *e;	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        memset(u, 0, sizeof(struct uri));	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        /* protocol part */	Err	codevoid.de	70
i+        for (e = s = str; *e && (isalpha((int)*e) || isdigit((int)*e) ||	Err	codevoid.de	70
i+             *e == '+' || *e == '-' || *e == '.'); e++)	Err	codevoid.de	70
i+                ;	Err	codevoid.de	70
i+        if (strncmp(e, "://", sizeof("://") - 1))	Err	codevoid.de	70
i+                return 0;	Err	codevoid.de	70
i+        if (e - s + 1 >= sizeof(u->proto))	Err	codevoid.de	70
i+                return 0;	Err	codevoid.de	70
i+        memcpy(u->proto, s, e - s);	Err	codevoid.de	70
i+        u->proto[e - s] = '\0';	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        e += sizeof("://") - 1;	Err	codevoid.de	70
i+        s = e;	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        e = &e[strcspn(s, ":/")];	Err	codevoid.de	70
i+        if (e - s + 1 >= sizeof(u->host))	Err	codevoid.de	70
i+                return 0;	Err	codevoid.de	70
i+        memcpy(u->host, s, e - s);	Err	codevoid.de	70
i+        u->host[e - s] = '\0';	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        if (*e == ':') {	Err	codevoid.de	70
i+                s = ++e;	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+                e = &s[strcspn(s, "/")];	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+                if (e - s + 1 >= sizeof(u->port))	Err	codevoid.de	70
i+                        return 0;	Err	codevoid.de	70
i+                memcpy(u->port, s, e - s);	Err	codevoid.de	70
i+                u->port[e - s] = '\0';	Err	codevoid.de	70
i+        }	Err	codevoid.de	70
i+        if (*e && *e != '/')	Err	codevoid.de	70
i+                return 0; /* invalid path */	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        s = e;	Err	codevoid.de	70
i+        e = s + strlen(s);	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        if (e - s + 1 >= sizeof(u->path))	Err	codevoid.de	70
i+                return 0;	Err	codevoid.de	70
i+        memcpy(u->path, s, e - s);	Err	codevoid.de	70
i+        u->path[e - s] = '\0';	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        return 1;	Err	codevoid.de	70
i+}	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+int	Err	codevoid.de	70
i+main(void)	Err	codevoid.de	70
i+{	Err	codevoid.de	70
i+        struct uri u;	Err	codevoid.de	70
i+        const char *p, *qs, *path;	Err	codevoid.de	70
i+        char query[1024] = "", param[1024] = "", uri[1024] = "";	Err	codevoid.de	70
i+        int _type = '1';	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        if (pledge("stdio inet dns", NULL) == -1)	Err	codevoid.de	70
i+                die(500, "pledge: %s\n", strerror(errno));	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        if (!(qs = getenv("QUERY_STRING")))	Err	codevoid.de	70
i+                qs = "";	Err	codevoid.de	70
i+        if ((p = getparam(qs, "q"))) {	Err	codevoid.de	70
i+                if (decodeparam(query, sizeof(query), p) == -1 ||	Err	codevoid.de	70
i+                    !checkparam(query))	Err	codevoid.de	70
i+                        die(400, "Invalid parameter: q\n");	Err	codevoid.de	70
i+        }	Err	codevoid.de	70
i+        if ((p = getparam(qs, "p"))) {	Err	codevoid.de	70
i+                if (decodeparam(param, sizeof(param), p) == -1 ||	Err	codevoid.de	70
i+                    !checkparam(param))	Err	codevoid.de	70
i+                        die(400, "Invalid parameter: p\n");	Err	codevoid.de	70
i+        }	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        path = "/";	Err	codevoid.de	70
i+        if (query[0]) {	Err	codevoid.de	70
i+                if (strncmp(query, "gopher://", sizeof("gopher://") - 1))	Err	codevoid.de	70
i+                        snprintf(uri, sizeof(uri), "gopher://%s", query);	Err	codevoid.de	70
i+                else	Err	codevoid.de	70
i+                        snprintf(uri, sizeof(uri), "%s", query);	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+                if (!parseuri(uri, &u))	Err	codevoid.de	70
i+                        die(400, "Invalid uri: %s\n", uri);	Err	codevoid.de	70
i+                if (u.host[0] == '\0')	Err	codevoid.de	70
i+                        die(400, "Invalid hostname\n");	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+                if (u.path[0] == '\0')	Err	codevoid.de	70
i+                        memcpy(u.path, "/", 2);	Err	codevoid.de	70
i+                if (u.port[0] == '\0')	Err	codevoid.de	70
i+                        memcpy(u.port, "70", 3);	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+                path = u.path;	Err	codevoid.de	70
i+                if (path[0] == '/') {	Err	codevoid.de	70
i+                        if (path[1]) {	Err	codevoid.de	70
i+                                _type = path[1];	Err	codevoid.de	70
i+                                path += 2;	Err	codevoid.de	70
i+                        }	Err	codevoid.de	70
i+                } else {	Err	codevoid.de	70
i+                        path = "/";	Err	codevoid.de	70
i+                }	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+                if (isblacklisted(u.host, u.port, path))	Err	codevoid.de	70
i+                        die(403, "%s:%s %s: blacklisted\n", u.host, u.port, path);	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+                headerset = 1;	Err	codevoid.de	70
i+                switch (_type) {	Err	codevoid.de	70
i+                case '0':	Err	codevoid.de	70
i+                        printf("Content-Type: text/plain; charset=utf-8\r\n\r\n");	Err	codevoid.de	70
i+                        fflush(stdout);	Err	codevoid.de	70
i+                        servefile(u.host, u.port, path);	Err	codevoid.de	70
i+                        return 0;	Err	codevoid.de	70
i+                case '1':	Err	codevoid.de	70
i+                case '7':	Err	codevoid.de	70
i+                        break; /* handled below */	Err	codevoid.de	70
i+                case '9':	Err	codevoid.de	70
i+                        printf("Content-Type: application/octet-stream\r\n");	Err	codevoid.de	70
i+                        if ((p = strrchr(path, '/')))	Err	codevoid.de	70
i+                                printf("Content-Disposition: attachment; filename=\"%s\"\r\n", p + 1);	Err	codevoid.de	70
i+                        printf("\r\n");	Err	codevoid.de	70
i+                        fflush(stdout);	Err	codevoid.de	70
i+                        servefile(u.host, u.port, path);	Err	codevoid.de	70
i+                        return 0;	Err	codevoid.de	70
i+                default:	Err	codevoid.de	70
i+                        write(1, "\r\n", 2);	Err	codevoid.de	70
i+                        servefile(u.host, u.port, path);	Err	codevoid.de	70
i+                        return 0;	Err	codevoid.de	70
i+                }	Err	codevoid.de	70
i+        }	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        fputs("Content-Type: text/html; charset=utf-8\r\n\r\n", stdout);	Err	codevoid.de	70
i+        headerset = 1;	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        fputs(	Err	codevoid.de	70
i+                "\n"	Err	codevoid.de	70
i+                "\n"	Err	codevoid.de	70
i+                "\n"	Err	codevoid.de	70
i+                "\n"	Err	codevoid.de	70
i+                "", stdout);	Err	codevoid.de	70
i+        xmlencode(query);	Err	codevoid.de	70
i+        if (query[0])	Err	codevoid.de	70
i+                fputs(" - ", stdout);	Err	codevoid.de	70
i+        fputs(	Err	codevoid.de	70
i+                "Gopher HTTP proxy\n"	Err	codevoid.de	70
i+                "\n"	Err	codevoid.de	70
i+                "\n"	Err	codevoid.de	70
i+                "\n"	Err	codevoid.de	70
i+                "\n"	Err	codevoid.de	70
i+                "\n"	Err	codevoid.de	70
i+                "\n"	Err	codevoid.de	70
i+                "
"	Err	codevoid.de	70
i+                "  URI: "	Err	codevoid.de	70
i+                "
" Err codevoid.de 70 i+ "
\n", stdout);	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        if (query[0]) {	Err	codevoid.de	70
i+                if (_type != '7')	Err	codevoid.de	70
i+                        param[0] = '\0';	Err	codevoid.de	70
i+                servedir(u.host, u.port, path, param);	Err	codevoid.de	70
i+        }	Err	codevoid.de	70
i+	Err	codevoid.de	70
i+        fputs("
\n\n\n", stdout); Err codevoid.de 70 i+ Err codevoid.de 70 i+ return 0; Err codevoid.de 70 i+} Err codevoid.de 70 .