# HG changeset patch # User David Demelier # Date 1580831083 -3600 # Node ID 836a698946f86a03d2b45443aca492daed8b1126 # Parent 15a06aa20298facdfac27291d32d8a042c1ecc81 pasterd: add basic routes diff -r 15a06aa20298 -r 836a698946f8 .hgignore --- a/.hgignore Tue Feb 04 13:35:52 2020 +0100 +++ b/.hgignore Tue Feb 04 16:44:43 2020 +0100 @@ -15,5 +15,9 @@ \.DS_Store$ # Temporary files. +extern/libsqlite3.a \.o$ \.d$ + +# Executables. +pasterd diff -r 15a06aa20298 -r 836a698946f8 Makefile --- a/Makefile Tue Feb 04 13:35:52 2020 +0100 +++ b/Makefile Tue Feb 04 16:44:43 2020 +0100 @@ -19,12 +19,12 @@ .POSIX: CC= cc -CFLAGS= -std=c18 -Wall -Wextra -pedantic -D_XOPEN_SOURCE=700 -g +CFLAGS= -std=c18 -pedantic -D_XOPEN_SOURCE=700 -g # Release # CFLAGS= -std=c18 -Wall -Wextra -pedantic -O3 -DNDEBUG -D_XOPEN_SOURCE=700 LDFLAGS= -static -lkcgi -lz -SRCS= database.c log.c pasterd.c paste.c util.c +SRCS= config.c database.c http.c log.c pasterd.c paste.c util.c OBJS= ${SRCS:.c=.o} DEPS= ${SRCS:.c=.d} @@ -33,17 +33,22 @@ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_DEFAULT_FOREIGN_KEYS=1 -UID= www +PREFIX= /usr/local +BINDIR= ${PREFIX}/bin +SHAREDIR= ${PREFIX}/share +VARDIR= ${PREFIX}/var + +DEFINES= -DSHAREDIR=\"${SHAREDIR}\" -DVARDIR=\"${VARDIR}\" .SUFFIXES: .SUFFIXES: .c .o -all: paster +all: pasterd -include ${DEPS} .c.o: - ${CC} ${CFLAGS} -MMD -Iextern -c $< + ${CC} ${CFLAGS} ${DEFINES} -MMD -Iextern -c $< extern/sqlite3.o: extern/sqlite3.c extern/sqlite3.h ${CC} ${CFLAGS} ${SQLITE_FLAGS} -MMD -c $< -o $@ @@ -58,7 +63,10 @@ rm -f extern/sqlite3.o extern/libsqlite3.a rm -f pasterd ${OBJS} ${DEPS} -run: pasterd - kfcgi -dv -s ./paster.sock -u ${UID} -U ${UID} -p . -- pasterd -f +install: + mkdir -p ${DESTDIR}${BINDIR} + cp pasterd ${DESTDIR}${BINDIR} + mkdir -p ${DESTDIR}${SHAREDIR}/paster + cp -R themes ${DESTDIR}${SHAREDIR}/paster .PHONY: all clean run diff -r 15a06aa20298 -r 836a698946f8 config.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.c Tue Feb 04 16:44:43 2020 +0100 @@ -0,0 +1,23 @@ +/* + * config.c -- pasterd options + * + * Copyright (c) 2020 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" + +struct config config = { + .themedir = SHAREDIR "/paster/themes/minimal" +}; diff -r 15a06aa20298 -r 836a698946f8 config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.h Tue Feb 04 16:44:43 2020 +0100 @@ -0,0 +1,29 @@ +/* + * config.h -- pasterd options + * + * Copyright (c) 2020 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef PASTER_CONFIG_H +#define PASTER_CONFIG_H + +#include + +extern struct config { + char themedir[PATH_MAX]; + int verbosity; +} config; + +#endif /* !PASTER_CONFIG_H */ diff -r 15a06aa20298 -r 836a698946f8 database.c --- a/database.c Tue Feb 04 13:35:52 2020 +0100 +++ b/database.c Tue Feb 04 16:44:43 2020 +0100 @@ -10,9 +10,6 @@ #include "paste.h" #include "util.h" -/* sqlite3 use const unsigned char *. */ -#define DUP(s) estrdup((const char *)(s)) - static sqlite3 *db; static const char *sql_init = @@ -41,7 +38,7 @@ " , visible\n" " , duration\n" " FROM paste\n" - " WHERE id = ?"; + " WHERE uuid = ?"; static const char *sql_insert = "INSERT INTO paste(\n" @@ -76,6 +73,13 @@ "\n" "END TRANSACTION"; +/* sqlite3 use const unsigned char *. */ +static char * +dup(const unsigned char *s) +{ + return estrdup(s ? (const char *)(s) : ""); +} + static const char * create_id(void) { @@ -100,11 +104,11 @@ static void convert(sqlite3_stmt *stmt, struct paste *paste) { - paste->uuid = DUP(sqlite3_column_text(stmt, 0)); - paste->title = DUP(sqlite3_column_text(stmt, 1)); - paste->author = DUP(sqlite3_column_text(stmt, 2)); - paste->language = DUP(sqlite3_column_text(stmt, 3)); - paste->code = DUP(sqlite3_column_text(stmt, 4)); + paste->uuid = dup(sqlite3_column_text(stmt, 0)); + paste->title = dup(sqlite3_column_text(stmt, 1)); + paste->author = dup(sqlite3_column_text(stmt, 2)); + paste->language = dup(sqlite3_column_text(stmt, 3)); + paste->code = dup(sqlite3_column_text(stmt, 4)); paste->timestamp = sqlite3_column_int64(stmt, 5); paste->visible = sqlite3_column_int(stmt, 6); paste->duration = sqlite3_column_int64(stmt, 7); diff -r 15a06aa20298 -r 836a698946f8 http.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/http.c Tue Feb 04 16:44:43 2020 +0100 @@ -0,0 +1,330 @@ +/* + * http.c -- HTTP parsing and rendering + * + * Copyright (c) 2020 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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_about(struct kreq *); +static void page_download(struct kreq *); + +enum page { + PAGE_INDEX, + PAGE_NEW, + PAGE_FORK, + PAGE_PASTE, + PAGE_ABOUT, + PAGE_DOWNLOAD, + PAGE_LAST /* Not used. */ +}; + +static const char *pages[] = { + [PAGE_INDEX] = "", + [PAGE_NEW] = "new", + [PAGE_FORK] = "fork", + [PAGE_PASTE] = "paste", + [PAGE_ABOUT] = "about", + [PAGE_DOWNLOAD] = "download", +}; + +static void (*handlers[])(struct kreq *req) = { + [PAGE_INDEX] = page_index, + [PAGE_NEW] = page_new, + [PAGE_FORK] = page_fork, + [PAGE_PASTE] = page_paste, + [PAGE_ABOUT] = page_about, + [PAGE_DOWNLOAD] = page_download +}; + +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 * +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 int +tmpl_paste(size_t index, void *arg) +{ + struct tmpl_paste *data = arg; + struct paste *paste = &data->paste; + + switch (index) { + case 0: + khttp_puts(data->req, paste->uuid); + break; + case 1: + khttp_puts(data->req, paste->title); + break; + case 2: + khttp_puts(data->req, paste->author); + break; + case 3: + khttp_puts(data->req, paste->language); + break; + case 4: + khttp_puts(data->req, paste->code); + break; + case 5: + /* TODO: timestamp here. */ + khttp_puts(data->req, "TODO"); + break; + case 6: + khttp_puts(data->req, bprintf("%s", paste->visible ? "Yes" : "No")); + break; + case 7: + /* TODO: convert time left. */ + khttp_puts(data->req, "TODO"); + break; + default: + break; + } + + return true; +} + +static int +tmpl_index_pastes(size_t index, void *arg) +{ + struct tmpl_index *data = arg; + struct paste *paste = &data->pastes[data->current]; + + switch (index) { + case 0: + khttp_puts(data->req, paste->uuid); + break; + case 1: + khttp_puts(data->req, paste->title); + break; + case 2: + khttp_puts(data->req, paste->author); + break; + case 3: + khttp_puts(data->req, paste->language); + break; + case 4: + khttp_puts(data->req, bprintf("%d", paste->duration)); + break; + default: + break; + } + + return true; +} + +static int +tmpl_index(size_t index, void *arg) +{ + /* No check, only one index. */ + struct tmpl_index *data = arg; + const char *keywords[] = { + "uuid", + "name", + "author", + "language", + "expiration" + }; + struct ktemplate kt = { + .key = keywords, + .keysz = 5, + .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 void +header(struct kreq *req) +{ + khttp_template(req, NULL, template("header.html")); +} + +static void +footer(struct kreq *req) +{ + khttp_template(req, NULL, template("footer.html")); +} + +static void +page_index(struct kreq *req) +{ + struct tmpl_index data = { + .req = req, + .count = 10 + }; + + khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[KMIME_TEXT_HTML]); + + if (!database_recents(data.pastes, &data.count)) { + khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[KHTTP_500]); + khttp_body(req); + khttp_template(req, NULL, template("500.html")); + } else { + const char *keywords[] = { "pastes" }; + struct ktemplate kt = { + .key = keywords, + .keysz = 1, + .arg = &data, + .cb = tmpl_index + }; + + khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[KHTTP_200]); + khttp_body(req); + header(req); + khttp_template(req, &kt, template("index.html")); + footer(req); + } + + khttp_free(req); +} + +static void +page_new(struct kreq *req) +{ + (void)req; +} + +static void +page_fork(struct kreq *req) +{ + (void)req; +} + +static void +page_paste(struct kreq *req) +{ + struct tmpl_paste data = { + .req = req + }; + + khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[KMIME_TEXT_HTML]); + + if (!database_get(&data.paste, req->path)) { + khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[KHTTP_404]); + khttp_body(req); + khttp_template(req, NULL, template("404.html")); + } else { + const char *keywords[] = { + "uuid", + "title", + "author", + "language", + "code", + "timestamp", + "visible", + "duration" + }; + const struct ktemplate kt = { + .key = keywords, + .keysz = 8, + .cb = tmpl_paste, + .arg = &data + }; + + khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[KHTTP_200]); + khttp_body(req); + header(req); + khttp_template(req, &kt, template("paste.html")); + footer(req); + khttp_free(req); + } +} + +static void +page_about(struct kreq *req) +{ + (void)req; +} + +static void +page_download(struct kreq *req) +{ + (void)req; +} + +static void +process(struct kreq *req) +{ + assert(req); + + handlers[req->page](req); +} + +void +http_fcgi_run(void) +{ + struct kreq req; + struct kfcgi *fcgi; + + if (khttp_fcgi_init(&fcgi, NULL, 0, pages, PAGE_LAST, 0) != KCGI_OK) + return; + + while (khttp_fcgi_parse(fcgi, &req) == KCGI_OK) + process(&req); + + khttp_fcgi_free(fcgi); +} + +void +http_cgi_run(void) +{ + struct kreq req; + + if (khttp_parse(&req, NULL, 0, pages, PAGE_LAST, 0) == KCGI_OK) + process(&req); +} diff -r 15a06aa20298 -r 836a698946f8 http.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/http.h Tue Feb 04 16:44:43 2020 +0100 @@ -0,0 +1,28 @@ +/* + * http.h -- HTTP parsing and rendering + * + * Copyright (c) 2020 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef PASTER_HTTP_H +#define PASTER_HTTP_H + +void +http_fcgi_run(void); + +void +http_cgi_run(void); + +#endif /* !PASTER_HTTP_H */ diff -r 15a06aa20298 -r 836a698946f8 pasterd.c --- a/pasterd.c Tue Feb 04 13:35:52 2020 +0100 +++ b/pasterd.c Tue Feb 04 16:44:43 2020 +0100 @@ -16,146 +16,47 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include -#include -#include -#include -#include -#include -#include - - #include #include +#include #include "database.h" - -#if 0 - -static const char *pages[] = { - "index", - "new", - "fork", - "get" -}; - - -static int -process(struct kreq *req) -{ - assert(req); +#include "http.h" +#include "log.h" - khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[KHTTP_200]); - khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[KMIME_TEXT_PLAIN]); - khttp_body(req); - khttp_puts(req, "== begin ==\n"); - - for (size_t i = 0; i < req->fieldsz; ++i) { - khttp_puts(req, req->fields[i].key); - khttp_putc(req, '='); - khttp_puts(req, req->fields[i].val); - khttp_putc(req, '\n'); - } - - khttp_puts(req, "== end ==\n"); - khttp_free(req); - - return 0; +static void +init(void) +{ + srand(time(NULL)); + log_open(); + database_open("test.db"); } -static int -kcgi_run(void) +static void +quit(void) { - struct kreq req; - struct kfcgi *fcgi; - - if (khttp_fcgi_init(&fcgi, NULL, 0, pages, 4, 0) != KCGI_OK) - return 1; - - while (khttp_fcgi_parse(fcgi, &req) == KCGI_OK) - process(&req); - - khttp_fcgi_free(fcgi); - - return 0; + database_finish(); + log_finish(); } - -static int -cgi_run(void) -{ - struct kreq req; - - if (khttp_parse(&req, NULL, 0, pages, 4, 0) != KCGI_OK) - return 1; - - return process(&req); -} - -#endif - -#include "util.h" -#include "paste.h" -#include "log.h" int main(int argc, char **argv) { - srand(time(NULL)); - - (void)argc; - (void)argv; - - struct paste paste = { - .title = estrdup("Test de C++"), - .author = estrdup("David Demelier"), - .language = estrdup("C"), - .code = estrdup("int main(void) { }"), - .duration = 60 - }; - - log_open(); - database_open("test.db"); - - + init(); - struct paste pastes[10]; - size_t n = 10; - - - database_clear(); - database_recents(pastes, &n); - for (size_t i = 0; i < n; ++i) { - printf("%s, %s, %lld\n", pastes[i].uuid, pastes[i].author, (long long int)pastes[i].timestamp); - paste_finish(&pastes[i]); - } - - - - paste_finish(&paste); - database_finish(); - log_finish(); - -#if 0 int opt; - int (*run)(void) = &(cgi_run); + void (*run)(void) = &(http_cgi_run); while ((opt = getopt(argc, argv, "f")) != -1) { switch (opt) { case 'f': - run = &(kcgi_run); + run = &(http_fcgi_run); break; default: break; } } - return run(); -#endif - -#if 0 - (void)argc; - (void)argv; - -#endif + run(); + quit(); } diff -r 15a06aa20298 -r 836a698946f8 themes/minimal/footer.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/minimal/footer.html Tue Feb 04 16:44:43 2020 +0100 @@ -0,0 +1,2 @@ + + diff -r 15a06aa20298 -r 836a698946f8 themes/minimal/header.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/minimal/header.html Tue Feb 04 16:44:43 2020 +0100 @@ -0,0 +1,6 @@ + + + + + + diff -r 15a06aa20298 -r 836a698946f8 themes/minimal/index-paste.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/minimal/index-paste.html Tue Feb 04 16:44:43 2020 +0100 @@ -0,0 +1,6 @@ + + @@name@@ + @@author@@ + @@language@@ + @@expiration@@ + diff -r 15a06aa20298 -r 836a698946f8 themes/minimal/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/minimal/index.html Tue Feb 04 16:44:43 2020 +0100 @@ -0,0 +1,16 @@ +

