/* 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 "server.h" const char *EventsAfterQuery = SQL( SELECT events.event, events.time, events.type, names.nick, names.user, names.host, events.target, events.message FROM events JOIN contexts USING (context) JOIN names USING (name) WHERE contexts.network = :network AND contexts.name = :context AND coalesce(contexts.query = :query, true) AND events.time >= strftime('%s', :time) ORDER BY events.time LIMIT :limit; ); const char *EventsBeforeQuery = SQL( WITH before AS ( SELECT events.event, events.time, events.type, names.nick, names.user, names.host, events.target, events.message FROM events JOIN contexts USING (context) JOIN names USING (name) WHERE contexts.network = :network AND contexts.name = :context AND coalesce(contexts.query = :query, true) AND events.time < strftime('%s', :time) ORDER BY events.time DESC LIMIT :limit ) SELECT * FROM before ORDER BY time; ); enum kcgi_err pageEvents(struct kreq *req) { if (!req->fieldmap[Network] || !req->fieldmap[Context]) { return httpFail(req, KHTTP_400); } const char *network = req->fieldmap[Network]->parsed.s; const char *context = req->fieldmap[Context]->parsed.s; if (!req->fieldmap[After] && !req->fieldmap[Before]) { struct tm *tm = gmtime(&(time_t) { time(NULL) }); if (!tm) err(EX_OSERR, "gmtime"); char time[sizeof("0000-00-00T00:00:00")]; strftime(time, sizeof(time), "%FT%T", tm); char *url = khttp_urlpart( NULL, NULL, Pages[Events], Keys[Network].name, network, Keys[Context].name, context, Keys[Before].name, time, NULL ); enum kcgi_err error = httpRedirect(req, url); free(url); return error; } const char *time = req->fieldmap[Before] ? req->fieldmap[Before]->parsed.s : req->fieldmap[After]->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, context) || htmlNav(&html, network, context); if (error) return error; sqlite3_stmt *events = stmt.eventsAfter; if (req->fieldmap[Before]) events = stmt.eventsBefore; dbBindText(events, ":network", network); dbBindText(events, ":context", context); if (pagePublic) dbBindInt(events, ":query", false); dbBindText(events, ":time", time); dbBindInt(events, ":limit", pageLimit); int result; while (SQLITE_ROW == (result = sqlite3_step(events))) { const char *msg = (const char *)sqlite3_column_text(events, 7); if (!msg) continue; error = 0 || khtml_puts(&html, msg) || khtml_elem(&html, KELEM_BR); if (error) break; } if (result != SQLITE_DONE) errx(EX_SOFTWARE, "%s", sqlite3_errmsg(db)); sqlite3_reset(events); return error || htmlFooter(&html) || khtml_close(&html); }