1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "str.h"
5 #include "array.h"
6 #include "message-address.h"
7 #include "smtp-common.h"
8 #include "smtp-parser.h"
9 #include "smtp-syntax.h"
10 #include "smtp-address.h"
11 
12 #include "smtp-params.h"
13 
14 #include <ctype.h>
15 
16 /*
17  * Common
18  */
19 
20 /* parse */
21 
22 static int
smtp_param_do_parse(struct smtp_parser * parser,struct smtp_param * param_r)23 smtp_param_do_parse(struct smtp_parser *parser, struct smtp_param *param_r)
24 {
25 	const unsigned char *pbegin = parser->cur;
26 
27 	/* esmtp-param    = esmtp-keyword ["=" esmtp-value]
28 	   esmtp-keyword  = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-")
29 	   esmtp-value    = 1*(%d33-60 / %d62-126)
30 	                  ; any CHAR excluding "=", SP, and control
31 	                  ; characters.  If this string is an email address,
32 	                  ; i.e., a Mailbox, then the "xtext" syntax [32]
33 	                  ; SHOULD be used.
34 	 */
35 
36 	if (parser->cur >= parser->end || !i_isalnum(*parser->cur)) {
37 		parser->error = "Unexpected character in parameter keyword";
38 		return -1;
39 	}
40 	parser->cur++;
41 
42 	while (parser->cur < parser->end &&
43 	       (i_isalnum(*parser->cur) || *parser->cur == '-'))
44 		parser->cur++;
45 	param_r->keyword = t_strndup(pbegin, parser->cur - pbegin);
46 
47 	if (parser->cur >= parser->end) {
48 		param_r->value = NULL;
49 		return 1;
50 	}
51 	if (*parser->cur != '=') {
52 		parser->error = "Unexpected character in parameter keyword";
53 		return -1;
54 	}
55 	parser->cur++;
56 
57 	pbegin = parser->cur;
58 	while (parser->cur < parser->end &&
59 	       smtp_char_is_esmtp_value(*parser->cur))
60 		parser->cur++;
61 
62 	if (parser->cur < parser->end) {
63 		parser->error = "Unexpected character in parameter value";
64 		return -1;
65 	}
66 	param_r->value = t_strndup(pbegin, parser->cur - pbegin);
67 	return 1;
68 }
69 
smtp_param_parse(pool_t pool,const char * text,struct smtp_param * param_r,const char ** error_r)70 int smtp_param_parse(pool_t pool, const char *text,
71 		     struct smtp_param *param_r, const char **error_r)
72 {
73 	struct smtp_parser parser;
74 
75 	i_zero(param_r);
76 
77 	if (text == NULL || *text == '\0') {
78 		if (error_r != NULL)
79 			*error_r = "Parameter is empty";
80 		return -1;
81 	}
82 
83 	smtp_parser_init(&parser, pool, text);
84 
85 	if (smtp_param_do_parse(&parser, param_r) <= 0) {
86 		if (error_r != NULL)
87 			*error_r = parser.error;
88 		return -1;
89 	}
90 	return 1;
91 }
92 
93 /* manipulate */
94 
smtp_params_copy(pool_t pool,ARRAY_TYPE (smtp_param)* dst,const ARRAY_TYPE (smtp_param)* src)95 void smtp_params_copy(pool_t pool, ARRAY_TYPE(smtp_param) *dst,
96 		      const ARRAY_TYPE(smtp_param) *src)
97 {
98 	const struct smtp_param *param;
99 
100 	if (!array_is_created(src))
101 		return;
102 
103 	p_array_init(dst, pool, array_count(src));
104 	array_foreach(src, param) {
105 		struct smtp_param param_new;
106 
107 		param_new.keyword = p_strdup(pool, param->keyword);
108 		param_new.value = p_strdup(pool, param->value);
109 		array_push_back(dst, &param_new);
110 	}
111 }
112 
smtp_params_add_one(ARRAY_TYPE (smtp_param)* params,pool_t pool,const char * keyword,const char * value)113 void smtp_params_add_one(ARRAY_TYPE(smtp_param) *params, pool_t pool,
114 			 const char *keyword, const char *value)
115 {
116 	struct smtp_param param;
117 
118 	if (!array_is_created(params))
119 		p_array_init(params, pool, 4);
120 
121 	i_zero(&param);
122 	param.keyword = p_strdup(pool, keyword);
123 	param.value = p_strdup(pool, value);
124 	array_push_back(params, &param);
125 }
126 
smtp_params_add_encoded(ARRAY_TYPE (smtp_param)* params,pool_t pool,const char * keyword,const unsigned char * value,size_t value_len)127 void smtp_params_add_encoded(ARRAY_TYPE(smtp_param) *params, pool_t pool,
128 			     const char *keyword, const unsigned char *value,
129 			     size_t value_len)
130 {
131 	string_t *value_enc = t_str_new(value_len * 2);
132 
133 	smtp_xtext_encode(value_enc, value, value_len);
134 	smtp_params_add_one(params, pool, keyword, str_c(value_enc));
135 }
136 
smtp_params_drop_one(ARRAY_TYPE (smtp_param)* params,const char * keyword,const char ** value_r)137 bool smtp_params_drop_one(ARRAY_TYPE(smtp_param) *params, const char *keyword,
138 			  const char **value_r)
139 {
140 	const struct smtp_param *param;
141 
142 	if (!array_is_created(params))
143 		return FALSE;
144 
145 	array_foreach(params, param) {
146 		if (strcasecmp(param->keyword, keyword) == 0) {
147 			if (value_r != NULL)
148 				*value_r = param->value;
149 			array_delete(params,
150 				     array_foreach_idx(params, param), 1);
151 			return TRUE;
152 		}
153 	}
154 	return FALSE;
155 }
156 
157 /* write */
158 
smtp_param_value_valid(const char * value)159 static bool smtp_param_value_valid(const char *value)
160 {
161 	const char *p = value;
162 
163 	while (*p != '\0' && smtp_char_is_esmtp_value(*p))
164 		p++;
165 	return (*p == '\0');
166 }
167 
smtp_param_write(string_t * out,const struct smtp_param * param)168 void smtp_param_write(string_t *out, const struct smtp_param *param)
169 {
170 	str_append(out, t_str_ucase(param->keyword));
171 	if (param->value != NULL) {
172 		i_assert(smtp_param_value_valid(param->value));
173 		str_append_c(out, '=');
174 		str_append(out, param->value);
175 	}
176 }
177 
178 static void
smtp_params_write(string_t * buffer,const char * const * param_keywords,const ARRAY_TYPE (smtp_param)* params)179 smtp_params_write(string_t *buffer, const char *const *param_keywords,
180 		  const ARRAY_TYPE(smtp_param) *params) ATTR_NULL(2)
181 {
182 	const struct smtp_param *param;
183 
184 	if (param_keywords == NULL || *param_keywords == NULL)
185 		return;
186 	if (!array_is_created(params))
187 		return;
188 
189 	array_foreach(params, param) {
190 		if (str_array_icase_find(param_keywords, param->keyword))
191 			smtp_param_write(buffer, param);
192 		str_append_c(buffer, ' ');
193 	}
194 }
195 
196 /* evaluate */
197 
198 const struct smtp_param *
smtp_params_get_param(const ARRAY_TYPE (smtp_param)* params,const char * keyword)199 smtp_params_get_param(const ARRAY_TYPE(smtp_param) *params,
200 		      const char *keyword)
201 {
202 	const struct smtp_param *param;
203 
204 	if (!array_is_created(params))
205 		return NULL;
206 
207 	array_foreach(params, param) {
208 		if (strcasecmp(param->keyword, keyword) == 0)
209 			return param;
210 	}
211 	return NULL;
212 }
213 
smtp_params_decode_param(const ARRAY_TYPE (smtp_param)* params,const char * keyword,string_t ** value_r,bool allow_nul,const char ** error_r)214 int smtp_params_decode_param(const ARRAY_TYPE(smtp_param) *params,
215 			     const char *keyword, string_t **value_r,
216 			     bool allow_nul, const char **error_r)
217 {
218 	const struct smtp_param *param;
219 
220 	param = smtp_params_get_param(params, keyword);
221 	if (param == NULL)
222 		return 0;
223 
224 	*value_r = t_str_new(strlen(param->value) * 2);
225 	if (smtp_xtext_decode(*value_r, param->value, allow_nul, error_r) <= 0)
226 		return -1;
227 	return 1;
228 }
229 
smtp_params_equal(const ARRAY_TYPE (smtp_param)* params1,const ARRAY_TYPE (smtp_param)* params2)230 bool smtp_params_equal(const ARRAY_TYPE(smtp_param) *params1,
231 		       const ARRAY_TYPE(smtp_param) *params2)
232 {
233 	const struct smtp_param *param1, *param2;
234 
235 	if (!array_is_created(params1) || array_count(params1) == 0) {
236 		return (!array_is_created(params2) ||
237 			array_count(params2) == 0);
238 	}
239 	if (!array_is_created(params2) || array_count(params2) == 0)
240 		return FALSE;
241 
242 	if (array_count(params1) != array_count(params2))
243 		return FALSE;
244 
245 	array_foreach(params1, param1) {
246 		param2 = smtp_params_get_param(params2, param1->keyword);
247 		if (param2 == NULL)
248 			return FALSE;
249 		if (null_strcmp(param1->value, param2->value) != 0)
250 			return FALSE;
251 	}
252 	return TRUE;
253 }
254 
255 /*
256  * MAIL parameters
257  */
258 
259 /* parse */
260 
261 struct smtp_params_mail_parser {
262 	pool_t pool;
263 	struct smtp_params_mail *params;
264 	enum smtp_capability caps;
265 
266 	enum smtp_param_parse_error error_code;
267 	const char *error;
268 };
269 
270 static int
smtp_params_mail_parse_auth(struct smtp_params_mail_parser * pmparser,const char * xtext)271 smtp_params_mail_parse_auth(struct smtp_params_mail_parser *pmparser,
272 			    const char *xtext)
273 {
274 	struct smtp_params_mail *params = pmparser->params;
275 	struct smtp_address *auth_addr;
276 	const char *value, *error;
277 
278 	/* AUTH=: RFC 4954, Section 5
279 
280 	   We ignore this parameter, but we do check it for validity
281 	 */
282 
283 	/* cannot specify this multiple times */
284 	if (params->auth != NULL) {
285 		pmparser->error = "Duplicate AUTH= parameter";
286 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
287 		return -1;
288 	}
289 	/* value required */
290 	if (xtext == NULL) {
291 		pmparser->error = "Missing AUTH= parameter value";
292 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
293 		return -1;
294 	}
295 	if (smtp_xtext_parse(xtext, &value, &error) < 0) {
296 		pmparser->error = t_strdup_printf(
297 			"Invalid AUTH= parameter value: %s", error);
298 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
299 		return -1;
300 	}
301 	if (strcmp(value, "<>") == 0) {
302 		params->auth = p_new(pmparser->pool, struct smtp_address, 1);
303 	} else if (smtp_address_parse_mailbox(
304 			pmparser->pool,	value,
305 			SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART,
306 			&auth_addr, &error) < 0)	{
307 		pmparser->error = t_strdup_printf(
308 			"Invalid AUTH= address value: %s", error);
309 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
310 		return -1;
311 	} else {
312 		params->auth = auth_addr;
313 	}
314 	/* ignore, our own AUTH data is added below */
315 	return 0;
316 }
317 
318 static int
smtp_params_mail_parse_body(struct smtp_params_mail_parser * pmparser,const char * value,const char * const * extensions)319 smtp_params_mail_parse_body(struct smtp_params_mail_parser *pmparser,
320 			    const char *value, const char *const *extensions)
321 {
322 	struct smtp_params_mail *params = pmparser->params;
323 	enum smtp_capability caps = pmparser->caps;
324 
325 	/* BODY=<type>: RFC 6152 */
326 
327 	/* cannot specify this multiple times */
328 	if (params->body.type != SMTP_PARAM_MAIL_BODY_TYPE_UNSPECIFIED) {
329 		pmparser->error = "Duplicate BODY= parameter";
330 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
331 		return -1;
332 	}
333 	/* value required */
334 	if (value == NULL) {
335 		pmparser->error = "Missing BODY= parameter value";
336 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
337 		return -1;
338 	}
339 
340 	value = t_str_ucase(value);
341 	params->body.ext = NULL;
342 	/* =7BIT: RFC 6152 */
343 	if (strcmp(value, "7BIT") == 0) {
344 		params->body.type = SMTP_PARAM_MAIL_BODY_TYPE_7BIT;
345 	/* =8BITMIME: RFC 6152 */
346 	} else if ((caps & SMTP_CAPABILITY_8BITMIME) != 0 &&
347 		   strcmp(value, "8BITMIME") == 0) {
348 		params->body.type = SMTP_PARAM_MAIL_BODY_TYPE_8BITMIME;
349 	/* =BINARYMIME: RFC 3030 */
350 	} else if ((caps & SMTP_CAPABILITY_BINARYMIME) != 0 &&
351 		   (caps & SMTP_CAPABILITY_CHUNKING) != 0 &&
352 		   strcmp(value, "BINARYMIME") == 0) {
353 		params->body.type = SMTP_PARAM_MAIL_BODY_TYPE_BINARYMIME;
354 	/* =?? */
355 	} else if (extensions != NULL &&
356 		   str_array_icase_find(extensions, value)) {
357 		params->body.type = SMTP_PARAM_MAIL_BODY_TYPE_EXTENSION;
358 		params->body.ext = p_strdup(pmparser->pool, value);
359 	} else {
360 		pmparser->error = "Unsupported mail BODY type";
361 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_NOT_SUPPORTED;
362 		return -1;
363 	}
364 	return 0;
365 }
366 
367 static int
smtp_params_mail_parse_envid(struct smtp_params_mail_parser * pmparser,const char * xtext)368 smtp_params_mail_parse_envid(struct smtp_params_mail_parser *pmparser,
369 			     const char *xtext)
370 {
371 	struct smtp_params_mail *params = pmparser->params;
372 	const unsigned char *p, *pend;
373 	const char *envid, *error;
374 
375 	/* ENVID=<envid>: RFC 3461 */
376 
377 	/* cannot specify this multiple times */
378 	if (params->envid != NULL) {
379 		pmparser->error = "Duplicate ENVID= parameter";
380 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
381 		return -1;
382 	}
383 	/* value required */
384 	if (xtext == NULL) {
385 		pmparser->error = "Missing ENVID= parameter value";
386 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
387 		return -1;
388 	}
389 	/* check xtext */
390 	if (smtp_xtext_parse(xtext, &envid, &error) < 0) {
391 		pmparser->error = t_strdup_printf(
392 			"Invalid ENVID= parameter value: %s", error);
393 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
394 		return -1;
395 	}
396 	/* RFC 3461, Section 4.4:
397 
398 	   Due to limitations in the Delivery Status Notification format, the
399 	   value of the ENVID parameter prior to encoding as "xtext" MUST
400 	   consist entirely of printable (graphic and white space) characters
401 	   from the US-ASCII repertoire.
402 	 */
403 	p = (const unsigned char *)envid;
404 	pend = p + strlen(envid);
405 	while (p < pend && smtp_char_is_textstr(*p))
406 		p++;
407 	if (p < pend) {
408 		pmparser->error =
409 			"Invalid ENVID= parameter value: "
410 			"Contains non-printable characters";
411 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
412 		return -1;
413 	}
414 	params->envid = p_strdup(pmparser->pool, envid);
415 	return 0;
416 }
417 
418 static int
smtp_params_mail_parse_ret(struct smtp_params_mail_parser * pmparser,const char * value)419 smtp_params_mail_parse_ret(struct smtp_params_mail_parser *pmparser,
420 			   const char *value)
421 {
422 	struct smtp_params_mail *params = pmparser->params;
423 
424 	/* RET=<keyword>: RFC 3461 */
425 
426 	/* cannot specify this multiple times */
427 	if (params->ret != SMTP_PARAM_MAIL_RET_UNSPECIFIED) {
428 		pmparser->error = "Duplicate RET= parameter";
429 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
430 		return -1;
431 	}
432 	/* value required */
433 	if (value == NULL) {
434 		pmparser->error = "Missing RET= parameter value";
435 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
436 		return -1;
437 	}
438 
439 	value = t_str_ucase(value);
440 	/* =FULL */
441 	if (strcmp(value, "FULL") == 0) {
442 		params->ret = SMTP_PARAM_MAIL_RET_FULL;
443 	/* =HDRS */
444 	} else if (strcmp(value, "HDRS") == 0) {
445 		params->ret = SMTP_PARAM_MAIL_RET_HDRS;
446 	} else {
447 		pmparser->error = "Unsupported RET= parameter keyword";
448 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_NOT_SUPPORTED;
449 		return -1;
450 	}
451 	return 0;
452 }
453 
454 static int
smtp_params_mail_parse_size(struct smtp_params_mail_parser * pmparser,const char * value)455 smtp_params_mail_parse_size(struct smtp_params_mail_parser *pmparser,
456 			    const char *value)
457 {
458 	struct smtp_params_mail *params = pmparser->params;
459 
460 	/* SIZE=<size-value>: RFC 1870 */
461 
462 	/* cannot specify this multiple times */
463 	if (params->size != 0) {
464 		pmparser->error = "Duplicate SIZE= parameter";
465 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
466 		return -1;
467 	}
468 	/* value required */
469 	if (value == NULL) {
470 		pmparser->error = "Missing SIZE= parameter value";
471 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
472 		return -1;
473 	}
474 
475 	/* size-value ::= 1*20DIGIT */
476 	if (str_to_uoff(value, &params->size) < 0) {
477 		pmparser->error = "Unsupported SIZE parameter value";
478 		pmparser->error_code = SMTP_PARAM_PARSE_ERROR_NOT_SUPPORTED;
479 		return -1;
480 	}
481 	return 0;
482 }
483 
smtp_params_mail_parse(pool_t pool,const char * args,enum smtp_capability caps,const char * const * extensions,const char * const * body_extensions,struct smtp_params_mail * params_r,enum smtp_param_parse_error * error_code_r,const char ** error_r)484 int smtp_params_mail_parse(pool_t pool, const char *args,
485 			   enum smtp_capability caps,
486 			   const char *const *extensions,
487 			   const char *const *body_extensions,
488 			   struct smtp_params_mail *params_r,
489 			   enum smtp_param_parse_error *error_code_r,
490 			   const char **error_r)
491 {
492 	struct smtp_params_mail_parser pmparser;
493 	struct smtp_param param;
494 	const char *const *argv;
495 	const char *error;
496 	int ret = 0;
497 
498 	i_zero(params_r);
499 
500 	i_zero(&pmparser);
501 	pmparser.pool = pool;
502 	pmparser.params = params_r;
503 	pmparser.caps = caps;
504 
505 	argv = t_strsplit(args, " ");
506 	for (; *argv != NULL; argv++) {
507 		if (smtp_param_parse(pool_datastack_create(), *argv,
508 				     &param, &error) < 0) {
509 			*error_r = t_strdup_printf(
510 				"Invalid MAIL parameter: %s", error);
511 			*error_code_r = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
512 			return -1;
513 		}
514 
515 		/* parse known parameters */
516 		param.keyword = t_str_ucase(param.keyword);
517 		if ((caps & SMTP_CAPABILITY_AUTH) != 0 &&
518 		    strcmp(param.keyword, "AUTH") == 0) {
519 			if (smtp_params_mail_parse_auth(
520 				&pmparser, param.value) < 0) {
521 				ret = -1;
522 				break;
523 			}
524 		} else if (strcmp(param.keyword, "BODY") == 0) {
525 			if (smtp_params_mail_parse_body(&pmparser, param.value,
526 							body_extensions) < 0) {
527 				ret = -1;
528 				break;
529 			}
530 		} else if ((caps & SMTP_CAPABILITY_DSN) != 0 &&
531 			   strcmp(param.keyword, "ENVID") == 0) {
532 			if (smtp_params_mail_parse_envid(&pmparser,
533 							 param.value) < 0) {
534 				ret = -1;
535 				break;
536 			}
537 		} else if ((caps & SMTP_CAPABILITY_DSN) != 0 &&
538 			   strcmp(param.keyword, "RET") == 0) {
539 			if (smtp_params_mail_parse_ret(&pmparser,
540 						       param.value) < 0) {
541 				ret = -1;
542 				break;
543 			}
544 		} else if ((caps & SMTP_CAPABILITY_SIZE) != 0 &&
545 			   strcmp(param.keyword, "SIZE") == 0) {
546 			if (smtp_params_mail_parse_size(&pmparser,
547 							param.value) < 0) {
548 				ret = -1;
549 				break;
550 			}
551 		} else if (extensions != NULL &&
552 			   str_array_icase_find(extensions, param.keyword)) {
553 			/* add the rest to ext_param for specific
554 			   applications */
555 			smtp_params_mail_add_extra(params_r, pool,
556 						   param.keyword, param.value);
557 		} else {
558 			/* RFC 5321, Section 4.1.1.11:
559 			   If the server SMTP does not recognize or cannot
560 			   implement one or more of the parameters associated
561 			   with a particular MAIL FROM or RCPT TO command, it
562 			   will return code 555. */
563 			*error_r = "Unsupported parameters";
564 			*error_code_r = SMTP_PARAM_PARSE_ERROR_NOT_SUPPORTED;
565 			return -1;
566 		}
567 	}
568 
569 	if (ret < 0) {
570 		*error_r = pmparser.error;
571 		*error_code_r = pmparser.error_code;
572 	}
573 	return ret;
574 }
575 
576 /* manipulate */
577 
smtp_params_mail_copy(pool_t pool,struct smtp_params_mail * dst,const struct smtp_params_mail * src)578 void smtp_params_mail_copy(pool_t pool, struct smtp_params_mail *dst,
579 			   const struct smtp_params_mail *src)
580 {
581 	i_zero(dst);
582 
583 	if (src == NULL)
584 		return;
585 
586 	dst->auth = smtp_address_clone(pool, src->auth);
587 	dst->body.type = src->body.type;
588 	dst->body.ext = p_strdup(pool, src->body.ext);
589 	dst->envid = p_strdup(pool, src->envid);
590 	dst->ret = src->ret;
591 	dst->size = src->size;
592 
593 	smtp_params_copy(pool, &dst->extra_params, &src->extra_params);
594 }
595 
smtp_params_mail_add_extra(struct smtp_params_mail * params,pool_t pool,const char * keyword,const char * value)596 void smtp_params_mail_add_extra(struct smtp_params_mail *params, pool_t pool,
597 				const char *keyword, const char *value)
598 {
599 	smtp_params_add_one(&params->extra_params, pool, keyword, value);
600 }
601 
smtp_params_mail_encode_extra(struct smtp_params_mail * params,pool_t pool,const char * keyword,const unsigned char * value,size_t value_len)602 void smtp_params_mail_encode_extra(struct smtp_params_mail *params, pool_t pool,
603 				   const char *keyword,
604 				   const unsigned char *value,
605 				   size_t value_len)
606 {
607 	smtp_params_add_encoded(&params->extra_params, pool,
608 				keyword, value, value_len);
609 }
610 
smtp_params_mail_drop_extra(struct smtp_params_mail * params,const char * keyword,const char ** value_r)611 bool smtp_params_mail_drop_extra(struct smtp_params_mail *params,
612 				 const char *keyword, const char **value_r)
613 {
614 	return smtp_params_drop_one(&params->extra_params, keyword, value_r);
615 }
616 
617 /* write */
618 
619 static void
smtp_params_mail_write_auth(string_t * buffer,enum smtp_capability caps,const struct smtp_params_mail * params)620 smtp_params_mail_write_auth(string_t *buffer, enum smtp_capability caps,
621 			    const struct smtp_params_mail *params)
622 {
623 	/* add AUTH= parameter */
624 	string_t *auth_addr;
625 
626 	if (params->auth == NULL)
627 		return;
628 	if ((caps & SMTP_CAPABILITY_AUTH) == 0)
629 		return;
630 
631 	auth_addr = t_str_new(256);
632 
633 	if (params->auth->localpart == NULL)
634 		str_append(auth_addr, "<>");
635 	else
636 		smtp_address_write(auth_addr, params->auth);
637 	str_append(buffer, "AUTH=");
638 	smtp_xtext_encode(buffer, str_data(auth_addr), str_len(auth_addr));
639 	str_append_c(buffer, ' ');
640 }
641 
642 static void
smtp_params_mail_write_body(string_t * buffer,enum smtp_capability caps,const struct smtp_params_mail * params)643 smtp_params_mail_write_body(string_t *buffer, enum smtp_capability caps,
644 			    const struct smtp_params_mail *params)
645 {
646 	/* BODY=<type>: RFC 6152 */
647 	/* =7BIT: RFC 6152 */
648 	switch (params->body.type) {
649 	case SMTP_PARAM_MAIL_BODY_TYPE_UNSPECIFIED:
650 		break;
651 	case SMTP_PARAM_MAIL_BODY_TYPE_7BIT:
652 		str_append(buffer, "BODY=7BIT ");
653 		break;
654 	/* =8BITMIME: RFC 6152 */
655 	case SMTP_PARAM_MAIL_BODY_TYPE_8BITMIME:
656 		i_assert((caps & SMTP_CAPABILITY_8BITMIME) != 0);
657 		str_append(buffer, "BODY=8BITMIME ");
658 		break;
659 	/* =BINARYMIME: RFC 3030 */
660 	case SMTP_PARAM_MAIL_BODY_TYPE_BINARYMIME:
661 		i_assert((caps & SMTP_CAPABILITY_BINARYMIME) != 0 &&
662 			 (caps & SMTP_CAPABILITY_CHUNKING) != 0);
663 		str_append(buffer, "BODY=BINARYMIME ");
664 		break;
665 	case SMTP_PARAM_MAIL_BODY_TYPE_EXTENSION:
666 		str_append(buffer, "BODY=");
667 		str_append(buffer, params->body.ext);
668 		str_append_c(buffer, ' ');
669 		break;
670 	default:
671 		i_unreached();
672 	}
673 }
674 
675 static void
smtp_params_mail_write_envid(string_t * buffer,enum smtp_capability caps,const struct smtp_params_mail * params)676 smtp_params_mail_write_envid(string_t *buffer, enum smtp_capability caps,
677 			     const struct smtp_params_mail *params)
678 {
679 	const char *envid = params->envid;
680 
681 	/* ENVID=<envid>: RFC 3461 */
682 
683 	if (envid == NULL)
684 		return;
685 	if ((caps & SMTP_CAPABILITY_DSN) == 0)
686 		return;
687 
688 	str_append(buffer, "ENVID=");
689 	smtp_xtext_encode(buffer, (const unsigned char *)envid, strlen(envid));
690 	str_append_c(buffer, ' ');
691 }
692 
693 static void
smtp_params_mail_write_ret(string_t * buffer,enum smtp_capability caps,const struct smtp_params_mail * params)694 smtp_params_mail_write_ret(string_t *buffer, enum smtp_capability caps,
695 			   const struct smtp_params_mail *params)
696 {
697 	if ((caps & SMTP_CAPABILITY_DSN) == 0)
698 		return;
699 	/* RET=<keyword>: RFC 3461 */
700 	switch (params->ret) {
701 	case SMTP_PARAM_MAIL_RET_UNSPECIFIED:
702 		break;
703 	case SMTP_PARAM_MAIL_RET_HDRS:
704 		str_append(buffer, "RET=HDRS ");
705 		break;
706 	case SMTP_PARAM_MAIL_RET_FULL:
707 		str_append(buffer, "RET=FULL ");
708 		break;
709 	default:
710 		i_unreached();
711 	}
712 }
713 
714 static void
smtp_params_mail_write_size(string_t * buffer,enum smtp_capability caps,const struct smtp_params_mail * params)715 smtp_params_mail_write_size(string_t *buffer, enum smtp_capability caps,
716 			    const struct smtp_params_mail *params)
717 {
718 	/* SIZE=<size-value>: RFC 1870 */
719 
720 	if (params->size == 0)
721 		return;
722 	if ((caps & SMTP_CAPABILITY_SIZE) == 0)
723 		return;
724 
725 	/* proxy the SIZE parameter (account for additional size) */
726 	str_printfa(buffer, "SIZE=%"PRIuUOFF_T" ", params->size);
727 }
728 
smtp_params_mail_write(string_t * buffer,enum smtp_capability caps,const char * const * extra_params,const struct smtp_params_mail * params)729 void smtp_params_mail_write(string_t *buffer, enum smtp_capability caps,
730 			    const char *const *extra_params,
731 			    const struct smtp_params_mail *params)
732 {
733 	size_t init_len = str_len(buffer);
734 
735 	smtp_params_mail_write_auth(buffer, caps, params);
736 	smtp_params_mail_write_body(buffer, caps, params);
737 	smtp_params_mail_write_envid(buffer, caps, params);
738 	smtp_params_mail_write_ret(buffer, caps, params);
739 	smtp_params_mail_write_size(buffer, caps, params);
740 
741 	smtp_params_write(buffer, extra_params, &params->extra_params);
742 
743 	if (str_len(buffer) > init_len)
744 		str_truncate(buffer, str_len(buffer)-1);
745 }
746 
747 /* evaluate */
748 
749 const struct smtp_param *
smtp_params_mail_get_extra(const struct smtp_params_mail * params,const char * keyword)750 smtp_params_mail_get_extra(const struct smtp_params_mail *params,
751 			   const char *keyword)
752 {
753 	return smtp_params_get_param(&params->extra_params, keyword);
754 }
755 
smtp_params_mail_decode_extra(const struct smtp_params_mail * params,const char * keyword,string_t ** value_r,bool allow_nul,const char ** error_r)756 int smtp_params_mail_decode_extra(const struct smtp_params_mail *params,
757 				  const char *keyword, string_t **value_r,
758 				  bool allow_nul, const char **error_r)
759 {
760 	return smtp_params_decode_param(&params->extra_params,
761 					keyword, value_r, allow_nul, error_r);
762 }
763 
764 /* events */
765 
766 static void
smtp_params_mail_add_auth_to_event(const struct smtp_params_mail * params,struct event * event)767 smtp_params_mail_add_auth_to_event(const struct smtp_params_mail *params,
768 				   struct event *event)
769 {
770 	/* AUTH: RFC 4954 */
771 	if (params->auth == NULL)
772 		return;
773 
774 	event_add_str(event, "mail_param_auth",
775 		      smtp_address_encode(params->auth));
776 }
777 
778 static void
smtp_params_mail_add_body_to_event(const struct smtp_params_mail * params,struct event * event)779 smtp_params_mail_add_body_to_event(const struct smtp_params_mail *params,
780 				   struct event *event)
781 {
782 	/* BODY: RFC 6152 */
783 	switch (params->body.type) {
784 	case SMTP_PARAM_MAIL_BODY_TYPE_UNSPECIFIED:
785 		break;
786 	case SMTP_PARAM_MAIL_BODY_TYPE_7BIT:
787 		event_add_str(event, "mail_param_body", "7BIT");
788 		break;
789 	case SMTP_PARAM_MAIL_BODY_TYPE_8BITMIME:
790 		event_add_str(event, "mail_param_body", "8BITMIME");
791 		break;
792 	case SMTP_PARAM_MAIL_BODY_TYPE_BINARYMIME:
793 		event_add_str(event, "mail_param_body", "BINARYMIME");
794 		break;
795 	case SMTP_PARAM_MAIL_BODY_TYPE_EXTENSION:
796 		event_add_str(event, "mail_param_body", params->body.ext);
797 		break;
798 	default:
799 		i_unreached();
800 	}
801 }
802 
803 static void
smtp_params_mail_add_envid_to_event(const struct smtp_params_mail * params,struct event * event)804 smtp_params_mail_add_envid_to_event(const struct smtp_params_mail *params,
805 				    struct event *event)
806 {
807 	/* ENVID: RFC 3461, Section 4.4 */
808 	if (params->envid == NULL)
809 		return;
810 
811 	event_add_str(event, "mail_param_envid", params->envid);
812 }
813 
814 static void
smtp_params_mail_add_ret_to_event(const struct smtp_params_mail * params,struct event * event)815 smtp_params_mail_add_ret_to_event(const struct smtp_params_mail *params,
816 				  struct event *event)
817 {
818 	/* RET: RFC 3461, Section 4.3 */
819 	switch (params->ret) {
820 	case SMTP_PARAM_MAIL_RET_UNSPECIFIED:
821 		break;
822 	case SMTP_PARAM_MAIL_RET_HDRS:
823 		event_add_str(event, "mail_param_ret", "HDRS");
824 		break;
825 	case SMTP_PARAM_MAIL_RET_FULL:
826 		event_add_str(event, "mail_param_ret", "FULL");
827 		break;
828 	default:
829 		i_unreached();
830 	}
831 }
832 
833 static void
smtp_params_mail_add_size_to_event(const struct smtp_params_mail * params,struct event * event)834 smtp_params_mail_add_size_to_event(const struct smtp_params_mail *params,
835 				   struct event *event)
836 {
837 	/* SIZE: RFC 1870 */
838 	if (params->size == 0)
839 		return;
840 
841 	event_add_int(event, "mail_param_size", params->size);
842 }
843 
smtp_params_mail_add_to_event(const struct smtp_params_mail * params,struct event * event)844 void smtp_params_mail_add_to_event(const struct smtp_params_mail *params,
845 				   struct event *event)
846 {
847 	smtp_params_mail_add_auth_to_event(params, event);
848 	smtp_params_mail_add_body_to_event(params, event);
849 	smtp_params_mail_add_envid_to_event(params, event);
850 	smtp_params_mail_add_ret_to_event(params, event);
851 	smtp_params_mail_add_size_to_event(params, event);
852 }
853 
854 /*
855  * RCPT parameters
856  */
857 
858 /* parse */
859 
860 struct smtp_params_rcpt_parser {
861 	pool_t pool;
862 	struct smtp_params_rcpt *params;
863 	enum smtp_param_rcpt_parse_flags flags;
864 	enum smtp_capability caps;
865 
866 	enum smtp_param_parse_error error_code;
867 	const char *error;
868 };
869 
870 static int
smtp_params_rcpt_parse_notify(struct smtp_params_rcpt_parser * prparser,const char * value)871 smtp_params_rcpt_parse_notify(struct smtp_params_rcpt_parser *prparser,
872 			      const char *value)
873 {
874 	struct smtp_params_rcpt *params = prparser->params;
875 	const char *const *list;
876 	bool valid, unsupported;
877 
878 	/* NOTIFY=<type>: RFC 3461
879 
880 	   notify-esmtp-value = "NEVER" / 1#notify-list-element
881 	   notify-list-element = "SUCCESS" / "FAILURE" / "DELAY"
882 
883 	   We check and normalize this parameter.
884 	*/
885 
886 	/* cannot specify this multiple times */
887 	if (params->notify != SMTP_PARAM_RCPT_NOTIFY_UNSPECIFIED) {
888 		prparser->error = "Duplicate NOTIFY= parameter";
889 		prparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
890 		return -1;
891 	}
892 	/* value required */
893 	if (value == NULL) {
894 		prparser->error = "Missing NOTIFY= parameter value";
895 		prparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
896 		return -1;
897 	}
898 
899 	valid = TRUE;
900 	unsupported = FALSE;
901 	list = t_strsplit(value, ","); /* RFC 822, Section 2.7 */
902 	while (*list != NULL) {
903 		if (**list != '\0') {
904 			/* NEVER */
905 			if (strcasecmp(*list, "NEVER") == 0) {
906 				if (params->notify != SMTP_PARAM_RCPT_NOTIFY_UNSPECIFIED)
907 					valid = FALSE;
908 				params->notify = SMTP_PARAM_RCPT_NOTIFY_NEVER;
909 			/* SUCCESS */
910 			} else if (strcasecmp(*list, "SUCCESS") == 0) {
911 				if ((params->notify & SMTP_PARAM_RCPT_NOTIFY_NEVER) != 0)
912 					valid = FALSE;
913 				params->notify |= SMTP_PARAM_RCPT_NOTIFY_SUCCESS;
914 			/* FAILURE */
915 			} else if (strcasecmp(*list, "FAILURE") == 0) {
916 				if ((params->notify & SMTP_PARAM_RCPT_NOTIFY_NEVER) != 0)
917 					valid = FALSE;
918 				params->notify |= SMTP_PARAM_RCPT_NOTIFY_FAILURE;
919 			/* DELAY */
920 			} else if (strcasecmp(*list, "DELAY") == 0) {
921 				if ((params->notify & SMTP_PARAM_RCPT_NOTIFY_NEVER) != 0)
922 					valid = FALSE;
923 				params->notify |= SMTP_PARAM_RCPT_NOTIFY_DELAY;
924 			} else {
925 				unsupported = TRUE;
926 			}
927 		}
928 		list++;
929 	}
930 
931 	if (!valid || unsupported ||
932 	    params->notify == SMTP_PARAM_RCPT_NOTIFY_UNSPECIFIED) {
933 		prparser->error = "Invalid NOTIFY= parameter value";
934 		prparser->error_code = ((valid && unsupported) ?
935 					SMTP_PARAM_PARSE_ERROR_NOT_SUPPORTED :
936 					SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX);
937 		return -1;
938 	}
939 	return 0;
940 }
941 
942 static int
smtp_params_rcpt_parse_orcpt_rfc822(struct smtp_params_rcpt_parser * prparser,const char * addr_str,pool_t pool,const struct smtp_address ** addr_r)943 smtp_params_rcpt_parse_orcpt_rfc822(struct smtp_params_rcpt_parser *prparser,
944 				    const char *addr_str, pool_t pool,
945 				    const struct smtp_address **addr_r)
946 {
947 	struct message_address *rfc822_addr;
948 	struct smtp_address *addr;
949 
950 	rfc822_addr = message_address_parse(pool_datastack_create(),
951 					    (const unsigned char *)addr_str,
952 					    strlen(addr_str), 2, 0);
953 	if (rfc822_addr == NULL || rfc822_addr->next != NULL)
954 		return -1;
955 	if (rfc822_addr->invalid_syntax) {
956 		if (HAS_NO_BITS(prparser->flags,
957 				SMTP_PARAM_RCPT_FLAG_ORCPT_ALLOW_LOCALPART) ||
958 		    rfc822_addr->mailbox == NULL ||
959 		    *rfc822_addr->mailbox == '\0')
960 			return -1;
961 		rfc822_addr->invalid_syntax = FALSE;
962 	}
963 	if (smtp_address_create_from_msg(pool, rfc822_addr, &addr) < 0)
964 		return -1;
965 	*addr_r = addr;
966 	return 0;
967 }
968 
969 static int
smtp_params_rcpt_parse_orcpt(struct smtp_params_rcpt_parser * prparser,const char * value)970 smtp_params_rcpt_parse_orcpt(struct smtp_params_rcpt_parser *prparser,
971 			     const char *value)
972 {
973 	struct smtp_params_rcpt *params = prparser->params;
974 	struct smtp_parser parser;
975 	const unsigned char *p, *pend;
976 	string_t *address;
977 	const char *addr_type;
978 	int ret;
979 
980 	/* ORCPT=<address>: RFC 3461
981 
982 	   orcpt-parameter = "ORCPT=" original-recipient-address
983 	   original-recipient-address = addr-type ";" xtext
984 	   addr-type = atom
985 
986 	   We check and normalize this parameter.
987 	*/
988 
989 	/* cannot specify this multiple times */
990 	if (params->orcpt.addr_type != NULL) {
991 		prparser->error = "Duplicate ORCPT= parameter";
992 		prparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
993 		return -1;
994 	}
995 	/* value required */
996 	if (value == NULL) {
997 		prparser->error = "Missing ORCPT= parameter value";
998 		prparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
999 		return -1;
1000 	}
1001 
1002 	/* check addr-type */
1003 	smtp_parser_init(&parser, pool_datastack_create(), value);
1004 	if (smtp_parser_parse_atom(&parser, &addr_type) <= 0 ||
1005 	    parser.cur >= parser.end || *parser.cur != ';') {
1006 		prparser->error = "Invalid addr-type for ORCPT= parameter";
1007 		prparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
1008 		return -1;
1009 	}
1010 	params->orcpt.addr_type = p_strdup(prparser->pool, addr_type);
1011 	parser.cur++;
1012 
1013 	/* check xtext */
1014 	address = t_str_new(256);
1015 	if ((ret=smtp_parser_parse_xtext(&parser, address)) <= 0 ||
1016 		parser.cur < parser.end) {
1017 		if (ret < 0) {
1018 			prparser->error = t_strdup_printf(
1019 				"Invalid ORCPT= parameter: %s",
1020 				parser.error);
1021 			prparser->error_code =
1022 				SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
1023 		} else if (parser.cur < parser.end) {
1024 			prparser->error = "Invalid ORCPT= parameter: "
1025 				"Invalid character in xtext";
1026 			prparser->error_code =
1027 				SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
1028 		} else {
1029 			prparser->error = "Invalid ORCPT= parameter: "
1030 				"Empty address value";
1031 			prparser->error_code =
1032 				SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
1033 		}
1034 		return -1;
1035 	}
1036 
1037 	/* RFC 3461, Section 4.2:
1038 
1039 	   Due to limitations in the Delivery Status Notification format, the
1040 	   value of the original recipient address prior to encoding as "xtext"
1041 	   MUST consist entirely of printable (graphic and white space)
1042 	   characters from the US-ASCII repertoire.
1043 	 */
1044 	p = str_data(address);
1045 	pend = p + str_len(address);
1046 	while (p < pend && smtp_char_is_textstr(*p))
1047 		p++;
1048 	if (p < pend) {
1049 		prparser->error =
1050 			"Invalid ORCPT= address value: "
1051 			"Contains non-printable characters";
1052 		prparser->error_code = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
1053 		return -1;
1054 	}
1055 
1056 	params->orcpt.addr_raw = p_strdup(prparser->pool, str_c(address));
1057 
1058 	if (strcasecmp(params->orcpt.addr_type, "rfc822") == 0) {
1059 		if (smtp_params_rcpt_parse_orcpt_rfc822(
1060 			prparser, params->orcpt.addr_raw, prparser->pool,
1061 			&params->orcpt.addr) < 0) {
1062 			prparser->error = "Invalid ORCPT= address value: "
1063 				"Invalid RFC822 address";
1064 			prparser->error_code =
1065 				SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
1066 			return -1;
1067 		}
1068 	}
1069 	return 0;
1070 }
1071 
smtp_params_rcpt_parse(pool_t pool,const char * args,enum smtp_param_rcpt_parse_flags flags,enum smtp_capability caps,const char * const * extensions,struct smtp_params_rcpt * params_r,enum smtp_param_parse_error * error_code_r,const char ** error_r)1072 int smtp_params_rcpt_parse(pool_t pool, const char *args,
1073 			   enum smtp_param_rcpt_parse_flags flags,
1074 			   enum smtp_capability caps,
1075 			   const char *const *extensions,
1076 			   struct smtp_params_rcpt *params_r,
1077 			   enum smtp_param_parse_error *error_code_r,
1078 			   const char **error_r)
1079 {
1080 	struct smtp_params_rcpt_parser prparser;
1081 	struct smtp_param param;
1082 	const char *const *argv;
1083 	const char *error;
1084 	int ret = 0;
1085 
1086 	i_zero(params_r);
1087 
1088 	i_zero(&prparser);
1089 	prparser.pool = pool;
1090 	prparser.params = params_r;
1091 	prparser.flags = flags;
1092 	prparser.caps = caps;
1093 
1094 	argv = t_strsplit(args, " ");
1095 	for (; *argv != NULL; argv++) {
1096 		if (smtp_param_parse(pool_datastack_create(), *argv,
1097 				     &param, &error) < 0) {
1098 			*error_r = t_strdup_printf(
1099 				"Invalid RCPT parameter: %s", error);
1100 			*error_code_r = SMTP_PARAM_PARSE_ERROR_BAD_SYNTAX;
1101 			return -1;
1102 		}
1103 
1104 		/* parse known parameters */
1105 		param.keyword = t_str_ucase(param.keyword);
1106 		if ((caps & SMTP_CAPABILITY_DSN) != 0 &&
1107 		     strcmp(param.keyword, "NOTIFY") == 0) {
1108 			if (smtp_params_rcpt_parse_notify
1109 				(&prparser, param.value) < 0) {
1110 				ret = -1;
1111 				break;
1112 			}
1113 		} else if (((caps & SMTP_CAPABILITY_DSN) != 0 ||
1114 			    (caps & SMTP_CAPABILITY__ORCPT) != 0) &&
1115 			   strcmp(param.keyword, "ORCPT") == 0) {
1116 			if (smtp_params_rcpt_parse_orcpt
1117 				(&prparser, param.value) < 0) {
1118 				ret = -1;
1119 				break;
1120 			}
1121 		} else if (extensions != NULL &&
1122 			   str_array_icase_find(extensions, param.keyword)) {
1123 			/* add the rest to ext_param for specific applications
1124 			 */
1125 			smtp_params_rcpt_add_extra(params_r, pool,
1126 						   param.keyword, param.value);
1127 		} else {
1128 			/* RFC 5321, Section 4.1.1.11:
1129 			   If the server SMTP does not recognize or cannot
1130 			   implement one or more of the parameters associated
1131 			   with a particular MAIL FROM or RCPT TO command, it
1132 			   will return code 555. */
1133 			*error_r = "Unsupported parameters";
1134 			*error_code_r = SMTP_PARAM_PARSE_ERROR_NOT_SUPPORTED;
1135 			return -1;
1136 		}
1137 	}
1138 
1139 	if (ret < 0) {
1140 		*error_r = prparser.error;
1141 		*error_code_r = prparser.error_code;
1142 	}
1143 	return ret;
1144 }
1145 
1146 /* manipulate */
1147 
smtp_params_rcpt_copy(pool_t pool,struct smtp_params_rcpt * dst,const struct smtp_params_rcpt * src)1148 void smtp_params_rcpt_copy(pool_t pool, struct smtp_params_rcpt *dst,
1149 			   const struct smtp_params_rcpt *src)
1150 {
1151 	i_zero(dst);
1152 
1153 	if (src == NULL)
1154 		return;
1155 
1156 	dst->notify = src->notify;
1157 	dst->orcpt.addr_type = p_strdup(pool, src->orcpt.addr_type);
1158 	dst->orcpt.addr_raw = p_strdup(pool, src->orcpt.addr_raw);
1159 	dst->orcpt.addr = smtp_address_clone(pool, src->orcpt.addr);
1160 
1161 	smtp_params_copy(pool, &dst->extra_params, &src->extra_params);
1162 }
1163 
smtp_params_rcpt_add_extra(struct smtp_params_rcpt * params,pool_t pool,const char * keyword,const char * value)1164 void smtp_params_rcpt_add_extra(struct smtp_params_rcpt *params, pool_t pool,
1165 				const char *keyword, const char *value)
1166 {
1167 	smtp_params_add_one(&params->extra_params, pool, keyword, value);
1168 }
1169 
smtp_params_rcpt_encode_extra(struct smtp_params_rcpt * params,pool_t pool,const char * keyword,const unsigned char * value,size_t value_len)1170 void smtp_params_rcpt_encode_extra(struct smtp_params_rcpt *params, pool_t pool,
1171 				   const char *keyword,
1172 				   const unsigned char *value,
1173 				   size_t value_len)
1174 {
1175 	smtp_params_add_encoded(&params->extra_params, pool,
1176 				keyword, value, value_len);
1177 }
1178 
smtp_params_rcpt_drop_extra(struct smtp_params_rcpt * params,const char * keyword,const char ** value_r)1179 bool smtp_params_rcpt_drop_extra(struct smtp_params_rcpt *params,
1180 				 const char *keyword, const char **value_r)
1181 {
1182 	return smtp_params_drop_one(&params->extra_params, keyword, value_r);
1183 }
1184 
smtp_params_rcpt_set_orcpt(struct smtp_params_rcpt * params,pool_t pool,struct smtp_address * rcpt)1185 void smtp_params_rcpt_set_orcpt(struct smtp_params_rcpt *params, pool_t pool,
1186 				struct smtp_address *rcpt)
1187 {
1188 	params->orcpt.addr_type = "rfc822";
1189 	params->orcpt.addr = smtp_address_clone(pool, rcpt);
1190 	params->orcpt.addr_raw = p_strdup(pool, smtp_address_encode(rcpt));
1191 }
1192 
1193 /* write */
1194 
1195 static void
smtp_params_rcpt_write_notify(string_t * buffer,enum smtp_capability caps,const struct smtp_params_rcpt * params)1196 smtp_params_rcpt_write_notify(string_t *buffer, enum smtp_capability caps,
1197 			      const struct smtp_params_rcpt *params)
1198 {
1199 	if (params->notify == SMTP_PARAM_RCPT_NOTIFY_UNSPECIFIED)
1200 		return;
1201 	if ((caps & SMTP_CAPABILITY_DSN) == 0)
1202 		return;
1203 
1204 	/* NOTIFY=<type>: RFC 3461
1205 
1206 	   notify-esmtp-value = "NEVER" / 1#notify-list-element
1207 	   notify-list-element = "SUCCESS" / "FAILURE" / "DELAY"
1208 	*/
1209 
1210 	str_append(buffer, "NOTIFY=");
1211 	if ((params->notify & SMTP_PARAM_RCPT_NOTIFY_NEVER) != 0) {
1212 		i_assert(params->notify == SMTP_PARAM_RCPT_NOTIFY_NEVER);
1213 		str_append(buffer, "NEVER");
1214 	} else {
1215 		bool comma = FALSE;
1216 		if ((params->notify & SMTP_PARAM_RCPT_NOTIFY_SUCCESS) != 0) {
1217 			str_append(buffer, "SUCCESS");
1218 			comma = TRUE;
1219 		}
1220 		if ((params->notify & SMTP_PARAM_RCPT_NOTIFY_FAILURE) != 0) {
1221 			if (comma)
1222 				str_append_c(buffer, ',');
1223 			str_append(buffer, "FAILURE");
1224 			comma = TRUE;
1225 		}
1226 		if ((params->notify & SMTP_PARAM_RCPT_NOTIFY_DELAY) != 0) {
1227 			if (comma)
1228 				str_append_c(buffer, ',');
1229 			str_append(buffer, "DELAY");
1230 		}
1231 	}
1232 	str_append_c(buffer, ' ');
1233 }
1234 
1235 static void
smtp_params_rcpt_write_orcpt(string_t * buffer,enum smtp_capability caps,const struct smtp_params_rcpt * params)1236 smtp_params_rcpt_write_orcpt(string_t *buffer, enum smtp_capability caps,
1237 			     const struct smtp_params_rcpt *params)
1238 {
1239 	if (!smtp_params_rcpt_has_orcpt(params))
1240 		return;
1241 	if ((caps & SMTP_CAPABILITY_DSN) == 0 &&
1242 	    (caps & SMTP_CAPABILITY__ORCPT) == 0)
1243 		return;
1244 
1245 	/* ORCPT=<address>: RFC 3461 */
1246 
1247 	str_printfa(buffer, "ORCPT=%s;", params->orcpt.addr_type);
1248 	if (strcasecmp(params->orcpt.addr_type, "rfc822") == 0) {
1249 		smtp_xtext_encode_cstr(
1250 			buffer, smtp_address_encode(params->orcpt.addr));
1251 	} else {
1252 		i_assert(params->orcpt.addr_raw != NULL);
1253 		smtp_xtext_encode_cstr(buffer, params->orcpt.addr_raw);
1254 	}
1255 	str_append_c(buffer, ' ');
1256 }
1257 
smtp_params_rcpt_write(string_t * buffer,enum smtp_capability caps,const char * const * extra_params,const struct smtp_params_rcpt * params)1258 void smtp_params_rcpt_write(string_t *buffer, enum smtp_capability caps,
1259 			    const char *const *extra_params,
1260 			    const struct smtp_params_rcpt *params)
1261 {
1262 	size_t init_len = str_len(buffer);
1263 
1264 	smtp_params_rcpt_write_notify(buffer, caps, params);
1265 	smtp_params_rcpt_write_orcpt(buffer, caps, params);
1266 
1267 	smtp_params_write(buffer, extra_params, &params->extra_params);
1268 
1269 	if (str_len(buffer) > init_len)
1270 		str_truncate(buffer, str_len(buffer)-1);
1271 }
1272 
1273 /* evaluate */
1274 
1275 const struct smtp_param *
smtp_params_rcpt_get_extra(const struct smtp_params_rcpt * params,const char * keyword)1276 smtp_params_rcpt_get_extra(const struct smtp_params_rcpt *params,
1277 			   const char *keyword)
1278 {
1279 	return smtp_params_get_param(&params->extra_params, keyword);
1280 }
1281 
smtp_params_rcpt_decode_extra(const struct smtp_params_rcpt * params,const char * keyword,string_t ** value_r,bool allow_nul,const char ** error_r)1282 int smtp_params_rcpt_decode_extra(const struct smtp_params_rcpt *params,
1283 				  const char *keyword, string_t **value_r,
1284 				  bool allow_nul, const char **error_r)
1285 {
1286 	return smtp_params_decode_param(&params->extra_params,
1287 					keyword, value_r, allow_nul, error_r);
1288 }
1289 
smtp_params_rcpt_equal(const struct smtp_params_rcpt * params1,const struct smtp_params_rcpt * params2)1290 bool smtp_params_rcpt_equal(const struct smtp_params_rcpt *params1,
1291 			    const struct smtp_params_rcpt *params2)
1292 {
1293 	if (params1 == NULL || params2 == NULL)
1294 		return (params1 == params2);
1295 
1296 	/* NOTIFY: RFC 3461, Section 4.1 */
1297 	if (params1->notify != params2->notify)
1298 		return FALSE;
1299 
1300 	/* ORCPT: RFC 3461, Section 4.2 */
1301 	if (null_strcasecmp(params1->orcpt.addr_type,
1302 			    params2->orcpt.addr_type) != 0)
1303 		return FALSE;
1304 	if (null_strcasecmp(params1->orcpt.addr_type, "rfc822") == 0) {
1305 		if (!smtp_address_equals(params1->orcpt.addr,
1306 					 params2->orcpt.addr))
1307 			return FALSE;
1308 	} else {
1309 		if (null_strcmp(params1->orcpt.addr_raw,
1310 				params2->orcpt.addr_raw) != 0)
1311 			return FALSE;
1312 	}
1313 
1314 	/* extra parameters */
1315 	return smtp_params_equal(&params1->extra_params,
1316 				 &params2->extra_params);
1317 }
1318 
1319 /* events */
1320 
1321 static void
smtp_params_rcpt_add_notify_to_event(const struct smtp_params_rcpt * params,struct event * event)1322 smtp_params_rcpt_add_notify_to_event(const struct smtp_params_rcpt *params,
1323 				     struct event *event)
1324 {
1325 	/* NOTIFY: RFC 3461, Section 4.1 */
1326 	if (params->notify == SMTP_PARAM_RCPT_NOTIFY_UNSPECIFIED)
1327 		return;
1328 	if ((params->notify & SMTP_PARAM_RCPT_NOTIFY_NEVER) != 0) {
1329 		i_assert(params->notify ==
1330 			 SMTP_PARAM_RCPT_NOTIFY_NEVER);
1331 		event_add_str(event, "rcpt_param_notify", "NEVER");
1332 	} else {
1333 		string_t *str = t_str_new(32);
1334 		if ((params->notify & SMTP_PARAM_RCPT_NOTIFY_SUCCESS) != 0)
1335 			str_append(str, "SUCCESS");
1336 		if ((params->notify & SMTP_PARAM_RCPT_NOTIFY_FAILURE) != 0) {
1337 			if (str_len(str) > 0)
1338 				str_append_c(str, ',');
1339 			str_append(str, "FAILURE");
1340 		}
1341 		if ((params->notify & SMTP_PARAM_RCPT_NOTIFY_DELAY) != 0) {
1342 			if (str_len(str) > 0)
1343 				str_append_c(str, ',');
1344 			str_append(str, "DELAY");
1345 		}
1346 		event_add_str(event, "rcpt_param_notify", str_c(str));
1347 	}
1348 }
1349 
1350 static void
smtp_params_rcpt_add_orcpt_to_event(const struct smtp_params_rcpt * params,struct event * event)1351 smtp_params_rcpt_add_orcpt_to_event(const struct smtp_params_rcpt *params,
1352 				    struct event *event)
1353 {
1354 	/* ORCPT: RFC 3461, Section 4.2 */
1355 	if (params->orcpt.addr_type == NULL)
1356 		return;
1357 
1358 	event_add_str(event, "rcpt_param_orcpt_type",
1359 		      params->orcpt.addr_type);
1360 	if (strcasecmp(params->orcpt.addr_type, "rfc822") == 0) {
1361 		event_add_str(event, "rcpt_param_orcpt",
1362 			      smtp_address_encode(params->orcpt.addr));
1363 	} else {
1364 		i_assert(params->orcpt.addr_raw != NULL);
1365 		event_add_str(event, "rcpt_param_orcpt",
1366 			      params->orcpt.addr_raw);
1367 	}
1368 }
1369 
smtp_params_rcpt_add_to_event(const struct smtp_params_rcpt * params,struct event * event)1370 void smtp_params_rcpt_add_to_event(const struct smtp_params_rcpt *params,
1371 				   struct event *event)
1372 {
1373 	smtp_params_rcpt_add_notify_to_event(params, event);
1374 	smtp_params_rcpt_add_orcpt_to_event(params, event);
1375 }
1376