2#if defined(USE_ESP_IDF) && defined(USE_WEBSERVER_OTA)
7#include "multipart_parser.h"
10namespace web_server_idf {
12static const char *
const TAG =
"multipart";
18 memset(&settings_, 0,
sizeof(settings_));
19 settings_.on_header_field = on_header_field;
20 settings_.on_header_value = on_header_value;
21 settings_.on_part_data = on_part_data;
22 settings_.on_part_data_end = on_part_data_end;
24 ESP_LOGV(TAG,
"Initializing multipart parser with boundary: '%s' (len: %zu)", boundary.c_str(), boundary.length());
27 parser_ = multipart_parser_init(boundary.c_str(), &settings_);
29 multipart_parser_set_data(parser_,
this);
31 ESP_LOGE(TAG,
"Failed to initialize multipart parser");
37 multipart_parser_free(parser_);
43 ESP_LOGE(TAG,
"Parser not initialized");
47 size_t parsed = multipart_parser_execute(parser_, data,
len);
50 ESP_LOGW(TAG,
"Parser consumed %zu of %zu bytes - possible error", parsed,
len);
56void MultipartReader::process_header_(
const char *value,
size_t length) {
58 std::string value_str(value,
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 if (str.length() < prefix.length()) {
115 return str_ncmp_ci(str.c_str(), prefix.c_str(), prefix.length());
121 size_t search_pos = 0;
123 while (search_pos < header.length()) {
125 const char *found =
stristr(header.c_str() + search_pos, param.c_str());
129 size_t pos = found - header.c_str();
132 if (pos > 0 && header[pos - 1] !=
' ' && header[pos - 1] !=
';' && header[pos - 1] !=
'\t') {
133 search_pos = pos + 1;
138 pos += param.length();
141 while (pos < header.length() && (header[pos] ==
' ' || header[pos] ==
'\t')) {
145 if (pos >= header.length() || header[pos] !=
'=') {
153 while (pos < header.length() && (header[pos] ==
' ' || header[pos] ==
'\t')) {
157 if (pos >= header.length()) {
162 if (header[pos] ==
'"') {
164 size_t end = header.find(
'"', pos);
165 if (
end != std::string::npos) {
166 return header.substr(pos,
end - pos);
174 while (
end < header.length() && header[
end] !=
';' && header[
end] !=
',' && header[
end] !=
' ' &&
175 header[
end] !=
'\t') {
179 return header.substr(pos,
end - pos);
194 if (!
stristr(content_type,
"multipart/form-data")) {
199 const char *b =
stristr(content_type,
"boundary=");
204 const char *start = b + 9;
207 while (*start ==
' ' || *start ==
'\t') {
216 const char *
end = start;
221 while (*
end && *
end !=
'"') {
224 *boundary_len =
end - start;
227 while (*
end && *
end !=
' ' && *
end !=
';' && *
end !=
'\r' && *
end !=
'\n' && *
end !=
'\t') {
230 *boundary_len =
end - start;
233 if (*boundary_len == 0) {
237 *boundary_start = start;
244 size_t start = str.find_first_not_of(
" \t\r\n");
245 if (start == std::string::npos) {
248 size_t end = str.find_last_not_of(
" \t\r\n");
249 return str.substr(start,
end - start + 1);
MultipartReader(const std::string &boundary)
size_t parse(const char *data, size_t len)
bool str_startswith_case_insensitive(const std::string &str, const std::string &prefix)
bool parse_multipart_boundary(const char *content_type, const char **boundary_start, size_t *boundary_len)
std::string extract_header_param(const std::string &header, const std::string ¶m)
bool str_ncmp_ci(const char *s1, const char *s2, size_t n)
std::string str_trim(const std::string &str)
const char * stristr(const char *haystack, const char *needle)
Providing packet encoding functions for exchanging data with a remote host.