Coverage Report

Created: 2025-07-11 06:59

/src/dovecot/src/lib-mail/message-address.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "llist.h"
5
#include "str.h"
6
#include "strescape.h"
7
#include "smtp-address.h"
8
#include "message-parser.h"
9
#include "message-address.h"
10
#include "rfc822-parser.h"
11
12
struct message_address_parser_context {
13
  pool_t pool;
14
  struct rfc822_parser_context parser;
15
16
  struct message_address addr;
17
  struct message_address_list addr_list;
18
  string_t *str;
19
20
  bool fill_missing, non_strict_dots;
21
};
22
23
static void add_address(struct message_address_parser_context *ctx)
24
0
{
25
0
  struct message_address *addr;
26
27
0
  addr = p_new(ctx->pool, struct message_address, 1);
28
29
0
  memcpy(addr, &ctx->addr, sizeof(ctx->addr));
30
0
  i_zero(&ctx->addr);
31
32
0
  DLLIST2_APPEND(&ctx->addr_list.head, &ctx->addr_list.tail, addr);
33
0
}
34
35
/* quote with "" and escape all '\', '"' and "'" characters if need */
36
static void str_append_maybe_escape(string_t *dest, const char *cstr, bool escape_dot)
37
0
{
38
0
  const char *p;
39
40
  /* see if we need to quote it */
41
0
  for (p = cstr; *p != '\0'; p++) {
42
0
    if (!IS_ATEXT(*p) && (escape_dot || *p != '.'))
43
0
      break;
44
0
  }
45
46
0
  if (*p == '\0') {
47
0
    str_append_data(dest, cstr, (size_t) (p - cstr));
48
0
    return;
49
0
  }
50
51
  /* see if we need to escape it */
52
0
  for (p = cstr; *p != '\0'; p++) {
53
0
    if (IS_ESCAPED_CHAR(*p))
54
0
      break;
55
0
  }
56
57
0
  if (*p == '\0') {
58
    /* only quote */
59
0
    str_append_c(dest, '"');
60
0
    str_append_data(dest, cstr, (size_t) (p - cstr));
61
0
    str_append_c(dest, '"');
62
0
    return;
63
0
  }
64
65
  /* quote and escape */
66
0
  str_append_c(dest, '"');
67
0
  str_append_data(dest, cstr, (size_t) (p - cstr));
68
69
0
  for (; *p != '\0'; p++) {
70
0
    if (IS_ESCAPED_CHAR(*p))
71
0
      str_append_c(dest, '\\');
72
0
    str_append_c(dest, *p);
73
0
  }
74
75
0
  str_append_c(dest, '"');
76
0
}
77
78
static int
79
parse_nonstrict_dot_atom(struct rfc822_parser_context *ctx, string_t *str)
80
0
{
81
0
  int ret = -1;
82
83
0
  do {
84
0
    while (*ctx->data == '.') {
85
0
      str_append_c(str, '.');
86
0
      ctx->data++;
87
0
      if (ctx->data == ctx->end) {
88
        /* @domain is missing, but local-part
89
           parsing was successful */
90
0
        return 0;
91
0
      }
92
0
      ret = 1;
93
0
    }
94
0
    if (*ctx->data == '@')
95
0
      break;
96
0
    ret = rfc822_parse_atom(ctx, str);
97
0
  } while (ret > 0 && *ctx->data == '.');
98
0
  return ret;
99
0
}
100
101
static int parse_local_part(struct message_address_parser_context *ctx)
102
0
{
103
0
  int ret;
104
105
  /*
106
     local-part      = dot-atom / quoted-string / obs-local-part
107
     obs-local-part  = word *("." word)
108
  */
109
0
  i_assert(ctx->parser.data < ctx->parser.end);
110
111
0
  str_truncate(ctx->str, 0);
112
0
  if (*ctx->parser.data == '"')
113
0
    ret = rfc822_parse_quoted_string(&ctx->parser, ctx->str);
114
0
  else if (!ctx->non_strict_dots)
115
0
    ret = rfc822_parse_dot_atom(&ctx->parser, ctx->str);
116
0
  else
117
0
    ret = parse_nonstrict_dot_atom(&ctx->parser, ctx->str);
118
0
  if (ret < 0)
119
0
    return -1;
120
121
0
  ctx->addr.mailbox = p_strdup(ctx->pool, str_c(ctx->str));
122
0
  return ret;
123
0
}
124
125
static int parse_domain(struct message_address_parser_context *ctx)
126
0
{
127
0
  int ret;
128
129
0
  str_truncate(ctx->str, 0);
130
0
  if ((ret = rfc822_parse_domain(&ctx->parser, ctx->str)) < 0)
131
0
    return -1;
132
133
0
  ctx->addr.domain = p_strdup(ctx->pool, str_c(ctx->str));
134
0
  return ret;
135
0
}
136
137
static int parse_domain_list(struct message_address_parser_context *ctx)
138
0
{
139
0
  int ret;
140
141
  /* obs-domain-list = "@" domain *(*(CFWS / "," ) [CFWS] "@" domain) */
142
0
  str_truncate(ctx->str, 0);
143
0
  for (;;) {
144
0
    if (ctx->parser.data >= ctx->parser.end)
145
0
      return 0;
146
147
0
    if (*ctx->parser.data != '@')
148
0
      break;
149
150
0
    if (str_len(ctx->str) > 0)
151
0
      str_append_c(ctx->str, ',');
152
153
0
    str_append_c(ctx->str, '@');
154
0
    if ((ret = rfc822_parse_domain(&ctx->parser, ctx->str)) <= 0)
155
0
      return ret;
156
157
0
    while (rfc822_skip_lwsp(&ctx->parser) > 0 &&
158
0
           *ctx->parser.data == ',')
159
0
      ctx->parser.data++;
160
0
  }
161
0
  ctx->addr.route = p_strdup(ctx->pool, str_c(ctx->str));
162
0
  return 1;
163
0
}
164
165
static int parse_angle_addr(struct message_address_parser_context *ctx,
166
          bool parsing_path)
