about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2020-07-09 17:30:47 -0400
committerJune McEnroe <june@causal.agency>2020-07-09 17:30:47 -0400
commita86d093a0c90aa84b607f3467063193b2382e5cd (patch)
treeb335ce0b118b4ea4ef1a53141eb58096d287e7ca
parentAdd configure script and install target (diff)
downloadscooper-a86d093a0c90aa84b607f3467063193b2382e5cd.tar.gz
scooper-a86d093a0c90aa84b607f3467063193b2382e5cd.zip
Implement basic contexts listing
-rw-r--r--scooper.c66
1 files changed, 64 insertions, 2 deletions
diff --git a/scooper.c b/scooper.c
index 17c0ebc..2c7b58a 100644
--- a/scooper.c
+++ b/scooper.c
@@ -38,6 +38,24 @@ 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;
@@ -162,8 +180,52 @@ static enum kcgi_err networks(struct kreq *req) {
 	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) {
-	return KCGI_OK;
+	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) {
@@ -196,7 +258,6 @@ static void prepare(sqlite3_stmt **stmt, const char *query) {
 
 int main(int argc, char *argv[]) {
 	bool fastCGI = false;
-	bool public = false;
 
 	for (int opt; 0 < (opt = getopt(argc, argv, "fps:"));) {
 		switch (opt) {
@@ -228,6 +289,7 @@ int main(int argc, char *argv[]) {
 	sqlite3_finalize(check);
 
 	prepare(&stmt.networks, NetworksQuery);
+	prepare(&stmt.contexts, ContextsQuery);
 	
 	if (fastCGI) {
 		struct kfcgi *fcgi;