comparison 3rdparty/vmime/tests/net/smtp/SMTPTransportTestUtils.hpp @ 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
25 /** Accepts connection and fails on greeting.
26 */
27 class greetingErrorSMTPTestSocket : public lineBasedTestSocket
28 {
29 public:
30
31 void onConnected()
32 {
33 localSend("421 test.vmime.org Service not available, closing transmission channel\r\n");
34 disconnect();
35 }
36
37 void processCommand()
38 {
39 if (!haveMoreLines())
40 return;
41
42 getNextLine();
43
44 localSend("502 Command not implemented\r\n");
45 processCommand();
46 }
47 };
48
49
50 /** SMTP test server 1.
51 *
52 * Test send().
53 * Ensure MAIL and RCPT commands are sent correctly.
54 */
55 class MAILandRCPTSMTPTestSocket : public lineBasedTestSocket
56 {
57 public:
58
59 MAILandRCPTSMTPTestSocket()
60 {
61 m_recipients.insert("recipient1@test.vmime.org");
62 m_recipients.insert("recipient2@test.vmime.org");
63 m_recipients.insert("recipient3@test.vmime.org");
64
65 m_state = STATE_NOT_CONNECTED;
66 m_ehloSent = m_heloSent = m_mailSent = m_rcptSent = m_dataSent = m_quitSent = false;
67 }
68
69 ~MAILandRCPTSMTPTestSocket()
70 {
71 VASSERT("Client must send the DATA command", m_dataSent);
72 VASSERT("Client must send the QUIT command", m_quitSent);
73 }
74
75 void onConnected()
76 {
77 localSend("220 test.vmime.org Service ready\r\n");
78 processCommand();
79
80 m_state = STATE_COMMAND;
81 }
82
83 void processCommand()
84 {
85 if (!haveMoreLines())
86 return;
87
88 vmime::string line = getNextLine();
89 std::istringstream iss(line);
90
91 switch (m_state)
92 {
93 case STATE_NOT_CONNECTED:
94
95 localSend("451 Requested action aborted: invalid state\r\n");
96 break;
97
98 case STATE_COMMAND:
99 {
100 std::string cmd;
101 iss >> cmd;
102
103 if (cmd.empty())
104 {
105 localSend("500 Syntax error, command unrecognized\r\n");
106 }
107 else if (cmd == "EHLO")
108 {
109 localSend("502 Command not implemented\r\n");
110
111 m_ehloSent = true;
112 }
113 else if (cmd == "HELO")
114 {
115 VASSERT("Client must send the EHLO command before HELO", m_ehloSent);
116
117 localSend("250 OK\r\n");
118
119 m_heloSent = true;
120 }
121 else if (cmd == "MAIL")
122 {
123 VASSERT("Client must send the HELO command", m_heloSent);
124 VASSERT("The MAIL command must be sent only one time", !m_mailSent);
125
126 VASSERT_EQ("MAIL", std::string("MAIL FROM:<expeditor@test.vmime.org>"), line);
127
128 localSend("250 OK\r\n");
129
130 m_mailSent = true;
131 }
132 else if (cmd == "RCPT")
133 {
134 const vmime::size_t lt = line.find('<');
135 const vmime::size_t gt = line.find('>');
136
137 VASSERT("RCPT <", lt != vmime::string::npos);
138 VASSERT("RCPT >", gt != vmime::string::npos);
139 VASSERT("RCPT ><", gt >= lt);
140
141 const vmime::string recip = vmime::string
142 (line.begin() + lt + 1, line.begin() + gt);
143
144 std::set <vmime::string>::iterator it =
145 m_recipients.find(recip);
146
147 VASSERT(std::string("Recipient not found: '") + recip + "'",
148 it != m_recipients.end());
149
150 m_recipients.erase(it);
151
152 localSend("250 OK, recipient accepted\r\n");
153
154 m_rcptSent = true;
155 }
156 else if (cmd == "DATA")
157 {
158 VASSERT("Client must send the MAIL command", m_mailSent);
159 VASSERT("Client must send the RCPT command", m_rcptSent);
160 VASSERT("All recipients", m_recipients.empty());
161
162 localSend("354 Ready to accept data; end with <CRLF>.<CRLF>\r\n");
163
164 m_state = STATE_DATA;
165 m_msgData.clear();
166
167 m_dataSent = true;
168 }
169 else if (cmd == "NOOP")
170 {
171 localSend("250 Completed\r\n");
172 }
173 else if (cmd == "QUIT")
174 {
175 m_quitSent = true;
176
177 localSend("221 test.vmime.org Service closing transmission channel\r\n");
178 }
179 else
180 {
181 localSend("502 Command not implemented\r\n");
182 }
183
184 break;
185 }
186 case STATE_DATA:
187 {
188 if (line == ".")
189 {
190 VASSERT_EQ("Data", "Message data\r\n", m_msgData);
191
192 localSend("250 Message accepted for delivery\r\n");
193 m_state = STATE_COMMAND;
194 }
195 else
196 {
197 m_msgData += line + "\r\n";
198 }
199
200 break;
201 }
202
203 }
204
205 processCommand();
206 }
207
208 private:
209
210 enum State
211 {
212 STATE_NOT_CONNECTED,
213 STATE_COMMAND,
214 STATE_DATA
215 };
216
217 int m_state;
218
219 std::set <vmime::string> m_recipients;
220
221 std::string m_msgData;
222
223 bool m_ehloSent, m_heloSent, m_mailSent, m_rcptSent,
224 m_dataSent, m_quitSent;
225 };
226
227
228
229 /** SMTP test server 2.
230 *
231 * Test CHUNKING extension/BDAT command.
232 */
233 class chunkingSMTPTestSocket : public testSocket
234 {
235 public:
236
237 chunkingSMTPTestSocket()
238 {
239 m_state = STATE_NOT_CONNECTED;
240 m_bdatChunkCount = 0;
241 m_ehloSent = m_mailSent = m_rcptSent = m_quitSent = false;
242 }
243
244 ~chunkingSMTPTestSocket()
245 {
246 VASSERT_EQ("BDAT chunk count", 3, m_bdatChunkCount);
247 VASSERT("Client must send the QUIT command", m_quitSent);
248 }
249
250 void onConnected()
251 {
252 localSend("220 test.vmime.org Service ready\r\n");
253 processCommand();
254
255 m_state = STATE_COMMAND;
256 }
257
258 void onDataReceived()
259 {
260 if (m_state == STATE_DATA)
261 {
262 if (m_bdatChunkReceived != m_bdatChunkSize)
263 {
264 const size_t remaining = m_bdatChunkSize - m_bdatChunkReceived;
265 const size_t received = localReceiveRaw(NULL, remaining);
266
267 m_bdatChunkReceived += received;
268 }
269
270 if (m_bdatChunkReceived == m_bdatChunkSize)
271 {
272 m_state = STATE_COMMAND;
273 }
274 }
275
276 processCommand();
277 }
278
279 void processCommand()
280 {
281 vmime::string line;
282
283 if (!localReceiveLine(line))
284 return;
285
286 std::istringstream iss(line);
287
288 switch (m_state)
289 {
290 case STATE_NOT_CONNECTED:
291
292 localSend("451 Requested action aborted: invalid state\r\n");
293 break;
294
295 case STATE_COMMAND:
296 {
297 std::string cmd;
298 iss >> cmd;
299
300 if (cmd == "EHLO")
301 {
302 localSend("250-test.vmime.org says hello\r\n");
303 localSend("250 CHUNKING\r\n");
304
305 m_ehloSent = true;
306 }
307 else if (cmd == "HELO")
308 {
309 VASSERT("Client must not send the HELO command, as EHLO succeeded", false);
310 }
311 else if (cmd == "MAIL")
312 {
313 VASSERT("The MAIL command must be sent only one time", !m_mailSent);
314
315 localSend("250 OK\r\n");
316
317 m_mailSent = true;
318 }
319 else if (cmd == "RCPT")
320 {
321 localSend("250 OK, recipient accepted\r\n");
322
323 m_rcptSent = true;
324 }
325 else if (cmd == "DATA")
326 {
327 VASSERT("BDAT must be used here!", false);
328 }
329 else if (cmd == "BDAT")
330 {
331 VASSERT("Client must send the MAIL command", m_mailSent);
332 VASSERT("Client must send the RCPT command", m_rcptSent);
333
334 unsigned long chunkSize = 0;
335 iss >> chunkSize;
336
337 std::string last;
338 iss >> last;
339
340 if (m_bdatChunkCount == 0)
341 {
342 VASSERT_EQ("BDAT chunk1 size", 262144, chunkSize);
343 VASSERT_EQ("BDAT chunk1 last", "", last);
344 }
345 else if (m_bdatChunkCount == 1)
346 {
347 VASSERT_EQ("BDAT chunk2 size", 262144, chunkSize);
348 VASSERT_EQ("BDAT chunk2 last", "", last);
349 }
350 else if (m_bdatChunkCount == 2)
351 {
352 VASSERT_EQ("BDAT chunk3 size", 4712, chunkSize);
353 VASSERT_EQ("BDAT chunk3 last", "LAST", last);
354 }
355 else
356 {
357 VASSERT("No more BDAT command should be issued!", false);
358 }
359
360 m_bdatChunkSize = chunkSize;
361 m_bdatChunkReceived = 0;
362 m_bdatChunkCount++;
363 m_state = STATE_DATA;
364
365 localSend("250 chunk received\r\n");
366 }
367 else if (cmd == "NOOP")
368 {
369 localSend("250 Completed\r\n");
370 }
371 else if (cmd == "QUIT")
372 {
373 localSend("221 test.vmime.org Service closing transmission channel\r\n");
374
375 m_quitSent = true;
376 }
377 else
378 {
379 localSend("502 Command not implemented\r\n");
380 }
381
382 break;
383 }
384
385 }
386
387 processCommand();
388 }
389
390 private:
391
392 enum State
393 {
394 STATE_NOT_CONNECTED,
395 STATE_COMMAND,
396 STATE_DATA
397 };
398
399 int m_state;
400 int m_bdatChunkCount;
401 int m_bdatChunkSize, m_bdatChunkReceived;
402
403 bool m_ehloSent, m_mailSent, m_rcptSent, m_quitSent;
404 };
405
406
407 class SMTPTestMessage : public vmime::message
408 {
409 public:
410
411 vmime::size_t getChunkBufferSize() const
412 {
413 static vmime::net::smtp::SMTPChunkingOutputStreamAdapter chunkStream(vmime::null, 0, NULL);
414 return chunkStream.getBlockSize();
415 }
416
417 const std::vector <vmime::string>& getChunks() const
418 {
419 static std::vector <vmime::string> chunks;
420
421 if (chunks.size() == 0)
422 {
423 chunks.push_back(vmime::string(1000, 'A'));
424 chunks.push_back(vmime::string(3000, 'B'));
425 chunks.push_back(vmime::string(500000, 'C'));
426 chunks.push_back(vmime::string(25000, 'D'));
427 }
428
429 return chunks;
430 }
431
432 void generateImpl
433 (const vmime::generationContext& /* ctx */, vmime::utility::outputStream& outputStream,
434 const size_t /* curLinePos */ = 0, size_t* /* newLinePos */ = NULL) const
435 {
436 for (unsigned int i = 0, n = getChunks().size() ; i < n ; ++i)
437 {
438 const vmime::string& chunk = getChunks()[i];
439 outputStream.write(chunk.data(), chunk.size());
440 }
441 }
442 };
443
444
445
446 /** SMTP test server 3.
447 *
448 * Test SIZE extension.
449 */
450 template <bool WITH_CHUNKING>
451 class bigMessageSMTPTestSocket : public testSocket
452 {
453 public:
454
455 bigMessageSMTPTestSocket()
456 {
457 m_state = STATE_NOT_CONNECTED;
458 m_ehloSent = m_mailSent = m_rcptSent = m_quitSent = false;
459 }
460
461 ~bigMessageSMTPTestSocket()
462 {
463 VASSERT("Client must send the QUIT command", m_quitSent);
464 }
465
466 void onConnected()
467 {
468 localSend("220 test.vmime.org Service ready\r\n");
469 processCommand();
470
471 m_state = STATE_COMMAND;
472 }
473
474 void onDataReceived()
475 {
476 processCommand();
477 }
478
479 void processCommand()
480 {
481 vmime::string line;
482
483 if (!localReceiveLine(line))
484 return;
485
486 std::istringstream iss(line);
487
488 switch (m_state)
489 {
490 case STATE_NOT_CONNECTED:
491
492 localSend("451 Requested action aborted: invalid state\r\n");
493 break;
494
495 case STATE_COMMAND:
496 {
497 std::string cmd;
498 iss >> cmd;
499
500 if (cmd == "EHLO")
501 {
502 localSend("250-test.vmime.org says hello\r\n");
503
504 if (WITH_CHUNKING)
505 localSend("250-CHUNKING\r\n");
506
507 localSend("250 SIZE 1000000\r\n");
508
509 m_ehloSent = true;
510 }
511 else if (cmd == "HELO")
512 {
513 VASSERT("Client must not send the HELO command, as EHLO succeeded", false);
514 }
515 else if (cmd == "MAIL")
516 {
517 VASSERT("The MAIL command must be sent only one time", !m_mailSent);
518
519 std::string address;
520 iss >> address;
521
522 VASSERT_EQ("MAIL/address", "FROM:<expeditor@test.vmime.org>", address);
523
524 std::string option;
525 iss >> option;
526
527 VASSERT_EQ("MAIL/size", "SIZE=4194304", option);
528
529 localSend("552 Channel size limit exceeded\r\n");
530
531 m_mailSent = true;
532 }
533 else if (cmd == "NOOP")
534 {
535 localSend("250 Completed\r\n");
536 }
537 else if (cmd == "QUIT")
538 {
539 localSend("221 test.vmime.org Service closing transmission channel\r\n");
540
541 m_quitSent = true;
542 }
543 else
544 {
545 VASSERT("No other command should be sent", false);
546
547 localSend("502 Command not implemented\r\n");
548 }
549
550 break;
551 }
552
553 }
554
555 processCommand();
556 }
557
558 private:
559
560 enum State
561 {
562 STATE_NOT_CONNECTED,
563 STATE_COMMAND,
564 STATE_DATA
565 };
566
567 int m_state;
568
569 bool m_ehloSent, m_mailSent, m_rcptSent, m_quitSent;
570 };
571
572
573 template <unsigned long SIZE>
574 class SMTPBigTestMessage : public vmime::message
575 {
576 public:
577
578 size_t getGeneratedSize(const vmime::generationContext& /* ctx */)
579 {
580 return SIZE;
581 }
582
583 void generateImpl(const vmime::generationContext& /* ctx */,
584 vmime::utility::outputStream& outputStream,
585 const vmime::size_t /* curLinePos */ = 0,
586 vmime::size_t* /* newLinePos */ = NULL) const
587 {
588 for (unsigned int i = 0, n = SIZE ; i < n ; ++i)
589 outputStream.write("X", 1);
590 }
591 };
592
593 typedef SMTPBigTestMessage <4194304> SMTPBigTestMessage4MB;