From b6edd81b9759a5bba76569fb8acbbfc11b4a4ae6 Mon Sep 17 00:00:00 2001 From: "C. McEnroe" Date: Fri, 10 Jul 2020 20:47:52 -0400 Subject: Implement basic search page --- search.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- server.c | 1 + server.h | 3 ++- 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, -- cgit 1.4.1