13#include "multipart_parser.h"
16 char header_field[256];
17 size_t header_field_len;
18 char header_value[1024];
19 size_t header_value_len;
26 char content_type[256];
35static void mp_parse_headers(mp_parse_ctx *ctx);
51 if (ctx->have_value) {
53 mp_parse_headers(ctx);
55 if (ctx->header_field_len + len <
sizeof(ctx->header_field)) {
56 memcpy(ctx->header_field + ctx->header_field_len,
at, len);
57 ctx->header_field_len += len;
75 ctx->have_value =
true;
76 if (ctx->header_value_len + len <
sizeof(ctx->header_value)) {
77 memcpy(ctx->header_value + ctx->header_value_len,
at, len);
78 ctx->header_value_len += len;
93static void mp_parse_headers(mp_parse_ctx *ctx) {
94 if (ctx->header_field_len >=
sizeof(ctx->header_field))
95 ctx->header_field_len =
sizeof(ctx->header_field) - 1;
96 ctx->header_field[ctx->header_field_len] =
'\0';
98 if (ctx->header_value_len >=
sizeof(ctx->header_value))
99 ctx->header_value_len =
sizeof(ctx->header_value) - 1;
100 ctx->header_value[ctx->header_value_len] =
'\0';
102 if (
strcasecmp(ctx->header_field,
"Content-Disposition") == 0) {
109 if (
nlen <
sizeof(ctx->name)) {
111 ctx->name[
nlen] =
'\0';
121 if (
flen <
sizeof(ctx->filename)) {
123 ctx->filename[
flen] =
'\0';
127 }
else if (
strcasecmp(ctx->header_field,
"Content-Type") == 0) {
129 if (
vlen <
sizeof(ctx->content_type))
strcpy(ctx->content_type, ctx->header_value);
132 ctx->header_field_len = 0;
133 ctx->header_value_len = 0;
134 ctx->have_value =
false;
148 mp_parse_headers(ctx);
165 if (ctx->have_value) {
166 mp_parse_headers(ctx);
168 ctx->header_field_len = 0;
169 ctx->header_value_len = 0;
170 ctx->have_value =
false;
172 ctx->filename[0] =
'\0';
173 ctx->content_type[0] =
'\0';
196 if (len == 0)
return 0;
197 if (ctx->data_len + len > ctx->data_cap) {
198 size_t new_cap = (ctx->data_len + len) * 2;
209 memcpy(ctx->data + ctx->data_len,
at, len);
210 ctx->data_len += len;
229 if (!field)
return 1;
230 memset(field, 0,
sizeof(*field));
236 if (ctx->filename[0]) {
240 if (ctx->content_type[0]) {
244 field->
data = ctx->data;
246 field->
next = ctx->result->fields;
247 ctx->result->fields = field;
275 if (!result)
return NULL;
278 mp_parse_ctx ctx = {0};
282 .on_header_field = mp_on_header_field,
283 .on_header_value = mp_on_header_value,
284 .on_headers_complete = mp_on_headers_complete,
285 .on_part_data_begin = mp_on_part_data_begin,
286 .on_part_data = mp_on_part_data,
287 .on_part_data_end = mp_on_part_data_end,
364 if (!content_type)
return NULL;
365 const char *
p = content_type;
367 while (*
p && (
isspace((
unsigned char)*
p) || *
p ==
';'))
p++;
377 while (*
p && *
p !=
'"')
p++;
379 while (*
p && *
p !=
';' && !
isspace((
unsigned char)*
p))
p++;
390 while (*
p && *
p !=
';')
p++;
Memory allocation wrappers backed by libttak.
void cwist_free(void *ptr)
Free memory obtained via cwist_alloc/cwist_strdup/etc.
void * cwist_alloc(size_t size)
Allocate zeroed memory tracked by libttak.
#define CWIST_SCRATCH_DEFER
Attach to a cwist_scratch_t variable's declaration so its allocation (arena- or heap-backed,...
void cwist_multipart_result_destroy(cwist_multipart_result *result)
Destroy a parse result and everything it owns.
char * cwist_multipart_extract_boundary(const char *content_type)
Extract the boundary parameter from a Content-Type header value.
cwist_multipart_result * cwist_multipart_parse(const char *body, size_t body_len, const char *boundary)
Parse a multipart/form-data body into a field list.
RFC 7578 multipart/form-data parser.
char * filename
File name (NULL for non-file fields).
char * name
Form field name.
size_t data_len
Length of data in bytes.
struct cwist_multipart_field * next
char * data
Raw part body (may contain binary data).
char * content_type
Content-Type of the part (NULL if absent).
cwist_multipart_field * fields
Head of the field linked list.
Backing state for cwist_scratch_alloc()/CWIST_SCRATCH_DEFER: a single block-scoped allocation that pr...