/* * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv> * * 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 <stdint.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "kcgi.h" #include "kcgihtml.h" enum page { PAGE_INDEX, /* /index or just / */ PAGE_SENDDATA, /* /senddata */ PAGE__MAX }; enum key { KEY_INTEGER, KEY_FILE, KEY_PAGECOUNT, KEY_PAGESIZE, KEY__MAX }; typedef void (*disp)(struct kreq *); static void senddata(struct kreq *); static void sendindex(struct kreq *); static const disp disps[PAGE__MAX] = { sendindex, /* PAGE_INDEX */ senddata, /* PAGE_SENDDATA */ }; static const struct kvalid keys[KEY__MAX] = { { kvalid_int, "integer" }, /* KEY_INTEGER */ { NULL, "file" }, /* KEY_FILE */ { kvalid_uint, "count" }, /* KEY_PAGECOUNT */ { kvalid_uint, "size" }, /* KEY_PAGESIZE */ }; static const char *const pages[PAGE__MAX] = { "index", /* PAGE_INDEX */ "senddata" /* PAGE_SENDDATA */ }; static void resp_open(struct kreq *req, enum khttp http) { khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[http]); khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[req->mime]); khttp_body(req); } static void senddata(struct kreq *req) { int64_t i, j, k, nm, sz; char *buf; struct khtmlreq r; nm = 1024 * 1024; if (NULL != req->fieldmap[KEY_PAGECOUNT]) nm = req->fieldmap[KEY_PAGECOUNT]->parsed.i; if (0 == nm) nm = 1; sz = 1; if (NULL != req->fieldmap[KEY_PAGESIZE]) sz = req->fieldmap[KEY_PAGESIZE]->parsed.i; if (0 == sz || (uint64_t)sz > SIZE_MAX) sz = 1; buf = kmalloc(sz); resp_open(req, KHTTP_200); khtml_open(&r, req); khtml_elem(&r, KELEM_DOCTYPE); khtml_elem(&r, KELEM_HTML); khtml_elem(&r, KELEM_HEAD); khtml_elem(&r, KELEM_TITLE); khtml_puts(&r, "Have a banana."); khtml_closeelem(&r, 2); khtml_elem(&r, KELEM_BODY); for (i = k = 0; i < nm; i++) { for (j = 0; j < sz; j++) { if (72 == k++) { buf[j] = '\n'; k = 0; } else buf[j] = 65 + arc4random_uniform(24); } khtml_write(buf, sz, &r); } khtml_close(&r); free(buf); } static void sendindex(struct kreq *req) { char *page; struct khtmlreq r; const char *cp; kasprintf(&page, "%s/%s", req->pname, pages[PAGE_INDEX]); resp_open(req, KHTTP_200); khtml_open(&r, req); khtml_elem(&r, KELEM_DOCTYPE); khtml_elem(&r, KELEM_HTML); khtml_elem(&r, KELEM_HEAD); khtml_elem(&r, KELEM_TITLE); khtml_puts(&r, "Welcome!"); khtml_closeelem(&r, 2); khtml_elem(&r, KELEM_BODY); khtml_puts(&r, "Welcome!"); khtml_attr(&r, KELEM_FORM, KATTR_METHOD, "post", KATTR_ENCTYPE, "multipart/form-data", KATTR_ACTION, page, KATTR__MAX); khtml_elem(&r, KELEM_FIELDSET); khtml_elem(&r, KELEM_LEGEND); khtml_puts(&r, "Post (multipart)"); khtml_closeelem(&r, 1); khtml_elem(&r, KELEM_P); cp = NULL == req->fieldmap[KEY_INTEGER] ? "" : req->fieldmap[KEY_INTEGER]->val; khtml_attr(&r, KELEM_INPUT, KATTR_TYPE, "number", KATTR_NAME, keys[KEY_INTEGER].name, KATTR_VALUE, cp, KATTR__MAX); khtml_closeelem(&r, 1); khtml_elem(&r, KELEM_P); khtml_attr(&r, KELEM_INPUT, KATTR_TYPE, "file", KATTR_MULTIPLE, "", KATTR_NAME, keys[KEY_FILE].name, KATTR__MAX); if (NULL != req->fieldmap[KEY_FILE]) { if (NULL != req->fieldmap[KEY_FILE]->file) { khtml_puts(&r, "file: "); khtml_puts(&r, req->fieldmap[KEY_FILE]->file); khtml_puts(&r, " "); } if (NULL != req->fieldmap[KEY_FILE]->ctype) { khtml_puts(&r, "ctype: "); khtml_puts(&r, req->fieldmap[KEY_FILE]->ctype); } } khtml_closeelem(&r, 1); khtml_elem(&r, KELEM_P); khtml_attr(&r, KELEM_INPUT, KATTR_TYPE, "submit", KATTR__MAX); khtml_closeelem(&r, 0); khtml_close(&r); free(page); } int main(void) { struct kreq r; /* * Set up our main HTTP context. */ if (KCGI_OK != khttp_parse (&r, keys, KEY__MAX, pages, PAGE__MAX, PAGE_INDEX)) return(EXIT_FAILURE); if (KMETHOD_OPTIONS == r.method) { /* * Indicate that we accept GET and POST methods. * This isn't really needed. */ khttp_head(&r, kresps[KRESP_STATUS], "%s", khttps[KHTTP_200]); khttp_head(&r, kresps[KRESP_ALLOW], "OPTIONS GET POST"); khttp_body(&r); } else if (KMETHOD_GET != r.method && KMETHOD_POST != r.method) { /* * Don't accept non-GET and non-POST methods. */ resp_open(&r, KHTTP_405); } else if (PAGE__MAX == r.page || KMIME_TEXT_HTML != r.mime) { /* * We've been asked for an unknown page or something * with an unknown extension. */ resp_open(&r, KHTTP_404); khttp_puts(&r, "Page not found."); } else /* * Route to page handler. */ (*disps[r.page])(&r); /* * Clean up our context. */ khttp_free(&r); return(EXIT_SUCCESS); }