1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "str.h"
5 #include "message-address.h"
6 #include "smtp-parser.h"
7 #include "smtp-address.h"
8
9 /* From RFC 5321:
10
11 Reverse-path = Path / "<>"
12 Forward-path = Path
13
14 Path = "<" [ A-d-l ":" ] Mailbox ">"
15 A-d-l = At-domain *( "," At-domain )
16 ; Note that this form, the so-called "source
17 ; route", MUST BE accepted, SHOULD NOT be
18 ; generated, and SHOULD be ignored.
19 At-domain = "@" Domain
20
21 Domain = sub-domain *("." sub-domain)
22
23 sub-domain = Let-dig [Ldh-str]
24 Let-dig = ALPHA / DIGIT
25 Ldh-str = *( ALPHA / DIGIT / "-" ) Let-dig
26
27 address-literal = "[" ( IPv4-address-literal /
28 IPv6-address-literal /
29 General-address-literal ) "]"
30 ; See Section 4.1.3
31
32 Mailbox = Local-part "@" ( Domain / address-literal )
33
34 Local-part = Dot-string / Quoted-string
35 ; MAY be case-sensitive
36 Dot-string = Atom *("." Atom)
37 Atom = 1*atext
38 */
39
40 /*
41 * SMTP address parsing
42 */
43
44 struct smtp_address_parser {
45 struct smtp_parser parser;
46
47 struct smtp_address address;
48 const unsigned char *address_end;
49
50 bool parse:1;
51 bool path:1;
52 bool parsed_any:1;
53 bool totally_broken:1;
54 };
55
56 static int
smtp_parser_parse_dot_string(struct smtp_parser * parser,const char ** value_r)57 smtp_parser_parse_dot_string(struct smtp_parser *parser, const char **value_r)
58 {
59 const unsigned char *pbegin = parser->cur;
60
61 /* Dot-string = Atom *("." Atom)
62 */
63
64 /* NOTE: this deviates from Dot-String syntax to allow some Japanese
65 mail addresses with dots at non-standard places to be accepted. */
66
67 if (parser->cur >= parser->end ||
68 (!smtp_char_is_atext(*parser->cur) && *parser->cur != '.'))
69 return 0;
70 parser->cur++;
71
72 while (parser->cur < parser->end &&
73 (smtp_char_is_atext(*parser->cur) || *parser->cur == '.'))
74 parser->cur++;
75
76 if (value_r != NULL)
77 *value_r = t_strndup(pbegin, parser->cur - pbegin);
78 return 1;
79 }
80
81 static int
smtp_parse_localpart(struct smtp_parser * parser,const char ** localpart_r)82 smtp_parse_localpart(struct smtp_parser *parser, const char **localpart_r)
83 {
84 int ret;
85
86 if ((ret = smtp_parser_parse_quoted_string(parser, localpart_r)) != 0)
87 return ret;
88
89 return smtp_parser_parse_dot_string(parser, localpart_r);
90 }
91
92 static int
smtp_address_parser_find_end(struct smtp_address_parser * aparser,enum smtp_address_parse_flags flags)93 smtp_address_parser_find_end(struct smtp_address_parser *aparser,
94 enum smtp_address_parse_flags flags)
95 {
96 struct smtp_parser *parser = &aparser->parser;
97 const char *begin = (const char *)parser->begin, *end;
98 const char **address_p = NULL;
99
100 if (aparser->address_end != NULL)
101 return 0;
102
103 if (aparser->parse &&
104 HAS_ALL_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW))
105 address_p = &aparser->address.raw;
106 if (smtp_address_parse_any(begin, address_p, &end) < 0) {
107 parser->error = "Invalid character";
108 aparser->totally_broken = TRUE;
109 return -1;
110 }
111 aparser->parsed_any = TRUE;
112 aparser->address_end = (const unsigned char *)end;
113 if (aparser->path) {
114 i_assert(aparser->address_end > parser->begin);
115 aparser->address_end--;
116 }
117 return 0;
118 }
119
120 static int
smtp_parse_mailbox(struct smtp_address_parser * aparser,enum smtp_address_parse_flags flags)121 smtp_parse_mailbox(struct smtp_address_parser *aparser,
122 enum smtp_address_parse_flags flags)
123 {
124 struct smtp_parser *parser = &aparser->parser;
125 const char **value = NULL;
126 const unsigned char *p, *dp;
127 int ret;
128
129 /* Mailbox = Local-part "@" ( Domain / address-literal )
130 */
131
132 value = (aparser->parse ? &aparser->address.localpart : NULL);
133 if ((flags & SMTP_ADDRESS_PARSE_FLAG_STRICT) != 0 ||
134 (flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_BAD_LOCALPART) == 0 ||
135 aparser->path || *parser->cur == '\"') {
136 if ((ret = smtp_parse_localpart(parser, value)) <= 0)
137 return ret;
138 } else {
139 /* find the end of the address */
140 if (smtp_address_parser_find_end(aparser, flags) < 0)
141 return -1;
142 /* use the right-most '@' as separator */
143 dp = aparser->address_end - 1;
144 while (dp > parser->cur && *dp != '@')
145 dp--;
146 if (dp == parser->cur)
147 dp = aparser->address_end;
148 /* check whether the resulting localpart could be encoded as
149 quoted string */
150 for (p = parser->cur; p < dp; p++) {
151 if (!smtp_char_is_qtext(*p) &&
152 !smtp_char_is_qpair(*p)) {
153 parser->error =
154 "Invalid character in localpart";
155 return -1;
156 }
157 }
158 if (aparser->parse) {
159 aparser->address.localpart =
160 p_strdup_until(parser->pool, parser->cur, dp);
161 }
162 parser->cur = dp;
163 }
164
165 if ((parser->cur >= parser->end || *parser->cur != '@') &&
166 (flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART) == 0) {
167 if (parser->cur >= parser->end ||
168 (aparser->path && *parser->cur == '>'))
169 parser->error = "Missing domain";
170 else
171 parser->error = "Invalid character in localpart";
172 return -1;
173 }
174
175 if (parser->cur >= parser->end || *parser->cur != '@')
176 return 1;
177 parser->cur++;
178
179 value = (aparser->parse ? &aparser->address.domain : NULL);
180 if ((ret = smtp_parser_parse_domain(parser, value)) == 0 &&
181 (ret = smtp_parser_parse_address_literal(
182 parser, value, NULL)) == 0) {
183 if (parser->cur >= parser->end ||
184 (aparser->path && *parser->cur == '>')) {
185 parser->error = "Missing domain after '@'";
186 return -1;
187 } else {
188 parser->error = "Invalid domain";
189 return -1;
190 }
191 }
192 return ret;
193 }
194
smtp_parse_source_route(struct smtp_parser * parser)195 static int smtp_parse_source_route(struct smtp_parser *parser)
196 {
197 /* Source-route = [ A-d-l ":" ]
198 A-d-l = At-domain *( "," At-domain )
199 At-domain = "@" Domain
200 */
201
202 /* "@" Domain */
203 if (parser->cur >= parser->end || *parser->cur != '@')
204 return 0;
205 parser->cur++;
206
207 for (;;) {
208 /* Domain */
209 if (smtp_parser_parse_domain(parser, NULL) <= 0) {
210 parser->error =
211 "Missing domain after '@' in source route";
212 return -1;
213 }
214
215 /* *( "," At-domain ) */
216 if (parser->cur >= parser->end || *parser->cur != ',')
217 break;
218 parser->cur++;
219
220 /* "@" Domain */
221 if (parser->cur >= parser->end || *parser->cur != '@') {
222 parser->error = "Missing '@' after ',' in source route";
223 return -1;
224 }
225 parser->cur++;
226 }
227
228 /* ":" */
229 if (parser->cur >= parser->end || *parser->cur != ':') {
230 parser->error = "Missing ':' at end of source route";
231 return -1;
232 }
233 parser->cur++;
234 return 1;
235 }
236
237 static int
smtp_parse_path(struct smtp_address_parser * aparser,enum smtp_address_parse_flags flags)238 smtp_parse_path(struct smtp_address_parser *aparser,
239 enum smtp_address_parse_flags flags)
240 {
241 struct smtp_parser *parser = &aparser->parser;
242 int ret, sret = 0;
243
244 /* Path = "<" [ A-d-l ":" ] Mailbox ">"
245 */
246
247 /* "<" */
248 if (parser->cur < parser->end && *parser->cur == '<') {
249 aparser->path = TRUE;
250 parser->cur++;
251 } else if ((flags & SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL) == 0) {
252 return 0;
253 }
254
255 /* [ A-d-l ":" ] */
256 if (aparser->path && (sret = smtp_parse_source_route(parser)) < 0)
257 return -1;
258
259 /* Mailbox */
260 if ((ret = smtp_parse_mailbox(aparser, flags)) < 0)
261 return -1;
262 if (ret == 0) {
263 if (parser->cur < parser->end && *parser->cur == '>') {
264 if (sret > 0) {
265 parser->error =
266 "Path only consists of source route";
267 return -1;
268 }
269 if ((flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY)
270 == 0) {
271 parser->error = "Null path not allowed";
272 return -1;
273 }
274 } else {
275 parser->error = "Invalid character in localpart";
276 return -1;
277 }
278 }
279
280 /* ">" */
281 if (aparser->path) {
282 if (parser->cur >= parser->end || *parser->cur != '>') {
283 parser->error = "Missing '>' at end of path";
284 return -1;
285 }
286 parser->cur++;
287 } else if (parser->cur < parser->end && *parser->cur == '>') {
288 parser->error = "Unmatched '>' at end of path";
289 return -1;
290 }
291 return 1;
292 }
293
smtp_address_parse_mailbox(pool_t pool,const char * mailbox,enum smtp_address_parse_flags flags,struct smtp_address ** address_r,const char ** error_r)294 int smtp_address_parse_mailbox(pool_t pool, const char *mailbox,
295 enum smtp_address_parse_flags flags,
296 struct smtp_address **address_r,
297 const char **error_r)
298 {
299 struct smtp_address_parser aparser;
300 int ret;
301
302 if (address_r != NULL)
303 *address_r = NULL;
304 if (error_r != NULL)
305 *error_r = NULL;
306
307 if (error_r != NULL)
308 *error_r = NULL;
309
310 if ((mailbox == NULL || *mailbox == '\0')) {
311 if ((flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY) == 0) {
312 if (error_r != NULL)
313 *error_r = "Mailbox is empty string";
314 return -1;
315 }
316
317 if (address_r != NULL)
318 *address_r = p_new(pool, struct smtp_address, 1);
319 return 0;
320 }
321
322 i_zero(&aparser);
323 smtp_parser_init(&aparser.parser, pool_datastack_create(), mailbox);
324 aparser.address_end = aparser.parser.end;
325 aparser.parse = (address_r != NULL);
326
327 if ((ret = smtp_parse_mailbox(&aparser, flags)) <= 0) {
328 if (error_r != NULL) {
329 *error_r = (ret < 0 ? aparser.parser.error :
330 "Invalid character in localpart");
331 }
332 return -1;
333 }
334 if (aparser.parser.cur != aparser.parser.end) {
335 if (error_r != NULL)
336 *error_r = "Invalid character in mailbox";
337 return -1;
338 }
339
340 if (address_r != NULL)
341 *address_r = smtp_address_clone(pool, &aparser.address);
342 return 0;
343 }
344
345 static int
smtp_address_parse_path_broken(struct smtp_address_parser * aparser,enum smtp_address_parse_flags flags,const char ** endp_r)346 smtp_address_parse_path_broken(struct smtp_address_parser *aparser,
347 enum smtp_address_parse_flags flags,
348 const char **endp_r) ATTR_NULL(3)
349 {
350 struct smtp_parser *parser = &aparser->parser;
351 const char *begin = (const char *)parser->begin, *end;
352 const char *raw = aparser->address.raw;
353 const char **address_p = NULL;
354
355 i_zero(&aparser->address);
356 aparser->address.raw = raw;
357
358 if (aparser->totally_broken ||
359 HAS_NO_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN))
360 return -1;
361 if (*begin != '<' &&
362 HAS_NO_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL)) {
363 /* brackets missing; totally broken */
364 return -1;
365 }
366 i_assert(aparser->parse);
367 if (aparser->parsed_any) {
368 if (endp_r != NULL)
369 *endp_r = (const char *)aparser->address_end;
370 return 0;
371 }
372
373 if (HAS_ALL_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW))
374 address_p = &aparser->address.raw;
375 if (smtp_address_parse_any(begin, address_p, &end) < 0) {
376 /* totally broken */
377 return -1;
378 }
379 if (endp_r != NULL)
380 *endp_r = end;
381 return 0;
382 }
383
smtp_address_parse_path_full(pool_t pool,const char * path,enum smtp_address_parse_flags flags,struct smtp_address ** address_r,const char ** error_r,const char ** endp_r)384 int smtp_address_parse_path_full(pool_t pool, const char *path,
385 enum smtp_address_parse_flags flags,
386 struct smtp_address **address_r,
387 const char **error_r, const char **endp_r)
388 {
389 struct smtp_address_parser aparser;
390 int ret;
391
392 if (address_r != NULL)
393 *address_r = NULL;
394 if (error_r != NULL)
395 *error_r = NULL;
396 if (endp_r != NULL)
397 *endp_r = NULL;
398
399 if (path == NULL || *path == '\0') {
400 if ((flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY) == 0 ||
401 (flags & SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL) == 0) {
402 if (error_r != NULL)
403 *error_r = "Path is empty string";
404 return -1;
405 }
406 if (address_r != NULL)
407 *address_r = p_new(pool, struct smtp_address, 1);
408 if (endp_r != NULL)
409 *endp_r = path;
410 return 0;
411 }
412
413 i_zero(&aparser);
414 smtp_parser_init(&aparser.parser, pool_datastack_create(), path);
415 aparser.address_end = (endp_r != NULL ? NULL : aparser.parser.end);
416 aparser.parse = (address_r != NULL);
417
418 if ((ret = smtp_parse_path(&aparser, flags)) <= 0) {
419 if (error_r != NULL) {
420 *error_r = (ret < 0 ? aparser.parser.error :
421 "Missing '<' at beginning of path");
422 }
423 ret = -1;
424 } else if (endp_r != NULL) {
425 if (aparser.parser.cur == aparser.parser.end ||
426 *aparser.parser.cur == ' ' ||
427 HAS_NO_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN)) {
428 *endp_r = (const char *)aparser.parser.cur;
429 ret = 0;
430 } else {
431 if (error_r != NULL)
432 *error_r = "Invalid character in path";
433 ret = -1;
434 }
435 } else if (aparser.parser.cur == aparser.parser.end) {
436 ret = 0;
437 } else {
438 if (error_r != NULL)
439 *error_r = "Invalid character in path";
440 ret = -1;
441 }
442
443 if (ret < 0) {
444 /* normal parsing failed */
445 if (smtp_address_parse_path_broken(&aparser, flags,
446 endp_r) < 0) {
447 /* failed to parse it as a broken address as well */
448 return -1;
449 }
450 /* broken address */
451 } else if (HAS_ALL_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW) &&
452 aparser.address.localpart != NULL) {
453 if (aparser.path &&
454 ((const unsigned char *)(path + 1) < aparser.parser.cur)) {
455 aparser.address.raw = t_strdup_until(
456 path + 1, aparser.parser.cur - 1);
457 } else {
458 aparser.address.raw = t_strdup_until(
459 path, aparser.parser.cur);
460 }
461 }
462
463 if (address_r != NULL)
464 *address_r = smtp_address_clone(pool, &aparser.address);
465 return ret;
466 }
467
smtp_address_parse_path(pool_t pool,const char * path,enum smtp_address_parse_flags flags,struct smtp_address ** address_r,const char ** error_r)468 int smtp_address_parse_path(pool_t pool, const char *path,
469 enum smtp_address_parse_flags flags,
470 struct smtp_address **address_r,
471 const char **error_r)
472 {
473 return smtp_address_parse_path_full(pool, path, flags,
474 address_r, error_r, NULL);
475 }
476
smtp_address_parse_username(pool_t pool,const char * username,struct smtp_address ** address_r,const char ** error_r)477 int smtp_address_parse_username(pool_t pool, const char *username,
478 struct smtp_address **address_r,
479 const char **error_r)
480 {
481 enum smtp_address_parse_flags flags =
482 SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART |
483 SMTP_ADDRESS_PARSE_FLAG_ALLOW_BAD_LOCALPART;
484
485 struct smtp_address_parser aparser;
486 int ret;
487
488 if (address_r != NULL)
489 *address_r = NULL;
490 if (error_r != NULL)
491 *error_r = NULL;
492
493 if ((username == NULL || *username == '\0')) {
494 if (error_r != NULL)
495 *error_r = "Username is empty string";
496 return -1;
497 }
498
499 i_zero(&aparser);
500 smtp_parser_init(&aparser.parser, pool_datastack_create(), username);
501 aparser.address_end = aparser.parser.end;
502 aparser.parse = (address_r != NULL);
503
504 if ((ret = smtp_parse_mailbox(&aparser, flags)) <= 0) {
505 if (error_r != NULL) {
506 *error_r = (ret < 0 ? aparser.parser.error :
507 "Invalid character in user name");
508 }
509 return -1;
510 }
511 if (aparser.parser.cur != aparser.parser.end) {
512 if (error_r != NULL)
513 *error_r = "Invalid character in user name";
514 return -1;
515 }
516
517 if (address_r != NULL)
518 *address_r = smtp_address_clone(pool, &aparser.address);
519 return 0;
520 }
521
smtp_address_detail_parse(pool_t pool,const char * delimiters,struct smtp_address * address,const char ** username_r,char * delim_r,const char ** detail_r)522 void smtp_address_detail_parse(pool_t pool, const char *delimiters,
523 struct smtp_address *address,
524 const char **username_r, char *delim_r,
525 const char **detail_r)
526 {
527 const char *localpart;
528 const char *user, *p;
529 size_t idx;
530
531 i_assert(!smtp_address_isnull(address));
532
533 localpart = address->localpart;
534 user = localpart;
535 *detail_r = "";
536 *delim_r = '\0';
537
538 /* first character that matches the recipient_delimiter */
539 idx = strcspn(localpart, delimiters);
540 p = (localpart[idx] != '\0' ? &localpart[idx] : NULL);
541
542 if (p != NULL) {
543 *delim_r = *p;
544 /* user+detail */
545 user = p_strdup_until(pool, localpart, p);
546 *detail_r = p+1;
547 }
548
549 if (address->domain == NULL || *address->domain == '\0')
550 *username_r = user;
551 else if (strchr(user, '@') == NULL) {
552 /* username is just glued to the domain... no SMTP escaping */
553 *username_r = p_strconcat(pool, user, "@", address->domain,
554 NULL);
555 } else {
556 struct smtp_address uaddr;
557
558 /* username contains '@'; apply escaping */
559 smtp_address_init(&uaddr, user, address->domain);
560 if (pool->datastack_pool)
561 *username_r = smtp_address_encode(&uaddr);
562 else {
563 *username_r =
564 p_strdup(pool, smtp_address_encode(&uaddr));
565 }
566 }
567 }
568
smtp_address_detail_parse_temp(const char * delimiters,struct smtp_address * address,const char ** username_r,char * delim_r,const char ** detail_r)569 void smtp_address_detail_parse_temp(const char *delimiters,
570 struct smtp_address *address,
571 const char **username_r, char *delim_r,
572 const char **detail_r)
573 {
574 smtp_address_detail_parse(pool_datastack_create(), delimiters,
575 address, username_r, delim_r, detail_r);
576 }
577
smtp_address_parse_any(const char * in,const char ** address_r,const char ** endp_r)578 int smtp_address_parse_any(const char *in, const char **address_r,
579 const char **endp_r)
580 {
581 const unsigned char *p, *pend, *poffset;
582 bool path = FALSE;
583 bool quoted = FALSE;
584
585 if (endp_r != NULL)
586 *endp_r = in;
587
588 poffset = p = (const unsigned char *)in;
589 pend = p + strlen(in);
590 if (*p == '<') {
591 path = TRUE;
592 p++;
593 poffset = p;
594 }
595 if (*p == '"') {
596 quoted = TRUE;
597 p++;
598 }
599
600 while (p < pend) {
601 if (quoted && *p == '\\') {
602 p++;
603 if (p == pend || *p < 0x20)
604 return -1;
605 p++;
606 if (p == pend)
607 break;
608 }
609 switch (*p) {
610 case '"':
611 quoted = FALSE;
612 break;
613 case ' ':
614 if (!quoted) {
615 if (path)
616 return -1;
617 if (address_r != NULL)
618 *address_r = t_strdup_until(poffset, p);
619 if (endp_r != NULL)
620 *endp_r = (const char *)p;
621 return 0;
622 }
623 break;
624 case '>':
625 if (!quoted) {
626 if (address_r != NULL)
627 *address_r = t_strdup_until(poffset, p);
628 if (endp_r != NULL)
629 *endp_r = (const char *)(p + 1);
630 return 0;
631 }
632 break;
633 default:
634 if (*p < 0x20)
635 return -1;
636 break;
637 }
638 p++;
639 }
640 if (quoted || path)
641 return -1;
642 if (address_r != NULL)
643 *address_r = t_strdup_until(poffset, p);
644 if (endp_r != NULL)
645 *endp_r = (const char *)p;
646 return 0;
647 }
648
649 /*
650 * SMTP address construction
651 */
652
smtp_address_write(string_t * out,const struct smtp_address * address)653 void smtp_address_write(string_t *out, const struct smtp_address *address)
654 ATTR_NULL(2)
655 {
656 bool quoted = FALSE;
657 const unsigned char *p, *pend, *pblock;
658 size_t begin;
659
660 if (smtp_address_isnull(address))
661 return;
662 begin = str_len(out);
663
664 /* encode localpart */
665 p = (const unsigned char *)address->localpart;
666 pend = p + strlen(address->localpart);
667 pblock = p;
668 while (p < pend) {
669 while (p < pend && smtp_char_is_atext(*p))
670 p++;
671
672 if (!quoted && p < pend && (*p != '.' || p == pblock)) {
673 quoted = TRUE;
674 str_insert(out, begin, "\"");
675 }
676
677 str_append_data(out, pblock, p - pblock);
678 if (p >= pend)
679 break;
680
681 if (!quoted) {
682 str_append_c(out, '.');
683 } else {
684 i_assert(smtp_char_is_qpair(*p));
685 if (!smtp_char_is_qtext(*p))
686 str_append_c(out, '\\');
687 str_append_c(out, *p);
688 }
689
690 p++;
691 pblock = p;
692 }
693
694 if (p == pblock && !quoted) {
695 quoted = TRUE;
696 str_insert(out, begin, "\"");
697 }
698
699 if (quoted)
700 str_append_c(out, '\"');
701
702 if (address->domain == NULL || *address->domain == '\0')
703 return;
704
705 str_append_c(out, '@');
706 str_append(out, address->domain);
707 }
708
smtp_address_write_path(string_t * out,const struct smtp_address * address)709 void smtp_address_write_path(string_t *out, const struct smtp_address *address)
710 {
711 str_append_c(out, '<');
712 smtp_address_write(out, address);
713 str_append_c(out, '>');
714 }
715
smtp_address_encode(const struct smtp_address * address)716 const char *smtp_address_encode(const struct smtp_address *address)
717 {
718 string_t *str = t_str_new(256);
719 smtp_address_write(str, address);
720 return str_c(str);
721 }
722
smtp_address_encode_path(const struct smtp_address * address)723 const char *smtp_address_encode_path(const struct smtp_address *address)
724 {
725 string_t *str = t_str_new(256);
726 smtp_address_write_path(str, address);
727 return str_c(str);
728 }
729
smtp_address_encode_raw(const struct smtp_address * address)730 const char *smtp_address_encode_raw(const struct smtp_address *address)
731 {
732 if (address != NULL && address->raw != NULL && *address->raw != '\0')
733 return address->raw;
734
735 return smtp_address_encode(address);
736 }
737
smtp_address_encode_raw_path(const struct smtp_address * address)738 const char *smtp_address_encode_raw_path(const struct smtp_address *address)
739 {
740 if (address != NULL && address->raw != NULL && *address->raw != '\0')
741 return t_strconcat("<", address->raw, ">", NULL);
742
743 return smtp_address_encode_path(address);
744 }
745
746 /*
747 * SMTP address manipulation
748 */
749
smtp_address_init(struct smtp_address * address,const char * localpart,const char * domain)750 void smtp_address_init(struct smtp_address *address,
751 const char *localpart, const char *domain)
752 {
753 i_zero(address);
754 if (localpart == NULL || *localpart == '\0')
755 return;
756
757 address->localpart = localpart;
758 if (domain != NULL && *domain != '\0')
759 address->domain = domain;
760 }
761
smtp_address_init_from_msg(struct smtp_address * address,const struct message_address * msg_addr)762 int smtp_address_init_from_msg(struct smtp_address *address,
763 const struct message_address *msg_addr)
764 {
765 const unsigned char *p;
766
767 i_zero(address);
768 if (msg_addr->mailbox == NULL || *msg_addr->mailbox == '\0')
769 return 0;
770
771 /* The message_address_parse() function allows UTF-8 codepoints in
772 the localpart. For SMTP addresses that is not an option, so we
773 need to check this upon conversion. */
774 for (p = (const unsigned char *)msg_addr->mailbox; *p != '\0'; p++) {
775 if (!smtp_char_is_qpair(*p))
776 return -1;
777 }
778
779 address->localpart = msg_addr->mailbox;
780 if (msg_addr->domain != NULL && *msg_addr->domain != '\0')
781 address->domain = msg_addr->domain;
782 return 0;
783 }
784
785 struct smtp_address *
smtp_address_clone(pool_t pool,const struct smtp_address * src)786 smtp_address_clone(pool_t pool, const struct smtp_address *src)
787 {
788 struct smtp_address *new;
789 size_t size, lpsize = 0, dsize = 0, rsize = 0;
790 char *data, *localpart = NULL, *domain = NULL, *raw = NULL;
791
792 if (src == NULL)
793 return NULL;
794
795 /* @UNSAFE */
796
797 size = sizeof(struct smtp_address);
798 if (!smtp_address_isnull(src)) {
799 lpsize = strlen(src->localpart) + 1;
800 size = MALLOC_ADD(size, lpsize);
801 }
802 if (src->domain != NULL && *src->domain != '\0') {
803 dsize = strlen(src->domain) + 1;
804 size = MALLOC_ADD(size, dsize);
805 }
806 if (src->raw != NULL && *src->raw != '\0') {
807 rsize = strlen(src->raw) + 1;
808 size = MALLOC_ADD(size, rsize);
809 }
810
811 data = p_malloc(pool, size);
812 new = (struct smtp_address *)data;
813 if (lpsize > 0) {
814 localpart = PTR_OFFSET(data, sizeof(*new));
815 memcpy(localpart, src->localpart, lpsize);
816 }
817 if (dsize > 0) {
818 domain = PTR_OFFSET(data, sizeof(*new) + lpsize);
819 memcpy(domain, src->domain, dsize);
820 }
821 if (rsize > 0) {
822 raw = PTR_OFFSET(data, sizeof(*new) + lpsize + dsize);
823 memcpy(raw, src->raw, rsize);
824 }
825 new->localpart = localpart;
826 new->domain = domain;
827 new->raw = raw;
828
829 return new;
830 }
831
832 struct smtp_address *
smtp_address_create(pool_t pool,const char * localpart,const char * domain)833 smtp_address_create(pool_t pool, const char *localpart, const char *domain)
834 {
835 struct smtp_address addr;
836
837 smtp_address_init(&addr, localpart, domain);
838 return smtp_address_clone(pool, &addr);
839 }
840
841
smtp_address_create_from_msg(pool_t pool,const struct message_address * msg_addr,struct smtp_address ** address_r)842 int smtp_address_create_from_msg(pool_t pool,
843 const struct message_address *msg_addr,
844 struct smtp_address **address_r)
845 {
846 struct smtp_address addr;
847
848 if (smtp_address_init_from_msg(&addr, msg_addr) < 0) {
849 *address_r = NULL;
850 return -1;
851 }
852 *address_r = smtp_address_clone(pool, &addr);
853 return 0;
854 }
855
smtp_address_clone_temp(const struct smtp_address * src)856 struct smtp_address *smtp_address_clone_temp(const struct smtp_address *src)
857 {
858 struct smtp_address *new;
859
860 if (src == NULL)
861 return NULL;
862
863 new = t_new(struct smtp_address, 1);
864 new->localpart = t_strdup_empty(src->localpart);
865 new->domain = t_strdup_empty(src->domain);
866 new->raw = t_strdup_empty(src->raw);
867 return new;
868 }
869
870 struct smtp_address *
smtp_address_create_temp(const char * localpart,const char * domain)871 smtp_address_create_temp(const char *localpart, const char *domain)
872 {
873 struct smtp_address addr;
874
875 smtp_address_init(&addr, localpart, domain);
876 return smtp_address_clone_temp(&addr);
877 }
878
smtp_address_create_from_msg_temp(const struct message_address * msg_addr,struct smtp_address ** address_r)879 int smtp_address_create_from_msg_temp(const struct message_address *msg_addr,
880 struct smtp_address **address_r)
881 {
882 struct smtp_address addr;
883
884 if (smtp_address_init_from_msg(&addr, msg_addr) < 0) {
885 *address_r = NULL;
886 return -1;
887 }
888 *address_r = smtp_address_clone_temp(&addr);
889 return 0;
890 }
891
892 struct smtp_address *
smtp_address_add_detail(pool_t pool,const struct smtp_address * address,const char * detail,char delim_c)893 smtp_address_add_detail(pool_t pool, const struct smtp_address *address,
894 const char *detail, char delim_c)
895 {
896 struct smtp_address *new_addr;
897 const char delim[] = {delim_c, '\0'};
898
899 i_assert(!smtp_address_isnull(address));
900
901 new_addr = p_new(pool, struct smtp_address, 1);
902 new_addr->localpart = p_strconcat(pool, address->localpart, delim,
903 detail, NULL);
904 new_addr->domain = p_strdup_empty(pool, address->domain);
905
906 return new_addr;
907 }
908
909 struct smtp_address *
smtp_address_add_detail_temp(const struct smtp_address * address,const char * detail,char delim_c)910 smtp_address_add_detail_temp(const struct smtp_address *address,
911 const char *detail, char delim_c)
912 {
913 struct smtp_address *new_addr;
914 const char delim[] = {delim_c, '\0'};
915
916 i_assert(!smtp_address_isnull(address));
917
918 new_addr = t_new(struct smtp_address, 1);
919 new_addr->localpart = t_strconcat(address->localpart, delim, detail,
920 NULL);
921 new_addr->domain = t_strdup_empty(address->domain);
922
923 return new_addr;
924 }
925
smtp_address_cmp(const struct smtp_address * address1,const struct smtp_address * address2)926 int smtp_address_cmp(const struct smtp_address *address1,
927 const struct smtp_address *address2)
928 {
929 bool null1, null2;
930 int ret;
931
932 null1 = smtp_address_isnull(address1);
933 null2 = smtp_address_isnull(address2);
934 if (null1)
935 return (null2 ? 0 : -1);
936 else if (null2)
937 return 1;
938 if ((ret = null_strcasecmp(address1->domain, address2->domain)) != 0)
939 return ret;
940 return null_strcmp(address1->localpart, address2->localpart);
941 }
942
smtp_address_cmp_icase(const struct smtp_address * address1,const struct smtp_address * address2)943 int smtp_address_cmp_icase(const struct smtp_address *address1,
944 const struct smtp_address *address2)
945 {
946 bool null1, null2;
947 int ret;
948
949 null1 = smtp_address_isnull(address1);
950 null2 = smtp_address_isnull(address2);
951 if (null1)
952 return (null2 ? 0 : -1);
953 else if (null2)
954 return 1;
955 if ((ret = null_strcasecmp(address1->domain, address2->domain)) != 0)
956 return ret;
957 return null_strcasecmp(address1->localpart, address2->localpart);
958 }
959