/* 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 #include #include #include #if KCGI_VMAJOR == 0 && KCGI_VMINOR < 12 #define khttp_urlpart(...) kutil_urlpart(NULL, __VA_ARGS__) #endif #define SQL(...) #__VA_ARGS__ enum { DatabaseVersion = 4 }; #define ENUM_TYPE \ X(Privmsg, "privmsg") \ X(Notice, "notice") \ X(Action, "action") \ X(Join, "join") \ X(Part, "part") \ X(Quit, "quit") \ X(Kick, "kick") \ X(Nick, "nick") \ X(Topic, "topic") \ X(Ban, "ban") \ X(Unban, "unban") enum Type { #define X(id, name) id, ENUM_TYPE #undef X TypesLen, }; struct Event { int64_t event; time_t time; const char *network; const char *context; enum Type type; const char *nick; const char *user; const char *host; const char *target; const char *message; }; extern sqlite3 *db; extern const char *NetworksQuery; extern const char *ContextsQuery; extern const char *EventsAfterQuery; extern const char *EventsBeforeQuery; extern const char *SearchQuery; extern struct Statements { sqlite3_stmt *networks; sqlite3_stmt *contexts; sqlite3_stmt *eventsAfter; sqlite3_stmt *eventsBefore; sqlite3_stmt *search; } stmt; static inline void dbClose(void) { if (stmt.networks) sqlite3_finalize(stmt.networks); if (stmt.contexts) sqlite3_finalize(stmt.contexts); if (stmt.eventsAfter) sqlite3_finalize(stmt.eventsAfter); if (stmt.eventsBefore) sqlite3_finalize(stmt.eventsBefore); if (stmt.search) sqlite3_finalize(stmt.search); sqlite3_close(db); } static inline 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 inline 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 inline 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)); } #define ENUM_PAGES \ X(Networks, "networks") \ X(Contexts, "contexts") \ X(Events, "events") \ X(Search, "search") \ X(Stylesheet, "stylesheet") enum { #define X(page, path) page, ENUM_PAGES #undef X PagesLen, }; extern const char *Pages[PagesLen]; #define ENUM_KEYS \ X(Network, "network", kvalid_stringne) \ X(Context, "context", kvalid_stringne) \ X(After, "after", kvalid_stringne) \ X(Before, "before", kvalid_stringne) \ X(Query, "query", kvalid_stringne) \ X(Offset, "offset", kvalid_int) enum { #define X(key, name, valid) key, ENUM_KEYS #undef X KeysLen, }; extern const struct kvalid Keys[KeysLen]; extern bool pagePublic; extern int pageLimit; extern int pageRecent; enum kcgi_err pageNetworks(struct kreq *req); enum kcgi_err pageContexts(struct kreq *req); enum kcgi_err pageEvents(struct kreq *req); enum kcgi_err pageSearch(struct kreq *req); static inline enum kcgi_err httpHead(struct kreq *req, enum khttp http, enum kmime mime) { return khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[http]) || khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[mime]); } static inline enum kcgi_err httpRedirect(struct kreq *req, const char *url) { return httpHead(req, KHTTP_302, KMIME_TEXT_PLAIN) || khttp_head(req, kresps[KRESP_LOCATION], "%s", url) || khttp_body(req) || khttp_printf(req, "%s\n", url); } static inline enum kcgi_err httpFail(struct kreq *req, enum khttp http) { return httpHead(req, http, KMIME_TEXT_PLAIN) || khttp_body(req) || khttp_printf(req, "%s\n", khttps[http]); } extern const char *htmlStylesheet; enum kcgi_err htmlHead(struct khtmlreq *html, const char *title); enum kcgi_err htmlNav( struct khtmlreq *html, const char *network, const char *context ); enum kcgi_err htmlFooter(struct khtmlreq *html); enum kcgi_err htmlEvent(struct khtmlreq *html, struct Event event);