167
0
{
168
  /* "<" [ "@" route ":" ] local-part "@" domain ">" */
169
0
  i_assert(*ctx->parser.data == '<');
170
0
  ctx->parser.data++;
171
172
0
  if (rfc822_skip_lwsp(&ctx->parser) <= 0)
173
0
    return -1;
174
175
0
  if (*ctx->parser.data == '@') {
176
0
    if (parse_domain_list(ctx) > 0 && *ctx->parser.data == ':') {
177
0
      ctx->parser.data++;
178
0
    } else if (parsing_path && (ctx->parser.data >= ctx->parser.end || *ctx->parser.data != ':')) {
179
0
      return -1;
180
0
    } else {
181
0
      if (ctx->fill_missing)
182
0
        ctx->addr.route = "INVALID_ROUTE";
183
0
      if (ctx->parser.data >= ctx->parser.end)
184
0
        return -1;
185
      /* try to continue anyway */
186
0
    }
187
0
    if (rfc822_skip_lwsp(&ctx->parser) <= 0)
188
0
      return -1;
189
0
  }
190
191
0
  if (*ctx->parser.data == '>') {
192
    /* <> address isn't valid */
193
0
  } else {
194
0
    if (parse_local_part(ctx) <= 0)
195
0
      return -1;
196
0
    if (*ctx->parser.data == '@') {
197
0
      if (parse_domain(ctx) <= 0)
198
0
        return -1;
199
0
    }
200
0
  }
201
202
0
  if (*ctx->parser.data != '>')
203
0
    return -1;
204
0
  ctx->parser.data++;
205
206
0
  return rfc822_skip_lwsp(&ctx->parser);
207
0
}
208
209
static int parse_name_addr(struct message_address_parser_context *ctx)
210
0
{
211
  /*
212
     name-addr       = [display-name] angle-addr
213
     display-name    = phrase
214
  */
215
0
  str_truncate(ctx->str, 0);
216
0
  if (rfc822_parse_phrase(&ctx->parser, ctx->str) <= 0 ||
217
0
      *ctx->parser.data != '<')
218
0
    return -1;
219
220
0
  ctx->addr.name = p_strdup(ctx->pool, str_c(ctx->str));
221
0
  if (*ctx->addr.name == '\0') {
222
    /* Cope with "<address>" without display name */
223
0
    ctx->addr.name = NULL;
224
0
  }
225
0
  if (parse_angle_addr(ctx, FALSE) < 0) {
226
    /* broken */
227
0
    if (ctx->fill_missing)
228
0
      ctx->addr.domain = "SYNTAX_ERROR";
229
0
    ctx->addr.invalid_syntax = TRUE;
230
0
  }
231
0
  return ctx->parser.data < ctx->parser.end ? 1 : 0;
232
0
}
233
234
static int parse_addr_spec(struct message_address_parser_context *ctx)
235
0
{
236
  /* addr-spec       = local-part "@" domain */
237
0
  int ret, ret2 = -2;
238
239
0
  i_assert(ctx->parser.data < ctx->parser.end);
240
241
0
  str_truncate(ctx->parser.last_comment, 0);
242
243
0
  bool quoted_string = *ctx->parser.data == '"';
244
0
  ret = parse_local_part(ctx);
245
0
  if (ret <= 0) {
246
    /* end of input or parsing local-part failed */
247
0
    ctx->addr.invalid_syntax = TRUE;
248
0
  }
249
0
  if (ret != 0 && ctx->parser.data < ctx->parser.end &&
250
0
      *ctx->parser.data == '@') {
251
0
    ret2 = parse_domain(ctx);
252
0
    if (ret2 <= 0)
253
0
      ret = ret2;
254
0
  }
255
256
0
  if (str_len(ctx->parser.last_comment) > 0)
257
0
    ctx->addr.name = p_strdup(ctx->pool, str_c(ctx->parser.last_comment));
258
0
  else if (ret2 == -2) {
259
    /* So far we've read user without @domain and without
260
       (Display Name). We'll assume that a single "user" (already
261
       read into addr.mailbox) is a mailbox, but if it's followed
262
       by anything else it's a display-name. */
263
0
    str_append_c(ctx->str, ' ');
264
0
    size_t orig_str_len = str_len(ctx->str);
265
0
    (void)rfc822_parse_phrase(&ctx->parser, ctx->str);
266
0
    if (str_len(ctx->str) != orig_str_len) {
267
0
      ctx->addr.mailbox = NULL;
268
0
      ctx->addr.name = p_strdup(ctx->pool, str_c(ctx->str));
269
0
    } else {
270
0
      if (!quoted_string)
271
0
        ctx->addr.domain = "";
272
0
    }
273
0
    ctx->addr.invalid_syntax = TRUE;
274
0
    ret = -1;
275
0
  }
276
0
  return ret;
277
0
}
278
279
static void add_fixed_address(struct message_address_parser_context *ctx)
280
0
{
281
0
  if (ctx->addr.mailbox == NULL) {
282
0
    ctx->addr.mailbox = !ctx->fill_missing ? "" : "MISSING_MAILBOX";
283
0
    ctx->addr.invalid_syntax = TRUE;
284
0
  }
285
0
  if (ctx->addr.domain == NULL || ctx->addr.domain[0] == '\0') {
286
0
    ctx->addr.domain = !ctx->fill_missing ? "" : "MISSING_DOMAIN";
287
0
    ctx->addr.invalid_syntax = TRUE;
288
0
  }
289
0
  add_address(ctx);
290
0
}
291
292
static int parse_mailbox(struct message_address_parser_context *ctx)
293
0
{
294
0
  const unsigned char *start;
295
0
  int ret;
296
297
  /* mailbox         = name-addr / addr-spec */
298
0
  start = ctx->parser.data;
299
0
  if ((ret = parse_name_addr(ctx)) < 0) {
300
    /* nope, should be addr-spec */
301
0
    ctx->parser.data = start;
302
0
    ret = parse_addr_spec(ctx);
303
0
    if (ctx->addr.invalid_syntax && ctx->addr.name == NULL &&
304
0
        ctx->addr.mailbox != NULL && ctx->addr.domain == NULL) {
305
0
      ctx->addr.name = ctx->addr.mailbox;
306
0
      ctx->addr.mailbox = NULL;
307
0
    }
308
0
  }
309
310
0
  if (ret < 0)
311
0
    ctx->addr.invalid_syntax = TRUE;
312
0
  add_fixed_address(ctx);
313
0
  return ret;
314
0
}
315
316
static int parse_group(struct message_address_parser_context *ctx)
317
0
{
318
0
  int ret;
319
320
  /*
321
     group           = display-name ":" [mailbox-list / CFWS] ";" [CFWS]
322
     display-name    = phrase
323
  */
324
0
  str_truncate(ctx->str, 0);
325
0
  if (rfc822_parse_phrase(&ctx->parser, ctx->str) <= 0 ||
326
0
      *ctx->parser.data != ':')
327
0
    return -1;
328
329
  /* from now on don't return -1 even if there are problems, so that
330
     the caller knows this is a group */
331
0
  ctx->parser.data++;
332
0
  if ((ret = rfc822_skip_lwsp(&ctx->parser)) <= 0)
333
0
    ctx->addr.invalid_syntax = TRUE;
334
335
0
  ctx->addr.mailbox = p_strdup(ctx->pool, str_c(ctx->str));
336
0
  add_address(ctx);
337
338
0
  if (ret > 0 && *ctx->parser.data != ';') {
339
0
    for (;;) {
340
      /* mailbox-list    =
341
          (mailbox *("," mailbox)) / obs-mbox-list */
342
0
      if (parse_mailbox(ctx) <= 0) {
343
        /* broken mailbox - try to continue anyway. */
344
0
      }
345
0
      if (ctx->parser.data >= ctx->parser.end ||
346
0
          *ctx->parser.data != ',')
347
0
        break;
348
0
      ctx->parser.data++;
349
0
      if (rfc822_skip_lwsp(&ctx->parser) <= 0) {
350
0
        ret = -1;
351
0
        break;
352
0
      }
353
0
    }
354
0
  }
355
0
  if (ret >= 0) {
356
0
    if (ctx->parser.data >= ctx->parser.end ||
357
0
        *ctx->parser.data != ';')
358
0
      ret = -1;
359
0
    else {
360
0
      ctx->parser.data++;
361
0
      ret = rfc822_skip_lwsp(&ctx->parser);
362
0
    }
363
0
  }
364
0
  if (ret < 0)
365
0
    ctx->addr.invalid_syntax = TRUE;
366
367
0
  add_address(ctx);
368
0
  return ret == 0 ? 0 : 1;
369
0
}
370
371
static int parse_address(struct message_address_parser_context *ctx)
372
0
{
373
0
  const unsigned char *start;
374
0
  int ret;
375
376
  /* address         = mailbox / group */
377
0
  start = ctx->parser.data;
378
0
  if ((ret = parse_group(ctx)) < 0) {
379
    /* not a group, try mailbox */
380
0
    ctx->parser.data = start;
381
0
    ret = parse_mailbox(ctx);
382
0
  }
383
0
  return ret;
384
0
}
385
386
static int parse_address_list(struct message_address_parser_context *ctx,
387
            unsigned int max_addresses)
