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