/* 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 // Why does it return (const unsigned char *)? #define sqlite3_column_text(...) (const char *)sqlite3_column_text(__VA_ARGS__) #define SQL(...) #__VA_ARGS__ #define ENUM_PAGES \ X("networks", Networks) \ X("contexts", Contexts) \ X("events", Events) \ X("search", Search) \ X("stylesheet", Stylesheet) extern const char *CSS; enum { #define X(path, page) page, ENUM_PAGES #undef X PagesLen, }; extern const char *Pages[PagesLen]; #define ENUM_KEYS \ X(kvalid_stringne, "network", Network) \ X(kvalid_stringne, "context", Context) \ X(kvalid_stringne, "before", Before) \ X(kvalid_stringne, "after", After) \ X(kvalid_stringne, "query", Query) \ X(kvalid_int, "offset", Offset) \ X(kvalid_int, "tidy", Tidy) \ X(NULL, "export", Export) enum Key { #define X(valid, name, key) key, ENUM_KEYS #undef X KeysLen, }; extern const struct kvalid Keys[KeysLen]; extern int contextsRecent; extern bool contextsPublic; extern const char *NetworksQuery; extern const char *ContextsQuery; extern const char *ContextsMOTDQuery; enum kcgi_err networksPage(struct kreq *req); enum kcgi_err contextsPage(struct kreq *req); extern int eventsGap; extern int eventsOverlap; extern int eventsLimit; extern const char *EventsTopicQuery; extern const char *EventsAfterQuery; extern const char *EventsBeforeQuery; extern const char *SearchQuery; enum kcgi_err eventsPage(struct kreq *req); enum kcgi_err searchPage(struct kreq *req); extern sqlite3 *db; #define ENUM_STMTS \ X(networks, NetworksQuery) \ X(contexts, ContextsQuery) \ X(motd, ContextsMOTDQuery) \ X(topic, EventsTopicQuery) \ X(eventsAfter, EventsAfterQuery) \ X(eventsBefore, EventsBeforeQuery) \ X(search, SearchQuery) extern struct Statements { #define X(name, query) sqlite3_stmt *name; ENUM_STMTS #undef X } stmt; static inline void dbPrepare(sqlite3_stmt **stmt, const char *query) { int error = sqlite3_prepare_v3( db, query, -1, SQLITE_PREPARE_PERSISTENT, stmt, NULL ); if (error) errx(EX_SOFTWARE, "%s: %s", sqlite3_errmsg(db), query); } 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)); } enum { DatabaseVersionMin = 4, DatabaseVersionMax = 5, }; #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 bool htmlHideHost; extern const char *htmlStylesheet; enum kcgi_err htmlHead( struct khtmlreq *html, struct kreq *req, const char *title ); enum kcgi_err htmlHidden(struct khtmlreq *html, struct kreq *req, enum Key key); enum kcgi_err htmlNav(struct khtmlreq *html, struct kreq *req); enum kcgi_err htmlIRC(struct khtmlreq *html, const char *str); enum kcgi_err htmlEvent( struct khtmlreq *html, struct kreq *req, struct Event *event ); enum kcgi_err htmlFooter(struct khtmlreq *html, struct kreq *req); static inline enum kcgi_err httpHead(struct kreq *req, enum khttp http, enum kmime mime) { enum kcgi_err error = 0 || khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[http]) || khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[mime]); if (req->fieldmap[Export]) { error = error || khttp_head( req, kresps[KRESP_CONTENT_DISPOSITION], "attachment; filename=\"%s.%s\"", req->pagename, ksuffixes[mime] ); } return error; } 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]); }