NAWA 0.9
Web Application Framework for C++
MimeMultipart.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2019-2022 Tobias Flaig.
3 *
4 * This file is part of nawa.
5 *
6 * nawa is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License,
8 * version 3, as published by the Free Software Foundation.
9 *
10 * nawa is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with nawa. If not, see <https://www.gnu.org/licenses/>.
17 */
18
24#include <boost/algorithm/string.hpp>
25#include <nawa/Exception.h>
27#include <nawa/util/utils.h>
28#include <regex>
29
30using namespace nawa;
31using namespace std;
32
33struct MimeMultipart::Data {
34 string contentType;
35 vector<Part> parts;
36};
37
38struct MimeMultipart::Part::Data {
39 string partName;
40 string filename;
41 string contentType;
42 unordered_map<string, string> headers;
43 string content;
44};
45
47
49
51
53
55
57
59
61
63
65
67
69
71
73
75
77
79
80NAWA_COMPLEX_DATA_ACCESSORS_IMPL(MimeMultipart, parts, vector<MimeMultipart::Part>)
81
82MimeMultipart::MimeMultipart(std::string const& contentType, std::string content) : MimeMultipart() {
83 parse(contentType, std::move(content));
84}
85
86void MimeMultipart::parse(std::string const& contentType, std::string content) {
87 regex findBoundary(R"X(boundary="?([A-Za-z0-9'()+_,\-.\/:=? ]+)"?)X");
88 smatch boundaryMatch;
89 if (!regex_search(contentType, boundaryMatch, findBoundary) || boundaryMatch.size() != 2) {
90 throw Exception(__PRETTY_FUNCTION__, 1, "Could not find boundary in content type.");
91 }
92 string boundary = "--";
93 boundary += boundaryMatch[1];
94 size_t boundaryLen = boundary.length();
95
96 regex matchPartAndFileName(R"X((;| )name="?([^"]+)"?(; ?filename="?([^"]+)"?)?)X");
97 auto extractPartAndFileName = [&](string const& contentDisposition) -> pair<string, string> {
98 smatch sm;
99 if (!regex_search(contentDisposition, sm, matchPartAndFileName) || sm.size() < 3) {
100 return make_pair(string(), string());
101 }
102 return make_pair(sm[2], sm.size() >= 5 ? sm[4] : string());
103 };
104
105 // parse content
106 while (!content.empty()) {
107
108 // check for boundary
109 if (content.length() < boundaryLen + 2 || content.substr(0, boundaryLen) != boundary) {
110 throw Exception(__PRETTY_FUNCTION__, 2, "Malformed MIME payload.");
111 }
112 content = content.substr(boundaryLen);
113
114 // if followed by --, this is the end
115 if (content.substr(0, 2) == "--") {
116 break;
117 }
118
119 // newline must follow, and content must still have at least 2 (\r\n) + 1 (hdrs) + 2 (\r\n) + boundaryLen + 2 chars
120 if (content.length() < boundaryLen + 6 || content.substr(0, 2) != "\r\n") {
121 throw Exception(__PRETTY_FUNCTION__, 2, "Malformed MIME payload.");
122 }
123 content = content.substr(2);
124
125 // find next boundary
126 size_t nextBoundaryPos = content.find(boundary);
127 if (nextBoundaryPos == string::npos) {
128 throw Exception(__PRETTY_FUNCTION__, 2, "Malformed MIME payload.");
129 }
130
131 // headers section goes until the next \r\n\r\n or, alternatively, the next boundary
132 size_t headersEndPos = content.find("\r\n\r\n");
133 if (headersEndPos > nextBoundaryPos) {
134 headersEndPos = nextBoundaryPos;
135 }
136 if (headersEndPos < 4) {
137 throw Exception(__PRETTY_FUNCTION__, 2, "Malformed MIME payload.");
138 }
139
140 Part currentPart;
141 currentPart.data->headers = utils::parseHeaders(content.substr(0, headersEndPos));
142 currentPart.data->contentType = currentPart.data->headers.count("content-type") ? currentPart.data->headers.at(
143 "content-type")
144 : "";
145 if (currentPart.data->headers.count("content-disposition")) {
146 tie(currentPart.data->partName, currentPart.data->filename) = extractPartAndFileName(
147 currentPart.data->headers.at("content-disposition"));
148 }
149
150 // is there a part content in between? (headersEndPos + "\r\n\r\n" + content + "\r\n")
151 if (nextBoundaryPos > headersEndPos + 7) {
152 currentPart.data->content = content.substr(headersEndPos + 4, nextBoundaryPos - 2 - (headersEndPos + 4));
153 }
154
155 data->parts.push_back(currentPart);
156 content = content.substr(nextBoundaryPos);
157 }
158
159 data->contentType = contentType.substr(0, contentType.find_first_of(';'));
160}
161
163 data->contentType.clear();
164 data->parts.clear();
165}
Exception class that can be used by apps to catch errors resulting from nawa function calls.
Parser for MIME multipart, especially in POST form data.
std::string & content() noexcept
std::string & filename() noexcept
std::string & partName() noexcept
HeadersMap & headers() noexcept
std::string & contentType() noexcept
std::unordered_map< std::string, std::string > HeadersMap
Definition: MimeMultipart.h:43
#define NAWA_DEFAULT_DESTRUCTOR_IMPL(Class)
Definition: macros.h:36
#define NAWA_MOVE_CONSTRUCTOR_IMPL_WITH_NS(Namespace, Class)
Definition: macros.h:90
#define NAWA_COPY_CONSTRUCTOR_IMPL(Class)
Definition: macros.h:46
#define NAWA_MOVE_ASSIGNMENT_OPERATOR_IMPL_WITH_NS(Namespace, Class)
Definition: macros.h:105
#define NAWA_COMPLEX_DATA_ACCESSORS_IMPL(Class, Member, Type)
Definition: macros.h:144
#define NAWA_DEFAULT_CONSTRUCTOR_IMPL_WITH_NS(Namespace, Class)
Definition: macros.h:42
#define NAWA_MOVE_ASSIGNMENT_OPERATOR_IMPL(Class)
Definition: macros.h:98
#define NAWA_DEFAULT_CONSTRUCTOR_IMPL(Class)
Definition: macros.h:40
#define NAWA_COPY_ASSIGNMENT_OPERATOR_IMPL(Class)
Definition: macros.h:56
#define NAWA_MOVE_CONSTRUCTOR_IMPL(Class)
Definition: macros.h:88
#define NAWA_COPY_CONSTRUCTOR_IMPL_WITH_NS(Namespace, Class)
Definition: macros.h:48
#define NAWA_COPY_ASSIGNMENT_OPERATOR_IMPL_WITH_NS(Namespace, Class)
Definition: macros.h:63
#define NAWA_DEFAULT_DESTRUCTOR_IMPL_WITH_NS(Namespace, Class)
Definition: macros.h:37
std::unordered_map< std::string, std::string > parseHeaders(std::string rawHeaders)
Definition: utils.cpp:550
Definition: AppInit.h:31
Contains useful functions that improve the readability and facilitate maintenance of the NAWA code.