388
0
{
389
0
  int ret = 0;
390
391
  /* address-list    = (address *("," address)) / obs-addr-list */
392
0
  while (max_addresses > 0) {
393
0
    max_addresses--;
394
0
    if ((ret = parse_address(ctx)) == 0)
395
0
      break;
396
0
    if (ctx->parser.data >= ctx->parser.end ||
397
0
        *ctx->parser.data != ',') {
398
0
      ret = -1;
399
0
      break;
400
0
    }
401
0
    ctx->parser.data++;
402
0
    if ((ret = rfc822_skip_lwsp(&ctx->parser)) <= 0) {
403
0
      if (ret < 0) {
404
        /* ends with some garbage */
405
0
        add_fixed_address(ctx);
406
0
      }
407
0
      break;
408
0
    }
409
0
  }
410
0
  return ret;
411
0
}
412
413
static int parse_path(struct message_address_parser_context *ctx)
414
0
{
415
0
  int ret;
416
417
0
  if (rfc822_skip_lwsp(&ctx->parser) <= 0)
418
0
    return -1;
419
0
  if (*ctx->parser.data != '<') {
420
    /* Cope with paths that omit < and >. This is a syntax
421
       violation, but we allow it to account for a rather wide
422
       selection of software that does not follow the standards.
423
     */
424
0
    if ((ret=parse_local_part(ctx)) > 0 &&
425
0
        *ctx->parser.data == '@') {
426
0
      ret = parse_domain(ctx);
427
0
    }
428
0
  } else {
429
0
    ret = parse_angle_addr(ctx, TRUE);
430
0
  }
431
0
  if (ret < 0 || (ret=rfc822_skip_lwsp(&ctx->parser)) < 0 ||
432
0
      ctx->parser.data != ctx->parser.end ||
433
0
      (ctx->addr.mailbox != NULL &&
434
0
       (ctx->addr.domain == NULL || *ctx->addr.domain == '\0')) ||
435
0
      (ctx->addr.mailbox == NULL && ctx->addr.domain != NULL)) {
436
0
    ctx->addr.invalid_syntax = TRUE;
437
0
    ret = -1;
438
0
  }
439
0
  add_address(ctx);
440
0
  return ret;
441
0
}
442
443
static void
444
message_address_parse_real(pool_t pool, const unsigned char *data, size_t size,
445
         unsigned int max_addresses,
446
         enum message_address_parse_flags flags,
447
         struct message_address_list *list_r)
