Mercurial > paster
diff http.c @ 51:07b6887d3557
pasterd: split into individual pages
While here add a similar mini.css theme as in imgup.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 21 Dec 2020 18:22:44 +0100 |
parents | 48834441dc86 |
children | fba88439ec0a |
line wrap: on
line diff
--- a/http.c Mon Apr 27 12:08:16 2020 +0200 +++ b/http.c Mon Dec 21 18:22:44 2020 +0100 @@ -17,34 +17,24 @@ */ #include <sys/types.h> -#include <sys/stat.h> #include <assert.h> -#include <ctype.h> -#include <limits.h> #include <stdarg.h> #include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> #include <kcgi.h> -#include <kcgihtml.h> -#include "config.h" #include "database.h" #include "http.h" #include "log.h" -#include "paste.h" -#include "util.h" -static void page_index(struct kreq *); -static void page_new(struct kreq *); -static void page_fork(struct kreq *); -static void page_paste(struct kreq *); -static void page_download(struct kreq *); -static void page_search(struct kreq *); -static void page_static(struct kreq *); +#include "page-download.h" +#include "page-fork.h" +#include "page-index.h" +#include "page-new.h" +#include "page-paste.h" +#include "page-search.h" +#include "page-static.h" +#include "page.h" enum page { PAGE_INDEX, @@ -77,837 +67,6 @@ [PAGE_STATIC] = page_static }; -struct tmpl_index { - struct kreq *req; - struct paste pastes[10]; - size_t count; - size_t current; -}; - -struct tmpl_paste { - struct kreq *req; - struct paste paste; -}; - -static const char *tmpl_index_keywords[] = { - "pastes" -}; - -static const char *tmpl_index_pastes_keywords[] = { - "id", - "name", - "author", - "language", - "expiration", - "date" -}; - -static const char *tmpl_paste_keywords[] = { - "id", - "title", - "author", - "language", - "code", - "timestamp", - "visible", - "duration" -}; - -static const char *tmpl_new_keywords[] = { - "title", /* /fork only */ - "author", /* /fork only */ - "code", /* /fork only */ - "private", /* /fork only */ - "languages", - "durations" -}; - -static const char *languages[] = { - "nohighlight", - "1c", - "abnf", - "accesslog", - "actionscript", - "ada", - "apache", - "applescript", - "arduino", - "armasm", - "asciidoc", - "aspectj", - "autohotkey", - "autoit", - "avrasm", - "awk", - "axapta", - "bash", - "basic", - "bnf", - "brainfuck", - "cal", - "capnproto", - "ceylon", - "clean", - "clojure", - "clojure-repl", - "cmake", - "coffeescript", - "coq", - "cos", - "cpp", - "crmsh", - "crystal", - "cs", - "csp", - "css", - "dart", - "delphi", - "diff", - "django", - "d", - "dns", - "dockerfile", - "dos", - "dsconfig", - "dts", - "dust", - "ebnf", - "elixir", - "elm", - "erb", - "erlang", - "erlang-repl", - "excel", - "fix", - "flix", - "fortran", - "fsharp", - "gams", - "gauss", - "gcode", - "gherkin", - "glsl", - "go", - "golo", - "gradle", - "groovy", - "haml", - "handlebars", - "haskell", - "haxe", - "hsp", - "htmlbars", - "http", - "hy", - "inform7", - "ini", - "irpf90", - "java", - "javascript", - "jboss-cli", - "json", - "julia", - "julia-repl", - "kotlin", - "lasso", - "ldif", - "leaf", - "less", - "lisp", - "livecodeserver", - "livescript", - "llvm", - "lsl", - "lua", - "makefile", - "markdown", - "mathematica", - "matlab", - "maxima", - "mel", - "mercury", - "mipsasm", - "mizar", - "mojolicious", - "monkey", - "moonscript", - "n1ql", - "nginx", - "nimrod", - "nix", - "nsis", - "objectivec", - "ocaml", - "openscad", - "oxygene", - "parser3", - "perl", - "pf", - "php", - "pony", - "powershell", - "processing", - "profile", - "prolog", - "protobuf", - "puppet", - "purebasic", - "python", - "q", - "qml", - "rib", - "r", - "roboconf", - "routeros", - "rsl", - "ruby", - "ruleslanguage", - "rust", - "scala", - "scheme", - "scilab", - "scss", - "shell", - "smali", - "smalltalk", - "sml", - "sqf", - "sql", - "stan", - "stata", - "step21", - "stylus", - "subunit", - "swift", - "taggerscript", - "tap", - "tcl", - "tex", - "thrift", - "tp", - "twig", - "typescript", - "vala", - "vbnet", - "vbscript-html", - "vbscript", - "verilog", - "vhdl", - "vim", - "x86asm", - "xl", - "xml", - "xquery", - "yaml", - "zephir", - NULL -}; - -static const struct { - const char *title; - long long int secs; -} durations[] = { - { "month", PASTE_MONTH }, - { "week", PASTE_WEEK }, - { "day", PASTE_DAY }, - { "hour", PASTE_HOUR }, - { NULL, -1 } -}; - -static const char * -template(const char *filename) -{ - /* Build path to the template file. */ - static char path[PATH_MAX]; - - snprintf(path, sizeof (path), "%s/%s", config.themedir, filename); - - return path; -} - -static long long int -duration(const char *val) -{ - if (strcmp(val, "hour") == 0) - return PASTE_HOUR; - if (strcmp(val, "day") == 0) - return PASTE_DAY; - if (strcmp(val, "week") == 0) - return PASTE_WEEK; - if (strcmp(val, "month") == 0) - return PASTE_MONTH; - - /* Default to month. */ - return PASTE_MONTH; -} - -static const char * -ttl(time_t timestamp, long long int duration) -{ - const time_t now = time(NULL); - const long long int left = duration - difftime(now, timestamp); - - if (left < PASTE_HOUR) - return bprintf("%lld minute(s)", left / 60); - if (left < PASTE_DAY) - return bprintf("%lld hour(s)", left / 3600); - - /* Other in days. */ - return bprintf("%lld day(s)", left / 86400); -} - -static void -replace(char **dst, const char *s) -{ - assert(dst); - assert(s); - - /* Trim leading spaces. */ - while (*s && isspace(*s)) - s++; - - if (*s) { - free(*dst); - *dst = estrdup(s); - } -} - -static void -render_languages(struct kreq *req, const struct paste *paste) -{ - for (const char **l = languages; *l != NULL; ++l) { - const char *line; - - if (paste->language && strcmp(paste->language, *l) == 0) - line = bprintf("<option value=\"%s\" selected>%s</option>", *l, *l); - else - line = bprintf("<option value=\"%s\">%s</option>", *l, *l); - - khttp_puts(req, line); - } -} - -static void -render_durations(struct kreq *req, const struct paste *paste) -{ - for (size_t i = 0; durations[i].title != NULL; ++i) { - const char *line; - - if (paste->duration == durations[i].secs) - line = bprintf("<option value=\"%s\" selected>%s</option>", - durations[i].title, durations[i].title); - else - line = bprintf("<option value=\"%s\">%s</option>", - durations[i].title, durations[i].title); - - khttp_puts(req, line); - } -} - -static int -tmpl_paste(size_t index, void *arg) -{ - struct tmpl_paste *data = arg; - struct paste *paste = &data->paste; - struct khtmlreq htmlreq; - - khtml_open(&htmlreq, data->req, KHTML_PRETTY); - - switch (index) { - case 0: - khtml_puts(&htmlreq, paste->id); - break; - case 1: - khtml_puts(&htmlreq, paste->title); - break; - case 2: - khtml_puts(&htmlreq, paste->author); - break; - case 3: - khtml_puts(&htmlreq, paste->language); - break; - case 4: - khtml_puts(&htmlreq, paste->code); - break; - case 5: - khtml_puts(&htmlreq, bstrftime("%c", localtime(&paste->timestamp))); - break; - case 6: - khtml_puts(&htmlreq, bprintf("%s", paste->visible ? "Yes" : "No")); - break; - case 7: - khtml_puts(&htmlreq, ttl(paste->timestamp, paste->duration)); - break; - default: - break; - } - - khtml_close(&htmlreq); - - return true; -} - -static int -tmpl_index_pastes(size_t index, void *arg) -{ - struct tmpl_index *data = arg; - struct paste *paste = &data->pastes[data->current]; - struct khtmlreq htmlreq; - - khtml_open(&htmlreq, data->req, KHTML_PRETTY); - - switch (index) { - case 0: - khtml_puts(&htmlreq, paste->id); - break; - case 1: - khtml_puts(&htmlreq, paste->title); - break; - case 2: - khtml_puts(&htmlreq, paste->author); - break; - case 3: - khtml_puts(&htmlreq, paste->language); - break; - case 4: - khtml_puts(&htmlreq, ttl(paste->timestamp, paste->duration)); - break; - case 5: - khtml_puts(&htmlreq, bstrftime("%c", localtime(&paste->timestamp))); - break; - default: - break; - } - - khtml_close(&htmlreq); - - return true; -} - -static int -tmpl_index(size_t index, void *arg) -{ - /* No check, only one index. */ - (void)index; - - struct tmpl_index *data = arg; - const struct ktemplate kt = { - .key = tmpl_index_pastes_keywords, - .keysz = 6, - .arg = data, - .cb = tmpl_index_pastes - }; - - for (size_t i = 0; i < data->count; ++i) { - khttp_template(data->req, &kt, template("index-paste.html")); - data->current++; - } - - return true; -} - -static int -tmpl_new(size_t index, void *arg) -{ - struct tmpl_paste *data = arg; - struct paste *paste = &data->paste; - struct khtmlreq htmlreq; - - khtml_open(&htmlreq, data->req, KHTML_PRETTY); - - switch (index) { - case 0: - if (paste->title) - khtml_puts(&htmlreq, paste->title); - break; - case 1: - if (paste->author) - khtml_puts(&htmlreq, paste->author); - break; - case 2: - if (paste->code) - khtml_puts(&htmlreq, paste->code); - break; - case 3: - /* Add checked attribute to combobox. */ - if (!paste->visible) - khttp_puts(data->req, "checked"); - break; - case 4: - render_languages(data->req, paste); - break; - case 5: - render_durations(data->req, paste); - break; - default: - break; - }; - - khtml_close(&htmlreq); - - return true; -} - -static void -page_header(struct kreq *req) -{ - khttp_template(req, NULL, template("header.html")); -} - -static void -page_footer(struct kreq *req) -{ - khttp_template(req, NULL, template("footer.html")); -} - -static void -page(struct kreq *req, const struct ktemplate *tmpl, enum khttp status, const char *file) -{ - khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[KMIME_TEXT_HTML]); - khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[status]); - khttp_body(req); - page_header(req); - khttp_template(req, tmpl, template(file)); - page_footer(req); - khttp_free(req); -} - -static void -page_index_get(struct kreq *req) -{ - struct tmpl_index data = { - .req = req, - .count = 10 - }; - - if (!database_recents(data.pastes, &data.count)) - page(req, NULL, KHTTP_500, "500.html"); - else { - struct ktemplate kt = { - .key = tmpl_index_keywords, - .keysz = 1, - .arg = &data, - .cb = tmpl_index - }; - - page(req, &kt, KHTTP_200, "index.html"); - } - - for (size_t i = 0; i < data.count; ++i) - paste_finish(&data.pastes[i]); -} - -static void -page_index(struct kreq *req) -{ - switch (req->method) { - case KMETHOD_GET: - page_index_get(req); - break; - default: - page(req, NULL, KHTTP_400, "400.html"); - break; - } -} - -static void -page_new_get(struct kreq *req) -{ - struct tmpl_paste data = { - .req = req - }; - const struct ktemplate kt = { - .key = tmpl_new_keywords, - .keysz = 6, - .cb = tmpl_new, - .arg = &data - }; - - page(req, &kt, KHTTP_200, "new.html"); -} - -static void -page_new_post(struct kreq *req) -{ - struct paste paste = { - .author = estrdup("Anonymous"), - .title = estrdup("Untitled"), - .language = estrdup("nohighlight"), - .visible = true - }; - - for (size_t i = 0; i < req->fieldsz; ++i) { - const char *key = req->fields[i].key; - const char *val = req->fields[i].val; - - if (strcmp(key, "title") == 0) - replace(&paste.title, val); - else if (strcmp(key, "author") == 0) - replace(&paste.author, val); - else if (strcmp(key, "language") == 0) - replace(&paste.language, val); - else if (strcmp(key, "duration") == 0) - paste.duration = duration(val); - else if (strcmp(key, "code") == 0) - paste.code = estrdup(val); - else if (strcmp(key, "private") == 0) - paste.visible = strcmp(val, "on") != 0; - } - - /* Add empty string if needed. */ - if (!paste.code) - paste.code = estrdup(""); - - if (!database_insert(&paste)) - page(req, NULL, KHTTP_500, "500.html"); - else { - /* Redirect to paste details. */ - khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[KHTTP_302]); - khttp_head(req, kresps[KRESP_LOCATION], "/paste/%s", paste.id); - khttp_body(req); - khttp_free(req); - } - - paste_finish(&paste); -} - -static void -page_new(struct kreq *req) -{ - switch (req->method) { - case KMETHOD_GET: - page_new_get(req); - break; - case KMETHOD_POST: - page_new_post(req); - break; - default: - break; - } -} - -static void -page_fork_get(struct kreq *req) -{ - struct tmpl_paste data = { - .req = req - }; - - if (!database_get(&data.paste, req->path)) - page(req, NULL, KHTTP_404, "404.html"); - else { - const struct ktemplate kt = { - .key = tmpl_new_keywords, - .keysz = 6, - .cb = tmpl_new, - .arg = &data - }; - - page(req, &kt, KHTTP_200, "new.html"); - paste_finish(&data.paste); - } -} - -static void -page_fork(struct kreq *req) -{ - switch (req->method) { - case KMETHOD_GET: - page_fork_get(req); - break; - default: - page(req, NULL, KHTTP_400, "400.html"); - break; - } -} - -static void -page_paste_get(struct kreq *req) -{ - struct tmpl_paste data = { - .req = req - }; - - if (!database_get(&data.paste, req->path)) - page(req, NULL, KHTTP_404, "404.html"); - else { - const struct ktemplate kt = { - .key = tmpl_paste_keywords, - .keysz = 8, - .cb = tmpl_paste, - .arg = &data - }; - - page(req, &kt, KHTTP_200, "paste.html"); - paste_finish(&data.paste); - } -} - -static void -page_paste(struct kreq *req) -{ - switch (req->method) { - case KMETHOD_GET: - page_paste_get(req); - break; - default: - page(req, NULL, KHTTP_400, "400.html"); - break; - } -} - -static void -page_download_get(struct kreq *req) -{ - struct paste paste; - - if (!database_get(&paste, req->path)) - page(req, NULL, KHTTP_404, "404.html"); - else { - khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[KMIME_APP_OCTET_STREAM]); -#if 0 - /* TODO: this seems to generated truncated files. */ - khttp_head(req, kresps[KRESP_CONTENT_LENGTH], "%zu", strlen(paste.code)); -#endif - khttp_head(req, kresps[KRESP_CONNECTION], "keep-alive"); - khttp_head(req, kresps[KRESP_CONTENT_DISPOSITION], - "attachment; filename=\"%s.%s\"", paste.id, paste.language); - khttp_body(req); - khttp_puts(req, paste.code); - khttp_free(req); - paste_finish(&paste); - } -} - -static void -page_download(struct kreq *req) -{ - switch (req->method) { - case KMETHOD_GET: - page_download_get(req); - break; - default: - page(req, NULL, KHTTP_400, "400.html"); - break; - } -} - -static void -page_search_get(struct kreq *req) -{ - /* We re-use the /new form with an empty paste. */ - struct tmpl_paste data = { - .req = req - }; - const struct ktemplate kt = { - .key = tmpl_new_keywords, - .keysz = 6, - .cb = tmpl_new, - .arg = &data - }; - - page(req, &kt, KHTTP_200, "search.html"); -} - -static void -page_search_post(struct kreq *req) -{ - struct tmpl_index data = { - .req = req, - .count = 10 - }; - - const char *title = NULL; - const char *author = NULL; - const char *language = NULL; - - for (size_t i = 0; i < req->fieldsz; ++i) { - const char *key = req->fields[i].key; - const char *val = req->fields[i].val; - - if (strcmp(key, "title") == 0) - title = val; - else if (strcmp(key, "author") == 0) - author = val; - else if (strcmp(key, "language") == 0) - language = val; - } - - /* Sets to null if they are empty. */ - if (title && strlen(title) == 0) - title = NULL; - if (author && strlen(author) == 0) - author = NULL; - if (language && strlen(language) == 0) - language = NULL; - - if (!database_search(data.pastes, &data.count, title, author, language)) - page(req, NULL, KHTTP_500, "500.html"); - else { - struct ktemplate kt = { - .key = tmpl_index_keywords, - .keysz = 1, - .arg = &data, - .cb = tmpl_index - }; - - page(req, &kt, KHTTP_200, "index.html"); - } - - for (size_t i = 0; i < data.count; ++i) - paste_finish(&data.pastes[i]); -} - -static void -page_search(struct kreq *req) -{ - switch (req->method) { - case KMETHOD_GET: - page_search_get(req); - break; - case KMETHOD_POST: - page_search_post(req); - break; - default: - page(req, NULL, KHTTP_400, "400.html"); - break; - } -} - -static void -page_static_get(struct kreq *req) -{ - struct stat st; - char path[PATH_MAX]; - - snprintf(path, sizeof (path), "%s%s", config.themedir, req->fullpath); - - if (stat(path, &st) < 0) - page(req, NULL, KHTTP_404, "404.html"); - else { - khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[KHTTP_200]); - khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[req->mime]); - khttp_head(req, kresps[KRESP_CONTENT_LENGTH], - "%llu", (unsigned long long)(st.st_size)); - khttp_body(req); - khttp_template(req, NULL, path); - khttp_free(req); - } -} - -static void -page_static(struct kreq *req) -{ - switch (req->method) { - case KMETHOD_GET: - page_static_get(req); - break; - default: - page(req, NULL, KHTTP_400, "400.html"); - break; - } -} - static void process(struct kreq *req) { @@ -916,7 +75,7 @@ log_debug("http: accessing page '%s'", req->path); if (req->page == PAGE_LAST) - page(req, NULL, KHTTP_404, "404.html"); + page(req, NULL, KHTTP_404, "pages/404.html"); else handlers[req->page](req); }