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