diff options
| author | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-10-09 10:52:48 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-10-09 10:52:48 +0200 |
| commit | d08e10515211f6d6c549b36894af02c2d021832f (patch) | |
| tree | 2ed9b278c979a92afbc938a70f23d8a2ca843f31 | |
| parent | 197b8c52b74322cd2289fd365e4cb65074682870 (diff) | |
Use libweb's form API
This functionality was moved from slcl to libweb since it can be shared
with other web applications.
| -rw-r--r-- | main.c | 432 |
1 files changed, 106 insertions, 326 deletions
@@ -12,6 +12,7 @@ #include "hex.h" #include "page.h" #include "style.h" +#include <libweb/form.h> #include <libweb/handler.h> #include <libweb/http.h> #include <libweb/wildcard_cmp.h> @@ -36,11 +37,6 @@ #define STYLE_PATH "style.css" -struct form -{ - char *key, *value; -}; - struct user_args { struct auth *a; @@ -125,188 +121,14 @@ end: return ret; } -static char *alloc_form_data(const char *const s, const char **const end) -{ - const char *const next = strchr(s, '&'); - const size_t len = next ? next - s : strlen(s); - char *const data = malloc(len + 1); - - if (!data) - { - fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno)); - return NULL; - } - - memcpy(data, s, len); - data[len] = '\0'; - *end = s + len; - - if (next) - *end += 1; - - return data; -} - -static void form_free(struct form *const f) -{ - if (f) - { - free(f->key); - free(f->value); - } -} - -static void forms_free(struct form *const f, const size_t n) -{ - if (f) - for (size_t i = 0; i < n; i++) - form_free(&f[i]); - - free(f); -} - -static int append_form(struct form **const forms, const char **const s, - size_t *const n) -{ - int ret = -1; - const char *end; - char *const data = alloc_form_data(*s, &end), *enckey = NULL, - *encvalue = NULL, *key = NULL, *value = NULL; - struct form *f = NULL, *fs = NULL; - - if (!data) - { - fprintf(stderr, "%s: alloc_form_data failed\n", __func__); - goto end; - } - - const char *const sep = strchr(data, '='); - - if (!sep) - { - fprintf(stderr, "%s: strchr(3) returned NULL\n", __func__); - ret = 1; - goto end; - } - else if (!data || !*(sep + 1)) - { - fprintf(stderr, "%s: expected key=value (%s)\n", __func__, data); - ret = 1; - goto end; - } - - const size_t keylen = sep - data; - - if (!(enckey = strndup(data, keylen))) - { - fprintf(stderr, "%s: strndup(3) enckey: %s\n", - __func__, strerror(errno)); - goto end; - } - else if (!(encvalue = strdup(sep + 1))) - { - fprintf(stderr, "%s: strdup(3) encvalue: %s\n", - __func__, strerror(errno)); - goto end; - } - else if (!(fs = realloc(*forms, (*n + 1) * sizeof **forms))) - { - fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno)); - goto end; - } - - *forms = fs; - - /* HTML input forms use '+' for whitespace, rather than %20. */ - if ((ret = http_decode_url(enckey, true, &key))) - { - fprintf(stderr, "%s: http_decode_url enckey failed\n", __func__); - goto end; - } - else if ((ret = http_decode_url(encvalue, true, &value))) - { - fprintf(stderr, "%s: http_decode_url encvalue failed\n", __func__); - goto end; - } - - f = &(*forms)[(*n)++]; - - *f = (const struct form) - { - .key = key, - .value = value - }; - - *s = end; - ret = 0; - -end: - if (ret) - { - free(key); - free(value); - } - - free(enckey); - free(encvalue); - free(data); - return ret; -} - -static int get_forms(const struct http_payload *const pl, - struct form **const forms, size_t *const outn) -{ - int ret = -1; - const struct http_post *const p = &pl->u.post; - struct form *f = NULL; - - if (!p->data) - { - fprintf(stderr, "%s: expected non-NULL buffer\n", __func__); - ret = 1; - goto end; - } - - const char *s = p->data; - - *outn = 0; - - while (*s) - if ((ret = append_form(&f, &s, outn))) - { - if (ret < 0) - fprintf(stderr, "%s: append_form failed\n", __func__); - - goto end; - } - - *forms = f; - ret = 0; - -end: - if (ret) - forms_free(f, *outn); - - return ret; -} - static int check_credentials(struct auth *const a, - const struct form *const forms, const size_t n, char **const cookie) + const struct form *const forms, char **const cookie) { - const char *username = NULL, *pwd = NULL; + const char *const username = form_value(forms, "username"), + *const pwd = form_value(forms, "password"); *cookie = NULL; - for (size_t i = 0; i < n; i++) - { - const struct form *const f = &forms[i]; - - if (!strcmp(f->key, "username")) - username = f->value; - else if (!strcmp(f->key, "password")) - pwd = f->value; - } - if (!username || !pwd) { fprintf(stderr, "%s: missing credentials\n", __func__); @@ -325,19 +147,18 @@ static int login(const struct http_payload *const pl, struct http_response *const r, void *const user) { int ret = -1; - size_t n = 0; struct form *forms = NULL; const struct user_args *const ua = user; char *cookie = NULL; - if ((ret = get_forms(pl, &forms, &n))) + if ((ret = form_alloc(pl->u.post.data, &forms))) { if (ret < 0) fprintf(stderr, "%s: get_forms failed\n", __func__); goto end; } - else if ((ret = check_credentials(ua->a, forms, n, &cookie))) + else if ((ret = check_credentials(ua->a, forms, &cookie))) { if (ret < 0) fprintf(stderr, "%s: check_credentials failed\n", __func__); @@ -359,7 +180,7 @@ static int login(const struct http_payload *const pl, ret = 0; end: - forms_free(forms, n); + form_free(forms); free(cookie); if (ret > 0 && (ret = page_failed_login(r))) @@ -563,7 +384,6 @@ static int check_search_input(const struct http_payload *const p, { int ret = auth_cookie(a, &p->cookie); struct form *forms = NULL; - size_t n = 0; if (ret < 0) { @@ -575,7 +395,7 @@ static int check_search_input(const struct http_payload *const p, *f = page_forbidden; goto end; } - else if ((ret = get_forms(p, &forms, &n))) + else if ((ret = form_alloc(p->u.post.data, &forms))) { if (ret < 0) fprintf(stderr, "%s: get_forms failed\n", __func__); @@ -585,17 +405,8 @@ static int check_search_input(const struct http_payload *const p, goto end; } - const char *tdir = NULL, *tres = NULL; - - for (size_t i = 0; i < n; i++) - { - const struct form *const f = &forms[i]; - - if (!strcmp(f->key, "dir")) - tdir = f->value; - else if (!strcmp(f->key, "name")) - tres = f->value; - } + const char *const tdir = form_value(forms, "dir"), + *const tres = form_value(forms, "name"); if (!tdir) { @@ -639,7 +450,7 @@ static int check_search_input(const struct http_payload *const p, } end: - forms_free(forms, n); + form_free(forms); return ret; } @@ -858,13 +669,12 @@ static int share(const struct http_payload *const p, int ret = -1; struct form *forms = NULL; - size_t n = 0; char *sympath = NULL; struct dynstr userpath; dynstr_init(&userpath); - if ((ret = get_forms(p, &forms, &n))) + if ((ret = form_alloc(p->u.post.data, &forms))) { if (ret < 0) fprintf(stderr, "%s: get_forms failed\n", __func__); @@ -873,17 +683,18 @@ static int share(const struct http_payload *const p, goto end; } - else if (n != 1) + + const char *const path = form_value(forms, "name"), + *const username = p->cookie.field; + struct stat sb; + + if (!path) { - fprintf(stderr, "%s: expected 1 form, got %zu\n", __func__, n); + fprintf(stderr, "%s: missing \"name\"\n", __func__); ret = page_bad_request(r); goto end; } - - const char *const path = forms->value, *const username = p->cookie.field; - struct stat sb; - - if (path_invalid(path) || *path != '/') + else if (path_invalid(path) || *path != '/') { fprintf(stderr, "%s: invalid path %s\n", __func__, path); ret = page_bad_request(r); @@ -923,7 +734,7 @@ static int share(const struct http_payload *const p, ret = 0; end: - forms_free(forms, n); + form_free(forms); free(sympath); dynstr_free(&userpath); return ret; @@ -1681,7 +1492,6 @@ static int createdir(const struct http_payload *const p, const struct auth *const a = ua->a; struct dynstr d, userd; struct form *forms = NULL; - size_t n = 0; char *encurl = NULL; dynstr_init(&d); @@ -1693,7 +1503,7 @@ static int createdir(const struct http_payload *const p, ret = page_forbidden(r); goto end; } - else if ((ret = get_forms(p, &forms, &n))) + else if ((ret = form_alloc(p->u.post.data, &forms))) { if (ret < 0) fprintf(stderr, "%s: get_forms failed\n", __func__); @@ -1702,30 +1512,9 @@ static int createdir(const struct http_payload *const p, goto end; } - else if (n != 2) - { - fprintf(stderr, "%s: expected 2 forms, got %zu\n", __func__, n); - ret = page_bad_request(r); - goto end; - } - const char *name = NULL, *dir = NULL; - - for (size_t i = 0; i < n; i++) - { - const struct form *const f = &forms[i]; - - if (!strcmp(f->key, "name")) - name = f->value; - else if (!strcmp(f->key, "dir")) - dir = f->value; - else - { - fprintf(stderr, "%s: unexpected key %s\n", __func__, f->key); - ret = page_bad_request(r); - goto end; - } - } + const char *const name = form_value(forms, "name"), + *const dir = form_value(forms, "dir"); if (!name || !dir) { @@ -1811,58 +1600,69 @@ static int createdir(const struct http_payload *const p, ret = 0; end: - forms_free(forms, n); + form_free(forms); dynstr_free(&userd); dynstr_free(&d); free(encurl); return ret; } -static int check_rm_input(const struct form *const forms, const size_t n, - struct page_rm *const rm, int (**const cb)(struct http_response *)) +static int iterate(const char *key, const char *value, void *user) { - for (size_t i = 0; i < n; i++) + struct page_rm *const rm = user; + + if (!strcmp(key, "dir")) + { + if (!rm->dir) + rm->dir = value; + else + { + fprintf(stderr, "%s: directory defined more than once\n", + __func__); + return 1; + } + } + else if (!strcmp(key, "path")) { - const struct form *const f = &forms[i]; + const char *const path = value; - if (!strcmp(f->key, "dir")) + if (path_isrel(path)) { - if (!rm->dir) - rm->dir = f->value; - else - { - fprintf(stderr, "%s: directory defined more than once\n", - __func__); - *cb = page_bad_request; - return 1; - } + fprintf(stderr, "%s: invalid path %s\n", __func__, rm->dir); + return 1; } - else if (!strcmp(f->key, "path")) + + const char **tmp = realloc(rm->items, (rm->n + 1) * sizeof *tmp); + + if (!tmp) { - const char *const path = f->value; + fprintf(stderr, "%s: realloc(3): %s\n", + __func__, strerror(errno)); + return -1; + } - if (path_isrel(path)) - { - fprintf(stderr, "%s: invalid path %s\n", __func__, rm->dir); - *cb = page_bad_request; - return 1; - } + tmp[rm->n++] = path; + rm->items = tmp; + } + + return 0; +} - const char **tmp = realloc(rm->items, (rm->n + 1) * sizeof *tmp); +static int check_rm_input(const struct form *const forms, + struct page_rm *const rm, int (**const cb)(struct http_response *)) +{ + const int ret = form_foreach(forms, iterate, rm); - if (!tmp) - { - fprintf(stderr, "%s: realloc(3): %s\n", - __func__, strerror(errno)); - return -1; - } + if (ret) + { + if (ret < 0) + fprintf(stderr, "%s: form_foreach failed\n", __func__); + else + *cb = page_bad_request; - tmp[rm->n++] = path; - rm->items = tmp; - } + return ret; } - - if (!rm->dir) + else if (!(rm->dir = form_value(forms, "dir"))) { fprintf(stderr, "%s: expected non-null dir\n", __func__); *cb = page_bad_request; @@ -1885,7 +1685,6 @@ static int confirm_rm(const struct http_payload *const p, const struct user_args *const ua = user; struct form *forms = NULL; const char **items = NULL; - size_t n = 0; int (*f)(struct http_response *); struct page_rm rm = {0}; @@ -1895,7 +1694,7 @@ static int confirm_rm(const struct http_payload *const p, ret = page_forbidden(r); goto end; } - else if ((ret = get_forms(p, &forms, &n))) + else if ((ret = form_alloc(p->u.post.data, &forms))) { if (ret < 0) fprintf(stderr, "%s: get_forms failed\n", __func__); @@ -1904,7 +1703,7 @@ static int confirm_rm(const struct http_payload *const p, goto end; } - else if ((ret = check_rm_input(forms, n, &rm, &f))) + else if ((ret = check_rm_input(forms, &rm, &f))) { if (ret < 0) fprintf(stderr, "%s: check_rm_input failed\n", __func__); @@ -1921,38 +1720,12 @@ static int confirm_rm(const struct http_payload *const p, } end: - forms_free(forms, n); + form_free(forms); free(items); free(rm.items); return ret; } -static const char *find_rm_dir(const struct form *const forms, const size_t n, - int (**const cb)(struct http_response *)) -{ - const char *dir = NULL; - - for (size_t i = 0; i < n; i++) - { - const struct form *const f = &forms[i]; - - if (!strcmp(f->key, "dir")) - { - if (!dir) - dir = f->value; - else - { - fprintf(stderr, "%s: directory defined more than once\n", - __func__); - *cb = page_bad_request; - return NULL; - } - } - } - - return dir; -} - static int fifo_notify_remove(int *const fd, const char *const path, const char *const body) { @@ -2130,27 +1903,30 @@ end: return ret; } -static int do_rm(struct user_args *const ua, const struct form *const forms, - const size_t n, const char *const dir) +struct do_rm +{ + struct user_args *const ua; + const char *const dir; +}; + +static int do_rm(const char *const key, const char *const value, + void *const user) { - for (size_t i = 0; i < n; i++) + const struct do_rm *const rm = user; + + if (!strcmp(key, "path")) { - const struct form *const f = &forms[i]; + const char *const path = value; - if (!strcmp(f->key, "path")) + if (path_isrel(path)) { - const char *const path = f->value; - - if (path_isrel(path)) - { - fprintf(stderr, "%s: invalid path %s\n", __func__, path); - return 1; - } - else if (rm_item(ua, dir, f->value)) - { - fprintf(stderr, "%s: rm_item failed\n", __func__); - return -1; - } + fprintf(stderr, "%s: invalid path %s\n", __func__, path); + return 1; + } + else if (rm_item(rm->ua, rm->dir, value)) + { + fprintf(stderr, "%s: rm_item failed\n", __func__); + return -1; } } @@ -2164,7 +1940,6 @@ static int rm(const struct http_payload *const p, struct user_args *const ua = user; const struct auth *const a = ua->a; struct form *forms = NULL; - size_t n = 0; const struct http_cookie *const c = &p->cookie; const char *username = c->field, *adir; struct dynstr d, userdir; @@ -2183,7 +1958,7 @@ static int rm(const struct http_payload *const p, fprintf(stderr, "%s: auth_dir failed\n", __func__); goto end; } - else if ((ret = get_forms(p, &forms, &n))) + else if ((ret = form_alloc(p->u.post.data, &forms))) { if (ret < 0) fprintf(stderr, "%s: get_forms failed\n", __func__); @@ -2193,13 +1968,12 @@ static int rm(const struct http_payload *const p, goto end; } - int (*f)(struct http_response *); - const char *const dir = find_rm_dir(forms, n, &f); + const char *const dir = form_value(forms, "dir"); if (!dir) { fprintf(stderr, "%s: expected non-null directory\n", __func__); - ret = f(r); + ret = page_bad_request(r); goto end; } else if (dirname_invalid(dir)) @@ -2213,13 +1987,19 @@ static int rm(const struct http_payload *const p, fprintf(stderr, "%s: dynstr_append failed\n", __func__); goto end; } - else if ((ret = do_rm(ua, forms, n, userdir.str))) + + struct do_rm args = + { + .dir = userdir.str, + .ua = ua + }; + + if ((ret = form_foreach(forms, do_rm, &args))) { if (ret < 0) fprintf(stderr, "%s: do_rm failed\n", __func__); - else if ((ret = f(r))) - fprintf(stderr, "%s: rm callback failed\n", __func__); + ret = page_bad_request(r); goto end; } else if (dynstr_append(&d, "/user%s", dir)) @@ -2244,7 +2024,7 @@ static int rm(const struct http_payload *const p, end: dynstr_free(&d); dynstr_free(&userdir); - forms_free(forms, n); + form_free(forms); return ret; } |
