2#if defined(USE_ESP32) && defined(USE_WEBSERVER_OTA)
7#include "multipart_parser.h"
11static const char *
const TAG =
"multipart";
17 memset(&settings_, 0,
sizeof(settings_));
18 settings_.on_header_field = on_header_field;
19 settings_.on_header_value = on_header_value;
20 settings_.on_part_data = on_part_data;
21 settings_.on_part_data_end = on_part_data_end;
23 ESP_LOGV(TAG,
"Initializing multipart parser with boundary: '%s' (len: %zu)", boundary.c_str(), boundary.length());
26 parser_ = multipart_parser_init(boundary.c_str(), &settings_);
28 multipart_parser_set_data(parser_,
this);
30 ESP_LOGE(TAG,
"Failed to initialize multipart parser");
36 multipart_parser_free(parser_);
42 ESP_LOGE(TAG,
"Parser not initialized");
46 size_t parsed = multipart_parser_execute(parser_, data,
len);
49 ESP_LOGW(TAG,
"Parser consumed %zu of %zu bytes - possible error", parsed,
len);
55void MultipartReader::process_header_(
const char *value,
size_t length) {
57 const char *field = current_header_field_.c_str();
58 size_t field_len = current_header_field_.length();
69 current_header_field_.clear();
72int MultipartReader::on_header_field(multipart_parser *parser,
const char *at,
size_t length) {
74 reader->current_header_field_.assign(at,
length);
78int MultipartReader::on_header_value(multipart_parser *parser,
const char *at,
size_t length) {
80 reader->process_header_(at,
length);
84int MultipartReader::on_part_data(multipart_parser *parser,
const char *at,
size_t length) {
87 if (reader->has_file() && reader->data_callback_) {
92 reader->data_callback_(
reinterpret_cast<const uint8_t *
>(at),
length);
97int MultipartReader::on_part_data_end(multipart_parser *parser) {
99 ESP_LOGV(TAG,
"Part data end");
100 if (reader->part_complete_callback_) {
101 reader->part_complete_callback_();
104 reader->current_part_ = Part{};
112 size_t prefix_len = strlen(prefix);
113 if (str_len < prefix_len) {
123 size_t param_len = strlen(param);
124 size_t search_pos = 0;
126 while (search_pos < header_len) {
128 const char *found =
strcasestr_n(header + search_pos, header_len - search_pos, param);
133 size_t pos = found - header;
136 if (
pos > 0 && header[
pos - 1] !=
' ' && header[
pos - 1] !=
';' && header[
pos - 1] !=
'\t') {
137 search_pos =
pos + 1;
145 while (
pos < header_len && (header[
pos] ==
' ' || header[
pos] ==
'\t')) {
149 if (
pos >= header_len || header[
pos] !=
'=') {
157 while (
pos < header_len && (header[
pos] ==
' ' || header[
pos] ==
'\t')) {
161 if (
pos >= header_len) {
167 if (header[
pos] ==
'"') {
169 const char *
end =
static_cast<const char *
>(memchr(header +
pos,
'"', header_len -
pos));
171 out.assign(header +
pos,
end - (header +
pos));
181 while (
end < header_len && header[
end] !=
';' && header[
end] !=
',' && header[
end] !=
' ' && header[
end] !=
'\t') {
200 size_t content_type_len = strlen(content_type);
203 if (!
strcasestr_n(content_type, content_type_len,
"multipart/form-data")) {
208 const char *b =
strcasestr_n(content_type, content_type_len,
"boundary=");
213 const char *start = b + 9;
216 while (*start ==
' ' || *start ==
'\t') {
225 const char *
end = start;
230 while (*
end && *
end !=
'"') {
233 *boundary_len =
end - start;
236 while (*
end && *
end !=
' ' && *
end !=
';' && *
end !=
'\r' && *
end !=
'\n' && *
end !=
'\t') {
239 *boundary_len =
end - start;
242 if (*boundary_len == 0) {
246 *boundary_start = start;
253 const char *start = str;
254 const char *
end = str +
len;
255 while (start <
end && (*start ==
' ' || *start ==
'\t' || *start ==
'\r' || *start ==
'\n'))
257 while (
end > start && (
end[-1] ==
' ' ||
end[-1] ==
'\t' ||
end[-1] ==
'\r' ||
end[-1] ==
'\n'))
259 out.assign(start,
end - start);
MultipartReader(const std::string &boundary)
size_t parse(const char *data, size_t len)
void extract_header_param(const char *header, size_t header_len, const char *param, std::string &out)
bool parse_multipart_boundary(const char *content_type, const char **boundary_start, size_t *boundary_len)
const char * strcasestr_n(const char *haystack, size_t haystack_len, const char *needle)
void str_trim(const char *str, size_t len, std::string &out)
bool str_ncmp_ci(const char *s1, const char *s2, size_t n)
bool str_startswith_case_insensitive(const char *str, size_t str_len, const char *prefix)