40 if (c >=
'0' && c <=
'9')
42 if (c >=
'a' && c <=
'f')
44 if (c >=
'A' && c <=
'F')
53 for (i = 0; i < len; i++) {
54 unsigned char c = (
unsigned char)s[i];
63 c = (
unsigned char)((hi << 4) | lo);
80 Malloc(dst,
char, len + 1);
83 if (n == (
size_t)-1 ||
133 for (i = 0; i < (*form)->nb_fields; i++) {
151 size_t i,
start, npairs = 0;
154 if ((!s && len > 0) || max_fields == 0)
160 for (i = 0,
start = 0; i <= len; i++) {
161 if (i == len || s[i] ==
'&') {
162 if (i >
start && ++npairs > max_fields) {
178 for (i = 0,
start = 0; i <= len; i++) {
179 size_t eq, nlen, vlen;
181 if (i < len && s[i] !=
'&')
188 while (
eq < i && s[
eq] !=
'=')
194 if (!name || !value || nlen == 0) {
226 if (strcmp(f->
name, name) == 0) {
254#define _MP_PREAMBLE_MAX 1024
257 return (c >=
'A' && c <=
'Z') ? c -
'A' +
'a' : c;
261static int _mp_ieq(
const char* s,
size_t n,
const char* lit) {
263 if (strlen(lit) != n)
265 for (i = 0; i < n; i++)
266 if (
_mp_lc((
unsigned char)s[i]) != lit[i])
272 if ((c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') || (c >=
'0' && c <=
'9'))
274 return c !=
'\0' && strchr(
"!#$%&'*+-.^_`|~", c) != NULL;
279 if ((c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') || (c >=
'0' && c <=
'9'))
281 return c !=
'\0' && strchr(
"'()+_,-./:=? ", c) != NULL;
297 while (i < n && (v[i] ==
' ' || v[i] ==
'\t'))
304 while (i < n && (v[i] ==
' ' || v[i] ==
'\t'))
307 while (i < n &&
_mp_tchar((
unsigned char)v[i]))
309 if (i == ns || i == n || v[i] !=
'=')
315 if (i < n && v[i] ==
'"') {
334 while (i < n && v[i] !=
';' && v[i] !=
' ' && v[i] !=
'\t') {
355 size_t i = 0, ts, pos;
358 if (!content_type || !out)
361 while (i < len && (content_type[i] ==
' ' || content_type[i] ==
'\t'))
364 while (i < len && content_type[i] !=
';' && content_type[i] !=
' ' && content_type[i] !=
'\t')
366 if (!
_mp_ieq(content_type + ts, i - ts,
"multipart/form-data"))
380 if (r < 0 || !found) {
390 for (i = 0; i < n; i++) {
391 unsigned char c = (
unsigned char)s[i];
392 if (c < 0x20 || c == 0x7f)
402static size_t _mp_find_delim(
const char* b,
size_t len,
size_t from,
const char* boundary,
size_t blen,
size_t* after,
int* closing) {
404 for (i = from; i + 4 + blen <= len; i++) {
406 if (b[i] !=
'\r' || b[i + 1] !=
'\n' || b[i + 2] !=
'-' || b[i + 3] !=
'-' || memcmp(b + i + 4, boundary, blen) != 0)
409 if (k + 2 <= len && b[k] ==
'-' && b[k + 1] ==
'-') {
414 while (k < len && (b[k] ==
' ' || b[k] ==
'\t'))
416 if (k + 2 <= len && b[k] ==
'\r' && b[k + 1] ==
'\n') {
429 int have_disp = 0, have_type = 0;
431 size_t le = i, colon, vs, ve;
432 while (
le + 1 < n && !(
h[
le] ==
'\r' &&
h[
le + 1] ==
'\n'))
437 while (colon <
le &&
h[colon] !=
':')
439 if (colon == i || colon >=
le)
443 for (k = i; k < colon; k++)
448 while (vs <
le && (
h[vs] ==
' ' ||
h[vs] ==
'\t'))
451 while (ve > vs && (
h[ve - 1] ==
' ' ||
h[ve - 1] ==
'\t'))
455 for (k = vs; k < ve; k++)
456 if (((
unsigned char)
h[k] < 0x20 &&
h[k] !=
'\t') ||
h[k] == 0x7f)
459 if (
_mp_ieq(
h + i, colon - i,
"content-disposition")) {
460 size_t pos = vs, ts = vs;
461 int r, have_name = 0;
466 while (pos < ve &&
h[pos] !=
';' &&
h[pos] !=
' ' &&
h[pos] !=
'\t')
468 if (!
_mp_ieq(
h + ts, pos - ts,
"form-data"))
485 if (r < 0 || !have_name)
487 }
else if (
_mp_ieq(
h + i, colon - i,
"content-type")) {
509 size_t blen, cursor, cap = 8, after = 0;
512 if (!buf || !boundary || max_parts == 0)
514 blen = strlen(boundary);
518 if (len >= 2 + blen && buf[0] ==
'-' && buf[1] ==
'-' && memcmp(buf + 2, boundary, blen) == 0) {
520 while (k < len && (buf[k] ==
' ' || buf[k] ==
'\t'))
522 if (k + 2 > len || buf[k] !=
'\r' || buf[k + 1] !=
'\n')
527 if (d == (
size_t)-1 || closing)
540 size_t hend, d, scan;
543 for (hend = 0; hend + 3 < scan; hend++)
544 if (buf[cursor + hend] ==
'\r' && buf[cursor + hend + 1] ==
'\n' && buf[cursor + hend + 2] ==
'\r' && buf[cursor + hend + 3] ==
'\n')
546 if (hend + 3 >= scan)
552 size_t ncap = cap * 2 < max_parts ? cap * 2 : max_parts;
556 memcpy(grown, mp->
parts, cap *
sizeof(*grown));
562 memset(part, 0,
sizeof(*part));
569 if (d == (
size_t)-1 || d + 2 < part->
body_off)
593 if (strcmp(mp->
parts[i - 1].
name, name) == 0)
594 return &mp->
parts[i - 1];
609 if (strcmp(mp->
parts[i].
name, name) == 0)
static int h(void *ctx, const N_ROUTE_MATCH *m)
a handler that does nothing (the test only routes)
static N_HTTP_SERVER * start(N_HTTP_SERVER_CONFIG *cfg, char *port, int base)
start a server on the first free port from base
static int eq(double a, double b)
#define FreeNoLog(__ptr)
Free Handler without log.
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
#define __n_assert(__ptr, __ret)
macro to assert things
#define Free(__ptr)
Free Handler to get errors.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_DEBUG
debug-level messages
size_t nb_parts
number of parts
size_t body_len
body length
size_t value_len
length of value
char content_type[127+1]
the part's Content-Type as sent, "" when absent: untrusted
char * value
decoded value, "" for "name" or "name="
int has_filename
non-zero when the part carried a filename parameter (a file field)
size_t name_len
length of name
size_t body_off
body offset in the parsed buffer
N_HTTP_FIELD * fields
the fields, nb_fields of them
char name[200+1]
field name (non-empty, UTF-8, no control byte)
char filename[255+1]
the filename as sent (UTF-8, no control byte, may be ""): untrusted, never use it as a path
N_HTTP_FORM_PART * parts
the parts, in order
size_t nb_fields
number of fields
#define N_HTTP_FORM_PART_TYPE_MAX
longest part Content-Type kept
#define N_HTTP_FORM_SKIP_INVALID
n_http_form_parse(): drop a pair that fails to decode instead of failing
int n_http_form_multipart_boundary(const char *content_type, size_t len, char *out)
boundary of a multipart/form-data Content-Type, parsed strictly
N_HTTP_FORM_MULTIPART * n_http_form_multipart_parse(const char *buf, size_t len, const char *boundary, size_t max_parts)
parse a multipart/form-data body strictly, without copying it
#define N_HTTP_FORM_PART_HEAD_MAX
largest header block of one part
void n_http_form_multipart_free(N_HTTP_FORM_MULTIPART **mp)
free a parsed body and set *mp to NULL
size_t n_http_form_count(const N_HTTP_FORM *form, const char *name)
number of fields named name (exact, case-sensitive)
const N_HTTP_FORM_PART * n_http_form_multipart_get(const N_HTTP_FORM_MULTIPART *mp, const char *name)
the LAST part named name (PHP semantics)
const char * n_http_form_get(const N_HTTP_FORM *form, const char *name, size_t *value_len)
value of the LAST field named name (PHP semantics)
N_HTTP_FORM * n_http_form_parse(const char *s, size_t len, size_t max_fields, int flags)
decode a query string or an urlencoded body into fields
int n_http_percent_decode(const char *s, size_t len, int flags, N_STR **out)
percent-decode [s, s+len), appending to *out; malformed escapes, a decoded NUL and (without N_HTTP_FO...
size_t n_http_form_multipart_count(const N_HTTP_FORM_MULTIPART *mp, const char *name)
number of parts named name
#define N_HTTP_FORM_PLUS_SPACE
decode '+' as a space (query strings and urlencoded forms, not paths)
#define N_HTTP_FORM_BOUNDARY_MAX
longest boundary RFC 2046 allows
#define N_HTTP_FORM_PART_NAME_MAX
longest multipart field name accepted
#define N_HTTP_FORM_PART_FILENAME_MAX
longest multipart filename accepted
#define N_HTTP_FORM_RAW_BYTES
accept decoded bytes that are not well-formed UTF-8 (NUL stays refused)
void n_http_form_free(N_HTTP_FORM **form)
free a form and set *form to NULL
one decoded name=value pair; both are NUL-terminated and hold no NUL
decoded fields, in the order they were sent
a parsed multipart/form-data body
one part of a multipart/form-data body; the body is NOT copied
N_STR * nstrcat_ex(N_STR **dest, void *src, NSTRBYTE size, int resize_flag)
Append data into N_STR using internal N_STR size and cursor position.
N_STR * new_nstr(NSTRBYTE size)
create a new N_STR string
int n_str_utf8_valid(const char *s, size_t len)
Test that [s, s+len) is well-formed UTF-8 (RFC 3629): no overlong form, no surrogate,...
A box including a string and his lenght.
Common headers and low-level functions & define.
static uint64_t le(const unsigned char *p, int n)
read a little endian unsigned value
N_STR and string function declaration.