/* $Id$ */ /* * Copyright (c) 2016 Kristaps Dzonsons * * Permission to use, copy, modify, and 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 #include "extern.h" /* * Start with five pages. * As you add more pages, you'll start by giving them an identifier key * in this enum. */ enum page { PAGE_INDEX, PAGE_BOOKS, PAGE_AUTHORS, PAGE_SERIES, PAGE__MAX }; enum key { KEY_ID, KEY__MAX }; static const char *const pages[PAGE__MAX] = { "index", /* PAGE_INDEX */ "books", /* PAGE_BOOKS */ "authors", /* PAGE_AUTHORS */ "series", /* PAGE_SERIES */ }; static const struct kvalid keys[KEY__MAX] = { { kvalid_int, "id" }, /* KEY_ID */ }; /* * Fill out all HTTP secure headers. * Use the existing document's MIME type. */ static void http_alloc(struct kreq *r, enum khttp code) { khttp_head(r, kresps[KRESP_STATUS], "%s", khttps[code]); khttp_head(r, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[r->mime]); khttp_head(r, "X-Content-Type-Options", "nosniff"); khttp_head(r, "X-Frame-Options", "DENY"); khttp_head(r, "X-XSS-Protection", "1; mode=block"); } /* * Fill out all headers with http_alloc() then start the HTTP document * body (no more headers after this point!) */ static void http_open(struct kreq *r, enum khttp code) { http_alloc(r, code); khttp_body(r); } void puterror(struct kjsonreq *req, char *message) { kjson_obj_open(req); kjson_putstringp(req, "msg", message); kjson_obj_close(req); } void putbook(struct kjsonreq *req, struct book *b) { kjson_obj_open(req); kjson_putintp(req, "id", b->id); kjson_putstringp(req, "title", b->title); kjson_obj_close(req); } static void sendbooks(struct kreq *r) { struct kjsonreq req; const char *errid; int64_t id = -1; struct book *b = NULL; int res, i = 0; if (NULL != r->fieldmap[KEY_ID]) id = r->fieldmap[KEY_ID]->parsed.i; if (r->path[0] != '\0') id = strtonum(r->path, INT64_MIN, INT64_MAX, &errid); if (id > 0) { b = db_book_load(r, id); } http_open(r, (id > 0 && NULL == b) ? KHTTP_404 : KHTTP_200); kjson_open(&req, r); if (id >0 && NULL == b) { puterror(&req, "Unknown book"); } else if (NULL != b) { putbook(&req, b); db_book_free(b); } else { kjson_array_open(&req); struct book **books = kcalloc(10, sizeof(struct book)); res = db_books_load(r, books, 10); while (i < res) { putbook(&req, books[i]); db_book_free(books[i]); i++; } free(books); kjson_array_close(&req); } kjson_close(&req); } static void sendauthors(struct kreq *r) { struct kjsonreq req; http_open(r, KHTTP_200); kjson_open(&req, r); kjson_obj_open(&req); kjson_putstringp(&req, "data", "authors"); kjson_obj_close(&req); kjson_close(&req); } static void sendseries(struct kreq *r) { struct kjsonreq req; http_open(r, KHTTP_200); kjson_open(&req, r); kjson_obj_open(&req); kjson_putstringp(&req, "data", "series"); kjson_obj_close(&req); kjson_close(&req); } static void sendindex(struct kreq *r) { struct kjsonreq req; http_open(r, KHTTP_200); kjson_open(&req, r); kjson_obj_open(&req); kjson_putstringp(&req, "data", "index"); kjson_obj_close(&req); kjson_close(&req); } int main(void) { struct kreq r; enum kcgi_err er; /* Actually parse HTTP document. */ er = khttp_parsex(&r, ksuffixmap, kmimetypes, KMIME__MAX, keys, KEY__MAX, pages, PAGE__MAX, KMIME_APP_JSON, PAGE_INDEX, NULL, NULL, 0, NULL); if (KCGI_OK != er) { fprintf(stderr, "HTTP parse error: %d\n", er); return(EXIT_FAILURE); } #ifdef __OpenBSD__ if (-1 == pledge("stdio rpath cpath wpath flock fattr", NULL)) { fputs("pledge", stderr); khttp_free(&r); return(EXIT_FAILURE); } #endif /* * Front line of defence: make sure we're a proper method, make * sure we're a page, make sure we're a JSON file. */ if (KMETHOD_GET != r.method && KMETHOD_POST != r.method) { http_open(&r, KHTTP_405); khttp_free(&r); return(EXIT_SUCCESS); } else if (PAGE__MAX == r.page || KMIME_APP_JSON != r.mime) { http_open(&r, KHTTP_404); khttp_puts(&r, "Page not found."); khttp_free(&r); return(EXIT_SUCCESS); } if ( ! db_open(&r, DATADIR "/" DATABASE)) { http_open(&r, KHTTP_500); json_emptydoc(&r); khttp_free(&r); return(EXIT_SUCCESS); } switch (r.page) { case (PAGE_INDEX): sendindex(&r); break; case (PAGE_BOOKS): sendbooks(&r); break; case (PAGE_AUTHORS): sendauthors(&r); break; case (PAGE_SERIES): sendseries(&r); break; default: abort(); } db_close(&r); khttp_free(&r); return(EXIT_SUCCESS); }