xref: /openbsd-src/usr.sbin/smtpd/envelope.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: envelope.c,v 1.29 2014/04/19 12:30:54 gilles Exp $	*/
2 
3 /*
4  * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
5  * Copyright (c) 2011-2013 Gilles Chehade <gilles@poolp.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/queue.h>
22 #include <sys/tree.h>
23 #include <sys/socket.h>
24 #include <sys/stat.h>
25 
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 
29 #include <ctype.h>
30 #include <err.h>
31 #include <errno.h>
32 #include <event.h>
33 #include <fcntl.h>
34 #include <imsg.h>
35 #include <inttypes.h>
36 #include <libgen.h>
37 #include <pwd.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
42 #include <unistd.h>
43 
44 #include "smtpd.h"
45 #include "log.h"
46 
47 static int envelope_upgrade_v1(struct dict *);
48 static int envelope_ascii_load(struct envelope *, struct dict *);
49 static void envelope_ascii_dump(const struct envelope *, char **, size_t *,
50     const char *);
51 
52 void
53 envelope_set_errormsg(struct envelope *e, char *fmt, ...)
54 {
55 	int ret;
56 	va_list ap;
57 
58 	va_start(ap, fmt);
59 	ret = vsnprintf(e->errorline, sizeof(e->errorline), fmt, ap);
60 	va_end(ap);
61 
62 	/* this should not happen */
63 	if (ret == -1)
64 		err(1, "vsnprintf");
65 
66 	if ((size_t)ret >= sizeof(e->errorline))
67 		(void)strlcpy(e->errorline + (sizeof(e->errorline) - 4), "...", 4);
68 }
69 
70 void
71 envelope_set_esc_class(struct envelope *e, enum enhanced_status_class class)
72 {
73 	e->esc_class = class;
74 }
75 
76 void
77 envelope_set_esc_code(struct envelope *e, enum enhanced_status_code code)
78 {
79 	e->esc_code = code;
80 }
81 
82 static int
83 envelope_buffer_to_dict(struct dict *d,  const char *ibuf, size_t buflen)
84 {
85 	static char	 lbuf[sizeof(struct envelope)];
86 	size_t		 len;
87 	char		*buf, *field, *nextline;
88 
89 	memset(lbuf, 0, sizeof lbuf);
90 	if (strlcpy(lbuf, ibuf, sizeof lbuf) >= sizeof lbuf)
91 		goto err;
92 	buf = lbuf;
93 
94 	while (buflen > 0) {
95 		len = strcspn(buf, "\n");
96 		buf[len] = '\0';
97 		nextline = buf + len + 1;
98 		buflen -= (nextline - buf);
99 
100 		field = buf;
101 		while (*buf && (isalnum((unsigned char)*buf) || *buf == '-'))
102 			buf++;
103 		if (! *buf)
104 			goto err;
105 
106 		/* skip whitespaces before separator */
107 		while (*buf && isspace((unsigned char)*buf))
108 			*buf++ = 0;
109 
110 		/* we *want* ':' */
111 		if (*buf != ':')
112 			goto err;
113 		*buf++ = 0;
114 
115 		/* skip whitespaces after separator */
116 		while (*buf && isspace((unsigned char)*buf))
117 			*buf++ = 0;
118 		dict_set(d, field, buf);
119 		buf = nextline;
120 	}
121 
122 	return (1);
123 
124 err:
125 	return (0);
126 }
127 
128 int
129 envelope_load_buffer(struct envelope *ep, const char *ibuf, size_t buflen)
130 {
131 	struct dict	 d;
132 	const char	*val, *errstr;
133 	long long	 version;
134 	int	 	 ret = 0;
135 
136 	dict_init(&d);
137 	if (! envelope_buffer_to_dict(&d, ibuf, buflen)) {
138 		log_debug("debug: cannot parse envelope to dict");
139 		goto end;
140 	}
141 
142 	val = dict_get(&d, "version");
143 	if (val == NULL) {
144 		log_debug("debug: envelope version not found");
145 		goto end;
146 	}
147 	version = strtonum(val, 1, 64, &errstr);
148 	if (errstr) {
149 		log_debug("debug: cannot parse envelope version: %s", val);
150 		goto end;
151 	}
152 
153 	switch (version) {
154 	case 1:
155 		log_debug("debug: upgrading envelope to version 1");
156 		if (!envelope_upgrade_v1(&d)) {
157 			log_debug("debug: failed to upgrade envelope to version 1");
158 			goto end;
159 		}
160 		/* FALLTRHOUGH */
161 	case 2:
162 		/* Can be missing in some v2 envelopes */
163 		if (dict_get(&d, "smtpname") == NULL)
164 			dict_xset(&d, "smtpname", env->sc_hostname);
165 		break;
166 	default:
167 		log_debug("debug: bad envelope version %lld", version);
168 		goto end;
169 	}
170 
171 	memset(ep, 0, sizeof *ep);
172 	ret = envelope_ascii_load(ep, &d);
173 	if (ret)
174 		ep->version = SMTPD_ENVELOPE_VERSION;
175 end:
176 	while (dict_poproot(&d, NULL))
177 		;
178 	return (ret);
179 }
180 
181 int
182 envelope_dump_buffer(const struct envelope *ep, char *dest, size_t len)
183 {
184 	char	*p = dest;
185 
186 	envelope_ascii_dump(ep, &dest, &len, "version");
187 	envelope_ascii_dump(ep, &dest, &len, "tag");
188 	envelope_ascii_dump(ep, &dest, &len, "type");
189 	envelope_ascii_dump(ep, &dest, &len, "smtpname");
190 	envelope_ascii_dump(ep, &dest, &len, "helo");
191 	envelope_ascii_dump(ep, &dest, &len, "hostname");
192 	envelope_ascii_dump(ep, &dest, &len, "errorline");
193 	envelope_ascii_dump(ep, &dest, &len, "sockaddr");
194 	envelope_ascii_dump(ep, &dest, &len, "sender");
195 	envelope_ascii_dump(ep, &dest, &len, "rcpt");
196 	envelope_ascii_dump(ep, &dest, &len, "dest");
197 	envelope_ascii_dump(ep, &dest, &len, "ctime");
198 	envelope_ascii_dump(ep, &dest, &len, "last-try");
199 	envelope_ascii_dump(ep, &dest, &len, "last-bounce");
200 	envelope_ascii_dump(ep, &dest, &len, "expire");
201 	envelope_ascii_dump(ep, &dest, &len, "retry");
202 	envelope_ascii_dump(ep, &dest, &len, "flags");
203 	envelope_ascii_dump(ep, &dest, &len, "dsn-notify");
204 	envelope_ascii_dump(ep, &dest, &len, "dsn-ret");
205 	envelope_ascii_dump(ep, &dest, &len, "dsn-envid");
206 	envelope_ascii_dump(ep, &dest, &len, "dsn-orcpt");
207 	envelope_ascii_dump(ep, &dest, &len, "esc-class");
208 	envelope_ascii_dump(ep, &dest, &len, "esc-code");
209 
210 	switch (ep->type) {
211 	case D_MDA:
212 		envelope_ascii_dump(ep, &dest, &len, "mda-buffer");
213 		envelope_ascii_dump(ep, &dest, &len, "mda-method");
214 		envelope_ascii_dump(ep, &dest, &len, "mda-user");
215 		envelope_ascii_dump(ep, &dest, &len, "mda-usertable");
216 		break;
217 	case D_MTA:
218 		envelope_ascii_dump(ep, &dest, &len, "mta-relay");
219 		envelope_ascii_dump(ep, &dest, &len, "mta-relay-auth");
220 		envelope_ascii_dump(ep, &dest, &len, "mta-relay-cert");
221 		envelope_ascii_dump(ep, &dest, &len, "mta-relay-flags");
222 		envelope_ascii_dump(ep, &dest, &len, "mta-relay-heloname");
223 		envelope_ascii_dump(ep, &dest, &len, "mta-relay-helotable");
224 		envelope_ascii_dump(ep, &dest, &len, "mta-relay-source");
225 		break;
226 	case D_BOUNCE:
227 		envelope_ascii_dump(ep, &dest, &len, "bounce-expire");
228 		envelope_ascii_dump(ep, &dest, &len, "bounce-delay");
229 		envelope_ascii_dump(ep, &dest, &len, "bounce-type");
230 		break;
231 	default:
232 		return (0);
233 	}
234 
235 	if (dest == NULL)
236 		return (0);
237 
238 	return (dest - p);
239 }
240 
241 static int
242 ascii_load_uint8(uint8_t *dest, char *buf)
243 {
244 	const char *errstr;
245 
246 	*dest = strtonum(buf, 0, 0xff, &errstr);
247 	if (errstr)
248 		return 0;
249 	return 1;
250 }
251 
252 static int
253 ascii_load_uint16(uint16_t *dest, char *buf)
254 {
255 	const char *errstr;
256 
257 	*dest = strtonum(buf, 0, 0xffff, &errstr);
258 	if (errstr)
259 		return 0;
260 	return 1;
261 }
262 
263 static int
264 ascii_load_uint32(uint32_t *dest, char *buf)
265 {
266 	const char *errstr;
267 
268 	*dest = strtonum(buf, 0, 0xffffffff, &errstr);
269 	if (errstr)
270 		return 0;
271 	return 1;
272 }
273 
274 static int
275 ascii_load_time(time_t *dest, char *buf)
276 {
277 	const char *errstr;
278 
279 	*dest = strtonum(buf, 0, LLONG_MAX, &errstr);
280 	if (errstr)
281 		return 0;
282 	return 1;
283 }
284 
285 static int
286 ascii_load_type(enum delivery_type *dest, char *buf)
287 {
288 	if (strcasecmp(buf, "mda") == 0)
289 		*dest = D_MDA;
290 	else if (strcasecmp(buf, "mta") == 0)
291 		*dest = D_MTA;
292 	else if (strcasecmp(buf, "bounce") == 0)
293 		*dest = D_BOUNCE;
294 	else
295 		return 0;
296 	return 1;
297 }
298 
299 static int
300 ascii_load_string(char *dest, char *buf, size_t len)
301 {
302 	if (strlcpy(dest, buf, len) >= len)
303 		return 0;
304 	return 1;
305 }
306 
307 static int
308 ascii_load_sockaddr(struct sockaddr_storage *ss, char *buf)
309 {
310 	struct sockaddr_in6 ssin6;
311 	struct sockaddr_in  ssin;
312 
313 	memset(&ssin, 0, sizeof ssin);
314 	memset(&ssin6, 0, sizeof ssin6);
315 
316 	if (!strcmp("local", buf)) {
317 		ss->ss_family = AF_LOCAL;
318 	}
319 	else if (strncasecmp("IPv6:", buf, 5) == 0) {
320 		if (inet_pton(AF_INET6, buf + 5, &ssin6.sin6_addr) != 1)
321 			return 0;
322 		ssin6.sin6_family = AF_INET6;
323 		memcpy(ss, &ssin6, sizeof(ssin6));
324 		ss->ss_len = sizeof(struct sockaddr_in6);
325 	}
326 	else {
327 		if (inet_pton(AF_INET, buf, &ssin.sin_addr) != 1)
328 			return 0;
329 		ssin.sin_family = AF_INET;
330 		memcpy(ss, &ssin, sizeof(ssin));
331 		ss->ss_len = sizeof(struct sockaddr_in);
332 	}
333 	return 1;
334 }
335 
336 static int
337 ascii_load_mda_method(enum action_type *dest, char *buf)
338 {
339 	if (strcasecmp(buf, "mbox") == 0)
340 		*dest = A_MBOX;
341 	else if (strcasecmp(buf, "maildir") == 0)
342 		*dest = A_MAILDIR;
343 	else if (strcasecmp(buf, "filename") == 0)
344 		*dest = A_FILENAME;
345 	else if (strcasecmp(buf, "mda") == 0)
346 		*dest = A_MDA;
347 	else if (strcasecmp(buf, "lmtp") == 0)
348 		*dest = A_LMTP;
349 	else
350 		return 0;
351 	return 1;
352 }
353 
354 static int
355 ascii_load_mailaddr(struct mailaddr *dest, char *buf)
356 {
357 	if (! text_to_mailaddr(dest, buf))
358 		return 0;
359 	return 1;
360 }
361 
362 static int
363 ascii_load_flags(enum envelope_flags *dest, char *buf)
364 {
365 	char *flag;
366 
367 	while ((flag = strsep(&buf, " ,|")) != NULL) {
368 		if (strcasecmp(flag, "authenticated") == 0)
369 			*dest |= EF_AUTHENTICATED;
370 		else if (strcasecmp(flag, "enqueued") == 0)
371 			;
372 		else if (strcasecmp(flag, "bounce") == 0)
373 			*dest |= EF_BOUNCE;
374 		else if (strcasecmp(flag, "internal") == 0)
375 			*dest |= EF_INTERNAL;
376 		else
377 			return 0;
378 	}
379 	return 1;
380 }
381 
382 static int
383 ascii_load_mta_relay_url(struct relayhost *relay, char *buf)
384 {
385 	if (! text_to_relayhost(relay, buf))
386 		return 0;
387 	return 1;
388 }
389 
390 static int
391 ascii_load_mta_relay_flags(uint16_t *dest, char *buf)
392 {
393 	char *flag;
394 
395 	while ((flag = strsep(&buf, " ,|")) != NULL) {
396 		if (strcasecmp(flag, "verify") == 0)
397 			*dest |= F_TLS_VERIFY;
398 		else if (strcasecmp(flag, "tls") == 0)
399 			*dest |= F_STARTTLS;
400 		else
401 			return 0;
402 	}
403 
404 	return 1;
405 }
406 
407 static int
408 ascii_load_bounce_type(enum bounce_type *dest, char *buf)
409 {
410 	if (strcasecmp(buf, "error") == 0)
411 		*dest = B_ERROR;
412 	else if (strcasecmp(buf, "warn") == 0)
413 		*dest = B_WARNING;
414 	else if (strcasecmp(buf, "dsn") == 0)
415 		*dest = B_DSN;
416 	else
417 		return 0;
418 	return 1;
419 }
420 
421 static int
422 ascii_load_dsn_ret(enum dsn_ret *ret, char *buf)
423 {
424 	if (strcasecmp(buf, "HDRS") == 0)
425 		*ret = DSN_RETHDRS;
426 	else if (strcasecmp(buf, "FULL") == 0)
427 		*ret = DSN_RETFULL;
428 	else
429 		return 0;
430 	return 1;
431 }
432 
433 static int
434 ascii_load_field(const char *field, struct envelope *ep, char *buf)
435 {
436 	if (strcasecmp("bounce-delay", field) == 0)
437 		return ascii_load_time(&ep->agent.bounce.delay, buf);
438 
439 	if (strcasecmp("bounce-expire", field) == 0)
440 		return ascii_load_time(&ep->agent.bounce.expire, buf);
441 
442 	if (strcasecmp("bounce-type", field) == 0)
443 		return ascii_load_bounce_type(&ep->agent.bounce.type, buf);
444 
445 	if (strcasecmp("ctime", field) == 0)
446 		return ascii_load_time(&ep->creation, buf);
447 
448 	if (strcasecmp("dest", field) == 0)
449 		return ascii_load_mailaddr(&ep->dest, buf);
450 
451 	if (strcasecmp("errorline", field) == 0)
452 		return ascii_load_string(ep->errorline, buf,
453 		    sizeof ep->errorline);
454 
455 	if (strcasecmp("expire", field) == 0)
456 		return ascii_load_time(&ep->expire, buf);
457 
458 	if (strcasecmp("flags", field) == 0)
459 		return ascii_load_flags(&ep->flags, buf);
460 
461 	if (strcasecmp("helo", field) == 0)
462 		return ascii_load_string(ep->helo, buf, sizeof ep->helo);
463 
464 	if (strcasecmp("hostname", field) == 0)
465 		return ascii_load_string(ep->hostname, buf,
466 		    sizeof ep->hostname);
467 
468 	if (strcasecmp("last-bounce", field) == 0)
469 		return ascii_load_time(&ep->lastbounce, buf);
470 
471 	if (strcasecmp("last-try", field) == 0)
472 		return ascii_load_time(&ep->lasttry, buf);
473 
474 	if (strcasecmp("mda-buffer", field) == 0)
475 		return ascii_load_string(ep->agent.mda.buffer, buf,
476 		    sizeof ep->agent.mda.buffer);
477 
478 	if (strcasecmp("mda-method", field) == 0)
479 		return ascii_load_mda_method(&ep->agent.mda.method, buf);
480 
481 	if (strcasecmp("mda-user", field) == 0)
482 		return ascii_load_string(ep->agent.mda.username, buf,
483 		    sizeof ep->agent.mda.username);
484 
485 	if (strcasecmp("mda-usertable", field) == 0)
486 		return ascii_load_string(ep->agent.mda.usertable, buf,
487 		    sizeof ep->agent.mda.usertable);
488 
489 	if (strcasecmp("mta-relay", field) == 0) {
490 		int ret;
491 		uint16_t flags = ep->agent.mta.relay.flags;
492 		ret = ascii_load_mta_relay_url(&ep->agent.mta.relay, buf);
493 		if (! ret)
494 			return (0);
495 		ep->agent.mta.relay.flags |= flags;
496 		return ret;
497 	}
498 
499 	if (strcasecmp("mta-relay-auth", field) == 0)
500 		return ascii_load_string(ep->agent.mta.relay.authtable, buf,
501 		    sizeof ep->agent.mta.relay.authtable);
502 
503 	if (strcasecmp("mta-relay-cert", field) == 0)
504 		return ascii_load_string(ep->agent.mta.relay.pki_name, buf,
505 		    sizeof ep->agent.mta.relay.pki_name);
506 
507 	if (strcasecmp("mta-relay-flags", field) == 0)
508 		return ascii_load_mta_relay_flags(&ep->agent.mta.relay.flags, buf);
509 
510 	if (strcasecmp("mta-relay-heloname", field) == 0)
511 		return ascii_load_string(ep->agent.mta.relay.heloname, buf,
512 		    sizeof ep->agent.mta.relay.heloname);
513 
514 	if (strcasecmp("mta-relay-helotable", field) == 0)
515 		return ascii_load_string(ep->agent.mta.relay.helotable, buf,
516 		    sizeof ep->agent.mta.relay.helotable);
517 
518 	if (strcasecmp("mta-relay-source", field) == 0)
519 		return ascii_load_string(ep->agent.mta.relay.sourcetable, buf,
520 		    sizeof ep->agent.mta.relay.sourcetable);
521 
522 	if (strcasecmp("retry", field) == 0)
523 		return ascii_load_uint16(&ep->retry, buf);
524 
525 	if (strcasecmp("rcpt", field) == 0)
526 		return ascii_load_mailaddr(&ep->rcpt, buf);
527 
528 	if (strcasecmp("sender", field) == 0)
529 		return ascii_load_mailaddr(&ep->sender, buf);
530 
531 	if (strcasecmp("smtpname", field) == 0)
532 		return ascii_load_string(ep->smtpname, buf, sizeof(ep->smtpname));
533 
534 	if (strcasecmp("sockaddr", field) == 0)
535 		return ascii_load_sockaddr(&ep->ss, buf);
536 
537 	if (strcasecmp("tag", field) == 0)
538 		return ascii_load_string(ep->tag, buf, sizeof ep->tag);
539 
540 	if (strcasecmp("type", field) == 0)
541 		return ascii_load_type(&ep->type, buf);
542 
543 	if (strcasecmp("version", field) == 0)
544 		return ascii_load_uint32(&ep->version, buf);
545 
546 	if (strcasecmp("dsn-notify", field) == 0)
547 		return ascii_load_uint8(&ep->dsn_notify, buf);
548 
549 	if (strcasecmp("dsn-orcpt", field) == 0)
550 		return ascii_load_mailaddr(&ep->dsn_orcpt, buf);
551 
552 	if (strcasecmp("dsn-ret", field) == 0)
553 		return ascii_load_dsn_ret(&ep->dsn_ret, buf);
554 
555 	if (strcasecmp("dsn-envid", field) == 0)
556 		return ascii_load_string(ep->dsn_envid, buf, sizeof(ep->dsn_envid));
557 
558 	if (strcasecmp("esc-class", field) == 0)
559 		return ascii_load_uint8(&ep->esc_class, buf);
560 
561 	if (strcasecmp("esc-code", field) == 0)
562 		return ascii_load_uint8(&ep->esc_code, buf);
563 
564 	return (0);
565 }
566 
567 static int
568 envelope_ascii_load(struct envelope *ep, struct dict *d)
569 {
570 	const char	       *field;
571 	char		       *value;
572 	void		       *hdl;
573 
574 	hdl = NULL;
575 	while (dict_iter(d, &hdl, &field, (void **)&value))
576 		if (! ascii_load_field(field, ep, value))
577 			goto err;
578 
579 	return (1);
580 
581 err:
582 	log_warnx("envelope: invalid field \"%s\"", field);
583 	return (0);
584 }
585 
586 
587 static int
588 ascii_dump_uint8(uint8_t src, char *dest, size_t len)
589 {
590 	return bsnprintf(dest, len, "%d", src);
591 }
592 
593 static int
594 ascii_dump_uint16(uint16_t src, char *dest, size_t len)
595 {
596 	return bsnprintf(dest, len, "%d", src);
597 }
598 
599 static int
600 ascii_dump_uint32(uint32_t src, char *dest, size_t len)
601 {
602 	return bsnprintf(dest, len, "%d", src);
603 }
604 
605 static int
606 ascii_dump_time(time_t src, char *dest, size_t len)
607 {
608 	return bsnprintf(dest, len, "%lld", (long long) src);
609 }
610 
611 static int
612 ascii_dump_string(const char *src, char *dest, size_t len)
613 {
614 	return bsnprintf(dest, len, "%s", src);
615 }
616 
617 static int
618 ascii_dump_type(enum delivery_type type, char *dest, size_t len)
619 {
620 	char *p = NULL;
621 
622 	switch (type) {
623 	case D_MDA:
624 		p = "mda";
625 		break;
626 	case D_MTA:
627 		p = "mta";
628 		break;
629 	case D_BOUNCE:
630 		p = "bounce";
631 		break;
632 	default:
633 		return 0;
634 	}
635 
636 	return bsnprintf(dest, len, "%s", p);
637 }
638 
639 static int
640 ascii_dump_mda_method(enum action_type type, char *dest, size_t len)
641 {
642 	char *p = NULL;
643 
644 	switch (type) {
645 	case A_LMTP:
646 		p = "lmtp";
647 		break;
648 	case A_MAILDIR:
649 		p = "maildir";
650 		break;
651 	case A_MBOX:
652 		p = "mbox";
653 		break;
654 	case A_FILENAME:
655 		p = "filename";
656 		break;
657 	case A_MDA:
658 		p = "mda";
659 		break;
660 	default:
661 		return 0;
662 	}
663 	return bsnprintf(dest, len, "%s", p);
664 }
665 
666 static int
667 ascii_dump_mailaddr(const struct mailaddr *addr, char *dest, size_t len)
668 {
669 	return bsnprintf(dest, len, "%s@%s",
670 	    addr->user, addr->domain);
671 }
672 
673 static int
674 ascii_dump_flags(enum envelope_flags flags, char *buf, size_t len)
675 {
676 	size_t cpylen = 0;
677 
678 	buf[0] = '\0';
679 	if (flags) {
680 		if (flags & EF_AUTHENTICATED)
681 			cpylen = strlcat(buf, "authenticated", len);
682 		if (flags & EF_BOUNCE) {
683 			if (buf[0] != '\0')
684 				(void)strlcat(buf, " ", len);
685 			cpylen = strlcat(buf, "bounce", len);
686 		}
687 		if (flags & EF_INTERNAL) {
688 			if (buf[0] != '\0')
689 				(void)strlcat(buf, " ", len);
690 			cpylen = strlcat(buf, "internal", len);
691 		}
692 	}
693 
694 	return cpylen < len ? 1 : 0;
695 }
696 
697 static int
698 ascii_dump_mta_relay_url(const struct relayhost *relay, char *buf, size_t len)
699 {
700 	return bsnprintf(buf, len, "%s", relayhost_to_text(relay));
701 }
702 
703 static int
704 ascii_dump_mta_relay_flags(uint16_t flags, char *buf, size_t len)
705 {
706 	size_t cpylen = 0;
707 
708 	buf[0] = '\0';
709 	if (flags) {
710 		if (flags & F_TLS_VERIFY) {
711 			if (buf[0] != '\0')
712 				(void)strlcat(buf, " ", len);
713 			cpylen = strlcat(buf, "verify", len);
714 		}
715 		if (flags & F_STARTTLS) {
716 			if (buf[0] != '\0')
717 				(void)strlcat(buf, " ", len);
718 			cpylen = strlcat(buf, "tls", len);
719 		}
720 	}
721 
722 	return cpylen < len ? 1 : 0;
723 }
724 
725 static int
726 ascii_dump_bounce_type(enum bounce_type type, char *dest, size_t len)
727 {
728 	char *p = NULL;
729 
730 	switch (type) {
731 	case B_ERROR:
732 		p = "error";
733 		break;
734 	case B_WARNING:
735 		p = "warn";
736 		break;
737 	case B_DSN:
738 		p = "dsn";
739 		break;
740 	default:
741 		return 0;
742 	}
743 	return bsnprintf(dest, len, "%s", p);
744 }
745 
746 
747 static int
748 ascii_dump_dsn_ret(enum dsn_ret flag, char *dest, size_t len)
749 {
750         size_t cpylen = 0;
751 
752         dest[0] = '\0';
753         if (flag == DSN_RETFULL)
754                 cpylen = strlcat(dest, "FULL", len);
755         else if (flag == DSN_RETHDRS)
756                 cpylen = strlcat(dest, "HDRS", len);
757 
758         return cpylen < len ? 1 : 0;
759 }
760 
761 static int
762 ascii_dump_field(const char *field, const struct envelope *ep,
763     char *buf, size_t len)
764 {
765 	if (strcasecmp(field, "bounce-delay") == 0) {
766 		if (ep->agent.bounce.type != B_WARNING)
767 			return (1);
768 		return ascii_dump_time(ep->agent.bounce.delay, buf, len);
769 	}
770 
771 	if (strcasecmp(field, "bounce-expire") == 0) {
772 		if (ep->agent.bounce.type != B_WARNING)
773 			return (1);
774 		return ascii_dump_time(ep->agent.bounce.expire, buf, len);
775 	}
776 
777 	if (strcasecmp(field, "bounce-type") == 0)
778 		return ascii_dump_bounce_type(ep->agent.bounce.type, buf, len);
779 
780 	if (strcasecmp(field, "ctime") == 0)
781 		return ascii_dump_time(ep->creation, buf, len);
782 
783 	if (strcasecmp(field, "dest") == 0)
784 		return ascii_dump_mailaddr(&ep->dest, buf, len);
785 
786 	if (strcasecmp(field, "errorline") == 0)
787 		return ascii_dump_string(ep->errorline, buf, len);
788 
789 	if (strcasecmp(field, "expire") == 0)
790 		return ascii_dump_time(ep->expire, buf, len);
791 
792 	if (strcasecmp(field, "flags") == 0)
793 		return ascii_dump_flags(ep->flags, buf, len);
794 
795 	if (strcasecmp(field, "helo") == 0)
796 		return ascii_dump_string(ep->helo, buf, len);
797 
798 	if (strcasecmp(field, "hostname") == 0)
799 		return ascii_dump_string(ep->hostname, buf, len);
800 
801 	if (strcasecmp(field, "last-bounce") == 0)
802 		return ascii_dump_time(ep->lastbounce, buf, len);
803 
804 	if (strcasecmp(field, "last-try") == 0)
805 		return ascii_dump_time(ep->lasttry, buf, len);
806 
807 	if (strcasecmp(field, "mda-buffer") == 0)
808 		return ascii_dump_string(ep->agent.mda.buffer, buf, len);
809 
810 	if (strcasecmp(field, "mda-method") == 0)
811 		return ascii_dump_mda_method(ep->agent.mda.method, buf, len);
812 
813 	if (strcasecmp(field, "mda-user") == 0)
814 		return ascii_dump_string(ep->agent.mda.username, buf, len);
815 
816 	if (strcasecmp(field, "mda-usertable") == 0)
817 		return ascii_dump_string(ep->agent.mda.usertable, buf, len);
818 
819 	if (strcasecmp(field, "mta-relay") == 0) {
820 		if (ep->agent.mta.relay.hostname[0])
821 			return ascii_dump_mta_relay_url(&ep->agent.mta.relay, buf, len);
822 		return (1);
823 	}
824 
825 	if (strcasecmp(field, "mta-relay-auth") == 0)
826 		return ascii_dump_string(ep->agent.mta.relay.authtable,
827 		    buf, len);
828 
829 	if (strcasecmp(field, "mta-relay-cert") == 0)
830 		return ascii_dump_string(ep->agent.mta.relay.pki_name,
831 		    buf, len);
832 
833 	if (strcasecmp(field, "mta-relay-flags") == 0)
834 		return ascii_dump_mta_relay_flags(ep->agent.mta.relay.flags,
835 		    buf, len);
836 
837 	if (strcasecmp(field, "mta-relay-heloname") == 0)
838 		return ascii_dump_string(ep->agent.mta.relay.heloname,
839 		    buf, len);
840 
841 	if (strcasecmp(field, "mta-relay-helotable") == 0)
842 		return ascii_dump_string(ep->agent.mta.relay.helotable,
843 		    buf, len);
844 
845 	if (strcasecmp(field, "mta-relay-source") == 0)
846 		return ascii_dump_string(ep->agent.mta.relay.sourcetable,
847 		    buf, len);
848 
849 	if (strcasecmp(field, "retry") == 0)
850 		return ascii_dump_uint16(ep->retry, buf, len);
851 
852 	if (strcasecmp(field, "rcpt") == 0)
853 		return ascii_dump_mailaddr(&ep->rcpt, buf, len);
854 
855 	if (strcasecmp(field, "sender") == 0)
856 		return ascii_dump_mailaddr(&ep->sender, buf, len);
857 
858 	if (strcasecmp(field, "smtpname") == 0)
859 		return ascii_dump_string(ep->smtpname, buf, len);
860 
861 	if (strcasecmp(field, "sockaddr") == 0)
862 		return ascii_dump_string(ss_to_text(&ep->ss), buf, len);
863 
864 	if (strcasecmp(field, "tag") == 0)
865 		return ascii_dump_string(ep->tag, buf, len);
866 
867 	if (strcasecmp(field, "type") == 0)
868 		return ascii_dump_type(ep->type, buf, len);
869 
870 	if (strcasecmp(field, "version") == 0)
871 		return ascii_dump_uint32(SMTPD_ENVELOPE_VERSION, buf, len);
872 
873 	if (strcasecmp(field, "dsn-notify") == 0)
874 		return ascii_dump_uint8(ep->dsn_notify, buf, len);
875 
876 	if (strcasecmp(field, "dsn-ret") == 0)
877 		return ascii_dump_dsn_ret(ep->dsn_ret, buf, len);
878 
879 	if (strcasecmp(field, "dsn-orcpt") == 0) {
880 		if (ep->dsn_orcpt.user[0] && ep->dsn_orcpt.domain[0])
881 			return ascii_dump_mailaddr(&ep->dsn_orcpt, buf, len);
882 		return 1;
883 	}
884 
885 	if (strcasecmp(field, "dsn-envid") == 0)
886 		return ascii_dump_string(ep->dsn_envid, buf, len);
887 
888 	if (strcasecmp(field, "esc-class") == 0) {
889 		if (ep->esc_class)
890 			return ascii_dump_uint8(ep->esc_class, buf, len);
891 		return 1;
892 	}
893 
894 	if (strcasecmp(field, "esc-code") == 0) {
895 		if (ep->esc_class)
896 			return ascii_dump_uint8(ep->esc_code, buf, len);
897 		return 1;
898 	}
899 
900 	return (0);
901 }
902 
903 static void
904 envelope_ascii_dump(const struct envelope *ep, char **dest, size_t *len, const char *field)
905 {
906 	char	buf[8192];
907 	int	l;
908 
909 	if (*dest == NULL)
910 		return;
911 
912 	memset(buf, 0, sizeof buf);
913 	if (! ascii_dump_field(field, ep, buf, sizeof buf))
914 		goto err;
915 	if (buf[0] == '\0')
916 		return;
917 
918 	l = snprintf(*dest, *len, "%s: %s\n", field, buf);
919 	if (l == -1 || (size_t) l >= *len)
920 		goto err;
921 	*dest += l;
922 	*len -= l;
923 
924 	return;
925 err:
926 	*dest = NULL;
927 }
928 
929 static int
930 envelope_upgrade_v1(struct dict *d)
931 {
932 	static char	 buf_relay[1024];
933 	char		*val;
934 
935 	/*
936 	 * very very old envelopes had a "msgid" field
937 	 */
938 	dict_pop(d, "msgid");
939 
940 	/*
941 	 * rename "mta-relay-helo" field to "mta-relay-helotable"
942 	 */
943 	if ((val = dict_get(d, "mta-relay-helo"))) {
944 		dict_xset(d, "mta-relay-helotable", val);
945 		dict_xpop(d, "mta-relay-helo");
946 	}
947 
948 	/*
949 	 * "ssl" becomes "secure" in "mta-relay" scheme
950 	 */
951 	if ((val = dict_get(d, "mta-relay"))) {
952 		if (strncasecmp("ssl://", val, 6) == 0) {
953 			if (! bsnprintf(buf_relay, sizeof(buf_relay), "secure://%s", val+6))
954 				return (0);
955 			dict_set(d, "mta-relay", buf_relay);
956 		}
957 		else if (strncasecmp("ssl+auth://", val, 11) == 0) {
958 			if (! bsnprintf(buf_relay, sizeof(buf_relay), "secure+auth://%s", val+11))
959 				return (0);
960 			dict_set(d, "mta-relay", buf_relay);
961 		}
962 	}
963 
964 	return (1);
965 }
966