1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "test-lib.h"
4 #include "str.h"
5 #include "array.h"
6 #include "test-common.h"
7 #include "smtp-common.h"
8 #include "smtp-address.h"
9 #include "smtp-params.h"
10 
11 static const char *test_extensions[] = { "FROP", "FRUP", NULL };
12 
13 static struct smtp_address test_address1 =
14 	{ .localpart = NULL, .domain = NULL };
15 static struct smtp_address test_address2 =
16 	{ .localpart = "user+detail", .domain = NULL };
17 static struct smtp_address test_address3 =
18 	{ .localpart = "e=mc2", .domain = "example.com" };
19 
20 static struct smtp_param test_params1[] = {
21 	{ .keyword = "FROP", .value = "friep" }
22 };
23 static struct smtp_param test_params2[] = {
24 	{ .keyword = "FROP", .value = "friep" },
25 	{ .keyword = "FRUP", .value = "frml" }
26 };
27 
28 static struct buffer test_params_buffer1 = {
29 	.data = (void*)&test_params1,
30 	.used = sizeof(test_params1)
31 };
32 static struct buffer test_params_buffer2 = {
33 	.data = (void*)&test_params2,
34 	.used = sizeof(test_params2)
35 };
36 
37 /* Valid mail params tests */
38 
39 struct valid_mail_params_parse_test {
40 	const char *input, *output;
41 
42 	enum smtp_capability caps;
43 	const char *const *extensions;
44 	const char *const *body_extensions;
45 
46 	struct smtp_params_mail params;
47 };
48 
49 static const struct valid_mail_params_parse_test
50 valid_mail_params_parse_tests[] = {
51 	/* AUTH */
52 	{
53 		.input = "AUTH=<>",
54 		.caps = SMTP_CAPABILITY_AUTH,
55 		.params = {
56 			.auth = &test_address1
57 		}
58 	},
59 	{
60 		.input = "AUTH=user+2Bdetail",
61 		.caps = SMTP_CAPABILITY_AUTH,
62 		.params = {
63 			.auth = &test_address2
64 		}
65 	},
66 	{
67 		.input = "AUTH=e+3Dmc2@example.com",
68 		.caps = SMTP_CAPABILITY_AUTH,
69 		.params = {
70 			.auth = &test_address3
71 		}
72 	},
73 	/* BODY */
74 	{
75 		.input = "",
76 		.caps = SMTP_CAPABILITY_8BITMIME,
77 		.params = {
78 			.body = {
79 				.type = SMTP_PARAM_MAIL_BODY_TYPE_UNSPECIFIED,
80 			}
81 		}
82 	},
83 	{
84 		.input = "BODY=7BIT",
85 		.caps = SMTP_CAPABILITY_8BITMIME,
86 		.params = {
87 			.body = {
88 				.type = SMTP_PARAM_MAIL_BODY_TYPE_7BIT,
89 			}
90 		}
91 	},
92 	{
93 		.input = "BODY=8BITMIME",
94 		.caps = SMTP_CAPABILITY_8BITMIME,
95 		.params = {
96 			.body = {
97 				.type = SMTP_PARAM_MAIL_BODY_TYPE_8BITMIME,
98 			}
99 		}
100 	},
101 	{
102 		.input = "BODY=BINARYMIME",
103 		.caps = SMTP_CAPABILITY_8BITMIME |
104 			SMTP_CAPABILITY_BINARYMIME |
105 			SMTP_CAPABILITY_CHUNKING,
106 		.params = {
107 			.body = {
108 				.type = SMTP_PARAM_MAIL_BODY_TYPE_BINARYMIME,
109 			}
110 		}
111 	},
112 	{
113 		.input = "BODY=FROP",
114 		.caps = SMTP_CAPABILITY_8BITMIME |
115 			SMTP_CAPABILITY_BINARYMIME |
116 			SMTP_CAPABILITY_CHUNKING,
117 		.body_extensions = test_extensions,
118 		.params = {
119 			.body = {
120 				.type = SMTP_PARAM_MAIL_BODY_TYPE_EXTENSION,
121 				.ext = "FROP"
122 			}
123 		}
124 	},
125 	/* ENVID */
126 	{
127 		.input = "",
128 		.caps = SMTP_CAPABILITY_DSN,
129 		.params = {
130 			.envid = NULL,
131 		}
132 	},
133 	{
134 		.input = "ENVID=",
135 		.caps = SMTP_CAPABILITY_DSN,
136 		.params = {
137 			.envid = "",
138 		}
139 	},
140 	{
141 		.input = "ENVID=AABBCCDD",
142 		.caps = SMTP_CAPABILITY_DSN,
143 		.params = {
144 			.envid = "AABBCCDD",
145 		}
146 	},
147 	{
148 		.input = "ENVID=AA+2BBB+3DCC+2BDD",
149 		.caps = SMTP_CAPABILITY_DSN,
150 		.params = {
151 			.envid = "AA+BB=CC+DD",
152 		}
153 	},
154 	/* RET */
155 	{
156 		.input = "",
157 		.caps = SMTP_CAPABILITY_DSN,
158 		.params = {
159 			.ret = SMTP_PARAM_MAIL_RET_UNSPECIFIED,
160 		}
161 	},
162 	{
163 		.input = "RET=HDRS",
164 		.caps = SMTP_CAPABILITY_DSN,
165 		.params = {
166 			.ret = SMTP_PARAM_MAIL_RET_HDRS,
167 		}
168 	},
169 	{
170 		.input = "RET=FULL",
171 		.caps = SMTP_CAPABILITY_DSN,
172 		.params = {
173 			.ret = SMTP_PARAM_MAIL_RET_FULL,
174 		}
175 	},
176 	/* SIZE */
177 	{
178 		.input = "",
179 		.caps = SMTP_CAPABILITY_SIZE,
180 		.params = {
181 			.size = 0
182 		}
183 	},
184 	{
185 		.input = "SIZE=267914296",
186 		.caps = SMTP_CAPABILITY_SIZE,
187 		.params = {
188 			.size = 267914296
189 		}
190 	},
191 	/* <extensions> */
192 	{
193 		.input = "FROP=friep",
194 		.caps = SMTP_CAPABILITY_SIZE,
195 		.extensions = test_extensions,
196 		.params = {
197 			.extra_params = {
198 				.arr = {
199 					.buffer = &test_params_buffer1,
200 					.element_size = sizeof(struct smtp_param)
201 				}
202 			}
203 		}
204 	},
205 	{
206 		.input = "FROP=friep FRUP=frml",
207 		.extensions = test_extensions,
208 		.params = {
209 			.extra_params = {
210 				.arr = {
211 					.buffer = &test_params_buffer2,
212 					.element_size = sizeof(struct smtp_param)
213 				}
214 			}
215 		}
216 	}
217 };
218 
219 unsigned int valid_mail_params_parse_test_count =
220 	N_ELEMENTS(valid_mail_params_parse_tests);
221 
222 static void
test_smtp_mail_params_auth(const struct smtp_params_mail * test,const struct smtp_params_mail * parsed)223 test_smtp_mail_params_auth(const struct smtp_params_mail *test,
224 			   const struct smtp_params_mail *parsed)
225 {
226 	if (parsed->auth->localpart == NULL ||
227 	    test->auth->localpart == NULL) {
228 		test_out(t_strdup_printf("params.auth->localpart = %s",
229 					 parsed->auth->localpart),
230 			 (parsed->auth->localpart == test->auth->localpart));
231 	} else {
232 		test_out(t_strdup_printf("params.auth->localpart = \"%s\"",
233 					 parsed->auth->localpart),
234 			 strcmp(parsed->auth->localpart,
235 				test->auth->localpart) == 0);
236 	}
237 	if (parsed->auth->domain == NULL ||
238 	    test->auth->domain == NULL) {
239 		test_out(t_strdup_printf("params.auth->domain = %s",
240 					 parsed->auth->domain),
241 			 (parsed->auth->domain == test->auth->domain));
242 	} else {
243 		test_out(t_strdup_printf("params.auth->domain = \"%s\"",
244 					 parsed->auth->domain),
245 			 strcmp(parsed->auth->domain,
246 				test->auth->domain) == 0);
247 	}
248 }
249 
250 static void
test_smtp_mail_params_body(const struct smtp_params_mail * test,const struct smtp_params_mail * parsed)251 test_smtp_mail_params_body(const struct smtp_params_mail *test,
252 			   const struct smtp_params_mail *parsed)
253 {
254 	const char *type_name = NULL;
255 
256 	switch (parsed->body.type) {
257 	case SMTP_PARAM_MAIL_BODY_TYPE_UNSPECIFIED:
258 		type_name = "<UNSPECIFIED>";
259 		break;
260 	case SMTP_PARAM_MAIL_BODY_TYPE_7BIT:
261 		type_name = "7BIT";
262 		break;
263 	case SMTP_PARAM_MAIL_BODY_TYPE_8BITMIME:
264 		type_name = "8BITMIME";
265 		break;
266 	case SMTP_PARAM_MAIL_BODY_TYPE_BINARYMIME:
267 		type_name = "BINARYMIME";
268 		break;
269 	case SMTP_PARAM_MAIL_BODY_TYPE_EXTENSION:
270 		type_name = parsed->body.ext;
271 		break;
272 	default:
273 		i_unreached();
274 	}
275 
276 	test_out(t_strdup_printf("params.body.type = %s", type_name),
277 		 (parsed->body.type == test->body.type &&
278 		  (parsed->body.type != SMTP_PARAM_MAIL_BODY_TYPE_EXTENSION ||
279 		   (parsed->body.ext != NULL &&
280 		    strcmp(parsed->body.ext, test->body.ext) == 0))));
281 }
282 
283 static void
test_smtp_mail_params_envid(const struct smtp_params_mail * test,const struct smtp_params_mail * parsed)284 test_smtp_mail_params_envid(const struct smtp_params_mail *test,
285 			    const struct smtp_params_mail *parsed)
286 {
287 	if (parsed->envid == NULL || test->envid == NULL) {
288 		test_out(t_strdup_printf("params.auth->localpart = %s",
289 					 parsed->envid),
290 			 (parsed->envid == test->envid));
291 	} else {
292 		test_out(t_strdup_printf("params.auth->localpart = \"%s\"",
293 					 parsed->envid),
294 			 strcmp(parsed->envid, test->envid) == 0);
295 	}
296 }
297 
298 static void
test_smtp_mail_params_ret(const struct smtp_params_mail * test,const struct smtp_params_mail * parsed)299 test_smtp_mail_params_ret(const struct smtp_params_mail *test,
300 			  const struct smtp_params_mail *parsed)
301 {
302 	const char *ret_name = NULL;
303 
304 	switch (parsed->ret) {
305 	case SMTP_PARAM_MAIL_RET_UNSPECIFIED:
306 		ret_name = "<UNSPECIFIED>";
307 		break;
308 	case SMTP_PARAM_MAIL_RET_HDRS:
309 		ret_name = "HDRS";
310 		break;
311 	case SMTP_PARAM_MAIL_RET_FULL:
312 		ret_name = "FULL";
313 		break;
314 	default:
315 		i_unreached();
316 	}
317 
318 	test_out(t_strdup_printf("params.ret = %s", ret_name),
319 		 parsed->ret == test->ret);
320 }
321 
322 static void
test_smtp_mail_params_size(const struct smtp_params_mail * test,const struct smtp_params_mail * parsed)323 test_smtp_mail_params_size(const struct smtp_params_mail *test,
324 			   const struct smtp_params_mail *parsed)
325 {
326 	test_out(t_strdup_printf("params.size = %"PRIuUOFF_T, parsed->size),
327 		 parsed->size == test->size);
328 }
329 
330 static void
test_smtp_mail_params_extensions(const struct smtp_params_mail * test,const struct smtp_params_mail * parsed)331 test_smtp_mail_params_extensions(const struct smtp_params_mail *test,
332 				 const struct smtp_params_mail *parsed)
333 {
334 	const struct smtp_param *tparam, *pparam;
335 	unsigned int i;
336 
337 	if (!array_is_created(&test->extra_params) ||
338 	    array_count(&test->extra_params) == 0) {
339 		test_out(t_strdup_printf("params.extra_params.count = %u",
340 			 (!array_is_created(&parsed->extra_params) ?
341 			  0 : array_count(&parsed->extra_params))),
342 			 (!array_is_created(&parsed->extra_params) ||
343 			  array_count(&parsed->extra_params) == 0));
344 		return;
345 	}
346 
347 	if (!array_is_created(&parsed->extra_params) ||
348 	    array_count(&parsed->extra_params) == 0) {
349 		test_out("params.extra_params.count = 0", FALSE);
350 		return;
351 	}
352 
353 	if (array_count(&test->extra_params) !=
354 	    array_count(&parsed->extra_params)) {
355 		test_out(t_strdup_printf("params.extra_params.count = %u",
356 			 (!array_is_created(&parsed->extra_params) ?
357 			  0 : array_count(&parsed->extra_params))), FALSE);
358 		return;
359 	}
360 
361 	for (i = 0; i < array_count(&test->extra_params); i++) {
362 		tparam = array_idx(&test->extra_params, i);
363 		pparam = array_idx(&parsed->extra_params, i);
364 
365 		test_out(t_strdup_printf("params.extra_params[%u] = [\"%s\"=\"%s\"]",
366 					 i, pparam->keyword, pparam->value),
367 			 strcmp(pparam->keyword, tparam->keyword) == 0 &&
368 				((pparam->value == NULL && tparam->value == NULL) ||
369 				 (pparam->value != NULL && tparam->value != NULL &&
370 				  strcmp(pparam->value, tparam->value) == 0)));
371 	}
372 }
373 
test_smtp_mail_params_parse_valid(void)374 static void test_smtp_mail_params_parse_valid(void)
375 {
376 	unsigned int i;
377 
378 	for (i = 0; i < valid_mail_params_parse_test_count; i++) T_BEGIN {
379 		const struct valid_mail_params_parse_test *test;
380 		struct smtp_params_mail params;
381 		enum smtp_param_parse_error error_code;
382 		const char *error = NULL, *output;
383 		int ret;
384 
385 		test = &valid_mail_params_parse_tests[i];
386 		ret = smtp_params_mail_parse(pool_datastack_create(),
387 			test->input, test->caps, test->extensions,
388 			test->body_extensions, &params, &error_code, &error);
389 
390 		test_begin(t_strdup_printf("smtp mail params valid [%d]", i));
391 		test_out_reason(t_strdup_printf("parse(\"%s\")", test->input),
392 				ret >= 0, error);
393 
394 		if (ret >= 0) {
395 			string_t *encoded;
396 
397 			/* AUTH */
398 			if ((test->caps & SMTP_CAPABILITY_AUTH) != 0)
399 				test_smtp_mail_params_auth(&test->params, &params);
400 			/* BODY */
401 			if ((test->caps & SMTP_CAPABILITY_8BITMIME) != 0 ||
402 				(test->caps & SMTP_CAPABILITY_BINARYMIME) != 0)
403 				test_smtp_mail_params_body(&test->params, &params);
404 			/* ENVID */
405 			if ((test->caps & SMTP_CAPABILITY_DSN) != 0)
406 				test_smtp_mail_params_envid(&test->params, &params);
407 			/* RET */
408 			if ((test->caps & SMTP_CAPABILITY_DSN) != 0)
409 				test_smtp_mail_params_ret(&test->params, &params);
410 			/* SIZE */
411 			if ((test->caps & SMTP_CAPABILITY_SIZE) != 0)
412 				test_smtp_mail_params_size(&test->params, &params);
413 			/* <extensions> */
414 			if (test->extensions != NULL)
415 				test_smtp_mail_params_extensions(&test->params, &params);
416 
417 			encoded = t_str_new(256);
418 			smtp_params_mail_write(encoded, test->caps,
419 					       test->extensions, &params);
420 
421 			output = (test->output == NULL ? test->input : test->output);
422 			test_out(t_strdup_printf("encode() = \"%s\"",
423 						 str_c(encoded)),
424 				 strcmp(str_c(encoded), output) == 0);
425 		}
426 		test_end();
427 	} T_END;
428 }
429 
430 /* Invalid mail params tests */
431 
432 struct invalid_mail_params_parse_test {
433 	const char *input;
434 
435 	enum smtp_capability caps;
436 	const char *const *extensions;
437 };
438 
439 static const struct invalid_mail_params_parse_test
440 invalid_mail_params_parse_tests[] = {
441 	/* AUTH */
442 	{
443 		.input = "AUTH=<>",
444 	},
445 	{
446 		.input = "AUTH=++",
447 		.caps = SMTP_CAPABILITY_AUTH
448 	},
449 	/* BODY */
450 	{
451 		.input = "BODY=8BITMIME",
452 	},
453 	{
454 		.input = "BODY=BINARYMIME",
455 	},
456 	{
457 		.input = "BODY=BINARYMIME",
458 		.caps = SMTP_CAPABILITY_BINARYMIME
459 	},
460 	{
461 		.input = "BODY=FROP",
462 		.caps = SMTP_CAPABILITY_8BITMIME
463 	},
464 	/* ENVID */
465 	{
466 		.input = "ENVID=AABBCC",
467 	},
468 	{
469 		.input = "ENVID=++",
470 		.caps = SMTP_CAPABILITY_DSN
471 	},
472 	/* RET */
473 	{
474 		.input = "RET=FULL",
475 	},
476 	{
477 		.input = "RET=HDR",
478 	},
479 	{
480 		.input = "RET=FROP",
481 		.caps = SMTP_CAPABILITY_DSN
482 	},
483 	/* SIZE */
484 	{
485 		.input = "SIZE=13",
486 	},
487 	{
488 		.input = "SIZE=ABC",
489 		.caps = SMTP_CAPABILITY_SIZE
490 	}
491 };
492 
493 unsigned int invalid_mail_params_parse_test_count =
494 	N_ELEMENTS(invalid_mail_params_parse_tests);
495 
test_smtp_mail_params_parse_invalid(void)496 static void test_smtp_mail_params_parse_invalid(void)
497 {
498 	unsigned int i;
499 
500 	for (i = 0; i < invalid_mail_params_parse_test_count; i++) T_BEGIN {
501 		const struct invalid_mail_params_parse_test *test;
502 		struct smtp_params_mail params;
503 		enum smtp_param_parse_error error_code;
504 		const char *error = NULL;
505 		int ret;
506 
507 		test = &invalid_mail_params_parse_tests[i];
508 		ret = smtp_params_mail_parse(pool_datastack_create(),
509 					     test->input, test->caps,
510 					     test->extensions, NULL,
511 					     &params, &error_code, &error);
512 
513 		test_begin(t_strdup_printf("smtp mail params invalid [%d]", i));
514 		test_out_reason(t_strdup_printf("parse(\"%s\")", test->input),
515 				ret < 0, error);
516 		test_end();
517 	} T_END;
518 }
519 
520 /* Valid rcpt params tests */
521 
522 struct valid_rcpt_params_parse_test {
523 	const char *input, *output;
524 
525 	enum smtp_param_rcpt_parse_flags flags;
526 	enum smtp_capability caps;
527 	const char *const *extensions;
528 
529 	struct smtp_params_rcpt params;
530 };
531 
532 static const struct valid_rcpt_params_parse_test
533 valid_rcpt_params_parse_tests[] = {
534 	/* ORCPT */
535 	{
536 		.input = "ORCPT=rfc822;e+3Dmc2@example.com",
537 		.caps = SMTP_CAPABILITY_DSN,
538 		.params = {
539 			.orcpt = {
540 				.addr = &test_address3
541 			}
542 		}
543 	},
544 	{
545 		.input = "ORCPT=rfc822;<e+3Dmc2@example.com>",
546 		.output = "ORCPT=rfc822;e+3Dmc2@example.com",
547 		.caps = SMTP_CAPABILITY_DSN,
548 		.params = {
549 			.orcpt = {
550 				.addr = &test_address3
551 			}
552 		}
553 	},
554 	{
555 		.input = "ORCPT=rfc822;user+2Bdetail",
556 		.flags = SMTP_PARAM_RCPT_FLAG_ORCPT_ALLOW_LOCALPART,
557 		.caps = SMTP_CAPABILITY_DSN,
558 		.params = {
559 			.orcpt = {
560 				.addr = &test_address2
561 			}
562 		}
563 	},
564 	{
565 		.input = "ORCPT=rfc822;<user+2Bdetail>",
566 		.output = "ORCPT=rfc822;user+2Bdetail",
567 		.flags = SMTP_PARAM_RCPT_FLAG_ORCPT_ALLOW_LOCALPART,
568 		.caps = SMTP_CAPABILITY_DSN,
569 		.params = {
570 			.orcpt = {
571 				.addr = &test_address2
572 			}
573 		}
574 	},
575 	/* NOTIFY */
576 	{
577 		.input = "",
578 		.caps = SMTP_CAPABILITY_DSN,
579 		.params = {
580 			.notify = SMTP_PARAM_RCPT_NOTIFY_UNSPECIFIED,
581 		}
582 	},
583 	{
584 		.input = "NOTIFY=SUCCESS",
585 		.caps = SMTP_CAPABILITY_DSN,
586 		.params = {
587 			.notify = SMTP_PARAM_RCPT_NOTIFY_SUCCESS,
588 		}
589 	},
590 	{
591 		.input = "NOTIFY=FAILURE",
592 		.caps = SMTP_CAPABILITY_DSN,
593 		.params = {
594 			.notify = SMTP_PARAM_RCPT_NOTIFY_FAILURE,
595 		}
596 	},
597 	{
598 		.input = "NOTIFY=DELAY",
599 		.caps = SMTP_CAPABILITY_DSN,
600 		.params = {
601 			.notify = SMTP_PARAM_RCPT_NOTIFY_DELAY,
602 		}
603 	},
604 	{
605 		.input = "NOTIFY=NEVER",
606 		.caps = SMTP_CAPABILITY_DSN,
607 		.params = {
608 			.notify = SMTP_PARAM_RCPT_NOTIFY_NEVER,
609 		}
610 	},
611 	{
612 		.input = "NOTIFY=SUCCESS,FAILURE,DELAY",
613 		.caps = SMTP_CAPABILITY_DSN,
614 		.params = {
615 			.notify = SMTP_PARAM_RCPT_NOTIFY_SUCCESS |
616 				SMTP_PARAM_RCPT_NOTIFY_FAILURE |
617 				SMTP_PARAM_RCPT_NOTIFY_DELAY,
618 		}
619 	},
620 	/* <extensions> */
621 	{
622 		.input = "FROP=friep",
623 		.caps = SMTP_CAPABILITY_SIZE,
624 		.extensions = test_extensions,
625 		.params = {
626 			.extra_params = {
627 				.arr = {
628 					.buffer = &test_params_buffer1,
629 					.element_size = sizeof(struct smtp_param)
630 				}
631 			}
632 		}
633 	},
634 	{
635 		.input = "FROP=friep FRUP=frml",
636 		.extensions = test_extensions,
637 		.params = {
638 			.extra_params = {
639 				.arr = {
640 					.buffer = &test_params_buffer2,
641 					.element_size = sizeof(struct smtp_param)
642 				}
643 			}
644 		}
645 	}
646 };
647 
648 unsigned int valid_rcpt_params_parse_test_count =
649 	N_ELEMENTS(valid_rcpt_params_parse_tests);
650 
651 static void
test_smtp_rcpt_params_orcpt(const struct smtp_params_rcpt * test,const struct smtp_params_rcpt * parsed)652 test_smtp_rcpt_params_orcpt(const struct smtp_params_rcpt *test,
653 			    const struct smtp_params_rcpt *parsed)
654 {
655 	if (parsed->orcpt.addr == NULL) {
656 		test_out("params.orcpt.addr = NULL",
657 			test->orcpt.addr == NULL);
658 		return;
659 	}
660 
661 	if (parsed->orcpt.addr->localpart == NULL ||
662 	    test->orcpt.addr->localpart == NULL) {
663 		test_out(t_strdup_printf("params.orcpt.addr->localpart = %s",
664 					 parsed->orcpt.addr->localpart),
665 			 (parsed->orcpt.addr->localpart ==
666 			  test->orcpt.addr->localpart));
667 	} else {
668 		test_out(t_strdup_printf("params.orcpt.addr->localpart = \"%s\"",
669 					 parsed->orcpt.addr->localpart),
670 			 strcmp(parsed->orcpt.addr->localpart,
671 				test->orcpt.addr->localpart) == 0);
672 	}
673 	if (parsed->orcpt.addr->domain == NULL ||
674 	    test->orcpt.addr->domain == NULL) {
675 		test_out(t_strdup_printf("params.orcpt.addr->domain = %s",
676 					 parsed->orcpt.addr->domain),
677 			 (parsed->orcpt.addr->domain ==
678 			  test->orcpt.addr->domain));
679 	} else {
680 		test_out(t_strdup_printf("params.orcpt.addr->domain = \"%s\"",
681 					 parsed->orcpt.addr->domain),
682 			 strcmp(parsed->orcpt.addr->domain,
683 				test->orcpt.addr->domain) == 0);
684 	}
685 }
686 
687 
688 static void
test_smtp_rcpt_params_notify(const struct smtp_params_rcpt * test,const struct smtp_params_rcpt * parsed)689 test_smtp_rcpt_params_notify(const struct smtp_params_rcpt *test,
690 			     const struct smtp_params_rcpt *parsed)
691 {
692 	string_t *notify_name;
693 
694 	notify_name = t_str_new(64);
695 	if (parsed->notify == 0) {
696 		str_append(notify_name, "<UNSPECIFIED>");
697 	} else if ((parsed->notify & SMTP_PARAM_RCPT_NOTIFY_NEVER) != 0) {
698 		i_assert((parsed->notify & SMTP_PARAM_RCPT_NOTIFY_SUCCESS) == 0);
699 		i_assert((parsed->notify & SMTP_PARAM_RCPT_NOTIFY_FAILURE) == 0);
700 		i_assert((parsed->notify & SMTP_PARAM_RCPT_NOTIFY_DELAY) == 0);
701 		str_append(notify_name, "NEVER");
702 	} else {
703 		if ((parsed->notify & SMTP_PARAM_RCPT_NOTIFY_SUCCESS) != 0)
704 			str_append(notify_name, "SUCCESS");
705 		if ((parsed->notify & SMTP_PARAM_RCPT_NOTIFY_FAILURE) != 0) {
706 			if (str_len(notify_name) > 0)
707 				str_append_c(notify_name, ',');
708 			str_append(notify_name, "FAILURE");
709 		}
710 		if ((parsed->notify & SMTP_PARAM_RCPT_NOTIFY_DELAY) != 0) {
711 			if (str_len(notify_name) > 0)
712 				str_append_c(notify_name, ',');
713 			str_append(notify_name, "DELAY");
714 		}
715 	}
716 
717 	test_out(t_strdup_printf("params.notify = %s", str_c(notify_name)),
718 		 parsed->notify == test->notify);
719 }
720 
721 static void
test_smtp_rcpt_params_extensions(const struct smtp_params_rcpt * test,const struct smtp_params_rcpt * parsed)722 test_smtp_rcpt_params_extensions(const struct smtp_params_rcpt *test,
723 				 const struct smtp_params_rcpt *parsed)
724 {
725 	const struct smtp_param *tparam, *pparam;
726 	unsigned int i;
727 
728 	if (!array_is_created(&test->extra_params) ||
729 	    array_count(&test->extra_params) == 0) {
730 		test_out(t_strdup_printf("params.extra_params.count = %u",
731 			 (!array_is_created(&parsed->extra_params) ?
732 			  0 : array_count(&parsed->extra_params))),
733 			 (!array_is_created(&parsed->extra_params) ||
734 			  array_count(&parsed->extra_params) == 0));
735 		return;
736 	}
737 
738 	if (!array_is_created(&parsed->extra_params) ||
739 	    array_count(&parsed->extra_params) == 0) {
740 		test_out("params.extra_params.count = 0", FALSE);
741 		return;
742 	}
743 
744 	if (array_count(&test->extra_params) !=
745 	    array_count(&parsed->extra_params)) {
746 		test_out(t_strdup_printf("params.extra_params.count = %u",
747 			 (!array_is_created(&parsed->extra_params) ? 0 :
748 				array_count(&parsed->extra_params))), FALSE);
749 		return;
750 	}
751 
752 	for (i = 0; i < array_count(&test->extra_params); i++) {
753 		tparam = array_idx(&test->extra_params, i);
754 		pparam = array_idx(&parsed->extra_params, i);
755 
756 		test_out(t_strdup_printf("params.extra_params[%u] = [\"%s\"=\"%s\"]",
757 					 i, pparam->keyword, pparam->value),
758 			 strcmp(pparam->keyword, tparam->keyword) == 0 &&
759 				((pparam->value == NULL && tparam->value == NULL) ||
760 				 (pparam->value != NULL && tparam->value != NULL &&
761 				  strcmp(pparam->value, tparam->value) == 0)));
762 	}
763 }
764 
test_smtp_rcpt_params_parse_valid(void)765 static void test_smtp_rcpt_params_parse_valid(void)
766 {
767 	unsigned int i;
768 
769 	for (i = 0; i < valid_rcpt_params_parse_test_count; i++) T_BEGIN {
770 		const struct valid_rcpt_params_parse_test *test;
771 		struct smtp_params_rcpt params;
772 		enum smtp_param_parse_error error_code;
773 		const char *error = NULL, *output;
774 		int ret;
775 
776 		test = &valid_rcpt_params_parse_tests[i];
777 		ret = smtp_params_rcpt_parse(pool_datastack_create(),
778 					     test->input, test->flags,
779 					     test->caps, test->extensions,
780 					     &params, &error_code, &error);
781 
782 		test_begin(t_strdup_printf("smtp rcpt params valid [%d]", i));
783 		test_out_reason(t_strdup_printf("parse(\"%s\")",
784 			test->input), ret >= 0, error);
785 
786 		if (ret >= 0) {
787 			string_t *encoded;
788 
789 			/* ORCPT */
790 			if ((test->caps & SMTP_CAPABILITY_DSN) != 0)
791 				test_smtp_rcpt_params_orcpt(&test->params, &params);
792 			/* NOTIFY */
793 			if ((test->caps & SMTP_CAPABILITY_DSN) != 0)
794 				test_smtp_rcpt_params_notify(&test->params, &params);
795 			/* <extensions> */
796 			if (test->extensions != NULL)
797 				test_smtp_rcpt_params_extensions(&test->params, &params);
798 
799 			encoded = t_str_new(256);
800 			smtp_params_rcpt_write(encoded, test->caps,
801 					       test->extensions, &params);
802 
803 			output = (test->output == NULL ? test->input : test->output);
804 			test_out(t_strdup_printf("encode() = \"%s\"",
805 						 str_c(encoded)),
806 				 strcmp(str_c(encoded), output) == 0);
807 		}
808 		test_end();
809 	} T_END;
810 }
811 
812 /* Invalid rcpt params tests */
813 
814 struct invalid_rcpt_params_parse_test {
815 	const char *input;
816 
817 	enum smtp_param_rcpt_parse_flags flags;
818 	enum smtp_capability caps;
819 	const char *const *extensions;
820 };
821 
822 static const struct invalid_rcpt_params_parse_test
823 invalid_rcpt_params_parse_tests[] = {
824 	/* DSN */
825 	{
826 		.input = "ORCPT=rfc822;frop@example.com",
827 	},
828 	{
829 		.input = "ORCPT=rfc822;<>",
830 		.caps = SMTP_CAPABILITY_DSN
831 	},
832 	{
833 		.input = "ORCPT=rfc822;",
834 		.caps = SMTP_CAPABILITY_DSN
835 	},
836 	{
837 		.input = "ORCPT=++",
838 		.caps = SMTP_CAPABILITY_DSN
839 	},
840 	{
841 		.input = "ORCPT=rfc822;++",
842 		.caps = SMTP_CAPABILITY_DSN
843 	},
844 	{
845 		.input = "NOTIFY=SUCCESS",
846 	},
847 	{
848 		.input = "NOTIFY=FROP",
849 		.caps = SMTP_CAPABILITY_DSN
850 	},
851 	{
852 		.input = "NOTIFY=NEVER,SUCCESS",
853 		.caps = SMTP_CAPABILITY_DSN
854 	}
855 };
856 
857 unsigned int invalid_rcpt_params_parse_test_count =
858 	N_ELEMENTS(invalid_rcpt_params_parse_tests);
859 
test_smtp_rcpt_params_parse_invalid(void)860 static void test_smtp_rcpt_params_parse_invalid(void)
861 {
862 	unsigned int i;
863 
864 	for (i = 0; i < invalid_rcpt_params_parse_test_count; i++) T_BEGIN {
865 		const struct invalid_rcpt_params_parse_test *test;
866 		struct smtp_params_rcpt params;
867 		enum smtp_param_parse_error error_code;
868 		const char *error = NULL;
869 		int ret;
870 
871 		test = &invalid_rcpt_params_parse_tests[i];
872 		ret = smtp_params_rcpt_parse(pool_datastack_create(),
873 					     test->input, test->flags,
874 					     test->caps, test->extensions,
875 					     &params, &error_code, &error);
876 
877 		test_begin(t_strdup_printf("smtp rcpt params invalid [%d]", i));
878 		test_out_reason(t_strdup_printf("parse(\"%s\")", test->input),
879 				ret < 0, error);
880 		test_end();
881 	} T_END;
882 }
883 
main(void)884 int main(void)
885 {
886 	static void (*test_functions[])(void) = {
887 		test_smtp_mail_params_parse_valid,
888 		test_smtp_mail_params_parse_invalid,
889 		test_smtp_rcpt_params_parse_valid,
890 		test_smtp_rcpt_params_parse_invalid,
891 		NULL
892 	};
893 	return test_run(test_functions);
894 }
895