about summary refs log tree commit diff
path: root/scooper.c
diff options
context:
space:
mode:
Diffstat (limited to 'scooper.c')
-rw-r--r--scooper.c319
1 files changed, 0 insertions, 319 deletions
diff --git a/scooper.c b/scooper.c
deleted file mode 100644
index 2c7b58a..0000000
--- a/scooper.c
+++ /dev/null
@@ -1,319 +0,0 @@
-/* Copyright (C) 2020  C. 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
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <err.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sysexits.h>
-#include <unistd.h>
-
-#include <kcgi.h>
-#include <kcgihtml.h>
-#include <sqlite3.h>
-
-#if KCGI_VMAJOR == 0 && KCGI_VMINOR < 12
-#define khttp_urlpart(...) kutil_urlpart(req, __VA_ARGS__)
-#endif
-
-#define SQL(...) #__VA_ARGS__
-
-enum { DatabaseVersion = 4 };
-
-static sqlite3 *db;
-
-static int dbParam(sqlite3_stmt *stmt, const char *param) {
-	int index = sqlite3_bind_parameter_index(stmt, param);
-	if (index) return index;
-	errx(EX_SOFTWARE, "no such parameter %s: %s", param, sqlite3_sql(stmt));
-}
-
-static void
-dbBindInt(sqlite3_stmt *stmt, const char *param, sqlite3_int64 value) {
-	if (!sqlite3_bind_int64(stmt, dbParam(stmt, param), value)) return;
-	errx(EX_SOFTWARE, "sqlite3_bind_int64: %s", sqlite3_errmsg(db));
-}
-
-static void
-dbBindText(sqlite3_stmt *stmt, const char *param, const char *value) {
-	if (!sqlite3_bind_text(stmt, dbParam(stmt, param), value, -1, NULL)) return;
-	errx(EX_SOFTWARE, "sqlite3_bind_text: %s", sqlite3_errmsg(db));
-}
-
-static struct {
-	sqlite3_stmt *networks;
-	sqlite3_stmt *contexts;
-	sqlite3_stmt *events;
-	sqlite3_stmt *search;
-} stmt;
-
-static void dbClose(void) {
-	if (stmt.networks) sqlite3_finalize(stmt.networks);
-	if (stmt.contexts) sqlite3_finalize(stmt.contexts);
-	if (stmt.events) sqlite3_finalize(stmt.events);
-	if (stmt.search) sqlite3_finalize(stmt.search);
-	sqlite3_close(db);
-}
-
-#define ENUM_PAGES \
-	X(Networks, "networks") \
-	X(Contexts, "contexts") \
-	X(Events, "events") \
-	X(Search, "search")
-
-enum {
-#define X(page, path) page,
-	ENUM_PAGES
-#undef X
-	PagesLen,
-};
-
-static const char *Pages[PagesLen] = {
-#define X(page, path) [page] = path,
-	ENUM_PAGES
-#undef X
-};
-
-#define ENUM_KEYS \
-	X(Network, "network", kvalid_stringne) \
-	X(Context, "context", kvalid_stringne) \
-	X(After, "after", kvalid_stringne) \
-	X(Query, "query", kvalid_stringne)
-
-enum {
-#define X(key, name, valid) key,
-	ENUM_KEYS
-#undef X
-	KeysLen,
-};
-
-static const struct kvalid Keys[KeysLen] = {
-#define X(key, name, valid) [key] = { valid, name },
-	ENUM_KEYS
-#undef X
-};
-
-static enum kcgi_err head(struct kreq *req, enum khttp http, enum kmime mime) {
-	return khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[http])
-		|| khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[mime]);
-}
-
-static enum kcgi_err fail(struct kreq *req, enum khttp http) {
-	return head(req, http, KMIME_TEXT_PLAIN)
-		|| khttp_body(req)
-		|| khttp_puts(req, khttps[http])
-		|| khttp_putc(req, '\n');
-}
-
-static const char *stylesheet;
-static enum kcgi_err htmlStylesheet(struct khtmlreq *html) {
-	if (!stylesheet) return KCGI_OK;
-	return khtml_attr(
-		html, KELEM_LINK,
-		KATTR_REL, "stylesheet",
-		KATTR_HREF, stylesheet,
-		KATTR__MAX
-	);
-}
-
-static enum kcgi_err htmlHead(struct khtmlreq *html, const char *title) {
-	return khtml_elem(html, KELEM_DOCTYPE)
-		|| khtml_attr(html, KELEM_META, KATTR_CHARSET, "utf-8", KATTR__MAX)
-		|| khtml_elem(html, KELEM_TITLE)
-		|| khtml_puts(html, title)
-		|| khtml_closeelem(html, 1)
-		|| htmlStylesheet(html)
-		|| khtml_elem(html, KELEM_H1)
-		|| khtml_puts(html, title)
-		|| khtml_closeelem(html, 1);
-}
-
-static const char *NetworksQuery = SQL(
-	SELECT DISTINCT network
-	FROM contexts
-	ORDER BY network;
-);
-
-static enum kcgi_err networks(struct kreq *req) {
-	struct khtmlreq html;
-	enum kcgi_err error = head(req, KHTTP_200, KMIME_TEXT_HTML)
-		|| khttp_body(req)
-		|| khtml_open(&html, req, KHTML_PRETTY)
-		|| htmlHead(&html, "Networks")
-		|| khtml_elem(&html, KELEM_UL);
-	if (error) return error;
-
-	int result;
-	while (SQLITE_ROW == (result = sqlite3_step(stmt.networks))) {
-		const char *network = (const char *)sqlite3_column_text(stmt.networks, 0);
-		char *href = khttp_urlpart(
-			NULL, NULL, Pages[Contexts], Keys[Network].name, network, NULL
-		);
-		if (!href) err(EX_OSERR, "khttp_urlpart");
-
-		error = khtml_elem(&html, KELEM_LI)
-			|| khtml_attr(&html, KELEM_A, KATTR_HREF, href, KATTR__MAX)
-			|| khtml_puts(&html, network)
-			|| khtml_closeelem(&html, 2);
-		free(href);
-		if (error) break;
-	}
-	if (result != SQLITE_DONE) errx(EX_SOFTWARE, "%s", sqlite3_errmsg(db));
-
-	sqlite3_reset(stmt.networks);
-	return error || khtml_close(&html);
-}
-
-static const char *ContextsQuery = SQL(
-	SELECT name
-	FROM contexts
-	WHERE network = :network AND coalesce(query = :query, true)
-	ORDER BY query, name;
-);
-
-bool public = false;
-
-static enum kcgi_err contexts(struct kreq *req) {
-	if (!req->fieldmap[Network]) return fail(req, KHTTP_404);
-	const char *network = req->fieldmap[Network]->parsed.s;
-
-	struct khtmlreq html;
-	enum kcgi_err error = head(req, KHTTP_200, KMIME_TEXT_HTML)
-		|| khttp_body(req)
-		|| khtml_open(&html, req, KHTML_PRETTY)
-		|| htmlHead(&html, network)
-		|| khtml_elem(&html, KELEM_UL);
-	if (error) return error;
-
-	dbBindText(stmt.contexts, ":network", network);
-	if (public) dbBindInt(stmt.contexts, ":query", false);
-
-	int result;
-	while (SQLITE_ROW == (result = sqlite3_step(stmt.contexts))) {
-		const char *context = (const char *)sqlite3_column_text(stmt.contexts, 0);
-		char *href = khttp_urlpart(
-			NULL, NULL, Pages[Events],
-			Keys[Network].name, network,
-			Keys[Context].name, context,
-			NULL
-		);
-		if (!href) err(EX_OSERR, "khttp_urlpart");
-
-		error = khtml_elem(&html, KELEM_LI)
-			|| khtml_attr(&html, KELEM_A, KATTR_HREF, href, KATTR__MAX)
-			|| khtml_puts(&html, context)
-			|| khtml_closeelem(&html, 2);
-		free(href);
-		if (error) break;
-	}
-	if (result != SQLITE_DONE) errx(EX_SOFTWARE, "%s", sqlite3_errmsg(db));
-
-	sqlite3_reset(stmt.contexts);
-	return error || khtml_close(&html);
-}
-
-static enum kcgi_err events(struct kreq *req) {
-	return KCGI_OK;
-}
-
-static enum kcgi_err search(struct kreq *req) {
-	return KCGI_OK;
-}
-
-static enum kcgi_err request(struct kreq *req) {
-	if (req->method != KMETHOD_HEAD && req->method != KMETHOD_GET) {
-		return fail(req, KHTTP_405);
-	}
-	switch (req->page) {
-		case Networks: return networks(req);
-		case Contexts: return contexts(req);
-		case Events:   return events(req);
-		case Search:   return search(req);
-		default:       return fail(req, KHTTP_404);
-	}
-}
-
-static void prepare(sqlite3_stmt **stmt, const char *query) {
-	int error = sqlite3_prepare_v3(
-		db, query, -1, SQLITE_PREPARE_PERSISTENT, stmt, NULL
-	);
-	if (error) errx(EX_SOFTWARE, "%s: %s", sqlite3_errmsg(db), query);
-}
-
-int main(int argc, char *argv[]) {
-	bool fastCGI = false;
-
-	for (int opt; 0 < (opt = getopt(argc, argv, "fps:"));) {
-		switch (opt) {
-			break; case 'f': fastCGI = true;
-			break; case 'p': public = true;
-			break; case 's': stylesheet = optarg;
-			break; default:  return EX_USAGE;
-		}
-	}
-	if (optind == argc) errx(EX_USAGE, "database path required");
-
-	int error = sqlite3_open_v2(argv[optind], &db, SQLITE_OPEN_READONLY, NULL);
-	if (error) errx(EX_NOINPUT, "%s: %s", argv[optind], sqlite3_errmsg(db));
-	atexit(dbClose);
-
-	sqlite3_stmt *check;
-	error = sqlite3_prepare_v2(
-		db, SQL(PRAGMA user_version;), -1, &check, NULL
-	);
-	if (error) errx(EX_SOFTWARE, "%s", sqlite3_errmsg(db));
-
-	error = sqlite3_step(check);
-	if (error != SQLITE_ROW) errx(EX_SOFTWARE, "%s", sqlite3_errmsg(db));
-
-	int version = sqlite3_column_int(check, 0);
-	if (version != DatabaseVersion) {
-		errx(EX_DATAERR, "unsupported database version %d", version);
-	}
-	sqlite3_finalize(check);
-
-	prepare(&stmt.networks, NetworksQuery);
-	prepare(&stmt.contexts, ContextsQuery);
-	
-	if (fastCGI) {
-		struct kfcgi *fcgi;
-		enum kcgi_err error = khttp_fcgi_init(
-			&fcgi, Keys, KeysLen, Pages, PagesLen, Networks
-		);
-		if (error) errx(EX_CONFIG, "khttp_fcgi_init: %s", kcgi_strerror(error));
-		for (
-			struct kreq req;
-			KCGI_OK == (error = khttp_fcgi_parse(fcgi, &req));
-			khttp_free(&req)
-		) {
-			error = request(&req);
-			if (error && error != KCGI_HUP) break;
-		}
-		errx(EX_PROTOCOL, "khttp_fcgi_parse: %s", kcgi_strerror(error));
-	} else {
-		struct kreq req;
-		enum kcgi_err error = khttp_parse(
-			&req, Keys, KeysLen, Pages, PagesLen, Networks
-		);
-		if (error) errx(EX_PROTOCOL, "khttp_parse: %s", kcgi_strerror(error));
-		error = request(&req);
-		if (error) errx(EX_PROTOCOL, "%s", kcgi_strerror(error));
-		khttp_free(&req);
-	}
-}