448
0
{
449
0
  struct message_address_parser_context ctx;
450
451
0
  i_zero(&ctx);
452
453
0
  rfc822_parser_init(&ctx.parser, data, size, t_str_new(128));
454
0
  ctx.parser.nul_replacement_str = RFC822_NUL_REPLACEMENT_STR;
455
0
  ctx.pool = pool;
456
0
  ctx.str = str_new(default_pool, 128);
457
0
  ctx.fill_missing = (flags & MESSAGE_ADDRESS_PARSE_FLAG_FILL_MISSING) != 0;
458
0
  ctx.non_strict_dots = (flags & MESSAGE_ADDRESS_PARSE_FLAG_STRICT_DOTS) == 0;
459
460
0
  if (rfc822_skip_lwsp(&ctx.parser) <= 0) {
461
    /* no addresses */
462
0
  } else {
463
0
    (void)parse_address_list(&ctx, max_addresses);
464
0
  }
465
0
  rfc822_parser_deinit(&ctx.parser);
466
0
  str_free(&ctx.str);
467
0
  *list_r = ctx.addr_list;
468
0
}
469
470
static int
471
message_address_parse_path_real(pool_t pool, const unsigned char *data,
472
        size_t size, struct message_address **addr_r)
473
0
{
474
0
  struct message_address_parser_context ctx;
475
0
  int ret;
476
477
0
  i_zero(&ctx);
478
0
  *addr_r = NULL;
479
480
0
  rfc822_parser_init(&ctx.parser, data, size, NULL);
481
0
  ctx.pool = pool;
482
0
  ctx.str = str_new(default_pool, 128);
483
484
0
  ret = parse_path(&ctx);
485
486
0
  rfc822_parser_deinit(&ctx.parser);
487
0
  str_free(&ctx.str);
488
0
  *addr_r = ctx.addr_list.head;
489
0
  return (ret < 0 ? -1 : 0);
490
0
}
491
492
struct message_address *
493
message_address_parse(pool_t pool, const unsigned char *data, size_t size,
494
          unsigned int max_addresses,
495
          enum message_address_parse_flags flags)
