Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F3071782
multipart.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
multipart.c
View Options
#include
<compat.h>
// IWYU pragma: keep
#include
<stdbool.h>
#include
<stdint.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<strings.h>
#include
"ant.h"
#include
"errors.h"
#include
"internal.h"
#include
"modules/blob.h"
#include
"modules/formdata.h"
#include
"modules/multipart.h"
#include
"modules/url.h"
static
ant_value_t
multipart_invalid
(
ant_t
*
js
)
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to parse body as FormData"
);
}
static
bool
ct_is_type
(
const
char
*
ct
,
const
char
*
type
)
{
size_t
type_len
=
0
;
if
(
!
ct
||
!
type
)
return
false
;
while
(
*
ct
==
' '
||
*
ct
==
'\t'
)
ct
++
;
type_len
=
strlen
(
type
);
if
(
strncasecmp
(
ct
,
type
,
type_len
)
!=
0
)
return
false
;
ct
+=
type_len
;
return
*
ct
==
'\0'
||
*
ct
==
';'
||
*
ct
==
' '
||
*
ct
==
'\t'
;
}
static
const
char
*
ct_find_next_param
(
const
char
*
p
)
{
scan
:
if
(
*
p
==
'\0'
)
return
NULL
;
if
(
*
p
==
';'
)
return
p
;
if
(
*
p
!=
'"'
)
{
p
++
;
goto
scan
;
}
quoted
:
p
++
;
if
(
*
p
==
'\0'
)
return
NULL
;
if
(
*
p
==
'\\'
)
goto
escaped
;
if
(
*
p
==
'"'
)
{
p
++
;
goto
scan
;
}
goto
quoted
;
escaped
:
p
++
;
if
(
*
p
==
'\0'
)
return
NULL
;
p
++
;
goto
quoted
;
}
static
char
*
ct_get_param_dup
(
const
char
*
ct
,
const
char
*
name
)
{
const
char
*
p
=
NULL
;
const
char
*
param_name
=
NULL
;
const
char
*
param_name_end
=
NULL
;
char
*
buf
=
NULL
;
size_t
name_len
=
0
;
size_t
len
=
0
;
size_t
cap
=
0
;
char
ch
=
'\0'
;
if
(
!
ct
||
!
name
)
return
NULL
;
name_len
=
strlen
(
name
);
if
(
name_len
==
0
)
return
NULL
;
p
=
strchr
(
ct
,
';'
);
next_param
:
if
(
!
p
)
return
NULL
;
p
++
;
while
(
*
p
==
' '
||
*
p
==
'\t'
)
p
++
;
if
(
*
p
==
'\0'
)
return
NULL
;
param_name
=
p
;
while
(
*
p
&&
*
p
!=
'='
&&
*
p
!=
';'
)
p
++
;
param_name_end
=
p
;
while
(
param_name_end
>
param_name
&&
(
param_name_end
[
-1
]
==
' '
||
param_name_end
[
-1
]
==
'\t'
)
)
param_name_end
--
;
if
(
*
p
!=
'='
)
goto
skip_param
;
if
((
size_t
)(
param_name_end
-
param_name
)
!=
name_len
)
goto
skip_param
;
if
(
strncasecmp
(
param_name
,
name
,
name_len
)
!=
0
)
goto
skip_param
;
p
++
;
while
(
*
p
==
' '
||
*
p
==
'\t'
)
p
++
;
if
(
*
p
==
'\0'
)
return
strdup
(
""
);
if
(
*
p
==
'"'
)
{
p
++
;
goto
quoted
;
}
unquoted
:
if
(
*
p
==
'\0'
||
*
p
==
';'
||
*
p
==
' '
||
*
p
==
'\t'
)
{
buf
=
malloc
(
len
+
1
);
if
(
!
buf
)
return
NULL
;
if
(
len
!=
0
)
memcpy
(
buf
,
p
-
len
,
len
);
buf
[
len
]
=
'\0'
;
return
buf
;
}
p
++
;
len
++
;
goto
unquoted
;
quoted
:
if
(
*
p
==
'\0'
)
goto
fail
;
if
(
*
p
==
'"'
)
goto
done
;
if
(
*
p
==
'\\'
)
goto
quoted_escape
;
ch
=
*
p
++
;
goto
append
;
quoted_escape
:
p
++
;
if
(
*
p
==
'\0'
)
goto
fail
;
ch
=
*
p
++
;
goto
append
;
append
:
if
(
len
==
cap
)
{
size_t
next_cap
=
cap
?
cap
*
2
:
32
;
char
*
next
=
realloc
(
buf
,
next_cap
);
if
(
!
next
)
goto
fail
;
buf
=
next
;
cap
=
next_cap
;
}
buf
[
len
++
]
=
ch
;
goto
quoted
;
done
:
p
++
;
if
(
len
==
cap
)
{
size_t
next_cap
=
cap
?
cap
+
1
:
1
;
char
*
next
=
realloc
(
buf
,
next_cap
);
if
(
!
next
)
goto
fail
;
buf
=
next
;
cap
=
next_cap
;
}
buf
[
len
]
=
'\0'
;
return
buf
;
skip_param
:
p
=
ct_find_next_param
(
p
);
goto
next_param
;
fail
:
free
(
buf
);
return
NULL
;
}
static
ant_value_t
parse_formdata_urlencoded
(
ant_t
*
js
,
const
uint8_t
*
data
,
size_t
size
)
{
ant_value_t
fd
=
0
;
char
*
body
=
NULL
;
char
*
cursor
=
NULL
;
fd
=
formdata_create_empty
(
js
);
if
(
is_err
(
fd
))
return
fd
;
if
(
!
data
||
size
==
0
)
return
fd
;
body
=
strndup
((
const
char
*
)
data
,
size
);
if
(
!
body
)
return
js_mkerr
(
js
,
"out of memory"
);
cursor
=
body
;
while
(
cursor
)
{
ant_value_t
r
=
0
;
char
*
amp
=
strchr
(
cursor
,
'&'
);
char
*
eq
=
NULL
;
char
*
raw_name
=
NULL
;
char
*
raw_value
=
NULL
;
char
*
name
=
NULL
;
char
*
value
=
NULL
;
if
(
amp
)
*
amp
=
'\0'
;
eq
=
strchr
(
cursor
,
'='
);
raw_name
=
cursor
;
raw_value
=
eq
?
(
eq
+
1
)
:
""
;
if
(
eq
)
*
eq
=
'\0'
;
name
=
form_urldecode
(
raw_name
);
value
=
form_urldecode
(
raw_value
);
if
(
!
name
||
!
value
)
{
free
(
name
);
free
(
value
);
free
(
body
);
return
js_mkerr
(
js
,
"out of memory"
);
}
r
=
formdata_append_string
(
js
,
fd
,
js_mkstr
(
js
,
name
,
strlen
(
name
)),
js_mkstr
(
js
,
value
,
strlen
(
value
))
);
free
(
name
);
free
(
value
);
if
(
is_err
(
r
))
{
free
(
body
);
return
r
;
}
cursor
=
amp
?
(
amp
+
1
)
:
NULL
;
}
free
(
body
);
return
fd
;
}
static
const
uint8_t
*
find_bytes
(
const
uint8_t
*
haystack
,
size_t
haystack_len
,
const
uint8_t
*
needle
,
size_t
needle_len
)
{
size_t
i
=
0
;
if
(
needle_len
==
0
)
return
haystack
;
if
(
haystack_len
<
needle_len
)
return
NULL
;
for
(
i
=
0
;
i
+
needle_len
<=
haystack_len
;
i
++
)
{
if
(
memcmp
(
haystack
+
i
,
needle
,
needle_len
)
==
0
)
return
haystack
+
i
;
}
return
NULL
;
}
typedef
struct
{
char
*
name
;
char
*
filename
;
char
*
content_type
;
}
multipart_part_info_t
;
static
void
multipart_part_info_clear
(
multipart_part_info_t
*
info
)
{
if
(
!
info
)
return
;
free
(
info
->
name
);
free
(
info
->
filename
);
free
(
info
->
content_type
);
info
->
name
=
NULL
;
info
->
filename
=
NULL
;
info
->
content_type
=
NULL
;
}
static
bool
multipart_parse_headers
(
ant_t
*
js
,
char
*
headers
,
multipart_part_info_t
*
info
,
ant_value_t
*
err_out
)
{
char
*
saveptr
=
NULL
;
for
(
char
*
line
=
strtok_r
(
headers
,
"
\r\n
"
,
&
saveptr
);
line
;
line
=
strtok_r
(
NULL
,
"
\r\n
"
,
&
saveptr
)
)
{
char
*
colon
=
strchr
(
line
,
':'
);
char
*
value
=
NULL
;
if
(
!
colon
)
continue
;
*
colon
=
'\0'
;
value
=
colon
+
1
;
while
(
*
value
==
' '
||
*
value
==
'\t'
)
value
++
;
if
(
strcasecmp
(
line
,
"Content-Disposition"
)
==
0
)
{
free
(
info
->
name
);
free
(
info
->
filename
);
info
->
name
=
ct_get_param_dup
(
value
,
"name"
);
info
->
filename
=
ct_get_param_dup
(
value
,
"filename"
);
continue
;
}
if
(
strcasecmp
(
line
,
"Content-Type"
)
==
0
)
{
free
(
info
->
content_type
);
info
->
content_type
=
strdup
(
value
);
if
(
!
info
->
content_type
)
{
*
err_out
=
js_mkerr
(
js
,
"out of memory"
);
return
false
;
}}
}
if
(
info
->
name
)
return
true
;
*
err_out
=
multipart_invalid
(
js
);
return
false
;
}
static
const
uint8_t
*
multipart_find_part_end
(
const
uint8_t
*
p
,
const
uint8_t
*
end
,
const
char
*
delim
,
size_t
delim_len
)
{
char
*
marker
=
malloc
(
delim_len
+
3
);
const
uint8_t
*
part_end
=
NULL
;
if
(
!
marker
)
return
NULL
;
snprintf
(
marker
,
delim_len
+
3
,
"
\r\n
%s"
,
delim
);
part_end
=
find_bytes
(
p
,
(
size_t
)(
end
-
p
),
(
const
uint8_t
*
)
marker
,
delim_len
+
2
);
free
(
marker
);
return
part_end
;
}
static
ant_value_t
multipart_append_part
(
ant_t
*
js
,
ant_value_t
fd
,
const
multipart_part_info_t
*
info
,
const
uint8_t
*
data
,
size_t
size
)
{
if
(
info
->
filename
)
{
ant_value_t
blob
=
blob_create
(
js
,
data
,
size
,
info
->
content_type
?
info
->
content_type
:
""
);
if
(
is_err
(
blob
))
return
blob
;
return
formdata_append_file
(
js
,
fd
,
js_mkstr
(
js
,
info
->
name
,
strlen
(
info
->
name
)),
blob
,
js_mkstr
(
js
,
info
->
filename
,
strlen
(
info
->
filename
))
);
}
return
formdata_append_string
(
js
,
fd
,
js_mkstr
(
js
,
info
->
name
,
strlen
(
info
->
name
)),
js_mkstr
(
js
,
data
,
size
)
);
}
static
ant_value_t
parse_formdata_multipart
(
ant_t
*
js
,
const
uint8_t
*
data
,
size_t
size
,
const
char
*
body_type
)
{
ant_value_t
fd
=
0
;
char
*
boundary
=
NULL
;
char
*
delim
=
NULL
;
const
uint8_t
*
p
=
data
;
const
uint8_t
*
end
=
data
+
size
;
size_t
delim_len
=
0
;
if
(
!
data
||
size
==
0
)
return
multipart_invalid
(
js
);
boundary
=
ct_get_param_dup
(
body_type
,
"boundary"
);
if
(
!
boundary
||
boundary
[
0
]
==
'\0'
)
goto
invalid
;
fd
=
formdata_create_empty
(
js
);
if
(
is_err
(
fd
))
goto
done
;
delim_len
=
strlen
(
boundary
)
+
2
;
delim
=
malloc
(
delim_len
+
1
);
if
(
!
delim
)
{
fd
=
js_mkerr
(
js
,
"out of memory"
);
goto
done
;
}
snprintf
(
delim
,
delim_len
+
1
,
"--%s"
,
boundary
);
if
((
size_t
)(
end
-
p
)
<
delim_len
||
memcmp
(
p
,
delim
,
delim_len
)
!=
0
)
goto
invalid
;
p
+=
delim_len
;
next_part
:
if
(
p
>
end
)
goto
done
;
if
((
size_t
)(
end
-
p
)
>=
2
&&
memcmp
(
p
,
"--"
,
2
)
==
0
)
goto
done
;
if
((
size_t
)(
end
-
p
)
<
2
||
memcmp
(
p
,
"
\r\n
"
,
2
)
!=
0
)
goto
invalid
;
p
+=
2
;
{
ant_value_t
r
=
0
;
ant_value_t
hdr_err
=
js_mkundef
();
const
uint8_t
*
hdr_end
=
find_bytes
(
p
,
(
size_t
)(
end
-
p
),
(
const
uint8_t
*
)
"
\r\n\r\n
"
,
4
);
char
*
headers
=
NULL
;
multipart_part_info_t
info
=
{
0
};
const
uint8_t
*
part_end
=
NULL
;
if
(
!
hdr_end
)
goto
invalid
;
headers
=
strndup
((
const
char
*
)
p
,
(
size_t
)(
hdr_end
-
p
));
if
(
!
headers
)
{
fd
=
js_mkerr
(
js
,
"out of memory"
);
goto
done
;
}
p
=
hdr_end
+
4
;
if
(
!
multipart_parse_headers
(
js
,
headers
,
&
info
,
&
hdr_err
))
{
free
(
headers
);
multipart_part_info_clear
(
&
info
);
if
(
is_err
(
hdr_err
))
{
fd
=
hdr_err
;
goto
done
;
}
goto
invalid
;
}
free
(
headers
);
part_end
=
multipart_find_part_end
(
p
,
end
,
delim
,
delim_len
);
if
(
!
part_end
)
{
multipart_part_info_clear
(
&
info
);
goto
invalid
;
}
r
=
multipart_append_part
(
js
,
fd
,
&
info
,
p
,
(
size_t
)(
part_end
-
p
));
multipart_part_info_clear
(
&
info
);
if
(
is_err
(
r
))
{
fd
=
r
;
goto
done
;
}
p
=
part_end
+
2
+
delim_len
;
if
((
size_t
)(
end
-
p
)
>=
2
&&
memcmp
(
p
,
"--"
,
2
)
==
0
)
goto
done
;
}
goto
next_part
;
invalid
:
fd
=
multipart_invalid
(
js
);
done
:
free
(
boundary
);
free
(
delim
);
return
fd
;
}
ant_value_t
formdata_parse_body
(
ant_t
*
js
,
const
uint8_t
*
data
,
size_t
size
,
const
char
*
body_type
,
bool
has_body
)
{
if
(
body_type
&&
ct_is_type
(
body_type
,
"application/x-www-form-urlencoded"
))
{
return
parse_formdata_urlencoded
(
js
,
data
,
size
);
}
if
(
body_type
&&
ct_is_type
(
body_type
,
"multipart/form-data"
))
{
if
(
!
has_body
||
!
data
||
size
==
0
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to parse body as FormData"
);
return
parse_formdata_multipart
(
js
,
data
,
size
,
body_type
);
}
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to parse body as FormData"
);
}
typedef
struct
{
uint8_t
*
buf
;
size_t
size
;
size_t
cap
;
}
mp_buf_t
;
static
bool
mp_grow
(
mp_buf_t
*
b
,
size_t
need
)
{
size_t
nc
=
0
;
uint8_t
*
nb
=
NULL
;
if
(
b
->
size
+
need
<=
b
->
cap
)
return
true
;
nc
=
b
->
cap
?
b
->
cap
*
2
:
4096
;
while
(
nc
<
b
->
size
+
need
)
nc
*=
2
;
nb
=
realloc
(
b
->
buf
,
nc
);
if
(
!
nb
)
return
false
;
b
->
buf
=
nb
;
b
->
cap
=
nc
;
return
true
;
}
static
bool
mp_append
(
mp_buf_t
*
b
,
const
void
*
data
,
size_t
len
)
{
if
(
!
mp_grow
(
b
,
len
))
return
false
;
memcpy
(
b
->
buf
+
b
->
size
,
data
,
len
);
b
->
size
+=
len
;
return
true
;
}
static
bool
mp_append_str
(
mp_buf_t
*
b
,
const
char
*
s
)
{
return
mp_append
(
b
,
s
,
strlen
(
s
));
}
static
bool
mp_append_quoted
(
mp_buf_t
*
b
,
const
char
*
s
)
{
const
char
*
p
=
s
?
s
:
""
;
if
(
!
mp_append_str
(
b
,
"
\"
"
))
return
false
;
while
(
*
p
)
{
if
(
*
p
==
'"'
||
*
p
==
'\\'
)
if
(
!
mp_append
(
b
,
"
\\
"
,
1
))
return
false
;
if
(
!
mp_append
(
b
,
p
,
1
))
return
false
;
p
++
;
}
return
mp_append_str
(
b
,
"
\"
"
);
}
static
bool
mp_append_boundary
(
mp_buf_t
*
b
,
const
char
*
boundary
,
bool
closing
)
{
if
(
!
mp_append_str
(
b
,
"--"
))
return
false
;
if
(
!
mp_append_str
(
b
,
boundary
))
return
false
;
return
mp_append_str
(
b
,
closing
?
"--
\r\n
"
:
"
\r\n
"
);
}
static
bool
mp_append_text_part
(
mp_buf_t
*
b
,
const
fd_entry_t
*
e
)
{
const
char
*
val
=
e
->
str_value
?
e
->
str_value
:
""
;
if
(
!
mp_append_str
(
b
,
"Content-Disposition: form-data; name="
))
return
false
;
if
(
!
mp_append_quoted
(
b
,
e
->
name
?
e
->
name
:
""
))
return
false
;
if
(
!
mp_append_str
(
b
,
"
\r\n\r\n
"
))
return
false
;
return
mp_append_str
(
b
,
val
);
}
static
bool
mp_append_file_part
(
ant_t
*
js
,
mp_buf_t
*
b
,
ant_value_t
values_arr
,
const
fd_entry_t
*
e
)
{
ant_value_t
file_val
=
js_arr_get
(
js
,
values_arr
,
(
ant_offset_t
)
e
->
val_idx
);
blob_data_t
*
bd
=
blob_get_data
(
file_val
);
const
char
*
filename
=
(
bd
&&
bd
->
name
)
?
bd
->
name
:
"blob"
;
const
char
*
mime
=
(
bd
&&
bd
->
type
&&
bd
->
type
[
0
])
?
bd
->
type
:
"application/octet-stream"
;
if
(
!
mp_append_str
(
b
,
"Content-Disposition: form-data; name="
))
return
false
;
if
(
!
mp_append_quoted
(
b
,
e
->
name
?
e
->
name
:
""
))
return
false
;
if
(
!
mp_append_str
(
b
,
"; filename="
))
return
false
;
if
(
!
mp_append_quoted
(
b
,
filename
))
return
false
;
if
(
!
mp_append_str
(
b
,
"
\r\n
Content-Type: "
))
return
false
;
if
(
!
mp_append_str
(
b
,
mime
))
return
false
;
if
(
!
mp_append_str
(
b
,
"
\r\n\r\n
"
))
return
false
;
if
(
!
bd
||
!
bd
->
data
||
bd
->
size
==
0
)
return
true
;
return
mp_append
(
b
,
bd
->
data
,
bd
->
size
);
}
uint8_t
*
formdata_serialize_multipart
(
ant_t
*
js
,
ant_value_t
fd
,
size_t
*
out_size
,
char
**
out_boundary
)
{
ant_value_t
values_arr
=
js_get_slot
(
fd
,
SLOT_ENTRIES
);
ant_value_t
data_slot
=
js_get_slot
(
fd
,
SLOT_DATA
);
char
boundary
[
49
];
mp_buf_t
b
=
{
NULL
,
0
,
0
};
fd_data_t
*
d
=
NULL
;
if
(
vtype
(
data_slot
)
!=
T_NUM
)
return
NULL
;
d
=
(
fd_data_t
*
)(
uintptr_t
)(
size_t
)
js_getnum
(
data_slot
);
if
(
!
d
)
return
NULL
;
snprintf
(
boundary
,
sizeof
(
boundary
),
"----AntFormBoundary%08x%08x"
,
(
unsigned
)
rand
(),
(
unsigned
)
rand
()
);
if
(
d
->
count
==
0
)
{
uint8_t
*
empty
=
malloc
(
1
);
if
(
!
empty
)
return
NULL
;
*
out_size
=
0
;
*
out_boundary
=
strdup
(
boundary
);
if
(
!*
out_boundary
)
{
free
(
empty
);
return
NULL
;
}
return
empty
;
}
for
(
fd_entry_t
*
e
=
d
->
head
;
e
;
e
=
e
->
next
)
{
if
(
!
mp_append_boundary
(
&
b
,
boundary
,
false
))
goto
oom
;
if
(
!
e
->
is_file
&&
!
mp_append_text_part
(
&
b
,
e
))
goto
oom
;
if
(
e
->
is_file
&&
!
mp_append_file_part
(
js
,
&
b
,
values_arr
,
e
))
goto
oom
;
if
(
!
mp_append_str
(
&
b
,
"
\r\n
"
))
goto
oom
;
}
if
(
!
mp_append_boundary
(
&
b
,
boundary
,
true
))
goto
oom
;
*
out_size
=
b
.
size
;
*
out_boundary
=
strdup
(
boundary
);
if
(
!*
out_boundary
)
goto
oom
;
return
b
.
buf
;
oom
:
free
(
b
.
buf
);
return
NULL
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, Apr 4, 12:49 AM (1 d, 22 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
520903
Default Alt Text
multipart.c (13 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment