From d89e0160f908e00b314333b3d0030101f9649f2e Mon Sep 17 00:00:00 2001 From: "C. McEnroe" Date: Thu, 9 Jul 2020 14:14:27 -0400 Subject: Implement empty CGI/FastCGI server --- .gitignore | 1 + Makefile | 4 ++ scooper.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 scooper.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c4605b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +scooper diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ce4cdcf --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +CFLAGS += -std=c11 -Wall -Wextra -Wpedantic +LDLIBS = -lsqlite3 -lkcgi -lkcgihtml -lz + +scooper: diff --git a/scooper.c b/scooper.c new file mode 100644 index 0000000..130ea15 --- /dev/null +++ b/scooper.c @@ -0,0 +1,141 @@ +/* Copyright (C) 2020 C. McEnroe + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define SQL(...) #__VA_ARGS__ + +enum { DatabaseVersion = 4 }; + +static sqlite3 *db; + +static void dbClose(void) { + // TODO: Finalize statements. + 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_string) \ + X(Context, "context", kvalid_string) \ + X(After, "after", kvalid_string) \ + X(Query, "query", kvalid_string) + +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 void request(struct kreq *req) { +} + +int main(int argc, char *argv[]) { + bool fastCGI = false; + bool public = false; + const char *stylesheet = NULL; + + 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 *stmt; + error = sqlite3_prepare_v2( + db, SQL(PRAGMA user_version;), -1, &stmt, NULL + ); + if (error) errx(EX_SOFTWARE, "%s", sqlite3_errmsg(db)); + + error = sqlite3_step(stmt); + if (error != SQLITE_ROW) errx(EX_SOFTWARE, "%s", sqlite3_errmsg(db)); + + int version = sqlite3_column_int(stmt, 0); + if (version != DatabaseVersion) { + errx(EX_DATAERR, "unsupported database version %d", version); + } + sqlite3_finalize(stmt); + + // TODO: Prepare all statements with persist flag. + + 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) + ) { + request(&req); + } + errx(EX_DATAERR, "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_DATAERR, "khttp_parse: %s", kcgi_strerror(error)); + request(&req); + khttp_free(&req); + } +} -- cgit 1.4.1