comparison 3rdparty/vmime/tests/parser/bodyPartTest.cpp @ 0:a4671277546c tip

created the repository for the thymian project
author ferencd
date Tue, 17 Aug 2021 11:19:54 +0200
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:a4671277546c
1 //
2 // VMime library (http://www.vmime.org)
3 // Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 3 of
8 // the License, or (at your option) any later version.
9 //
10 // This program 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 GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 // Linking this library statically or dynamically with other modules is making
20 // a combined work based on this library. Thus, the terms and conditions of
21 // the GNU General Public License cover the whole combination.
22 //
23
24 #include "tests/testUtils.hpp"
25
26
27 VMIME_TEST_SUITE_BEGIN(bodyPartTest)
28
29 VMIME_TEST_LIST_BEGIN
30 VMIME_TEST(testParse)
31 VMIME_TEST(testGenerate)
32 VMIME_TEST(testParseGuessBoundary)
33 VMIME_TEST(testParseGuessBoundaryWithTransportPadding)
34 VMIME_TEST(testParseMissingLastBoundary)
35 VMIME_TEST(testPrologEpilog)
36 VMIME_TEST(testPrologEncoding)
37 VMIME_TEST(testSuccessiveBoundaries)
38 VMIME_TEST(testTransportPaddingInBoundary)
39 VMIME_TEST(testGenerate7bit)
40 VMIME_TEST(testTextUsageForQPEncoding)
41 VMIME_TEST(testParseVeryBigMessage)
42 VMIME_TEST_LIST_END
43
44
45 static const vmime::string extractComponentString
46 (const vmime::string& buffer, const vmime::component& c)
47 {
48 return vmime::string(buffer.begin() + c.getParsedOffset(),
49 buffer.begin() + c.getParsedOffset() + c.getParsedLength());
50 }
51
52 static const vmime::string extractContents(const vmime::shared_ptr <const vmime::contentHandler> cts)
53 {
54 std::ostringstream oss;
55 vmime::utility::outputStreamAdapter os(oss);
56
57 cts->extract(os);
58
59 return oss.str();
60 }
61
62
63 void testParse()
64 {
65 vmime::string str1 = "HEADER\r\n\r\nBODY";
66 vmime::bodyPart p1;
67 p1.parse(str1);
68
69 VASSERT_EQ("1", "HEADER\r\n\r\n", extractComponentString(str1, *p1.getHeader()));
70 VASSERT_EQ("2", "BODY", extractComponentString(str1, *p1.getBody()));
71
72 vmime::string str2 = "HEADER\n\nBODY";
73 vmime::bodyPart p2;
74 p2.parse(str2);
75
76 VASSERT_EQ("3", "HEADER\n\n", extractComponentString(str2, *p2.getHeader()));
77 VASSERT_EQ("4", "BODY", extractComponentString(str2, *p2.getBody()));
78
79 vmime::string str3 = "HEADER\r\n\nBODY";
80 vmime::bodyPart p3;
81 p3.parse(str3);
82
83 VASSERT_EQ("5", "HEADER\r\n\n", extractComponentString(str3, *p3.getHeader()));
84 VASSERT_EQ("6", "BODY", extractComponentString(str3, *p3.getBody()));
85 }
86
87 void testParseMissingLastBoundary()
88 {
89 vmime::string str =
90 "Content-Type: multipart/mixed; boundary=\"MY-BOUNDARY\""
91 "\r\n\r\n"
92 "--MY-BOUNDARY\r\nHEADER1\r\n\r\nBODY1\r\n"
93 "--MY-BOUNDARY\r\nHEADER2\r\n\r\nBODY2";
94
95 vmime::bodyPart p;
96 p.parse(str);
97
98 VASSERT_EQ("count", 2, p.getBody()->getPartCount());
99
100 VASSERT_EQ("part1-body", "BODY1", extractContents(p.getBody()->getPartAt(0)->getBody()->getContents()));
101 VASSERT_EQ("part2-body", "BODY2", extractContents(p.getBody()->getPartAt(1)->getBody()->getContents()));
102 }
103
104 void testGenerate()
105 {
106 vmime::bodyPart p1;
107 p1.getHeader()->getField("Foo")->setValue(vmime::string("bar"));
108 p1.getBody()->setContents(vmime::make_shared <vmime::stringContentHandler>("Baz"));
109
110 VASSERT_EQ("1", "Foo: bar\r\n\r\nBaz", p1.generate());
111 }
112
113 void testPrologEpilog()
114 {
115 const char testMail[] =
116 "To: test@vmime.org\r\n"
117 "From: test@vmime.org\r\n"
118 "Subject: Prolog and epilog test\r\n"
119 "Content-Type: multipart/mixed; \r\n"
120 " boundary=\"=_boundary\"\r\n"
121 "\r\n"
122 "Prolog text\r\n"
123 "--=_boundary\r\n"
124 "Content-Type: text/plain\r\n"
125 "\r\n"
126 "Part1\r\n"
127 "--=_boundary--\r\n"
128 "Epilog text";
129
130 vmime::bodyPart part;
131 part.parse(testMail);
132
133 VASSERT_EQ("prolog", "Prolog text", part.getBody()->getPrologText());
134 VASSERT_EQ("epilog", "Epilog text", part.getBody()->getEpilogText());
135 }
136
137 // Test for bug fix: prolog should not be encoded
138 // http://sourceforge.net/tracker/?func=detail&atid=525568&aid=3174903&group_id=69724
139 void testPrologEncoding()
140 {
141 const char testmail[] =
142 "To: test@vmime.org\r\n"
143 "From: test@vmime.org\r\n"
144 "Subject: Prolog encoding test\r\n"
145 "Content-Type: multipart/mixed; \r\n"
146 " boundary=\"=_+ZWjySayKqSf2CyrfnNpaAcO6-G1HpoXdHZ4YyswAWqEY39Q\"\r\n"
147 "\r\n"
148 "This is a multi-part message in MIME format. Your mail reader does not\r\n"
149 "understand MIME message format.\r\n"
150 "--=_+ZWjySayKqSf2CyrfnNpaAcO6-G1HpoXdHZ4YyswAWqEY39Q\r\n"
151 "Content-Type: text/html; charset=windows-1251\r\n"
152 "Content-Transfer-Encoding: quoted-printable\r\n"
153 "\r\n"
154 "=DD=F2=EE =F2=E5=EA=F1=F2=EE=E2=E0=FF =F7=E0=F1=F2=FC =F1=EB=EE=E6=ED=EE=E3=\r\n"
155 "=EE =F1=EE=EE=E1=F9=E5=ED=E8=FF\r\n"
156 "--=_+ZWjySayKqSf2CyrfnNpaAcO6-G1HpoXdHZ4YyswAWqEY39Q\r\n"
157 "Content-Type: application/octet-stream; charset=windows-1251\r\n"
158 "Content-Disposition: attachment; filename=FNS.zip\r\n"
159 "Content-Transfer-Encoding: base64\r\n"
160 "\r\n"
161 "UEsDBB...snap...EEAAAAAA==\r\n"
162 "--=_+ZWjySayKqSf2CyrfnNpaAcO6-G1HpoXdHZ4YyswAWqEY39Q--\r\n"
163 "Epilog text";
164
165 vmime::shared_ptr<vmime::message> msg = vmime::make_shared<vmime::message>();
166
167 std::string istr(testmail);
168
169 std::string ostr;
170 vmime::utility::outputStreamStringAdapter out(ostr);
171
172 for (int i = 0 ; i < 10 ; ++i)
173 {
174 ostr.clear();
175
176 msg->parse(istr);
177 msg->generate(out);
178
179 istr = ostr;
180 }
181
182 VASSERT_EQ("prolog", "This is a multi-part message in MIME format. Your mail reader"
183 " does not understand MIME message format.", msg->getBody()->getPrologText());
184 VASSERT_EQ("epilog", "Epilog text", msg->getBody()->getEpilogText());
185 }
186
187 void testSuccessiveBoundaries()
188 {
189 vmime::string str =
190 "Content-Type: multipart/mixed; boundary=\"MY-BOUNDARY\""
191 "\r\n\r\n"
192 "--MY-BOUNDARY\r\nHEADER1\r\n\r\nBODY1\r\n"
193 "--MY-BOUNDARY\r\n"
194 "--MY-BOUNDARY--\r\n";
195
196 vmime::bodyPart p;
197 p.parse(str);
198
199 VASSERT_EQ("count", 2, p.getBody()->getPartCount());
200
201 VASSERT_EQ("part1-body", "BODY1", extractContents(p.getBody()->getPartAt(0)->getBody()->getContents()));
202 VASSERT_EQ("part2-body", "", extractContents(p.getBody()->getPartAt(1)->getBody()->getContents()));
203 }
204
205 void testTransportPaddingInBoundary()
206 {
207 vmime::string str =
208 "Content-Type: multipart/mixed; boundary=\"MY-BOUNDARY\""
209 "\r\n\r\n"
210 "-- \t MY-BOUNDARY\r\nHEADER1\r\n\r\nBODY1\r\n"
211 "--MY-BOUNDARY\r\n"
212 "-- MY-BOUNDARY--\r\n";
213
214 vmime::bodyPart p;
215 p.parse(str);
216
217 VASSERT_EQ("count", 2, p.getBody()->getPartCount());
218
219 VASSERT_EQ("part1-body", "BODY1", extractContents(p.getBody()->getPartAt(0)->getBody()->getContents()));
220 VASSERT_EQ("part2-body", "", extractContents(p.getBody()->getPartAt(1)->getBody()->getContents()));
221 }
222
223 /** Ensure '7bit' encoding is used when body is 7-bit only. */
224 void testGenerate7bit()
225 {
226 vmime::shared_ptr <vmime::plainTextPart> p1 = vmime::make_shared <vmime::plainTextPart>();
227 p1->setText(vmime::make_shared <vmime::stringContentHandler>("Part1 is US-ASCII only."));
228
229 vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>();
230 p1->generateIn(msg, msg);
231
232 vmime::shared_ptr <vmime::header> header1 = msg->getBody()->getPartAt(0)->getHeader();
233 VASSERT_EQ("1", "7bit", header1->ContentTransferEncoding()->getValue()->generate());
234 }
235
236 void testTextUsageForQPEncoding()
237 {
238 vmime::shared_ptr <vmime::plainTextPart> part = vmime::make_shared <vmime::plainTextPart>();
239 part->setText(vmime::make_shared <vmime::stringContentHandler>("Part1-line1\r\nPart1-line2\r\n\x89"));
240
241 vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>();
242 part->generateIn(msg, msg);
243
244 vmime::shared_ptr <vmime::body> body = msg->getBody()->getPartAt(0)->getBody();
245 vmime::shared_ptr <vmime::header> header = msg->getBody()->getPartAt(0)->getHeader();
246
247 std::ostringstream oss;
248 vmime::utility::outputStreamAdapter os(oss);
249 body->generate(os, 80);
250
251 VASSERT_EQ("1", "quoted-printable", header->ContentTransferEncoding()->getValue()->generate());
252
253 // This should *NOT* be:
254 // Part1-line1=0D=0APart1-line2=0D=0A=89
255 VASSERT_EQ("2", "Part1-line1\r\nPart1-line2\r\n=89", oss.str());
256 }
257
258 void testParseGuessBoundary()
259 {
260 // Boundary is not specified in "Content-Type" field
261 // Parser will try to guess it from message contents.
262
263 vmime::string str =
264 "Content-Type: multipart/mixed"
265 "\r\n\r\n"
266 "--UNKNOWN-BOUNDARY\r\nHEADER1\r\n\r\nBODY1\r\n"
267 "--UNKNOWN-BOUNDARY\r\nHEADER2\r\n\r\nBODY2\r\n"
268 "--UNKNOWN-BOUNDARY--";
269
270 vmime::bodyPart p;
271 p.parse(str);
272
273 VASSERT_EQ("count", 2, p.getBody()->getPartCount());
274
275 VASSERT_EQ("part1-body", "BODY1", extractContents(p.getBody()->getPartAt(0)->getBody()->getContents()));
276 VASSERT_EQ("part2-body", "BODY2", extractContents(p.getBody()->getPartAt(1)->getBody()->getContents()));
277 }
278
279 void testParseGuessBoundaryWithTransportPadding()
280 {
281 // Boundary is not specified in "Content-Type" field
282 // Parser will try to guess it from message contents.
283 // Transport padding white spaces should be ignored.
284
285 vmime::string str =
286 "Content-Type: multipart/mixed"
287 "\r\n\r\n"
288 "-- \t UNKNOWN-BOUNDARY\r\nHEADER1\r\n\r\nBODY1\r\n"
289 "--UNKNOWN-BOUNDARY\r\nHEADER2\r\n\r\nBODY2\r\n"
290 "--UNKNOWN-BOUNDARY--";
291
292 vmime::bodyPart p;
293 p.parse(str);
294
295 VASSERT_EQ("count", 2, p.getBody()->getPartCount());
296
297 VASSERT_EQ("part1-body", "BODY1", extractContents(p.getBody()->getPartAt(0)->getBody()->getContents()));
298 VASSERT_EQ("part2-body", "BODY2", extractContents(p.getBody()->getPartAt(1)->getBody()->getContents()));
299 }
300
301 void testParseVeryBigMessage()
302 {
303 // When parsing from a seekable input stream, body contents should not
304 // be kept in memory in a "stringContentHandler" object. Instead, content
305 // should be accessible via a "streamContentHandler" object.
306
307 static const std::string BODY1_BEGIN = "BEGIN1BEGIN1BEGIN1";
308 static const std::string BODY1_LINE = "BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1";
309 static const std::string BODY1_END = "END1END1";
310 static const unsigned int BODY1_REPEAT = 35000;
311 static const unsigned int BODY1_LENGTH =
312 BODY1_BEGIN.length() + BODY1_LINE.length() * BODY1_REPEAT + BODY1_END.length();
313
314 static const std::string BODY2_LINE = "BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2";
315 static const unsigned int BODY2_REPEAT = 20000;
316
317 std::ostringstream oss;
318 oss << "Content-Type: multipart/mixed; boundary=\"MY-BOUNDARY\""
319 << "\r\n\r\n"
320 << "--MY-BOUNDARY\r\n"
321 << "HEADER1\r\n"
322 << "\r\n";
323
324 oss << BODY1_BEGIN;
325
326 for (unsigned int i = 0 ; i < BODY1_REPEAT ; ++i)
327 oss << BODY1_LINE;
328
329 oss << BODY1_END;
330
331 oss << "\r\n"
332 << "--MY-BOUNDARY\r\n"
333 << "HEADER2\r\n"
334 << "\r\n";
335
336 for (unsigned int i = 0 ; i < BODY2_REPEAT ; ++i)
337 oss << BODY2_LINE;
338
339 oss << "\r\n"
340 << "--MY-BOUNDARY--\r\n";
341
342 vmime::shared_ptr <vmime::utility::inputStreamStringAdapter> is =
343 vmime::make_shared <vmime::utility::inputStreamStringAdapter>(oss.str());
344
345 vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>();
346 msg->parse(is, oss.str().length());
347
348 vmime::shared_ptr <vmime::body> body1 = msg->getBody()->getPartAt(0)->getBody();
349 vmime::shared_ptr <const vmime::contentHandler> body1Cts = body1->getContents();
350
351 vmime::shared_ptr <vmime::body> body2 = msg->getBody()->getPartAt(1)->getBody();
352 vmime::shared_ptr <const vmime::contentHandler> body2Cts = body2->getContents();
353
354 vmime::string body1CtsExtracted;
355 vmime::utility::outputStreamStringAdapter body1CtsExtractStream(body1CtsExtracted);
356 body1Cts->extract(body1CtsExtractStream);
357
358 VASSERT_EQ("1.1", BODY1_LENGTH, body1Cts->getLength());
359 VASSERT("1.2", vmime::dynamicCast <const vmime::streamContentHandler>(body1Cts) != NULL);
360 VASSERT_EQ("1.3", BODY1_LENGTH, body1CtsExtracted.length());
361 VASSERT_EQ("1.4", BODY1_BEGIN, body1CtsExtracted.substr(0, BODY1_BEGIN.length()));
362 VASSERT_EQ("1.5", BODY1_END, body1CtsExtracted.substr(BODY1_LENGTH - BODY1_END.length(), BODY1_END.length()));
363
364 VASSERT_EQ("2.1", BODY2_LINE.length() * BODY2_REPEAT, body2Cts->getLength());
365 VASSERT("2.2", vmime::dynamicCast <const vmime::streamContentHandler>(body2Cts) != NULL);
366 }
367
368 VMIME_TEST_SUITE_END
369