aboutsummaryrefslogtreecommitdiff
path: root/form.c
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi92@disroot.org>2025-09-30 23:50:33 +0200
committerXavier Del Campo Romero <xavi92@disroot.org>2025-10-02 15:52:14 +0200
commita0f5f7509bb9040752fa61fe0fdb447608e22b1c (patch)
tree53337bce28ed75f26953c5969af6bc76d8841f2b /form.c
parentbba0b62f4e9e17927b9a2cda51dd5a4aa1b1f14e (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.c193
1 files changed, 193 insertions, 0 deletions
diff --git a/form.c b/form.c
new file mode 100644
index 0000000..784710b
--- /dev/null
+++ b/form.c
@@ -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;
+}