about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2020-07-10 20:47:52 -0400
committerJune McEnroe <june@causal.agency>2020-07-10 20:47:52 -0400
commitb6edd81b9759a5bba76569fb8acbbfc11b4a4ae6 (patch)
tree9785b74319394e290bc1596c2fed6e19f7e9d85d
parentFix hidden form fields (diff)
downloadscooper-b6edd81b9759a5bba76569fb8acbbfc11b4a4ae6.tar.gz
scooper-b6edd81b9759a5bba76569fb8acbbfc11b4a4ae6.zip
Implement basic search page
Diffstat (limited to '')
-rw-r--r--search.c83
-rw-r--r--server.c1
-rw-r--r--server.h3
3 files changed, 85 insertions, 2 deletions
diff --git a/search.c b/search.c
index c9a9044..767081b 100644
--- a/search.c
+++ b/search.c
@@ -21,6 +21,87 @@
 
 #include "server.h"
 
+const char *SearchQuery = SQL(
+	SELECT
+		events.event,
+		events.time,
+		contexts.network,
+		contexts.name,
+		events.type,
+		names.nick,
+		names.user,
+		names.host,
+		events.target,
+		highlight(search, 6, :highlight, :highlight)
+	FROM events
+	JOIN contexts USING (context)
+	JOIN names USING (name)
+	JOIN search ON search.rowid = events.event
+	WHERE coalesce(contexts.network = :network, true)
+		AND coalesce(contexts.name = :context, true)
+		AND coalesce(contexts.query = :query, true)
+		AND search MATCH :search
+	LIMIT :limit
+	OFFSET :offset;
+);
+
 enum kcgi_err pageSearch(struct kreq *req) {
-	return httpFail(req, KHTTP_501);
+	if (!req->fieldmap[Query]) {
+		return httpFail(req, KHTTP_400);
+	}
+	if (req->fieldmap[Context] && !req->fieldmap[Network]) {
+		return httpFail(req, KHTTP_400);
+	}
+	const char *query = req->fieldmap[Query]->parsed.s;
+
+	int64_t offset = 0;
+	const char *network = NULL;
+	const char *context = NULL;
+	if (req->fieldmap[Offset]) offset = req->fieldmap[Offset]->parsed.i;
+	if (req->fieldmap[Network]) network = req->fieldmap[Network]->parsed.s;
+	if (req->fieldmap[Context]) context = req->fieldmap[Context]->parsed.s;
+
+	enum kcgi_err error = httpHead(req, KHTTP_200, KMIME_TEXT_HTML);
+	if (req->method == KMETHOD_HEAD) return error;
+
+	struct khtmlreq html;
+	error = error
+		|| khttp_body(req)
+		|| khtml_open(&html, req, KHTML_PRETTY)
+		|| htmlHead(&html, query)
+		|| htmlNav(&html, network, context)
+		|| khtml_elem(&html, KELEM_TABLE);
+	if (error) return error;
+
+	dbBindText(stmt.search, ":highlight", "\26");
+	dbBindText(stmt.search, ":network", network);
+	dbBindText(stmt.search, ":context", context);
+	if (pagePublic) dbBindInt(stmt.search, ":query", false);
+	dbBindText(stmt.search, ":search", query);
+	dbBindInt(stmt.search, ":limit", pageLimit);
+	dbBindInt(stmt.search, ":offset", offset);
+
+	int result;
+	while (SQLITE_ROW == (result = sqlite3_step(stmt.search))) {
+		struct Event event = {
+			.event = sqlite3_column_int64(stmt.search, 0),
+			.time = sqlite3_column_int64(stmt.search, 1),
+			.network = (const char *)sqlite3_column_text(stmt.search, 2),
+			.context = (const char *)sqlite3_column_text(stmt.search, 3),
+			.type = sqlite3_column_int(stmt.search, 4),
+			.nick = (const char *)sqlite3_column_text(stmt.search, 5),
+			.user = (const char *)sqlite3_column_text(stmt.search, 6),
+			.host = (const char *)sqlite3_column_text(stmt.search, 7),
+			.target = (const char *)sqlite3_column_text(stmt.search, 8),
+			.message = (const char *)sqlite3_column_text(stmt.search, 9),
+		};
+		error = htmlEvent(&html, event);
+		if (error) break;
+	}
+	if (result != SQLITE_DONE) errx(EX_SOFTWARE, "%s", sqlite3_errmsg(db));
+	sqlite3_reset(stmt.search);
+
+	return error
+		|| htmlFooter(&html)
+		|| khtml_close(&html);
 }
diff --git a/server.c b/server.c
index 4955524..afbf238 100644
--- a/server.c
+++ b/server.c
@@ -114,6 +114,7 @@ int main(int argc, char *argv[]) {
 	prepare(&stmt.contexts, ContextsQuery);
 	prepare(&stmt.eventsAfter, EventsAfterQuery);
 	prepare(&stmt.eventsBefore, EventsBeforeQuery);
+	prepare(&stmt.search, SearchQuery);
 	
 	if (test) return EX_OK;
 	
diff --git a/server.h b/server.h
index bb9bc60..502e228 100644
--- a/server.h
+++ b/server.h
@@ -130,7 +130,8 @@ extern const char *Pages[PagesLen];
 	X(Context, "context", kvalid_stringne) \
 	X(After, "after", kvalid_stringne) \
 	X(Before, "before", kvalid_stringne) \
-	X(Query, "query", kvalid_stringne)
+	X(Query, "query", kvalid_stringne) \
+	X(Offset, "offset", kvalid_int)
 
 enum {
 #define X(key, name, valid) key,