123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886 |
- /*
- * Copyright (c) 2018 Markus Hennecke <markus-hennecke@markus-hennecke.de>
- *
- * 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 <sys/mman.h>
- #include <sys/time.h>
- #include <err.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <limits.h>
- #include <regex.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <unistd.h>
- #include "buffer.h"
- #include "filehelper.h"
- #include "handler.h"
- #include "helper.h"
- #define PAGE_URI_RX_PATTERN "/?((([a-z]{1,8}(-[a-z]{1,8})?))/)?" \
- "([_a-z0-9-]+)\\.html$"
- #define PAGE_URI_MAX_GROUPS 6
- #define PAGE_URI_LANG_GROUP 2
- #define PAGE_URI_PAGE_GROUP 5
- #define ACCEPT_LANGUAGE_RX_PATTERN "^ *([a-z]{1,8}(-[a-z]{1,8})?)" \
- " *(; *q *= *(1|0\\.[0-9]+))?$"
- #define ACCEPT_LANGUAGE_MAX_GROUPS 5
- #define HTTP_DATE_FMT "%a, %d %b %y %H:%M:%S GMT"
- const char *supported_request_methods[3] = {
- "GET",
- "POST",
- NULL
- };
- struct lang_pref *
- lang_pref_new(const char *_lang, const float _prio)
- {
- struct lang_pref *lp = malloc(sizeof(struct lang_pref));
- if (lp == NULL)
- err(1, NULL);
- strlcpy(lp->lang, _lang, sizeof(lp->lang));
- lp->priority = _prio;
- strlcpy(lp->short_lang, _lang, sizeof(lp->short_lang));
- strtok(lp->short_lang, "-");
- return lp;
- }
- void
- request_add_lang_pref(struct request *_req, struct lang_pref *_lang_pref)
- {
- struct lang_pref *l = TAILQ_FIRST(&_req->accept_languages);
- while (l) {
- if (_lang_pref->priority < l->priority) {
- l = TAILQ_NEXT(l, entries);
- continue;
- }
- TAILQ_INSERT_BEFORE(l, _lang_pref, entries);
- return;
- }
- TAILQ_INSERT_TAIL(&_req->accept_languages, _lang_pref, entries);
- }
- void
- request_parse_lang_pref(struct request *_req)
- {
- regex_t rx;
- regmatch_t pmatch[ACCEPT_LANGUAGE_MAX_GROUPS];
- char *env_accept_lang = getenv("HTTP_ACCEPT_LANGUAGE");
- if (env_accept_lang == NULL)
- return;
- char *accept_lang = strdup(env_accept_lang);
- // Split the string and feed the bits to request_add_lang_pref
- size_t len = strlen(accept_lang);
- char *end = accept_lang + len;
- char *it = accept_lang;
- int rc = regcomp(&rx, ACCEPT_LANGUAGE_RX_PATTERN,
- REG_EXTENDED | REG_ICASE);
- if (rc != 0) {
- warnx("regcomp: %s", rx_get_errormsg(rc, &rx));
- return;
- }
- while (it < end) {
- float prio = 1;
- size_t match_len;
- char *comma = strchr(it, ',');
- char *lang;
- if (comma == NULL)
- comma = end;
- else
- *comma = '\0';
- rc = regexec(&rx, it, ACCEPT_LANGUAGE_MAX_GROUPS, pmatch, 0);
- switch (rc) {
- case REG_NOMATCH:
- warnx("'%s' did not match language rx\n", it);
- goto cleanup_lang;
- case 0:
- if (pmatch[4].rm_so != -1)
- prio = atof(it + pmatch[4].rm_so);
- match_len = pmatch[1].rm_eo - pmatch[1].rm_so;
- lang = strndup(it + pmatch[1].rm_so, match_len);
- struct lang_pref *lp = lang_pref_new(lang, prio);
- // Only store available languages
- bool store = false;
- if (dir_entry_exists(lp->lang, _req->avail_languages)) {
- store = true;
- } else {
- lp->lang[0] = '\0';
- }
- if (dir_entry_exists(lp->short_lang,
- _req->avail_languages)) {
- store = true;
- } else {
- lp->short_lang[0] = '\0';
- }
- if (store) {
- request_add_lang_pref(_req, lp);
- } else {
- free(lp);
- }
- free(lang);
- break;
- default:
- warnx("regexec: %s", rx_get_errormsg(rc, &rx));
- break;
- }
- it = comma + 1;
- }
- cleanup_lang:
- free(accept_lang);
- regfree(&rx);
- }
- struct page_info *
- page_info_new(char *_path)
- {
- struct page_info *info = calloc(1, sizeof(struct page_info));
- if (NULL == info)
- err(1, NULL);
- info->path = _path;
- return info;
- }
- void
- page_info_free(struct page_info *_info)
- {
- if (NULL == _info)
- return;
- md_mmap_free(_info->content);
- md_mmap_free(_info->descr);
- memmap_free(_info->link);
- md_mmap_free(_info->login);
- memmap_free(_info->sort);
- memmap_free(_info->style);
- memmap_free(_info->script);
- memmap_free(_info->title);
- free(_info->path);
- free(_info);
- }
- struct request *
- request_new(char *_path_info)
- {
- size_t match_len;
- regmatch_t *rxmatch;
- struct request *req = calloc(1, sizeof(struct request));
- if (req == NULL)
- err(1, NULL);
- req->content_dir = open(cms_content_dir, O_DIRECTORY | O_RDONLY);
- if (-1 == req->content_dir)
- err(1, "%s", cms_content_dir);
- req->template_dir = open(cms_template_dir, O_DIRECTORY | O_RDONLY);
- if (-1 == req->template_dir)
- err(1, "%s", cms_template_dir);
- req->page_dir = -1;
- req->lang_dir = -1;
- req->path_info = strdup(_path_info);
- req->lang = NULL;
- req->page = NULL;
- req->path = CMS_ROOT_URL;
- TAILQ_INIT(&req->headers);
- TAILQ_INIT(&req->accept_languages);
- TAILQ_INIT(&req->cookies);
- TAILQ_INIT(&req->params);
- const char *req_method = getenv("REQUEST_METHOD");
- if (req_method) {
- int i = 0;
- const char **supported = &supported_request_methods[0];
- while (*supported) {
- if (strcmp(req_method, *supported) == 0) {
- req->request_method = i;
- break;
- }
- i++;
- supported++;
- }
- if (*supported == NULL)
- errx(1, "unsupported request method '%s'", req_method);
- }
- regex_t rx;
- int rc = regcomp(&rx, PAGE_URI_RX_PATTERN, REG_EXTENDED);
- if (rc != 0) {
- warnx("regcomp: %s", rx_get_errormsg(rc, &rx));
- request_free(req);
- return NULL;
- }
- regmatch_t rx_group_array[PAGE_URI_MAX_GROUPS];
- rc = regexec(&rx, _path_info, PAGE_URI_MAX_GROUPS,
- rx_group_array, 0);
- switch (rc) {
- case REG_NOMATCH:
- request_free(req);
- return NULL;
- case 0:
- rxmatch = &rx_group_array[PAGE_URI_PAGE_GROUP];
- match_len = rxmatch->rm_eo - rxmatch->rm_so;
- req->page = strndup(_path_info + rxmatch->rm_so, match_len);
- rxmatch = &rx_group_array[PAGE_URI_LANG_GROUP];
- match_len = rxmatch->rm_eo - rxmatch->rm_so;
- if (match_len > 0) {
- req->lang = strndup(_path_info + rxmatch->rm_so,
- match_len);
- }
- break;
- default:
- warnx("regexec: %s", rx_get_errormsg(rc, &rx));
- break;
- }
- regfree(&rx);
- req->avail_languages = get_dir_entries(cms_content_dir);
- request_parse_lang_pref(req);
- if (NULL == req->lang) {
- struct lang_pref *lang = TAILQ_FIRST(&req->accept_languages);
- if (lang == NULL) {
- req->lang = strndup(CMS_DEFAULT_LANGUAGE,
- sizeof(req->lang) - 1);
- } else {
- if (lang->lang[0] != '\0')
- req->lang = strdup(lang->lang);
- else
- req->lang = strdup(lang->short_lang);
- }
- }
- req->lang_dir = openat(req->content_dir, req->lang,
- O_DIRECTORY | O_RDONLY);
- if (req->lang_dir == -1) {
- request_free(req);
- _error("404 Not Found", NULL);
- }
- return req;
- }
- void
- request_free(struct request *_req)
- {
- if (_req) {
- if (-1 != _req->content_dir)
- close(_req->content_dir);
- if (-1 != _req->template_dir)
- close(_req->template_dir);
- if (-1 != _req->lang_dir)
- close(_req->lang_dir);
- if (-1 != _req->page_dir)
- close(_req->page_dir);
- page_info_free(_req->page_info);
- tmpl_data_free(_req->data);
- free(_req->path_info);
- free(_req->page);
- free(_req->lang);
- free(_req->status);
- struct header *h;
- while ((h = TAILQ_FIRST(&_req->headers))) {
- TAILQ_REMOVE(&_req->headers, h, entries);
- header_free(h);
- }
- struct lang_pref *lp;
- while ((lp = TAILQ_FIRST(&_req->accept_languages))) {
- TAILQ_REMOVE(&_req->accept_languages, lp, entries);
- free(lp);
- }
- struct cookie *c;
- while ((c = TAILQ_FIRST(&_req->cookies))) {
- TAILQ_REMOVE(&_req->cookies, c, entries);
- free(c);
- }
- struct param *p;
- while ((p = TAILQ_FIRST(&_req->params))) {
- TAILQ_REMOVE(&_req->params, p, entries);
- free(p);
- }
- dir_list_free(_req->avail_languages);
- session_free(_req->session);
- if (_req->session_store)
- session_store_cleanup(_req->session_store);
- session_store_free(_req->session_store);
- htpasswd_free(_req->htpasswd);
- memmap_free(_req->tmpl_file);
- }
- }
- struct page_info *
- request_fetch_page(struct request *_req)
- {
- struct dir_list *files = NULL;
- struct dir_entry *e;
- struct page_info *p = NULL;
- int cwd = -1;
- char *path;
- if (-1 == asprintf(&path, "%s/%s/%s", cms_content_dir, _req->lang,
- _req->page))
- err(1, NULL);
- cwd = open(path, O_DIRECTORY | O_RDONLY);
- if (-1 == cwd) {
- warn("%s", path);
- goto error_out;
- }
- p = page_info_new(path);
- files = get_dir_entries(path);
- if (files == NULL)
- goto error_out;
- TAILQ_FOREACH(e, &files->entries, entries) {
- if (strcmp("CONTENT", e->filename) == 0) {
- if (p->content == NULL)
- p->content = md_mmap_new_at(cwd, e->filename);
- } else if (strcmp("CONTENT.md", e->filename) == 0) {
- md_mmap_free(p->content);
- p->content = md_mmap_new_at(cwd, e->filename);
- md_mmap_parse(p->content);
- } else if (strcmp("DESCR", e->filename) == 0) {
- if (p->descr == NULL)
- p->descr = md_mmap_new_at(cwd, e->filename);
- } else if (strcmp("DESCR.md", e->filename) == 0) {
- md_mmap_free(p->descr);
- p->descr = md_mmap_new_at(cwd, e->filename);
- md_mmap_parse(p->descr);
- } else if (strcmp("LINK", e->filename) == 0) {
- p->link = memmap_new_at(cwd, e->filename);
- } else if (strcmp("LOGIN", e->filename) == 0) {
- p->login = md_mmap_new_at(cwd, e->filename);
- } else if (strcmp("SORT", e->filename) == 0) {
- p->sort = memmap_new_at(cwd, e->filename);
- } else if (strcmp("SCRIPT", e->filename) == 0) {
- p->script = memmap_new_at(cwd, e->filename);
- } else if (strcmp("SSL", e->filename) == 0) {
- p->ssl = true;
- } else if (strcmp("STYLE", e->filename) == 0) {
- p->style = memmap_new_at(cwd, e->filename);
- } else if (strcmp("SUB", e->filename) == 0) {
- p->sub = true;
- } else if (strcmp("TITLE", e->filename) == 0) {
- p->title = memmap_new_at(cwd, e->filename);
- }
- }
- // Test for all required fields, if not here we error out
- if (!(p->content && p->link && p->sort && p->title && p->descr))
- goto error_out;
- // Test if the If-Modified-Since header exists and the newest
- // file from the content directory have equal time stamps
- const char *if_modified_since = getenv("HTTP_IF_MODIFIED_SINCE");
- if (if_modified_since) {
- struct tm tm;
- if (strptime(if_modified_since, HTTP_DATE_FMT, &tm)) {
- if (timegm(&tm) >= files->newest)
- _error("304 Not Modified", NULL);
- }
- }
- char last_modified[30];
- strftime(last_modified, sizeof(last_modified),
- HTTP_DATE_FMT, gmtime(&files->newest));
- request_add_header(_req, "Last-Modified", last_modified);
- _req->page_info = p;
- _req->content = p->content;
- cleanup:
- if (files)
- dir_list_free(files);
- if (cwd != -1) {
- fchdir(cwd);
- close(cwd);
- }
- return p;
- error_out:
- page_info_free(p);
- p = NULL;
- goto cleanup;
- }
- struct tmpl_data *
- request_init_tmpl_data(struct request *_req)
- {
- _req->data = tmpl_data_new();
- tmpl_data_set_variable(_req->data, "CURRENT_PAGE", _req->path_info);
- if (_req->page_info->descr) {
- void *data;
- size_t size;
- md_mmap_content(_req->page_info->descr, &data, &size);
- tmpl_data_set_variablen(_req->data, "DESCR", data, size);
- }
- return _req->data;
- }
- bool
- request_handle_login(struct request *_req)
- {
- if (! _req->page_info->login)
- return true;
- request_parse_cookies(_req);
- _req->session_store = session_store_new(cms_session_db);
- struct cookie *c = request_cookie_get(_req, "sid");
- if (c != NULL)
- _req->session = session_load(c->value, _req->session_store);
- const bool new_session = (_req->session == NULL);
- const time_t now = time(NULL);
- const bool timeout = (_req->session
- && (_req->session->data.timeout < now));
- if (new_session || timeout) {
- struct session *session = session_new();
- if (timeout) {
- session_free(_req->session);
- }
- _req->session = session;
- request_cookie_remove(_req, c);
- cookie_free(c);
- c = cookie_new("sid", _req->session->sid);
- c->path = strdup("/");
- }
- request_cookie_set(_req, c);
- _req->session->data.timeout = now + (60 * 30);
- session_save(_req->session, _req->session_store, new_session);
- if (_req->request_method == POST) {
- request_parse_params(_req, getenv("QUERY_STRING"));
- char *content_type = getenv("CONTENT_TYPE");
- if (content_type
- && (strcmp(content_type,
- "application/x-www-form-urlencoded")
- == 0)) {
- if (request_read_post_body(_req))
- request_parse_params(_req, _req->req_body);
- }
- char *username = NULL;
- char *password = NULL;
- struct param *p = request_get_param(_req, "username", NULL);
- if (p)
- username = p->value;
- p = request_get_param(_req, "password", NULL);
- if (p)
- password = p->value;
- if (username && password) {
- if (_req->htpasswd == NULL)
- _req->htpasswd
- = htpasswd_init(cms_session_htpasswd);
- _req->session->data.loggedin
- = htpasswd_check_password(_req->htpasswd,
- username, password);
- session_save(_req->session, _req->session_store, false);
- }
- }
- if (!_req->session->data.loggedin) {
- _req->content = _req->page_info->login;
- }
- return (_req->content == _req->page_info->content);
- }
- struct buffer_list *
- request_render_page(struct request *_req, const char *_tmpl_filename)
- {
- struct buffer_list *cb;
- struct buffer_list *result = NULL;
- if (_req->content) {
- void *data;
- size_t size;
- md_mmap_content(_req->content, &data, &size);
- cb = tmpl_parse(data, size, _req->data);
- tmpl_data_set_variable(_req->data, "CONTENT",
- buffer_list_concat_string(cb));
- } else {
- _error("404 Not Found", NULL);
- }
- tmpl_data_set_variable(_req->data, "LANGUAGE", _req->lang);
- tmpl_data_set_variablen(_req->data, "TITLE",
- _req->page_info->title->data,
- memmap_chomp(_req->page_info->title));
- struct tmpl_loop *links = request_get_links(_req);
- struct tmpl_loop *lang_links = request_get_language_links(_req);
- if (links)
- tmpl_data_set_loop(_req->data, "LINK_LOOP", links);
- if (lang_links)
- tmpl_data_set_loop(_req->data, "LANGUAGE_LINKS", lang_links);
- _req->tmpl_file = memmap_new_at(_req->template_dir, _tmpl_filename);
- if (_req->tmpl_file == NULL)
- _error("500 Internal Server Error", NULL);
- result = tmpl_parse(_req->tmpl_file->data, _req->tmpl_file->size,
- _req->data);
- buffer_list_free(cb);
- return result;
- }
- void
- header_free(struct header *_header)
- {
- if (_header) {
- free(_header->key);
- free(_header->value);
- }
- }
- struct header *
- header_new(const char *_key, const char *_value)
- {
- struct header *h = malloc(sizeof(struct header));
- if (h == NULL)
- err(1, NULL);
- h->key = strdup(_key);
- h->value = strdup(_value);
- return h;
- }
- void
- request_add_header(struct request *_req, const char *_key, const char *_value)
- {
- struct header *h = header_new(_key, _value);
- TAILQ_INSERT_TAIL(&_req->headers, h, entries);
- }
- struct buffer_list *
- request_output_headers(struct request *_req)
- {
- struct header *h;
- struct buffer_list *bl = buffer_list_new();
- TAILQ_FOREACH(h, &_req->headers, entries) {
- char *header;
- if ((asprintf(&header, "%s: %s\n", h->key, h->value) == -1))
- err(1, NULL);
- buffer_list_add_string(bl, header);
- free(header);
- }
- return bl;
- }
- void
- request_cookie_mark_delete(struct request *_req, struct cookie *_cookie)
- {
- free(_cookie->value);
- _cookie->value = strdup("deleted");
- _cookie->expires = 1;
- request_cookie_set(_req, _cookie);
- }
- void
- request_cookie_set(struct request *_req, struct cookie *_cookie)
- {
- char expires[38] = { 0 };
- if (_cookie->expires != 0) {
- struct tm *tm = gmtime(&_cookie->expires);
- strftime(expires, sizeof(expires), "%a, %d %b %Y %T GMT", tm);
- }
- char *value;
- if (asprintf(&value, "%s=%s%s%s%s%s%s",
- _cookie->name, _cookie->value,
- (_cookie->path) ? "; path=" : "",
- (_cookie->path) ? _cookie->path : "",
- (_cookie->domain) ? "; domain=" : "",
- (_cookie->domain) ? _cookie->domain : "",
- (expires[0]) ? expires : "") == -1)
- err(1, NULL);
- request_add_header(_req, "Set-Cookie", value);
- free(value);
- }
- struct cookie *
- request_cookie_get(struct request *_req, const char *_name)
- {
- struct cookie *c;
- TAILQ_FOREACH(c, &_req->cookies, entries) {
- if (strcmp(c->name, _name) == 0)
- return c;
- }
- return NULL;
- }
- bool
- request_cookie_remove(struct request *_req, struct cookie *_c)
- {
- struct cookie *c = TAILQ_FIRST(&_req->cookies);
- while (c) {
- if (_c == c) {
- TAILQ_REMOVE(&_req->cookies, c, entries);
- return true;
- }
- c = TAILQ_NEXT(c, entries);
- }
- return false;
- }
- struct cookie *
- cookie_new(const char *_name, const char *_value)
- {
- struct cookie *c = calloc(1, sizeof(struct cookie));
- if (c == NULL)
- err(1, NULL);
- c->name = strdup(_name);
- c->value = strdup(_value);
- return c;
- }
- void
- cookie_free(struct cookie *_cookie)
- {
- if (_cookie) {
- free(_cookie->name);
- free(_cookie->value);
- free(_cookie->path);
- free(_cookie->domain);
- free(_cookie);
- }
- }
- void
- request_parse_cookies(struct request *_req)
- {
- const char *env_http_cookie = getenv("HTTP_COOKIE");
- if (env_http_cookie == NULL)
- return;
- char *cookies = strdup(env_http_cookie);
- if (cookies == NULL)
- err(1, NULL);
- char *key, *value;
- char *s = cookies;
- while (s && *s != '\0') {
- // Skip leading whitespace
- while (*s == ' ')
- s++;
- // Find a '='
- key = s;
- if ((s = strchr(s, '=')) != NULL) {
- *s++ = '\0';
- value = s;
- s = strchr(s, ';');
- if (s != NULL) {
- *s++ = '\0';
- }
- } else {
- // Try to find a ';', and continue after
- s = strchr(key, ';');
- if (s != NULL)
- s++;
- continue;
- }
- struct cookie * cookie = cookie_new(key, value);
- TAILQ_INSERT_TAIL(&_req->cookies, cookie, entries);
- }
- free(cookies);
- }
- struct param *
- param_new(const char *_name, const char *_value)
- {
- struct param *p = calloc(1, sizeof(struct param));
- if (_name)
- p->name = strdup(_name);
- if (_value) {
- p->value = strdup(_value);
- param_decode(p);
- }
- return p;
- }
- void
- param_decode(struct param *_param)
- {
- if (_param->value)
- decode_string(_param->value);
- }
- void
- param_free(struct param *_param)
- {
- if (_param) {
- free(_param->name);
- free(_param->value);
- }
- }
- void
- request_parse_params(struct request *_req, const char *_params)
- {
- if (_params == NULL)
- return;
- char *param_string = strdup(_params);
- if (param_string == NULL)
- err(1, NULL);
- char *s = param_string;
- char *name, *value;
- while (s && *s != '\0') {
- name = s;
- value = NULL;
- s = strchr(s, '=');
- if (s != NULL) {
- *s++ = '\0';
- value = s;
- s = strchr(s, '&');
- if (s != NULL) {
- *s++ = '\0';
- }
- }
- struct param *p = param_new(name, value);
- TAILQ_INSERT_TAIL(&_req->params, p, entries);
- }
- free(param_string);
- }
- struct param *
- request_get_param(struct request *_req, const char *_name, struct param *_p)
- {
- struct param *p;
- TAILQ_FOREACH(p, &_req->params, entries) {
- if (strcmp(_name, p->name) == 0) {
- if (p != _p)
- break;
- }
- }
- return p;
- }
- bool
- request_read_post_body(struct request *_req)
- {
- if (_req->request_method == POST) {
- const char *content_length_env = getenv("CONTENT_LENGTH");
- if (content_length_env == NULL)
- return false;
- errno = 0;
- char *ep;
- unsigned long content_length
- = strtoul(content_length_env, &ep, 10);
- if (content_length_env[0] == '\0' || *ep != '\0') {
- warnx("CONTENT_LENGTH is not a number");
- return false;
- }
- if (errno == ERANGE && content_length == ULONG_MAX) {
- warnx("CONTENT_LENGTH out of range");
- return false;
- }
- _req->req_body_size = content_length;
- _req->req_body = malloc(content_length + 1);
- unsigned char *buf = _req->req_body;
- ssize_t todo = content_length;
- off_t offset = 0;
- while (todo > 0) {
- ssize_t n = read(STDIN_FILENO, buf + offset,
- todo - offset);
- if (n == -1)
- err(1, NULL);
- todo -= n;
- offset += n;
- }
- buf[content_length] = '\0';
- return (todo == 0);
- }
- return false;
- }
- __dead void
- _error(const char *_status, const char *_content)
- {
- dprintf(STDOUT_FILENO, "Status: %s\r\n", _status);
- dprintf(STDOUT_FILENO, "Content-Length: %zd\r\n\r\n",
- _content ? strlen(_content) : 0);
- if (_content)
- dprintf(STDOUT_FILENO, "%s", _content);
- exit(0);
- }
|