From 394b100606595bbe3c8a12d6d99a85e817b8a863 Mon Sep 17 00:00:00 2001 From: Jason S Date: Fri, 6 Jun 2025 06:35:58 -0400 Subject: [PATCH 1/2] Modified parse_body in multipart.h to support multipart form data without trailing CRLF as allowed by RFC 2046. --- include/crow/multipart.h | 59 ++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/include/crow/multipart.h b/include/crow/multipart.h index 24040c962c..12368f9b13 100644 --- a/include/crow/multipart.h +++ b/include/crow/multipart.h @@ -177,30 +177,67 @@ namespace crow void parse_body(std::string body) { - std::string delimiter = dd + boundary; + std::string normal_delim = dd + boundary; + std::string final_delim = normal_delim + dd; // Create final delimiter - // TODO(EDev): Exit on error - while (body != (crlf)) + while (true) { - size_t found = body.find(delimiter); - if (found == std::string::npos) + size_t next_normal = body.find(normal_delim); + size_t next_final = body.find(final_delim); + + // Use the first boundary that appears + size_t found; + bool is_final = false; + + // Relaxed check: allow final_delim to be treated as final even if it's not perfectly aligned + if (next_final != std::string::npos && (next_normal == std::string::npos || next_final <= next_normal)) + { + found = next_final; + is_final = true; + } + + else if (next_normal != std::string::npos) + { + found = next_normal; + } + else { - // did not find delimiter; probably an ill-formed body; throw to indicate the issue to user throw bad_request("Unable to find delimiter in multipart message. Probably ill-formed body"); } + + // Extract the section before the boundary std::string section = body.substr(0, found); + body.erase(0, found); + + // Remove the matched delimiter + if (is_final) + { + body.erase(0, final_delim.length()); + } + else + { + body.erase(0, normal_delim.length()); + } + + // Strip CRLF if present + if (body.compare(0, 2, crlf) == 0) + { + body.erase(0, 2); + } - // +2 is the CRLF. - // We don't check it and delete it so that the same delimiter can be used for The last delimiter (--delimiter--CRLF). - body.erase(0, found + delimiter.length() + 2); if (!section.empty()) { part parsed_section(parse_section(section)); part_map.emplace( - (get_header_object(parsed_section.headers, "Content-Disposition").params.find("name")->second), - parsed_section); + get_header_object(parsed_section.headers, "Content-Disposition").params.at("name"), + parsed_section); parts.push_back(std::move(parsed_section)); } + + if (is_final) + { + break; // Exit after final part + } } } From a176594dea0b804472210a6175233269f64fa674 Mon Sep 17 00:00:00 2001 From: Jason S Date: Fri, 6 Jun 2025 12:27:57 -0400 Subject: [PATCH 2/2] Additional updates to multipart.h Cleaned up and commented code. Moved final_delim declaration outside loop and updated while loop condition to check state of final_delim instead. --- include/crow/multipart.h | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/include/crow/multipart.h b/include/crow/multipart.h index 12368f9b13..aed1be4078 100644 --- a/include/crow/multipart.h +++ b/include/crow/multipart.h @@ -178,18 +178,22 @@ namespace crow void parse_body(std::string body) { std::string normal_delim = dd + boundary; - std::string final_delim = normal_delim + dd; // Create final delimiter + std::string final_delim = normal_delim + dd; // Create final delimiter to check against - while (true) + bool is_final = false; // Create check for if final delimiter has been reached + + while (!is_final) { + // Find next regular and final delimiter size_t next_normal = body.find(normal_delim); size_t next_final = body.find(final_delim); // Use the first boundary that appears size_t found; - bool is_final = false; - // Relaxed check: allow final_delim to be treated as final even if it's not perfectly aligned + // If first boundary is final boundary, set is_final to true. + // Break ties between final and normal delimiter location by assigning priority to final + // This prevents treating final delim as a normal and then iterating again and producing an error if (next_final != std::string::npos && (next_normal == std::string::npos || next_final <= next_normal)) { found = next_final; @@ -229,15 +233,10 @@ namespace crow { part parsed_section(parse_section(section)); part_map.emplace( - get_header_object(parsed_section.headers, "Content-Disposition").params.at("name"), - parsed_section); + get_header_object(parsed_section.headers, "Content-Disposition").params.at("name"), + parsed_section); parts.push_back(std::move(parsed_section)); } - - if (is_final) - { - break; // Exit after final part - } } }