496
0
{
497
0
  struct message_address_list list;
498
0
  message_address_parse_full(pool, data, size, max_addresses, flags,
499
0
           &list);
500
0
  return list.head;
501
0
}
502
503
void message_address_parse_full(pool_t pool, const unsigned char *data,
504
        size_t size, unsigned int max_addresses,
505
        enum message_address_parse_flags flags,
506
        struct message_address_list *list_r)
507
0
{
508
0
  if (pool->datastack_pool) {
509
0
    message_address_parse_real(pool, data, size,
510
0
             max_addresses, flags, list_r);
511
0
  } else T_BEGIN {
512
0
    message_address_parse_real(pool, data, size,
513
0
             max_addresses, flags, list_r);
514
0
  } T_END;
515
0
}
516
517
int message_address_parse_path(pool_t pool, const unsigned char *data,
518
             size_t size, struct message_address **addr_r)
519
0
{
520
0
  int ret;
521
522
0
  if (pool->datastack_pool) {
523
0
    return message_address_parse_path_real(pool, data, size, addr_r);
524
0
  }
525
0
  T_BEGIN {
526
0
    ret = message_address_parse_path_real(pool, data, size, addr_r);
527
0
  } T_END;
528
0
  return ret;
529
0
}
530
531
void message_address_write(string_t *str, const struct message_address *addr)
532
0
{
533
0
  const char *tmp;
534
0
  bool first = TRUE, in_group = FALSE;
535
536
0
  if (addr == NULL)
537
0
    return;
538
539
  /* <> path */
540
0
  if (addr->mailbox == NULL && addr->domain == NULL) {
541
0
    i_assert(addr->next == NULL);
542
0
    str_append(str, "<>");
543
0
    return;
544
0
  }
545
546
  /* a) mailbox@domain
547
     b) name <@route:mailbox@domain>
548
     c) group: .. ; */
549
550
0
  while (addr != NULL) {
551
0
    if (first)
552
0
      first = FALSE;
553
0
    else
554
0
      str_append(str, ", ");
555
556
0
    if (addr->domain == NULL) {
557
0
      if (!in_group) {
558
        /* beginning of group. mailbox is the group
559
           name, others are NULL. */
560
0
        if (addr->mailbox != NULL && *addr->mailbox != '\0') {
561
          /* check for MIME encoded-word */
562
0
          if (strstr(addr->mailbox, "=?") != NULL)
563
            /* MIME encoded-word MUST NOT appear within a 'quoted-string'
564
               so escaping and quoting of phrase is not possible, instead
565
               use obsolete RFC822 phrase syntax which allow spaces */
566
0
            str_append(str, addr->mailbox);
567
0
          else
568
0
            str_append_maybe_escape(str, addr->mailbox, TRUE);
569
0
        } else {
570
          /* empty group name needs to be quoted */
571
0
          str_append(str, "\"\"");
572
0
        }
573
0
        str_append(str, ": ");
574
0
        first = TRUE;
575
0
      } else {
576
        /* end of group. all fields should be NULL. */
577
0
        i_assert(addr->mailbox == NULL);
578
579
        /* cut out the ", " */
580
0
        tmp = str_c(str)+str_len(str)-2;
581
0
        i_assert((tmp[0] == ',' || tmp[0] == ':') && tmp[1] == ' ');
582
0
        if (tmp[0] == ',' && tmp[1] == ' ')
583
0
          str_truncate(str, str_len(str)-2);
584
0
        else if (tmp[0] == ':' && tmp[1] == ' ')
585
0
          str_truncate(str, str_len(str)-1);
586
0
        str_append_c(str, ';');
587
0
      }
588
589
0
      in_group = !in_group;
590
0
    } else {
591
      /* "Display Name" <mailbox@domain> */
592
0
      i_assert(addr->mailbox != NULL);
593
594
0
      if (addr->name != NULL) {
595
        /* check for MIME encoded-word */
596
0
        if (strstr(addr->name, "=?") != NULL)
597
          /* MIME encoded-word MUST NOT appear within a 'quoted-string'
598
             so escaping and quoting of phrase is not possible, instead
599
             use obsolete RFC822 phrase syntax which allow spaces */
600
0
          str_append(str, addr->name);
601
0
        else
602
0
          str_append_maybe_escape(str, addr->name, TRUE);
603
0
      }
604
0
      if (addr->route != NULL ||
605
0
          addr->mailbox[0] != '\0' ||
606
0
          addr->domain[0] != '\0') {
607
0
        if (addr->name != NULL && addr->name[0] != '\0')
608
0
          str_append_c(str, ' ');
609
0
        str_append_c(str, '<');
610
0
        if (addr->route != NULL) {
611
0
          str_append(str, addr->route);
612
0
          str_append_c(str, ':');
613
0
        }
614
0
        if (addr->mailbox[0] == '\0')
615
0
          str_append(str, "\"\"");
616
0
        else
617
0
          str_append_maybe_escape(str, addr->mailbox, FALSE);
618
0
        if (addr->domain[0] != '\0') {
619
0
          str_append_c(str, '@');
620
0
          str_append(str, addr->domain);
621
0
        }
622
0
        str_append_c(str, '>');
623
0
      }
624
0
    }
625
626
0
    addr = addr->next;
627
0
  }
628
0
}
629
630
const char *message_address_to_string(const struct message_address *addr)
631
0
{
632
0
  string_t *str = t_str_new(256);
633
0
  message_address_write(str, addr);
634
0
  return str_c(str);
635
0
}
636
637
const char *message_address_first_to_string(const struct message_address *addr)
638
0
{
639
0
  struct message_address first_addr;
640
641
0
  first_addr = *addr;
642
0
  first_addr.prev = NULL;
643
0
  first_addr.next = NULL;
644
0
  first_addr.route = NULL;
645
0
  return message_address_to_string(&first_addr);
646
0
}
647
648
void message_address_init(struct message_address *addr,
649
  const char *name, const char *mailbox, const char *domain)
650
0
{
651
0
  i_zero(addr);
652
0
  addr->name = name;
653
0
  addr->mailbox = mailbox;
654
0
  addr->domain = domain;
655
0
}
656
657
void message_address_init_from_smtp(struct message_address *addr,
658
  const char *name, const struct smtp_address *smtp_addr)
659
0
{
660
0
  i_zero(addr);
661
0
  addr->name = name;
662
0
  addr->mailbox = smtp_addr->localpart;
663
0
  addr->domain = smtp_addr->domain;
664
0
}
665
666
static const char *address_headers[] = {
667
  "From", "Sender", "Reply-To",
668
  "To", "Cc", "Bcc",
669
  "Resent-From", "Resent-Sender", "Resent-To", "Resent-Cc", "Resent-Bcc"
670
};
671
672
bool message_header_is_address(const char *hdr_name)
673
0
{
674
0
  unsigned int i;
675
676
0
  for (i = 0; i < N_ELEMENTS(address_headers); i++) {
677
0
    if (strcasecmp(hdr_name, address_headers[i]) == 0)
678
0
      return TRUE;
679
0
  }
680
0
  return FALSE;
681
0
}