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, ¶m_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(¶m);
122 param.keyword = p_strdup(pool, keyword);
123 param.value = p_strdup(pool, value);
124 array_push_back(params, ¶m);
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, ¶ms->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 ¶m, &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(¶ms->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(¶ms->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(¶ms->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, ¶ms->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(¶ms->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(¶ms->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 ¶ms->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 ¶m, &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(¶ms->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(¶ms->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(¶ms->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, ¶ms->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(¶ms->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(¶ms->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(¶ms1->extra_params,
1316 ¶ms2->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