diff options
| author | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-09-30 23:50:33 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-10-02 15:52:14 +0200 |
| commit | a0f5f7509bb9040752fa61fe0fdb447608e22b1c (patch) | |
| tree | 53337bce28ed75f26953c5969af6bc76d8841f2b /form.c | |
| parent | bba0b62f4e9e17927b9a2cda51dd5a4aa1b1f14e (diff) | |
Implement form interface
This new interface allows library users to parse
application/x-www-form-urlencoded data conveniently.
Diffstat (limited to 'form.c')
| -rw-r--r-- | form.c | 193 |
1 files changed, 193 insertions, 0 deletions
@@ -0,0 +1,193 @@ +#define _POSIX_C_SOURCE 200809L + +#include "libweb/form.h" +#include "libweb/http.h" +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +struct pair +{ + char *key, *value; +}; + +struct form +{ + struct pair *pairs; + size_t n; +}; + +void form_free(struct form *const f) +{ + if (!f) + return; + + for (size_t i = 0; i < f->n; i++) + { + struct pair *const p = &f->pairs[i]; + + free(p->key); + free(p->value); + } + + free(f->pairs); + free(f); +} + +const char *form_value(const struct form *const f, const char *const key) +{ + for (size_t i = 0; i < f->n; i++) + { + const struct pair *const p = &f->pairs[i]; + + if (!strcmp(p->key, key)) + return p->value; + } + + return NULL; +} + +static char *alloc_form_data(const char *const s, const char **const end) +{ + const char *const next = strchr(s, '&'); + char *const data = next ? strndup(s, next - s) : strdup(s); + + if (!data) + { + fprintf(stderr, "%s: strndup/strdup(3): %s\n", __func__, + strerror(errno)); + return NULL; + } + + *end = next ? next + 1 : s + strlen(s); + return data; +} + +static int append(struct form *const forms, const char **const s) +{ + int ret = -1; + const char *end; + char *const data = alloc_form_data(*s, &end), *enckey = NULL, + *encvalue = NULL, *key = NULL, *value = NULL; + struct pair *p; + + if (!data) + { + fprintf(stderr, "%s: alloc_form_data failed\n", __func__); + goto end; + } + + const char *const sep = strchr(data, '='); + + if (!sep) + { + ret = 1; + goto end; + } + else if (!data || !*(sep + 1)) + { + 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; + } + /* HTML input forms use '+' for whitespace, rather than %20. */ + else 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; + } + else if (!(p = realloc(forms->pairs, (forms->n + 1) * sizeof *p))) + { + fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno)); + goto end; + } + + forms->pairs = p; + p[forms->n++] = (const struct pair) + { + .key = key, + .value = value + }; + + *s = end; + ret = 0; + +end: + if (ret) + { + free(key); + free(value); + } + + free(enckey); + free(encvalue); + free(data); + return ret; +} + +int form_foreach(const struct form *const f, const form_iter it, + void *const user) +{ + for (size_t i = 0; i < f->n; i++) + { + const struct pair *const p = &f->pairs[i]; + const int ret = it(p->key, p->value, user); + + if (ret) + return ret; + } + + return 0; +} + + +int form_alloc(const char *data, struct form **const out) +{ + int ret = -1; + struct form *const f = malloc(sizeof *f); + + if (!f) + { + fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno)); + goto failure; + } + + *f = (const struct form){0}; + + while (*data) + if ((ret = append(f, &data))) + { + if (ret < 0) + fprintf(stderr, "%s: append_form failed\n", __func__); + + goto failure; + } + + *out = f; + return 0; + +failure: + free(f); + return ret; +} |
