NAWA  0.8
Web Application Framework for C++
MimeMultipart.cpp
Go to the documentation of this file.
1 
6 /*
7  * Copyright (C) 2019-2021 Tobias Flaig.
8  *
9  * This file is part of nawa.
10  *
11  * nawa is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License,
13  * version 3, as published by the Free Software Foundation.
14  *
15  * nawa is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with nawa. If not, see <https://www.gnu.org/licenses/>.
22  */
23 
24 #include <boost/algorithm/string.hpp>
25 #include <nawa/Exception.h>
27 #include <nawa/util/utils.h>
28 #include <regex>
29 
30 using namespace nawa;
31 using namespace std;
32 
33 struct MimeMultipart::Data {
34  string contentType;
35  vector<Part> parts;
36 };
37 
38 struct 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 
80 NAWA_COMPLEX_DATA_ACCESSORS_IMPL(MimeMultipart, parts, vector<MimeMultipart::Part>)
81 
82 MimeMultipart::MimeMultipart(string const& contentType, string content) : MimeMultipart() {
83  parse(contentType, move(content));
84 }
85 
86 void MimeMultipart::parse(string const& contentType, 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 & partName() noexcept
std::string & contentType() noexcept
HeadersMap & headers() noexcept
std::string & content() noexcept
std::string & filename() noexcept
std::unordered_map< std::string, std::string > HeadersMap
Definition: MimeMultipart.h:43
void parse(std::string const &contentType, std::string content)
#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.