From 09909c0a3b326efd6a2b87df95d0e73c1e394c29 Mon Sep 17 00:00:00 2001 From: Xavier Del Campo Romero Date: Sat, 9 Sep 2023 00:19:39 +0200 Subject: http: Allow multiple non-file Content-Disposition Now, slweb accepts requests such as: --boundary Content-Disposition: form-data; name="field1" value1 --boundary Content-Disposition: form-data; name="field2" value2 --boundary Content-Disposition: form-data; name="field3"; filename="example.txt" The following breaking changes have been introduced: Member "dir" from struct http_post was a leftover from the days where slcl and slweb were one project. It did not make sense for slweb, since it should not decide which Content-Disposition names are allowed. In other words, "dir" was only relevant in the scope of slcl. Member "n" from struct http_post used to have two meanings: - The length of a URL-encoded request. - The number of files on a multipart/form-data request. Since "npairs" had to be introduced to struct http_post, it did not make sense to keep this dual meaning any more. Therefore, "n" has been restricted to the former, whereas a new member, called "nfiles", has been introduced for the latter. --- http.c | 52 ++++++++++++++++++++++++++++++++++++++++------------ include/slweb/http.h | 8 ++++++-- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/http.c b/http.c index 13790b8..e239972 100644 --- a/http.c +++ b/http.c @@ -75,10 +75,10 @@ struct http_ctx off_t len, written; char *boundary; - const char *dir; - size_t blen, nforms, nfiles; + size_t blen, nforms, nfiles, npairs; int fd; struct http_post_file *files; + struct http_post_pair *pairs; struct form { @@ -435,6 +435,7 @@ static void ctx_free(struct ctx *const c) struct multiform *const m = &c->u.mf; free(m->files); + free(m->pairs); free(m->boundary); if (m->fd >= 0 && close(m->fd)) @@ -1338,9 +1339,10 @@ static int end_boundary_line(struct http_ctx *const h) .resource = c->resource, .u.post = { - .dir = m->dir, .files = m->files, - .n = m->nfiles + .pairs = m->pairs, + .nfiles = m->nfiles, + .npairs = m->npairs } }; @@ -1495,26 +1497,52 @@ static int apply_from_file(struct http_ctx *const h, struct form *const f) return 0; } +static bool name_exists(const struct multiform *const m, + const struct form *const f) +{ + for (size_t i = 0; i < m->npairs; i++) + { + const struct http_post_pair *const p = &m->pairs[i]; + + if (!strcmp(f->name, p->name)) + { + fprintf(stderr, "%s: \"%s\" defined more than once\n", + f->name, __func__); + return true; + } + } + + return false; +} + static int apply_from_mem(struct http_ctx *const h, struct form *const f) { struct multiform *const m = &h->ctx.u.mf; + if (name_exists(m, f)) + return 1; + + struct http_post_pair *pairs, *p; + const size_t n = m->npairs + 1; + if (!(f->value = strndup(h->line, m->written))) { fprintf(stderr, "%s: strndup(3): %s\n", __func__, strerror(errno)); return -1; } - else if (!strcmp(f->name, "dir")) + else if (!(pairs = realloc(m->pairs, n * sizeof *m->pairs))) { - if (m->dir) - { - fprintf(stderr, "%s: \"dir\" defined more than once\n", __func__); - return 1; - } - - m->dir = f->value; + fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno)); + return -1; } + pairs[m->npairs++] = (const struct http_post_pair) + { + .name = f->name, + .value = f->value + }; + + m->pairs = pairs; return 0; } diff --git a/include/slweb/http.h b/include/slweb/http.h index ac1f51a..db2c0b6 100644 --- a/include/slweb/http.h +++ b/include/slweb/http.h @@ -26,8 +26,12 @@ struct http_payload { bool expect_continue; const void *data; - size_t n; - const char *dir; + size_t n, nfiles, npairs; + + const struct http_post_pair + { + const char *name, *value; + } *pairs; const struct http_post_file { -- cgit v1.2.3