Coverage Report

Created: 2025-07-23 06:46

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