aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi92@disroot.org>2025-10-09 10:52:48 +0200
committerXavier Del Campo Romero <xavi92@disroot.org>2025-10-09 10:52:48 +0200
commitd08e10515211f6d6c549b36894af02c2d021832f (patch)
tree2ed9b278c979a92afbc938a70f23d8a2ca843f31
parent197b8c52b74322cd2289fd365e4cb65074682870 (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.c432
1 files changed, 106 insertions, 326 deletions
diff --git a/main.c b/main.c
index 1b70542..9bf011a 100644
--- a/main.c
+++ b/main.c
@@ -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;
}