1 /* $OpenBSD: envelope.c,v 1.52 2024/01/03 08:11:15 op 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 <arpa/inet.h>
21
22 #include <ctype.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "smtpd.h"
27 #include "log.h"
28
29 static int envelope_ascii_load(struct envelope *, struct dict *);
30 static void envelope_ascii_dump(const struct envelope *, char **, size_t *,
31 const char *);
32
33 void
envelope_set_errormsg(struct envelope * e,char * fmt,...)34 envelope_set_errormsg(struct envelope *e, char *fmt, ...)
35 {
36 int ret;
37 va_list ap;
38
39 va_start(ap, fmt);
40 ret = vsnprintf(e->errorline, sizeof(e->errorline), fmt, ap);
41 va_end(ap);
42
43 /* this should not happen */
44 if (ret < 0)
45 fatal("vsnprintf");
46
47 if ((size_t)ret >= sizeof(e->errorline))
48 (void)strlcpy(e->errorline + (sizeof(e->errorline) - 4),
49 "...", 4);
50 }
51
52 void
envelope_set_esc_class(struct envelope * e,enum enhanced_status_class class)53 envelope_set_esc_class(struct envelope *e, enum enhanced_status_class class)
54 {
55 e->esc_class = class;
56 }
57
58 void
envelope_set_esc_code(struct envelope * e,enum enhanced_status_code code)59 envelope_set_esc_code(struct envelope *e, enum enhanced_status_code code)
60 {
61 e->esc_code = code;
62 }
63
64 static int
envelope_buffer_to_dict(struct dict * d,const char * ibuf,size_t buflen)65 envelope_buffer_to_dict(struct dict *d, const char *ibuf, size_t buflen)
66 {
67 static char lbuf[sizeof(struct envelope)];
68 size_t len;
69 char *buf, *field, *nextline;
70
71 memset(lbuf, 0, sizeof lbuf);
72 if (strlcpy(lbuf, ibuf, sizeof lbuf) >= sizeof lbuf)
73 goto err;
74 buf = lbuf;
75
76 while (buflen > 0) {
77 len = strcspn(buf, "\n");
78 buf[len] = '\0';
79 nextline = buf + len + 1;
80 buflen -= (nextline - buf);
81
82 field = buf;
83 while (*buf && (isalnum((unsigned char)*buf) || *buf == '-'))
84 buf++;
85 if (!*buf)
86 goto err;
87
88 /* skip whitespaces before separator */
89 while (*buf && isspace((unsigned char)*buf))
90 *buf++ = 0;
91
92 /* we *want* ':' */
93 if (*buf != ':')
94 goto err;
95 *buf++ = 0;
96
97 /* skip whitespaces after separator */
98 while (*buf && isspace((unsigned char)*buf))
99 *buf++ = 0;
100 dict_set(d, field, buf);
101 buf = nextline;
102 }
103
104 return (1);
105
106 err:
107 return (0);
108 }
109
110 int
envelope_load_buffer(struct envelope * ep,const char * ibuf,size_t buflen)111 envelope_load_buffer(struct envelope *ep, const char *ibuf, size_t buflen)
112 {
113 struct dict d;
114 const char *val, *errstr;
115 long long version;
116 int ret = 0;
117
118 dict_init(&d);
119 if (!envelope_buffer_to_dict(&d, ibuf, buflen)) {
120 log_debug("debug: cannot parse envelope to dict");
121 goto end;
122 }
123
124 val = dict_get(&d, "version");
125 if (val == NULL) {
126 log_debug("debug: envelope version not found");
127 goto end;
128 }
129 version = strtonum(val, 1, 64, &errstr);
130 if (errstr) {
131 log_debug("debug: cannot parse envelope version: %s", val);
132 goto end;
133 }
134
135 if (version != SMTPD_ENVELOPE_VERSION) {
136 log_debug("debug: bad envelope version %lld", version);
137 goto end;
138 }
139
140 memset(ep, 0, sizeof *ep);
141 ret = envelope_ascii_load(ep, &d);
142 if (ret)
143 ep->version = SMTPD_ENVELOPE_VERSION;
144 end:
145 while (dict_poproot(&d, NULL))
146 ;
147 return (ret);
148 }
149
150 int
envelope_dump_buffer(const struct envelope * ep,char * dest,size_t len)151 envelope_dump_buffer(const struct envelope *ep, char *dest, size_t len)
152 {
153 char *p = dest;
154
155 envelope_ascii_dump(ep, &dest, &len, "version");
156 envelope_ascii_dump(ep, &dest, &len, "dispatcher");
157 envelope_ascii_dump(ep, &dest, &len, "tag");
158 envelope_ascii_dump(ep, &dest, &len, "type");
159 envelope_ascii_dump(ep, &dest, &len, "smtpname");
160 envelope_ascii_dump(ep, &dest, &len, "helo");
161 envelope_ascii_dump(ep, &dest, &len, "hostname");
162 envelope_ascii_dump(ep, &dest, &len, "username");
163 envelope_ascii_dump(ep, &dest, &len, "errorline");
164 envelope_ascii_dump(ep, &dest, &len, "sockaddr");
165 envelope_ascii_dump(ep, &dest, &len, "sender");
166 envelope_ascii_dump(ep, &dest, &len, "rcpt");
167 envelope_ascii_dump(ep, &dest, &len, "dest");
168 envelope_ascii_dump(ep, &dest, &len, "ctime");
169 envelope_ascii_dump(ep, &dest, &len, "last-try");
170 envelope_ascii_dump(ep, &dest, &len, "last-bounce");
171 envelope_ascii_dump(ep, &dest, &len, "ttl");
172 envelope_ascii_dump(ep, &dest, &len, "retry");
173 envelope_ascii_dump(ep, &dest, &len, "flags");
174 envelope_ascii_dump(ep, &dest, &len, "dsn-notify");
175 envelope_ascii_dump(ep, &dest, &len, "dsn-ret");
176 envelope_ascii_dump(ep, &dest, &len, "dsn-envid");
177 envelope_ascii_dump(ep, &dest, &len, "dsn-orcpt");
178 envelope_ascii_dump(ep, &dest, &len, "esc-class");
179 envelope_ascii_dump(ep, &dest, &len, "esc-code");
180
181 switch (ep->type) {
182 case D_MDA:
183 envelope_ascii_dump(ep, &dest, &len, "mda-exec");
184 envelope_ascii_dump(ep, &dest, &len, "mda-subaddress");
185 envelope_ascii_dump(ep, &dest, &len, "mda-user");
186 break;
187 case D_MTA:
188 break;
189 case D_BOUNCE:
190 envelope_ascii_dump(ep, &dest, &len, "bounce-ttl");
191 envelope_ascii_dump(ep, &dest, &len, "bounce-delay");
192 envelope_ascii_dump(ep, &dest, &len, "bounce-type");
193 break;
194 default:
195 return (0);
196 }
197
198 if (dest == NULL)
199 return (0);
200
201 return (dest - p);
202 }
203
204 static int
ascii_load_uint8(uint8_t * dest,char * buf)205 ascii_load_uint8(uint8_t *dest, char *buf)
206 {
207 const char *errstr;
208
209 *dest = strtonum(buf, 0, 0xff, &errstr);
210 if (errstr)
211 return 0;
212 return 1;
213 }
214
215 static int
ascii_load_uint16(uint16_t * dest,char * buf)216 ascii_load_uint16(uint16_t *dest, char *buf)
217 {
218 const char *errstr;
219
220 *dest = strtonum(buf, 0, 0xffff, &errstr);
221 if (errstr)
222 return 0;
223 return 1;
224 }
225
226 static int
ascii_load_uint32(uint32_t * dest,char * buf)227 ascii_load_uint32(uint32_t *dest, char *buf)
228 {
229 const char *errstr;
230
231 *dest = strtonum(buf, 0, 0xffffffff, &errstr);
232 if (errstr)
233 return 0;
234 return 1;
235 }
236
237 static int
ascii_load_time(time_t * dest,char * buf)238 ascii_load_time(time_t *dest, char *buf)
239 {
240 const char *errstr;
241
242 *dest = strtonum(buf, 0, LLONG_MAX, &errstr);
243 if (errstr)
244 return 0;
245 return 1;
246 }
247
248 static int
ascii_load_type(enum delivery_type * dest,char * buf)249 ascii_load_type(enum delivery_type *dest, char *buf)
250 {
251 if (strcasecmp(buf, "mda") == 0)
252 *dest = D_MDA;
253 else if (strcasecmp(buf, "mta") == 0)
254 *dest = D_MTA;
255 else if (strcasecmp(buf, "bounce") == 0)
256 *dest = D_BOUNCE;
257 else
258 return 0;
259 return 1;
260 }
261
262 static int
ascii_load_string(char * dest,char * buf,size_t len)263 ascii_load_string(char *dest, char *buf, size_t len)
264 {
265 if (strlcpy(dest, buf, len) >= len)
266 return 0;
267 return 1;
268 }
269
270 static int
ascii_load_sockaddr(struct sockaddr_storage * ss,char * buf)271 ascii_load_sockaddr(struct sockaddr_storage *ss, char *buf)
272 {
273 if (!strcmp("local", buf)) {
274 ss->ss_family = AF_LOCAL;
275 }
276 else if (buf[0] == '[' && buf[strlen(buf)-1] == ']') {
277 struct addrinfo hints, *res0;
278
279 buf[strlen(buf)-1] = '\0';
280
281 /* getaddrinfo() is used to support scoped addresses. */
282 memset(&hints, 0, sizeof(hints));
283 hints.ai_family = AF_INET6;
284 hints.ai_flags = AI_NUMERICHOST;
285 if (getaddrinfo(buf+1, NULL, &hints, &res0) != 0)
286 return 0;
287 memcpy(ss, res0->ai_addr, res0->ai_addrlen);
288 ss->ss_len = res0->ai_addrlen;
289 freeaddrinfo(res0);
290 }
291 else {
292 struct sockaddr_in ssin;
293
294 memset(&ssin, 0, sizeof ssin);
295 if (inet_pton(AF_INET, buf, &ssin.sin_addr) != 1)
296 return 0;
297 ssin.sin_family = AF_INET;
298 memcpy(ss, &ssin, sizeof(ssin));
299 ss->ss_len = sizeof(struct sockaddr_in);
300 }
301 return 1;
302 }
303
304 static int
ascii_load_mailaddr(struct mailaddr * dest,char * buf)305 ascii_load_mailaddr(struct mailaddr *dest, char *buf)
306 {
307 if (!text_to_mailaddr(dest, buf))
308 return 0;
309 return 1;
310 }
311
312 static int
ascii_load_flags(enum envelope_flags * dest,char * buf)313 ascii_load_flags(enum envelope_flags *dest, char *buf)
314 {
315 char *flag;
316
317 while ((flag = strsep(&buf, " ,|")) != NULL) {
318 if (strcasecmp(flag, "authenticated") == 0)
319 *dest |= EF_AUTHENTICATED;
320 else if (strcasecmp(flag, "enqueued") == 0)
321 ;
322 else if (strcasecmp(flag, "bounce") == 0)
323 *dest |= EF_BOUNCE;
324 else if (strcasecmp(flag, "internal") == 0)
325 *dest |= EF_INTERNAL;
326 else
327 return 0;
328 }
329 return 1;
330 }
331
332 static int
ascii_load_bounce_type(enum bounce_type * dest,char * buf)333 ascii_load_bounce_type(enum bounce_type *dest, char *buf)
334 {
335 if (strcasecmp(buf, "error") == 0 || strcasecmp(buf, "failed") == 0)
336 *dest = B_FAILED;
337 else if (strcasecmp(buf, "warn") == 0 ||
338 strcasecmp(buf, "delayed") == 0)
339 *dest = B_DELAYED;
340 else if (strcasecmp(buf, "dsn") == 0 ||
341 strcasecmp(buf, "delivered") == 0)
342 *dest = B_DELIVERED;
343 else
344 return 0;
345 return 1;
346 }
347
348 static int
ascii_load_dsn_ret(enum dsn_ret * ret,char * buf)349 ascii_load_dsn_ret(enum dsn_ret *ret, char *buf)
350 {
351 if (strcasecmp(buf, "HDRS") == 0)
352 *ret = DSN_RETHDRS;
353 else if (strcasecmp(buf, "FULL") == 0)
354 *ret = DSN_RETFULL;
355 else
356 return 0;
357 return 1;
358 }
359
360 static int
ascii_load_field(const char * field,struct envelope * ep,char * buf)361 ascii_load_field(const char *field, struct envelope *ep, char *buf)
362 {
363 if (strcasecmp("dispatcher", field) == 0)
364 return ascii_load_string(ep->dispatcher, buf,
365 sizeof ep->dispatcher);
366
367 if (strcasecmp("bounce-delay", field) == 0)
368 return ascii_load_time(&ep->agent.bounce.delay, buf);
369
370 if (strcasecmp("bounce-ttl", field) == 0)
371 return ascii_load_time(&ep->agent.bounce.ttl, buf);
372
373 if (strcasecmp("bounce-type", field) == 0)
374 return ascii_load_bounce_type(&ep->agent.bounce.type, buf);
375
376 if (strcasecmp("ctime", field) == 0)
377 return ascii_load_time(&ep->creation, buf);
378
379 if (strcasecmp("dest", field) == 0)
380 return ascii_load_mailaddr(&ep->dest, buf);
381
382 if (strcasecmp("username", field) == 0)
383 return ascii_load_string(ep->username, buf, sizeof(ep->username));
384
385 if (strcasecmp("errorline", field) == 0)
386 return ascii_load_string(ep->errorline, buf,
387 sizeof ep->errorline);
388
389 if (strcasecmp("ttl", field) == 0)
390 return ascii_load_time(&ep->ttl, buf);
391
392 if (strcasecmp("flags", field) == 0)
393 return ascii_load_flags(&ep->flags, buf);
394
395 if (strcasecmp("helo", field) == 0)
396 return ascii_load_string(ep->helo, buf, sizeof ep->helo);
397
398 if (strcasecmp("hostname", field) == 0)
399 return ascii_load_string(ep->hostname, buf,
400 sizeof ep->hostname);
401
402 if (strcasecmp("last-bounce", field) == 0)
403 return ascii_load_time(&ep->lastbounce, buf);
404
405 if (strcasecmp("last-try", field) == 0)
406 return ascii_load_time(&ep->lasttry, buf);
407
408 if (strcasecmp("retry", field) == 0)
409 return ascii_load_uint16(&ep->retry, buf);
410
411 if (strcasecmp("rcpt", field) == 0)
412 return ascii_load_mailaddr(&ep->rcpt, buf);
413
414 if (strcasecmp("mda-exec", field) == 0)
415 return ascii_load_string(ep->mda_exec, buf, sizeof(ep->mda_exec));
416
417 if (strcasecmp("mda-subaddress", field) == 0)
418 return ascii_load_string(ep->mda_subaddress, buf, sizeof(ep->mda_subaddress));
419
420 if (strcasecmp("mda-user", field) == 0)
421 return ascii_load_string(ep->mda_user, buf, sizeof(ep->mda_user));
422
423 if (strcasecmp("sender", field) == 0)
424 return ascii_load_mailaddr(&ep->sender, buf);
425
426 if (strcasecmp("smtpname", field) == 0)
427 return ascii_load_string(ep->smtpname, buf,
428 sizeof(ep->smtpname));
429
430 if (strcasecmp("sockaddr", field) == 0)
431 return ascii_load_sockaddr(&ep->ss, buf);
432
433 if (strcasecmp("tag", field) == 0)
434 return ascii_load_string(ep->tag, buf, sizeof ep->tag);
435
436 if (strcasecmp("type", field) == 0)
437 return ascii_load_type(&ep->type, buf);
438
439 if (strcasecmp("version", field) == 0)
440 return ascii_load_uint32(&ep->version, buf);
441
442 if (strcasecmp("dsn-notify", field) == 0)
443 return ascii_load_uint8(&ep->dsn_notify, buf);
444
445 if (strcasecmp("dsn-orcpt", field) == 0)
446 return ascii_load_string(ep->dsn_orcpt, buf,
447 sizeof(ep->dsn_orcpt));
448
449 if (strcasecmp("dsn-ret", field) == 0)
450 return ascii_load_dsn_ret(&ep->dsn_ret, buf);
451
452 if (strcasecmp("dsn-envid", field) == 0)
453 return ascii_load_string(ep->dsn_envid, buf,
454 sizeof(ep->dsn_envid));
455
456 if (strcasecmp("esc-class", field) == 0)
457 return ascii_load_uint8(&ep->esc_class, buf);
458
459 if (strcasecmp("esc-code", field) == 0)
460 return ascii_load_uint8(&ep->esc_code, buf);
461
462 return (0);
463 }
464
465 static int
envelope_ascii_load(struct envelope * ep,struct dict * d)466 envelope_ascii_load(struct envelope *ep, struct dict *d)
467 {
468 const char *field;
469 char *value;
470 void *hdl;
471
472 hdl = NULL;
473 while (dict_iter(d, &hdl, &field, (void **)&value))
474 if (!ascii_load_field(field, ep, value))
475 goto err;
476
477 return (1);
478
479 err:
480 log_warnx("envelope: invalid field \"%s\"", field);
481 return (0);
482 }
483
484
485 static int
ascii_dump_uint8(uint8_t src,char * dest,size_t len)486 ascii_dump_uint8(uint8_t src, char *dest, size_t len)
487 {
488 return bsnprintf(dest, len, "%d", src);
489 }
490
491 static int
ascii_dump_uint16(uint16_t src,char * dest,size_t len)492 ascii_dump_uint16(uint16_t src, char *dest, size_t len)
493 {
494 return bsnprintf(dest, len, "%d", src);
495 }
496
497 static int
ascii_dump_uint32(uint32_t src,char * dest,size_t len)498 ascii_dump_uint32(uint32_t src, char *dest, size_t len)
499 {
500 return bsnprintf(dest, len, "%d", src);
501 }
502
503 static int
ascii_dump_time(time_t src,char * dest,size_t len)504 ascii_dump_time(time_t src, char *dest, size_t len)
505 {
506 return bsnprintf(dest, len, "%lld", (long long) src);
507 }
508
509 static int
ascii_dump_string(const char * src,char * dest,size_t len)510 ascii_dump_string(const char *src, char *dest, size_t len)
511 {
512 return bsnprintf(dest, len, "%s", src);
513 }
514
515 static int
ascii_dump_type(enum delivery_type type,char * dest,size_t len)516 ascii_dump_type(enum delivery_type type, char *dest, size_t len)
517 {
518 char *p = NULL;
519
520 switch (type) {
521 case D_MDA:
522 p = "mda";
523 break;
524 case D_MTA:
525 p = "mta";
526 break;
527 case D_BOUNCE:
528 p = "bounce";
529 break;
530 default:
531 return 0;
532 }
533
534 return bsnprintf(dest, len, "%s", p);
535 }
536
537 static int
ascii_dump_mailaddr(const struct mailaddr * addr,char * dest,size_t len)538 ascii_dump_mailaddr(const struct mailaddr *addr, char *dest, size_t len)
539 {
540 return bsnprintf(dest, len, "%s@%s",
541 addr->user, addr->domain);
542 }
543
544 static int
ascii_dump_flags(enum envelope_flags flags,char * buf,size_t len)545 ascii_dump_flags(enum envelope_flags flags, char *buf, size_t len)
546 {
547 size_t cpylen = 0;
548
549 buf[0] = '\0';
550 if (flags) {
551 if (flags & EF_AUTHENTICATED)
552 cpylen = strlcat(buf, "authenticated", len);
553 if (flags & EF_BOUNCE) {
554 if (buf[0] != '\0')
555 (void)strlcat(buf, " ", len);
556 cpylen = strlcat(buf, "bounce", len);
557 }
558 if (flags & EF_INTERNAL) {
559 if (buf[0] != '\0')
560 (void)strlcat(buf, " ", len);
561 cpylen = strlcat(buf, "internal", len);
562 }
563 }
564
565 return cpylen < len ? 1 : 0;
566 }
567
568 static int
ascii_dump_bounce_type(enum bounce_type type,char * dest,size_t len)569 ascii_dump_bounce_type(enum bounce_type type, char *dest, size_t len)
570 {
571 char *p = NULL;
572
573 switch (type) {
574 case B_FAILED:
575 p = "failed";
576 break;
577 case B_DELAYED:
578 p = "delayed";
579 break;
580 case B_DELIVERED:
581 p = "delivered";
582 break;
583 default:
584 return 0;
585 }
586 return bsnprintf(dest, len, "%s", p);
587 }
588
589
590 static int
ascii_dump_dsn_ret(enum dsn_ret flag,char * dest,size_t len)591 ascii_dump_dsn_ret(enum dsn_ret flag, char *dest, size_t len)
592 {
593 size_t cpylen = 0;
594
595 dest[0] = '\0';
596 if (flag == DSN_RETFULL)
597 cpylen = strlcat(dest, "FULL", len);
598 else if (flag == DSN_RETHDRS)
599 cpylen = strlcat(dest, "HDRS", len);
600
601 return cpylen < len ? 1 : 0;
602 }
603
604 static int
ascii_dump_field(const char * field,const struct envelope * ep,char * buf,size_t len)605 ascii_dump_field(const char *field, const struct envelope *ep,
606 char *buf, size_t len)
607 {
608 if (strcasecmp(field, "dispatcher") == 0)
609 return ascii_dump_string(ep->dispatcher, buf, len);
610
611 if (strcasecmp(field, "bounce-delay") == 0) {
612 if (ep->agent.bounce.type != B_DELAYED)
613 return (1);
614 return ascii_dump_time(ep->agent.bounce.delay, buf, len);
615 }
616
617 if (strcasecmp(field, "bounce-ttl") == 0) {
618 if (ep->agent.bounce.type != B_DELAYED)
619 return (1);
620 return ascii_dump_time(ep->agent.bounce.ttl, buf, len);
621 }
622
623 if (strcasecmp(field, "bounce-type") == 0)
624 return ascii_dump_bounce_type(ep->agent.bounce.type, buf, len);
625
626 if (strcasecmp(field, "ctime") == 0)
627 return ascii_dump_time(ep->creation, buf, len);
628
629 if (strcasecmp(field, "dest") == 0)
630 return ascii_dump_mailaddr(&ep->dest, buf, len);
631
632 if (strcasecmp(field, "username") == 0) {
633 if (ep->username[0])
634 return ascii_dump_string(ep->username, buf, len);
635 return 1;
636 }
637
638 if (strcasecmp(field, "errorline") == 0)
639 return ascii_dump_string(ep->errorline, buf, len);
640
641 if (strcasecmp(field, "ttl") == 0)
642 return ascii_dump_time(ep->ttl, buf, len);
643
644 if (strcasecmp(field, "flags") == 0)
645 return ascii_dump_flags(ep->flags, buf, len);
646
647 if (strcasecmp(field, "helo") == 0)
648 return ascii_dump_string(ep->helo, buf, len);
649
650 if (strcasecmp(field, "hostname") == 0)
651 return ascii_dump_string(ep->hostname, buf, len);
652
653 if (strcasecmp(field, "last-bounce") == 0)
654 return ascii_dump_time(ep->lastbounce, buf, len);
655
656 if (strcasecmp(field, "last-try") == 0)
657 return ascii_dump_time(ep->lasttry, buf, len);
658
659 if (strcasecmp(field, "retry") == 0)
660 return ascii_dump_uint16(ep->retry, buf, len);
661
662 if (strcasecmp(field, "rcpt") == 0)
663 return ascii_dump_mailaddr(&ep->rcpt, buf, len);
664
665 if (strcasecmp(field, "mda-exec") == 0) {
666 if (ep->mda_exec[0])
667 return ascii_dump_string(ep->mda_exec, buf, len);
668 return 1;
669 }
670
671 if (strcasecmp(field, "mda-subaddress") == 0) {
672 if (ep->mda_subaddress[0])
673 return ascii_dump_string(ep->mda_subaddress, buf, len);
674 return 1;
675 }
676
677 if (strcasecmp(field, "mda-user") == 0) {
678 if (ep->mda_user[0])
679 return ascii_dump_string(ep->mda_user, buf, len);
680 return 1;
681 }
682
683 if (strcasecmp(field, "sender") == 0)
684 return ascii_dump_mailaddr(&ep->sender, buf, len);
685
686 if (strcasecmp(field, "smtpname") == 0)
687 return ascii_dump_string(ep->smtpname, buf, len);
688
689 if (strcasecmp(field, "sockaddr") == 0)
690 return ascii_dump_string(ss_to_text(&ep->ss), buf, len);
691
692 if (strcasecmp(field, "tag") == 0)
693 return ascii_dump_string(ep->tag, buf, len);
694
695 if (strcasecmp(field, "type") == 0)
696 return ascii_dump_type(ep->type, buf, len);
697
698 if (strcasecmp(field, "version") == 0)
699 return ascii_dump_uint32(SMTPD_ENVELOPE_VERSION, buf, len);
700
701 if (strcasecmp(field, "dsn-notify") == 0)
702 return ascii_dump_uint8(ep->dsn_notify, buf, len);
703
704 if (strcasecmp(field, "dsn-ret") == 0)
705 return ascii_dump_dsn_ret(ep->dsn_ret, buf, len);
706
707 if (strcasecmp(field, "dsn-orcpt") == 0)
708 return ascii_dump_string(ep->dsn_orcpt, buf, len);
709
710 if (strcasecmp(field, "dsn-envid") == 0)
711 return ascii_dump_string(ep->dsn_envid, buf, len);
712
713 if (strcasecmp(field, "esc-class") == 0) {
714 if (ep->esc_class)
715 return ascii_dump_uint8(ep->esc_class, buf, len);
716 return 1;
717 }
718
719 if (strcasecmp(field, "esc-code") == 0) {
720 /* this is not a pasto, we dump esc_code if esc_class is !0 */
721 if (ep->esc_class)
722 return ascii_dump_uint8(ep->esc_code, buf, len);
723 return 1;
724 }
725
726 return (0);
727 }
728
729 static void
envelope_ascii_dump(const struct envelope * ep,char ** dest,size_t * len,const char * field)730 envelope_ascii_dump(const struct envelope *ep, char **dest, size_t *len,
731 const char *field)
732 {
733 char buf[8192];
734 int l;
735
736 if (*dest == NULL)
737 return;
738
739 memset(buf, 0, sizeof buf);
740 if (!ascii_dump_field(field, ep, buf, sizeof buf))
741 goto err;
742 if (buf[0] == '\0')
743 return;
744
745 l = snprintf(*dest, *len, "%s: %s\n", field, buf);
746 if (l < 0 || (size_t) l >= *len)
747 goto err;
748 *dest += l;
749 *len -= l;
750
751 return;
752 err:
753 *dest = NULL;
754 }
755