NAWA 0.9
Web Application Framework for C++
MimeEmail.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
25#include <nawa/util/crypto.h>
26#include <nawa/util/encoding.h>
27#include <nawa/util/utils.h>
28#include <random>
29#include <sstream>
30
31using namespace nawa;
32using namespace std;
33
34namespace {
39 string genBoundary() {
40 stringstream ret;
41 ret << "----=_";
42
43 // Use MD5 sum of a random integer
44 random_device rd;
45 ret << crypto::md5(to_string(rd()));
46
47 return ret.str();
48 }
49
50 string multipartTypeToString(mail::MimeEmail::MimePartList::MultipartType const& multipartType) {
51 switch (multipartType) {
52 case mail::MimeEmail::MimePartList::MultipartType::MIXED:
53 return "mixed";
54 case mail::MimeEmail::MimePartList::MultipartType::DIGEST:
55 return "digest";
56 case mail::MimeEmail::MimePartList::MultipartType::ALTERNATIVE:
57 return "alternative";
58 case mail::MimeEmail::MimePartList::MultipartType::RELATED:
59 return "related";
60 case mail::MimeEmail::MimePartList::MultipartType::REPORT:
61 return "report";
62 case mail::MimeEmail::MimePartList::MultipartType::SIGNED:
63 return "signed";
64 case mail::MimeEmail::MimePartList::MultipartType::ENCRYPTED:
65 return "encrypted";
66 }
67 return "";
68 }
69
75 string mergeMimePartList(mail::MimeEmail::MimePartList const& mimePartList, string const& boundary,
76 shared_ptr<mail::ReplacementRules> const& replacementRules) {
77 stringstream ret;
78
79 // iterate through the list
80 for (auto const& part : mimePartList.mimeParts()) {
81 ret << "--" << boundary << "\r\n";
82 // if the current part is a MIME part with content
83 if (part.mimePart()) {
84 auto const& mimePart = *(part.mimePart());
85 ret << "Content-Type: " << mimePart.contentType() << "\r\n";
86 if (!mimePart.contentDisposition().empty()) {
87 ret << "Content-Disposition: " << mimePart.contentDisposition() << "\r\n";
88 }
89 for (auto const& e : mimePart.partHeaders()) {
90 ret << e.first << ": " << e.second << "\r\n";
91 }
92
93 // apply the replacement rules (only if necessary) and the selected encoding afterwards
94 switch (mimePart.applyEncoding()) {
95 case mail::MimeEmail::MimePart::ApplyEncoding::BASE64:
96 ret << "Content-Transfer-Encoding: base64\r\n\r\n";
98 (mimePart.allowReplacements() && replacementRules)
99 ? utils::stringReplace(mimePart.partData(), *replacementRules)
100 : mimePart.partData(),
101 76, "\r\n");
102 break;
103 case mail::MimeEmail::MimePart::ApplyEncoding::QUOTED_PRINTABLE:
104 ret << "Content-Transfer-Encoding: quoted-printable\r\n\r\n";
106 (mimePart.allowReplacements() && replacementRules)
107 ? utils::stringReplace(mimePart.partData(), *replacementRules)
108 : mimePart.partData());
109 break;
110 case mail::MimeEmail::MimePart::ApplyEncoding::NONE:
111 ret << "\r\n"
112 << ((mimePart.allowReplacements() && replacementRules)
113 ? utils::stringReplace(mimePart.partData(), *replacementRules)
114 : mimePart.partData());
115 break;
116 }
117
118 ret << "\r\n\r\n";
119
120 }
121 // if the current part is another list with MIME parts (nested)
122 else if (part.mimePartList()) {
123 ret << "Content-Type: multipart/" << multipartTypeToString(part.mimePartList()->multipartType());
124 string partBoundary = genBoundary();
125 ret << "; boundary=\"" << partBoundary << "\"\r\n\r\n";
126 ret << mergeMimePartList(*part.mimePartList(), partBoundary, replacementRules) << "\r\n\r\n";
127 }
128 }
129
130 ret << "--" << boundary << "--";
131 return ret.str();
132 }
133}// namespace
134
135struct mail::MimeEmail::Data {
136 MimePartList mimePartList;
137};
138
140
142
143NAWA_COPY_CONSTRUCTOR_DERIVED_IMPL_WITH_NS(mail, MimeEmail, Email)
144
145NAWA_COPY_ASSIGNMENT_OPERATOR_DERIVED_IMPL(mail::MimeEmail, Email)
146
147NAWA_MOVE_CONSTRUCTOR_DERIVED_IMPL_WITH_NS(mail, MimeEmail, Email)
148
149NAWA_MOVE_ASSIGNMENT_OPERATOR_DERIVED_IMPL(mail::MimeEmail, Email)
150
151NAWA_COMPLEX_DATA_ACCESSORS_IMPL(mail::MimeEmail, mimePartList, mail::MimeEmail::MimePartList)
152
153struct mail::MimeEmail::MimePart::Data {
154 std::string contentType;
155 std::string contentDisposition;
156 ApplyEncoding applyEncoding = ApplyEncoding::QUOTED_PRINTABLE;
157 HeadersMap partHeaders;
158 bool allowReplacements = false;
159 std::string partData;
160};
161
162NAWA_DEFAULT_DESTRUCTOR_IMPL_WITH_NS(mail::MimeEmail, MimePart)
163
164NAWA_DEFAULT_CONSTRUCTOR_IMPL_WITH_NS(mail::MimeEmail, MimePart)
165
166NAWA_COPY_CONSTRUCTOR_IMPL_WITH_NS(mail::MimeEmail, MimePart)
167
168NAWA_COPY_ASSIGNMENT_OPERATOR_IMPL_WITH_NS(mail::MimeEmail, MimePart)
169
170NAWA_MOVE_CONSTRUCTOR_IMPL_WITH_NS(mail::MimeEmail, MimePart)
171
172NAWA_MOVE_ASSIGNMENT_OPERATOR_IMPL_WITH_NS(mail::MimeEmail, MimePart)
173
174NAWA_COMPLEX_DATA_ACCESSORS_IMPL(mail::MimeEmail::MimePart, contentType, string)
175
176NAWA_COMPLEX_DATA_ACCESSORS_IMPL(mail::MimeEmail::MimePart, contentDisposition, string)
177
178NAWA_PRIMITIVE_DATA_ACCESSORS_IMPL(mail::MimeEmail::MimePart, applyEncoding, mail::MimeEmail::MimePart::ApplyEncoding)
179
180NAWA_COMPLEX_DATA_ACCESSORS_IMPL(mail::MimeEmail::MimePart, partHeaders, mail::MimeEmail::HeadersMap)
181
182NAWA_PRIMITIVE_DATA_ACCESSORS_IMPL(mail::MimeEmail::MimePart, allowReplacements, bool)
183
184NAWA_COMPLEX_DATA_ACCESSORS_IMPL(mail::MimeEmail::MimePart, partData, string)
185
186struct mail::MimeEmail::MimePartList::Data {
187 MultipartType multipartType = MultipartType::MIXED;
188 std::vector<MimePartOrList> mimeParts;
189};
190
191NAWA_DEFAULT_DESTRUCTOR_IMPL_WITH_NS(mail::MimeEmail, MimePartList)
192
193NAWA_DEFAULT_CONSTRUCTOR_IMPL_WITH_NS(mail::MimeEmail, MimePartList)
194
195NAWA_COPY_CONSTRUCTOR_IMPL_WITH_NS(mail::MimeEmail, MimePartList)
196
197NAWA_COPY_ASSIGNMENT_OPERATOR_IMPL_WITH_NS(mail::MimeEmail, MimePartList)
198
199NAWA_MOVE_CONSTRUCTOR_IMPL_WITH_NS(mail::MimeEmail, MimePartList)
200
201NAWA_MOVE_ASSIGNMENT_OPERATOR_IMPL_WITH_NS(mail::MimeEmail, MimePartList)
202
203NAWA_PRIMITIVE_DATA_ACCESSORS_IMPL(mail::MimeEmail::MimePartList, multipartType, mail::MimeEmail::MimePartList::MultipartType)
204
205NAWA_COMPLEX_DATA_ACCESSORS_IMPL(mail::MimeEmail::MimePartList, mimeParts, vector<mail::MimeEmail::MimePartOrList>)
206
207struct mail::MimeEmail::MimePartOrList::Data {
208 unique_ptr<MimePart> mimePart;
209 unique_ptr<MimePartList> mimePartList;
210
211 Data() = default;
212
213 Data(Data const& other) {
214 operator=(other);
215 }
216
217 Data& operator=(MimePart::Data const& otherMimePartData) {
218 if (!mimePart) {
219 mimePart = make_unique<MimePart>();
220 }
221 if (mimePartList) {
222 mimePartList.reset(nullptr);
223 }
224 *mimePart->data = otherMimePartData;
225 return *this;
226 }
227
228 Data& operator=(MimePartList::Data const& otherMimePartListData) {
229 if (!mimePartList) {
230 mimePartList = make_unique<MimePartList>();
231 }
232 if (mimePart) {
233 mimePart.reset(nullptr);
234 }
235 *mimePartList->data = otherMimePartListData;
236 return *this;
237 }
238
239 Data& operator=(Data const& other) {
240 if (&other == this) {
241 return *this;
242 }
243 if (other.mimePart) {
244 if (!mimePart) {
245 mimePart = make_unique<MimePart>();
246 }
247 if (mimePartList) {
248 mimePartList.reset(nullptr);
249 }
250 *mimePart = *(other.mimePart);
251 } else if (other.mimePartList) {
252 if (!mimePartList) {
253 mimePartList = make_unique<MimePartList>();
254 }
255 if (mimePart) {
256 mimePart.reset(nullptr);
257 }
258 *mimePartList = *(other.mimePartList);
259 }
260 return *this;
261 }
262};
263
264NAWA_DEFAULT_DESTRUCTOR_IMPL_WITH_NS(mail::MimeEmail, MimePartOrList)
265
266NAWA_DEFAULT_CONSTRUCTOR_IMPL_WITH_NS(mail::MimeEmail, MimePartOrList)
267
268NAWA_COPY_CONSTRUCTOR_IMPL_WITH_NS(mail::MimeEmail, MimePartOrList)
269
270NAWA_COPY_ASSIGNMENT_OPERATOR_IMPL_WITH_NS(mail::MimeEmail, MimePartOrList)
271
272NAWA_MOVE_CONSTRUCTOR_IMPL_WITH_NS(mail::MimeEmail, MimePartOrList)
273
274NAWA_MOVE_ASSIGNMENT_OPERATOR_IMPL_WITH_NS(mail::MimeEmail, MimePartOrList)
275
276NAWA_COMPLEX_DATA_ACCESSORS_IMPL(mail::MimeEmail::MimePartOrList, mimePart, unique_ptr<mail::MimeEmail::MimePart>)
277
278NAWA_COMPLEX_DATA_ACCESSORS_IMPL(mail::MimeEmail::MimePartOrList, mimePartList, unique_ptr<mail::MimeEmail::MimePartList>)
279
280mail::MimeEmail::MimePartOrList::MimePartOrList(MimeEmail::MimePart const& otherMimePart) : MimePartOrList() {
281 *data = *otherMimePart.data;
282}
283
284mail::MimeEmail::MimePartOrList::MimePartOrList(MimeEmail::MimePartList const& otherMimePartList) : MimePartOrList() {
285 *data = *otherMimePartList.data;
286}
287
288mail::MimeEmail::MimePartOrList& mail::MimeEmail::MimePartOrList::operator=(MimeEmail::MimePart const& otherMimePart) {
289 *data = *otherMimePart.data;
290 return *this;
291}
292
293mail::MimeEmail::MimePartOrList& mail::MimeEmail::MimePartOrList::operator=(MimeEmail::MimePartList const& otherMimePartList) {
294 *data = *otherMimePartList.data;
295 return *this;
296}
297
298std::string mail::MimeEmail::getRaw(shared_ptr<ReplacementRules> const& replacementRules) const {
299 stringstream ret;
300 for (auto const& e : headers()) {
301 if (e.first == "MIME-Version" || e.first == "Content-Type")
302 continue;
303 ret << e.first << ": " << e.second << "\r\n";
304 }
305 string boundary = genBoundary();
306 ret << "MIME-Version: 1.0\r\nContent-Type: multipart/" << multipartTypeToString(data->mimePartList.multipartType());
307 ret << "; boundary=\"" << boundary;
308 ret << "\"\r\n\r\nThis is a multi-part message in MIME format\r\n\r\n";
309 ret << mergeMimePartList(data->mimePartList, boundary, replacementRules);
310
311 return ret.str();
312}
Structure representing a MIME email.
A bunch of useful cryptographic functions (esp. hashing), acting as a wrapper to C crypto libraries.
Namespace containing functions for text encoding and decoding.
#define NAWA_MOVE_CONSTRUCTOR_IMPL_WITH_NS(Namespace, Class)
Definition: macros.h:90
#define NAWA_PRIMITIVE_DATA_ACCESSORS_IMPL(Class, Member, Type)
Definition: macros.h:137
#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_CONSTRUCTOR_DERIVED_IMPL_WITH_NS(Namespace, Class, Parent)
Definition: macros.h:94
#define NAWA_COPY_ASSIGNMENT_OPERATOR_DERIVED_IMPL(Class, Parent)
Definition: macros.h:70
#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_MOVE_ASSIGNMENT_OPERATOR_DERIVED_IMPL(Class, Parent)
Definition: macros.h:112
#define NAWA_COPY_CONSTRUCTOR_DERIVED_IMPL_WITH_NS(Namespace, Class, Parent)
Definition: macros.h:52
#define NAWA_DEFAULT_DESTRUCTOR_IMPL_WITH_NS(Namespace, Class)
Definition: macros.h:37
std::string md5(std::string const &input, bool hex=true)
Definition: crypto.cpp:93
std::string base64Encode(std::string const &input, size_t breakAfter=0, std::string const &breakSequence="")
Definition: encoding.cpp:281
std::string quotedPrintableEncode(std::string const &input, std::string const &lineEnding="\r\n", bool replaceCrlf=false, bool qEncoding=false)
Definition: encoding.cpp:290
std::string stringReplace(std::string input, std::unordered_map< char, char > const &patterns)
Definition: utils.cpp:514
Definition: AppInit.h:31
Contains useful functions that improve the readability and facilitate maintenance of the NAWA code.