summary refs log tree commit diff
path: root/www/temp.causal.agency/up.c
diff options
context:
space:
mode:
Diffstat (limited to 'www/temp.causal.agency/up.c')
-rw-r--r--www/temp.causal.agency/up.c143
1 files changed, 90 insertions, 53 deletions
diff --git a/www/temp.causal.agency/up.c b/www/temp.causal.agency/up.c
index 885b8acd..561a8901 100644
--- a/www/temp.causal.agency/up.c
+++ b/www/temp.causal.agency/up.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2020  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -21,7 +21,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/capsicum.h>
 #include <sys/types.h>
 #include <sysexits.h>
 #include <time.h>
@@ -30,8 +29,7 @@
 #include <kcgi.h>
 #include <kcgihtml.h>
 
-static int cwd = -1;
-
+static const char *Page = "up";
 static const struct kvalid Key = { NULL, "file" };
 
 static enum kcgi_err head(struct kreq *req, enum khttp http, enum kmime mime) {
@@ -45,6 +43,28 @@ static enum kcgi_err fail(struct kreq *req, enum khttp http) {
 		|| khttp_printf(req, "%s\n", khttps[http]);
 }
 
+static int dir = -1;
+static const char *upload(const char *ext, void *ptr, size_t len) {
+	static char name[256];
+	snprintf(
+		name, sizeof(name), "%jx%08x%s%s",
+		(intmax_t)time(NULL), arc4random(),
+		(ext && ext[0] != '.' ? "." : ""), (ext ? ext : "")
+	);
+	int fd = openat(dir, name, O_CREAT | O_EXCL | O_WRONLY, 0644);
+	if (fd < 0) {
+		warn("%s", name);
+		return NULL;
+	}
+	ssize_t n = write(fd, ptr, len);
+	int error = close(fd);
+	if (n < 0 || error) {
+		warn("%s", name);
+		return NULL;
+	}
+	return name;
+}
+
 static enum kcgi_err handle(struct kreq *req) {
 	if (req->page) return fail(req, KHTTP_404);
 
@@ -83,74 +103,91 @@ static enum kcgi_err handle(struct kreq *req) {
 		struct kpair *field = req->fieldmap[0];
 		if (!field || !field->valsz) return fail(req, KHTTP_400);
 
-		char name[256];
 		const char *ext = strrchr(field->file, '.');
-		if (!ext) ext = "";
-		snprintf(
-			name, sizeof(name), "%jx%08x%s",
-			(intmax_t)time(NULL), arc4random(), ext
-		);
-
-		int fd = openat(cwd, name, O_CREAT | O_EXCL | O_WRONLY, 0644);
-		if (fd < 0) {
-			warn("openat");
-			return fail(req, KHTTP_507);
-		}
-		ssize_t len = write(fd, field->val, field->valsz);
-		int error = close(fd);
-		if (len < 0 || error) {
-			warn("write");
-			return fail(req, KHTTP_507);
-		}
+		const char *name = upload(ext, field->val, field->valsz);
+		if (!name) return fail(req, KHTTP_507);
 
 		return head(req, KHTTP_303, KMIME_TEXT_PLAIN)
 			|| khttp_head(req, kresps[KRESP_LOCATION], "/%s", name)
 			|| khttp_body(req)
 			|| khttp_puts(req, name);
 
+	} else if (req->method == KMETHOD_PUT) {
+		struct kpair *field = req->fields;
+		if (!field || !field->valsz) return fail(req, KHTTP_400);
+
+		const char *ext = req->suffix;
+		if (!ext[0]) ext = strrchr(field->file, '.');
+		const char *name = upload(ext, field->val, field->valsz);
+		if (!name) return fail(req, KHTTP_507);
+
+		return head(req, KHTTP_200, KMIME_TEXT_PLAIN)
+			|| khttp_body(req)
+			|| khttp_printf(
+				req, "%s://%s/%s\n", kschemes[req->scheme], req->host, name
+			);
+
 	} else {
 		return fail(req, KHTTP_405);
 	}
 }
 
-static void sandbox(void) {
-	cwd = open(".", O_DIRECTORY);
-	if (cwd < 0) err(EX_CONFIG, ".");
+int main(int argc, char *argv[]) {
+	int error;
+	const char *path = (argc > 1 ? argv[1] : ".");
+	dir = open(path, O_DIRECTORY);
+	if (dir < 0) err(EX_NOINPUT, "%s", path);
 
-	int error = cap_enter();
-	if (error) err(EX_OSERR, "cap_enter");
+#ifdef __OpenBSD__
+	error = unveil(path, "wc");
+	if (error) err(EX_OSERR, "unveil");
+#endif
 
-	cap_rights_t rights;
-	cap_rights_init(&rights, CAP_LOOKUP, CAP_CREATE, CAP_PWRITE);
-	error = cap_rights_limit(cwd, &rights);
-	if (error) err(EX_OSERR, "cap_rights_limit");
-}
+	if (!khttp_fcgi_test()) {
+#ifdef __OpenBSD__
+		error = pledge("stdio wpath cpath proc", NULL);
+		if (error) err(EX_OSERR, "pledge");
+#endif
 
-int main(void) {
-	const char *page = "up";
-	if (khttp_fcgi_test()) {
-		struct kfcgi *fcgi;
-		enum kcgi_err error = khttp_fcgi_init(&fcgi, &Key, 1, &page, 1, 0);
-		if (error) errx(EX_CONFIG, "khttp_fcgi_init: %s", kcgi_strerror(error));
-		sandbox();
-		for (
-			struct kreq req;
-			KCGI_OK == (error = khttp_fcgi_parse(fcgi, &req));
-			khttp_free(&req)
-		) {
-			error = handle(&req);
-			if (error && error != KCGI_HUP) break;
-		}
-		if (error != KCGI_EXIT) {
-			errx(EX_PROTOCOL, "khttp_fcgi_parse: %s", kcgi_strerror(error));
-		}
-		khttp_fcgi_free(fcgi);
-	} else {
 		struct kreq req;
-		enum kcgi_err error = khttp_parse(&req, &Key, 1, &page, 1, 0);
+		error = khttp_parse(&req, &Key, 1, &Page, 1, 0);
 		if (error) errx(EX_PROTOCOL, "khttp_parse: %s", kcgi_strerror(error));
+
+#ifdef __OpenBSD__
+		error = pledge("stdio wpath cpath", NULL);
+		if (error) err(EX_OSERR, "pledge");
+#endif
+
 		error = handle(&req);
 		if (error) errx(EX_PROTOCOL, "%s", kcgi_strerror(error));
 		khttp_free(&req);
+		return EX_OK;
+	}
+
+#ifdef __OpenBSD__
+	error = pledge("stdio wpath cpath unix sendfd recvfd proc", NULL);
+	if (error) err(EX_OSERR, "pledge");
+#endif
+
+	struct kfcgi *fcgi;
+	error = khttp_fcgi_init(&fcgi, &Key, 1, &Page, 1, 0);
+	if (error) errx(EX_CONFIG, "khttp_fcgi_init: %s", kcgi_strerror(error));
+
+#ifdef __OpenBSD__
+	error = pledge("stdio wpath cpath recvfd", NULL);
+	if (error) err(EX_OSERR, "pledge");
+#endif
+
+	for (
+		struct kreq req;
+		!(error = khttp_fcgi_parse(fcgi, &req));
+		khttp_free(&req)
+	) {
+		error = handle(&req);
+		if (error && error != KCGI_HUP) break;
+	}
+	if (error != KCGI_EXIT) {
+		errx(EX_PROTOCOL, "khttp_fcgi_parse: %s", kcgi_strerror(error));
 	}
+	khttp_fcgi_free(fcgi);
 }