1 /* $OpenBSD: envelope.c,v 1.51 2023/02/06 18:35:52 semarie 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 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 53 envelope_set_esc_class(struct envelope *e, enum enhanced_status_class class) 54 { 55 e->esc_class = class; 56 } 57 58 void 59 envelope_set_esc_code(struct envelope *e, enum enhanced_status_code code) 60 { 61 e->esc_code = code; 62 } 63 64 static int 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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_mailaddr(&ep->dsn_orcpt, buf); 447 448 if (strcasecmp("dsn-ret", field) == 0) 449 return ascii_load_dsn_ret(&ep->dsn_ret, buf); 450 451 if (strcasecmp("dsn-envid", field) == 0) 452 return ascii_load_string(ep->dsn_envid, buf, 453 sizeof(ep->dsn_envid)); 454 455 if (strcasecmp("esc-class", field) == 0) 456 return ascii_load_uint8(&ep->esc_class, buf); 457 458 if (strcasecmp("esc-code", field) == 0) 459 return ascii_load_uint8(&ep->esc_code, buf); 460 461 return (0); 462 } 463 464 static int 465 envelope_ascii_load(struct envelope *ep, struct dict *d) 466 { 467 const char *field; 468 char *value; 469 void *hdl; 470 471 hdl = NULL; 472 while (dict_iter(d, &hdl, &field, (void **)&value)) 473 if (!ascii_load_field(field, ep, value)) 474 goto err; 475 476 return (1); 477 478 err: 479 log_warnx("envelope: invalid field \"%s\"", field); 480 return (0); 481 } 482 483 484 static int 485 ascii_dump_uint8(uint8_t src, char *dest, size_t len) 486 { 487 return bsnprintf(dest, len, "%d", src); 488 } 489 490 static int 491 ascii_dump_uint16(uint16_t src, char *dest, size_t len) 492 { 493 return bsnprintf(dest, len, "%d", src); 494 } 495 496 static int 497 ascii_dump_uint32(uint32_t src, char *dest, size_t len) 498 { 499 return bsnprintf(dest, len, "%d", src); 500 } 501 502 static int 503 ascii_dump_time(time_t src, char *dest, size_t len) 504 { 505 return bsnprintf(dest, len, "%lld", (long long) src); 506 } 507 508 static int 509 ascii_dump_string(const char *src, char *dest, size_t len) 510 { 511 return bsnprintf(dest, len, "%s", src); 512 } 513 514 static int 515 ascii_dump_type(enum delivery_type type, char *dest, size_t len) 516 { 517 char *p = NULL; 518 519 switch (type) { 520 case D_MDA: 521 p = "mda"; 522 break; 523 case D_MTA: 524 p = "mta"; 525 break; 526 case D_BOUNCE: 527 p = "bounce"; 528 break; 529 default: 530 return 0; 531 } 532 533 return bsnprintf(dest, len, "%s", p); 534 } 535 536 static int 537 ascii_dump_mailaddr(const struct mailaddr *addr, char *dest, size_t len) 538 { 539 return bsnprintf(dest, len, "%s@%s", 540 addr->user, addr->domain); 541 } 542 543 static int 544 ascii_dump_flags(enum envelope_flags flags, char *buf, size_t len) 545 { 546 size_t cpylen = 0; 547 548 buf[0] = '\0'; 549 if (flags) { 550 if (flags & EF_AUTHENTICATED) 551 cpylen = strlcat(buf, "authenticated", len); 552 if (flags & EF_BOUNCE) { 553 if (buf[0] != '\0') 554 (void)strlcat(buf, " ", len); 555 cpylen = strlcat(buf, "bounce", len); 556 } 557 if (flags & EF_INTERNAL) { 558 if (buf[0] != '\0') 559 (void)strlcat(buf, " ", len); 560 cpylen = strlcat(buf, "internal", len); 561 } 562 } 563 564 return cpylen < len ? 1 : 0; 565 } 566 567 static int 568 ascii_dump_bounce_type(enum bounce_type type, char *dest, size_t len) 569 { 570 char *p = NULL; 571 572 switch (type) { 573 case B_FAILED: 574 p = "failed"; 575 break; 576 case B_DELAYED: 577 p = "delayed"; 578 break; 579 case B_DELIVERED: 580 p = "delivered"; 581 break; 582 default: 583 return 0; 584 } 585 return bsnprintf(dest, len, "%s", p); 586 } 587 588 589 static int 590 ascii_dump_dsn_ret(enum dsn_ret flag, char *dest, size_t len) 591 { 592 size_t cpylen = 0; 593 594 dest[0] = '\0'; 595 if (flag == DSN_RETFULL) 596 cpylen = strlcat(dest, "FULL", len); 597 else if (flag == DSN_RETHDRS) 598 cpylen = strlcat(dest, "HDRS", len); 599 600 return cpylen < len ? 1 : 0; 601 } 602 603 static int 604 ascii_dump_field(const char *field, const struct envelope *ep, 605 char *buf, size_t len) 606 { 607 if (strcasecmp(field, "dispatcher") == 0) 608 return ascii_dump_string(ep->dispatcher, buf, len); 609 610 if (strcasecmp(field, "bounce-delay") == 0) { 611 if (ep->agent.bounce.type != B_DELAYED) 612 return (1); 613 return ascii_dump_time(ep->agent.bounce.delay, buf, len); 614 } 615 616 if (strcasecmp(field, "bounce-ttl") == 0) { 617 if (ep->agent.bounce.type != B_DELAYED) 618 return (1); 619 return ascii_dump_time(ep->agent.bounce.ttl, buf, len); 620 } 621 622 if (strcasecmp(field, "bounce-type") == 0) 623 return ascii_dump_bounce_type(ep->agent.bounce.type, buf, len); 624 625 if (strcasecmp(field, "ctime") == 0) 626 return ascii_dump_time(ep->creation, buf, len); 627 628 if (strcasecmp(field, "dest") == 0) 629 return ascii_dump_mailaddr(&ep->dest, buf, len); 630 631 if (strcasecmp(field, "username") == 0) { 632 if (ep->username[0]) 633 return ascii_dump_string(ep->username, buf, len); 634 return 1; 635 } 636 637 if (strcasecmp(field, "errorline") == 0) 638 return ascii_dump_string(ep->errorline, buf, len); 639 640 if (strcasecmp(field, "ttl") == 0) 641 return ascii_dump_time(ep->ttl, buf, len); 642 643 if (strcasecmp(field, "flags") == 0) 644 return ascii_dump_flags(ep->flags, buf, len); 645 646 if (strcasecmp(field, "helo") == 0) 647 return ascii_dump_string(ep->helo, buf, len); 648 649 if (strcasecmp(field, "hostname") == 0) 650 return ascii_dump_string(ep->hostname, buf, len); 651 652 if (strcasecmp(field, "last-bounce") == 0) 653 return ascii_dump_time(ep->lastbounce, buf, len); 654 655 if (strcasecmp(field, "last-try") == 0) 656 return ascii_dump_time(ep->lasttry, buf, len); 657 658 if (strcasecmp(field, "retry") == 0) 659 return ascii_dump_uint16(ep->retry, buf, len); 660 661 if (strcasecmp(field, "rcpt") == 0) 662 return ascii_dump_mailaddr(&ep->rcpt, buf, len); 663 664 if (strcasecmp(field, "mda-exec") == 0) { 665 if (ep->mda_exec[0]) 666 return ascii_dump_string(ep->mda_exec, buf, len); 667 return 1; 668 } 669 670 if (strcasecmp(field, "mda-subaddress") == 0) { 671 if (ep->mda_subaddress[0]) 672 return ascii_dump_string(ep->mda_subaddress, buf, len); 673 return 1; 674 } 675 676 if (strcasecmp(field, "mda-user") == 0) { 677 if (ep->mda_user[0]) 678 return ascii_dump_string(ep->mda_user, buf, len); 679 return 1; 680 } 681 682 if (strcasecmp(field, "sender") == 0) 683 return ascii_dump_mailaddr(&ep->sender, buf, len); 684 685 if (strcasecmp(field, "smtpname") == 0) 686 return ascii_dump_string(ep->smtpname, buf, len); 687 688 if (strcasecmp(field, "sockaddr") == 0) 689 return ascii_dump_string(ss_to_text(&ep->ss), buf, len); 690 691 if (strcasecmp(field, "tag") == 0) 692 return ascii_dump_string(ep->tag, buf, len); 693 694 if (strcasecmp(field, "type") == 0) 695 return ascii_dump_type(ep->type, buf, len); 696 697 if (strcasecmp(field, "version") == 0) 698 return ascii_dump_uint32(SMTPD_ENVELOPE_VERSION, buf, len); 699 700 if (strcasecmp(field, "dsn-notify") == 0) 701 return ascii_dump_uint8(ep->dsn_notify, buf, len); 702 703 if (strcasecmp(field, "dsn-ret") == 0) 704 return ascii_dump_dsn_ret(ep->dsn_ret, buf, len); 705 706 if (strcasecmp(field, "dsn-orcpt") == 0) { 707 if (ep->dsn_orcpt.user[0] && ep->dsn_orcpt.domain[0]) 708 return ascii_dump_mailaddr(&ep->dsn_orcpt, buf, len); 709 return 1; 710 } 711 712 if (strcasecmp(field, "dsn-envid") == 0) 713 return ascii_dump_string(ep->dsn_envid, buf, len); 714 715 if (strcasecmp(field, "esc-class") == 0) { 716 if (ep->esc_class) 717 return ascii_dump_uint8(ep->esc_class, buf, len); 718 return 1; 719 } 720 721 if (strcasecmp(field, "esc-code") == 0) { 722 /* this is not a pasto, we dump esc_code if esc_class is !0 */ 723 if (ep->esc_class) 724 return ascii_dump_uint8(ep->esc_code, buf, len); 725 return 1; 726 } 727 728 return (0); 729 } 730 731 static void 732 envelope_ascii_dump(const struct envelope *ep, char **dest, size_t *len, 733 const char *field) 734 { 735 char buf[8192]; 736 int l; 737 738 if (*dest == NULL) 739 return; 740 741 memset(buf, 0, sizeof buf); 742 if (!ascii_dump_field(field, ep, buf, sizeof buf)) 743 goto err; 744 if (buf[0] == '\0') 745 return; 746 747 l = snprintf(*dest, *len, "%s: %s\n", field, buf); 748 if (l < 0 || (size_t) l >= *len) 749 goto err; 750 *dest += l; 751 *len -= l; 752 753 return; 754 err: 755 *dest = NULL; 756 } 757