From 0a054aeb9ce8430a62be05283300ec34e9c2b0af Mon Sep 17 00:00:00 2001 From: "C. McEnroe" Date: Fri, 17 Jul 2020 13:23:21 -0400 Subject: Add export option Adding the export query parameter to any page downloads a .html with the default stylesheet embedded, and all navigation, forms and internal links removed, for being able to share some part of a private instance of litterbox. --- .gitignore | 2 +- Makefile | 7 +++---- contexts.c | 4 ++-- css.sh | 2 +- default.css | 5 ++++- events.c | 10 ++++++---- html.c | 49 ++++++++++++++++++++++++++++++++++++++----------- networks.c | 4 ++-- search.c | 10 +++++----- server.c | 3 +-- server.h | 21 +++++++++++++++++---- 11 files changed, 80 insertions(+), 37 deletions(-) diff --git a/.gitignore b/.gitignore index 791d6d0..94ee777 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ *.o .test config.mk -css.h +css.c scooper tags diff --git a/Makefile b/Makefile index 1cb3bf4..b6387ef 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ TEST_DB = ~/.local/share/litterbox/litterbox.sqlite -include config.mk OBJS += contexts.o +OBJS += css.o OBJS += events.o OBJS += html.o OBJS += networks.o @@ -24,9 +25,7 @@ scooper: ${OBJS} ${OBJS}: server.h -server.o: css.h - -css.h: css.sh default.css color.css +css.c: css.sh default.css color.css sh css.sh default.css color.css > $@ test: .test @@ -39,7 +38,7 @@ tags: *.c *.h ctags -w *.c *.h clean: - rm -f scooper ${OBJS} css.h .test tags + rm -f scooper ${OBJS} css.c .test tags install: scooper scooper.1 install -d ${PREFIX}/bin ${MANDIR}/man1 diff --git a/contexts.c b/contexts.c index 5c5e5cc..d55181b 100644 --- a/contexts.c +++ b/contexts.c @@ -69,7 +69,7 @@ enum kcgi_err contextsPage(struct kreq *req) { struct khtmlreq html; error = error || khtml_open(&html, req, 0) - || htmlHead(&html, network) + || htmlHead(&html, req, network) || htmlNav(&html, req); if (error) return error; @@ -142,5 +142,5 @@ enum kcgi_err contextsPage(struct kreq *req) { } if (result != SQLITE_DONE) errx(EX_SOFTWARE, "%s", sqlite3_errmsg(db)); - return htmlFooter(&html) || khtml_close(&html); + return htmlFooter(&html, req) || khtml_close(&html); } diff --git a/css.sh b/css.sh index 80f28d1..be3e44b 100644 --- a/css.sh +++ b/css.sh @@ -3,7 +3,7 @@ set -eu cat <fieldmap[Export]) return KCGI_OK; struct tm tm = {0}; if (!strptime(current, "%F", &tm)) { tm = *gmtime(&(time_t) { time(NULL) }); @@ -66,6 +67,7 @@ dateForm(struct khtmlreq *html, struct kreq *req, const char *current) { static enum kcgi_err displayForm(struct khtmlreq *html, struct kreq *req, bool tidy) { + if (req->fieldmap[Export]) return KCGI_OK; return 0 || khtml_attr( html, KELEM_FORM, @@ -203,7 +205,7 @@ enum kcgi_err eventsPage(struct kreq *req) { struct khtmlreq html; error = error || khtml_open(&html, req, 0) - || htmlHead(&html, context) + || htmlHead(&html, req, context) || htmlNav(&html, req) || khtml_elem(&html, KELEM_DIV) || dateForm(&html, req, time) @@ -257,7 +259,7 @@ enum kcgi_err eventsPage(struct kreq *req) { event.target = sqlite3_column_text(events, i++); event.message = sqlite3_column_text(events, i++); - if (!rows) { + if (!rows && !req->fieldmap[Export]) { char *href = NULL; char *page = pageURL( network, context, Before, event.time + eventsOverlap, tidy @@ -291,7 +293,7 @@ enum kcgi_err eventsPage(struct kreq *req) { } if (result != SQLITE_DONE) errx(EX_SOFTWARE, "%s", sqlite3_errmsg(db)); - if (rows && (rows == eventsLimit || req->fieldmap[Before])) { + if ((rows == eventsLimit || req->fieldmap[Before]) && !req->fieldmap[Export]) { char *href = pageURL( network, context, After, prevTime - eventsOverlap, tidy ); @@ -313,5 +315,5 @@ enum kcgi_err eventsPage(struct kreq *req) { if (error) return error; } - return htmlFooter(&html) || khtml_close(&html); + return htmlFooter(&html, req) || khtml_close(&html); } diff --git a/html.c b/html.c index a28d84b..2f6be5f 100644 --- a/html.c +++ b/html.c @@ -30,18 +30,30 @@ const char *htmlStylesheet = "stylesheet.css"; -enum kcgi_err htmlHead(struct khtmlreq *html, const char *title) { +static enum kcgi_err htmlCSS(struct khtmlreq *html, struct kreq *req) { + if (req->fieldmap[Export]) { + return 0 + || khtml_elem(html, KELEM_STYLE) + || khttp_puts(req, CSS) + || khtml_closeelem(html, 1); + } else { + return khtml_attr( + html, KELEM_LINK, + KATTR_REL, "stylesheet", + KATTR_HREF, htmlStylesheet, + KATTR__MAX + ); + } +} + +enum kcgi_err +htmlHead(struct khtmlreq *html, struct kreq *req, const char *title) { return khtml_elem(html, KELEM_DOCTYPE) || khtml_attr(html, KELEM_META, KATTR_CHARSET, "utf-8", KATTR__MAX) || khtml_elem(html, KELEM_TITLE) || khtml_puts(html, title) || khtml_closeelem(html, 1) - || khtml_attr( - html, KELEM_LINK, - KATTR_REL, "stylesheet", - KATTR_HREF, htmlStylesheet, - KATTR__MAX - ) + || htmlCSS(html, req) || khtml_elem(html, KELEM_H1) || khtml_puts(html, title) || khtml_closeelem(html, 1); @@ -60,6 +72,8 @@ htmlHidden(struct khtmlreq *html, struct kreq *req, enum Key key) { } enum kcgi_err htmlNav(struct khtmlreq *html, struct kreq *req) { + if (req->fieldmap[Export]) return KCGI_OK; + enum kcgi_err error = 0 || khtml_elem(html, KELEM_NAV) || khtml_elem(html, KELEM_OL) @@ -255,10 +269,10 @@ static const char *timestamp(time_t time) { } static enum kcgi_err -eventTime(struct khtmlreq *html, const struct Event *event) { +eventTime(struct khtmlreq *html, struct kreq *req, struct Event *event) { char *page = NULL; char *href = NULL; - if (event->network && event->context) { + if (event->network && event->context && !req->fieldmap[Export]) { page = khttp_urlpart( NULL, NULL, Pages[Events], Keys[Network].name, event->network, @@ -290,6 +304,12 @@ eventTime(struct khtmlreq *html, const struct Event *event) { static enum kcgi_err eventNetwork(struct khtmlreq *html, struct kreq *req, struct Event *event) { if (!req->fieldmap[Query] || req->fieldmap[Network]) return KCGI_OK; + if (req->fieldmap[Export]) { + return 0 + || khtml_attr(html, KELEM_TD, KATTR_CLASS, "network", KATTR__MAX) + || khtml_puts(html, event->network) + || khtml_closeelem(html, 1); + } char *href = khttp_urlpart( NULL, NULL, Pages[Search], Keys[Query].name, req->fieldmap[Query]->parsed.s, @@ -309,6 +329,12 @@ eventNetwork(struct khtmlreq *html, struct kreq *req, struct Event *event) { static enum kcgi_err eventContext(struct khtmlreq *html, struct kreq *req, struct Event *event) { if (!req->fieldmap[Query] || req->fieldmap[Context]) return KCGI_OK; + if (req->fieldmap[Export]) { + return 0 + || khtml_attr(html, KELEM_TD, KATTR_CLASS, "context", KATTR__MAX) + || khtml_puts(html, event->context) + || khtml_closeelem(html, 1); + } char *href = khttp_urlpart( NULL, NULL, Pages[Search], Keys[Query].name, req->fieldmap[Query]->parsed.s, @@ -445,7 +471,7 @@ htmlEvent(struct khtmlreq *html, struct kreq *req, struct Event *event) { KATTR_CLASS, KATTRX_STRING, type, KATTR__MAX ) - || eventTime(html, event) + || eventTime(html, req, event) || eventNetwork(html, req, event) || eventContext(html, req, event) || eventNick(html, event) @@ -461,7 +487,8 @@ static const char *Columns = { "network, channel, query, nick, user, target, message" }; -enum kcgi_err htmlFooter(struct khtmlreq *html) { +enum kcgi_err htmlFooter(struct khtmlreq *html, struct kreq *req) { + if (req->fieldmap[Export]) return KCGI_OK; return 0 || khtml_closeto(html, 0) || khtml_elem(html, KELEM_FOOTER) diff --git a/networks.c b/networks.c index af3ad80..91f5131 100644 --- a/networks.c +++ b/networks.c @@ -53,7 +53,7 @@ enum kcgi_err networksPage(struct kreq *req) { struct khtmlreq html; error = error || khtml_open(&html, req, 0) - || htmlHead(&html, "Litterbox") + || htmlHead(&html, req, "Litterbox") || htmlNav(&html, req); if (error) return error; @@ -100,5 +100,5 @@ enum kcgi_err networksPage(struct kreq *req) { if (error) return error; } if (result != SQLITE_DONE) errx(EX_SOFTWARE, "%s", sqlite3_errmsg(db)); - return htmlFooter(&html) || khtml_close(&html); + return htmlFooter(&html, req) || khtml_close(&html); } diff --git a/search.c b/search.c index 67ce79c..d2bd4ca 100644 --- a/search.c +++ b/search.c @@ -78,12 +78,12 @@ enum kcgi_err searchPage(struct kreq *req) { struct khtmlreq html; error = error || khtml_open(&html, req, 0) - || htmlHead(&html, query) + || htmlHead(&html, req, query) || htmlNav(&html, req) || khtml_elem(&html, KELEM_TABLE); if (error) return error; - if (offset) { + if (offset && !req->fieldmap[Export]) { int64_t prev = offset - eventsLimit; char *href = offsetURL(network, context, query, (prev > 0 ? prev : 0)); error = 0 @@ -128,11 +128,11 @@ enum kcgi_err searchPage(struct kreq *req) { || khtml_attr(&html, KELEM_TR, KATTR_CLASS, "error", KATTR__MAX) || khtml_elem(&html, KELEM_TH) || khtml_puts(&html, sqlite3_errmsg(db)) - || htmlFooter(&html) + || htmlFooter(&html, req) || khtml_close(&html); } - if (rows == eventsLimit) { + if (rows == eventsLimit && !req->fieldmap[Export]) { char *href = offsetURL(network, context, query, offset + eventsLimit); error = 0 || khtml_attr(&html, KELEM_TR, KATTR_CLASS, "page", KATTR__MAX) @@ -152,5 +152,5 @@ enum kcgi_err searchPage(struct kreq *req) { if (error) return error; } - return htmlFooter(&html) || khtml_close(&html); + return htmlFooter(&html, req) || khtml_close(&html); } diff --git a/server.c b/server.c index 514dad3..8aea1e5 100644 --- a/server.c +++ b/server.c @@ -21,7 +21,6 @@ #include #include -#include "css.h" #include "server.h" sqlite3 *db; @@ -62,7 +61,7 @@ static enum kcgi_err stylesheet(struct kreq *req) { ) || khttp_body(req); if (req->method == KMETHOD_HEAD) return error; - return error || khttp_write(req, CSS, sizeof(CSS) - 1); + return error || khttp_puts(req, CSS); } static enum kcgi_err dispatch(struct kreq *req) { diff --git a/server.h b/server.h index 20d1803..4607d76 100644 --- a/server.h +++ b/server.h @@ -42,6 +42,8 @@ X(Search, "search") \ X(Stylesheet, "stylesheet") +extern const char *CSS; + enum { #define X(page, path) page, ENUM_PAGES @@ -57,7 +59,8 @@ extern const char *Pages[PagesLen]; X(Before, "before", kvalid_stringne) \ X(Tidy, "tidy", kvalid_int) \ X(Query, "query", kvalid_stringne) \ - X(Offset, "offset", kvalid_int) + X(Offset, "offset", kvalid_int) \ + X(Export, "export", NULL) enum Key { #define X(key, name, valid) key, @@ -166,19 +169,29 @@ struct Event { }; extern const char *htmlStylesheet; -enum kcgi_err htmlHead(struct khtmlreq *html, const char *title); +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); +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) { - return khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[http]) + 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 -- cgit 1.4.1