Recent pastes

+ + + + + + + + + + + + + @@pastes@@ + + diff -r 15a06aa20298 -r 836a698946f8 themes/minimal/paste.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/minimal/paste.html Tue Feb 04 16:44:43 2020 +0100 @@ -0,0 +1,35 @@ +
NameAuthorLanguageExpires in
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Identifier@@uuid@@
Title@@title@@
Author@@author@@
Language@@language@@
Date@@timestamp@@
Is visible?@@visible@@
Lifetime@@duration@@
+
+@@code@@
+	
diff -r 15a06aa20298 -r 836a698946f8 util.c --- a/util.c Tue Feb 04 13:35:52 2020 +0100 +++ b/util.c Tue Feb 04 16:44:43 2020 +0100 @@ -19,10 +19,10 @@ #include #include #include +#include #include #include #include -#include #include "util.h" @@ -51,3 +51,16 @@ return strcpy(ret, str); } + +const char * +bprintf(const char *fmt, ...) +{ + static char buf[BUFSIZ]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buf, sizeof (buf), fmt, ap); + va_end(ap); + + return buf; +} diff -r 15a06aa20298 -r 836a698946f8 util.h --- a/util.h Tue Feb 04 13:35:52 2020 +0100 +++ b/util.h Tue Feb 04 16:44:43 2020 +0100 @@ -19,12 +19,15 @@ #ifndef PASTER_UTIL_H #define PASTER_UTIL_H -#include +#include noreturn void -die(const char *fmt, ...); +die(const char *, ...); char * -estrdup(const char *str); +estrdup(const char *); + +const char * +bprintf(const char *, ...); #endif /* !PASTER_UTIL_H */