/* 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 "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) { 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); }