1 /* 2 * Copyright (c) 1998 Sendmail, Inc. All rights reserved. 3 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 4 * Copyright (c) 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * By using this file, you agree to the terms and conditions set 8 * forth in the LICENSE file which can be found at the top level of 9 * the sendmail distribution. 10 * 11 */ 12 13 #ifndef lint 14 static char sccsid[] = "@(#)headers.c 8.136 (Berkeley) 1/26/1999"; 15 #endif /* not lint */ 16 17 # include <errno.h> 18 # include "sendmail.h" 19 20 /* 21 ** SETUPHEADERS -- initialize headers in symbol table 22 ** 23 ** Parameters: 24 ** none 25 ** 26 ** Returns: 27 ** none 28 */ 29 30 void 31 setupheaders() 32 { 33 struct hdrinfo *hi; 34 STAB *s; 35 36 for (hi = HdrInfo; hi->hi_field != NULL; hi++) 37 { 38 s = stab(hi->hi_field, ST_HEADER, ST_ENTER); 39 s->s_header.hi_flags = hi->hi_flags; 40 s->s_header.hi_ruleset = NULL; 41 } 42 } 43 /* 44 ** CHOMPHEADER -- process and save a header line. 45 ** 46 ** Called by collect and by readcf to deal with header lines. 47 ** 48 ** Parameters: 49 ** line -- header as a text line. 50 ** def -- if set, this is a default value. 51 ** hdrp -- a pointer to the place to save the header. 52 ** e -- the envelope including this header. 53 ** 54 ** Returns: 55 ** flags for this header. 56 ** 57 ** Side Effects: 58 ** The header is saved on the header list. 59 ** Contents of 'line' are destroyed. 60 */ 61 62 struct hdrinfo NormalHeader = { NULL, 0, NULL }; 63 64 int 65 chompheader(line, def, hdrp, e) 66 char *line; 67 bool def; 68 HDR **hdrp; 69 register ENVELOPE *e; 70 { 71 register char *p; 72 register HDR *h; 73 HDR **hp; 74 char *fname; 75 char *fvalue; 76 bool cond = FALSE; 77 bool headeronly; 78 STAB *s; 79 struct hdrinfo *hi; 80 bool nullheader = FALSE; 81 BITMAP mopts; 82 83 if (tTd(31, 6)) 84 { 85 printf("chompheader: "); 86 xputs(line); 87 printf("\n"); 88 } 89 90 headeronly = hdrp != NULL; 91 if (!headeronly) 92 hdrp = &e->e_header; 93 94 /* strip off options */ 95 clrbitmap(mopts); 96 p = line; 97 if (*p == '?') 98 { 99 /* have some */ 100 register char *q = strchr(p + 1, *p); 101 102 if (q != NULL) 103 { 104 *q++ = '\0'; 105 while (*++p != '\0') 106 setbitn(*p, mopts); 107 p = q; 108 } 109 else 110 syserr("553 header syntax error, line \"%s\"", line); 111 cond = TRUE; 112 } 113 114 /* find canonical name */ 115 fname = p; 116 while (isascii(*p) && isgraph(*p) && *p != ':') 117 p++; 118 fvalue = p; 119 while (isascii(*p) && isspace(*p)) 120 p++; 121 if (*p++ != ':' || fname == fvalue) 122 { 123 syserr("553 header syntax error, line \"%s\"", line); 124 return 0; 125 } 126 *fvalue = '\0'; 127 128 /* strip field value on front */ 129 if (*p == ' ') 130 p++; 131 fvalue = p; 132 133 /* if the field is null, go ahead and use the default */ 134 while (isascii(*p) && isspace(*p)) 135 p++; 136 if (*p == '\0') 137 nullheader = TRUE; 138 139 /* security scan: long field names are end-of-header */ 140 if (strlen(fname) > 100) 141 return H_EOH; 142 143 /* check to see if it represents a ruleset call */ 144 if (def) 145 { 146 char hbuf[50]; 147 148 (void) expand(fvalue, hbuf, sizeof hbuf, e); 149 for (p = hbuf; isascii(*p) && isspace(*p); ) 150 p++; 151 if ((*p++ & 0377) == CALLSUBR) 152 { 153 auto char *endp; 154 155 if (strtorwset(p, &endp, ST_ENTER) > 0) 156 { 157 *endp = '\0'; 158 s = stab(fname, ST_HEADER, ST_ENTER); 159 s->s_header.hi_ruleset = newstr(p); 160 } 161 return 0; 162 } 163 } 164 165 /* see if it is a known type */ 166 s = stab(fname, ST_HEADER, ST_FIND); 167 if (s != NULL) 168 hi = &s->s_header; 169 else 170 hi = &NormalHeader; 171 172 if (tTd(31, 9)) 173 { 174 if (s == NULL) 175 printf("no header flags match\n"); 176 else 177 printf("header match, flags=%x, ruleset=%s\n", 178 hi->hi_flags, 179 hi->hi_ruleset == NULL ? "<NULL>" : hi->hi_ruleset); 180 } 181 182 /* see if this is a resent message */ 183 if (!def && !headeronly && bitset(H_RESENT, hi->hi_flags)) 184 e->e_flags |= EF_RESENT; 185 186 /* if this is an Errors-To: header keep track of it now */ 187 if (UseErrorsTo && !def && !headeronly && 188 bitset(H_ERRORSTO, hi->hi_flags)) 189 (void) sendtolist(fvalue, NULLADDR, &e->e_errorqueue, 0, e); 190 191 /* if this means "end of header" quit now */ 192 if (!headeronly && bitset(H_EOH, hi->hi_flags)) 193 return hi->hi_flags; 194 195 /* 196 ** Horrible hack to work around problem with Lotus Notes SMTP 197 ** mail gateway, which generates From: headers with newlines in 198 ** them and the <address> on the second line. Although this is 199 ** legal RFC 822, many MUAs don't handle this properly and thus 200 ** never find the actual address. 201 */ 202 203 if (bitset(H_FROM, hi->hi_flags) && SingleLineFromHeader) 204 { 205 while ((p = strchr(fvalue, '\n')) != NULL) 206 *p = ' '; 207 } 208 209 /* 210 ** If there is a check ruleset, verify it against the header. 211 */ 212 213 if (!def && hi->hi_ruleset != NULL) 214 (void) rscheck(hi->hi_ruleset, fvalue, NULL, e); 215 216 /* 217 ** Drop explicit From: if same as what we would generate. 218 ** This is to make MH (which doesn't always give a full name) 219 ** insert the full name information in all circumstances. 220 */ 221 222 p = "resent-from"; 223 if (!bitset(EF_RESENT, e->e_flags)) 224 p += 7; 225 if (!def && !headeronly && !bitset(EF_QUEUERUN, e->e_flags) && 226 strcasecmp(fname, p) == 0) 227 { 228 if (tTd(31, 2)) 229 { 230 printf("comparing header from (%s) against default (%s or %s)\n", 231 fvalue, e->e_from.q_paddr, e->e_from.q_user); 232 } 233 if (e->e_from.q_paddr != NULL && 234 (strcmp(fvalue, e->e_from.q_paddr) == 0 || 235 strcmp(fvalue, e->e_from.q_user) == 0)) 236 return hi->hi_flags; 237 } 238 239 /* delete default value for this header */ 240 for (hp = hdrp; (h = *hp) != NULL; hp = &h->h_link) 241 { 242 if (strcasecmp(fname, h->h_field) == 0 && 243 bitset(H_DEFAULT, h->h_flags) && 244 !bitset(H_FORCE, h->h_flags)) 245 { 246 if (nullheader) 247 { 248 /* user-supplied value was null */ 249 return 0; 250 } 251 h->h_value = NULL; 252 if (!cond) 253 { 254 /* copy conditions from default case */ 255 bcopy((char *)h->h_mflags, (char *)mopts, 256 sizeof mopts); 257 } 258 } 259 } 260 261 /* create a new node */ 262 h = (HDR *) xalloc(sizeof *h); 263 h->h_field = newstr(fname); 264 h->h_value = newstr(fvalue); 265 h->h_link = NULL; 266 bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts); 267 *hp = h; 268 h->h_flags = hi->hi_flags; 269 270 /* strip EOH flag if parsing MIME headers */ 271 if (headeronly) 272 h->h_flags &= ~H_EOH; 273 if (def) 274 h->h_flags |= H_DEFAULT; 275 if (cond) 276 h->h_flags |= H_CHECK; 277 278 /* hack to see if this is a new format message */ 279 if (!def && !headeronly && bitset(H_RCPT|H_FROM, h->h_flags) && 280 (strchr(fvalue, ',') != NULL || strchr(fvalue, '(') != NULL || 281 strchr(fvalue, '<') != NULL || strchr(fvalue, ';') != NULL)) 282 { 283 e->e_flags &= ~EF_OLDSTYLE; 284 } 285 286 return h->h_flags; 287 } 288 /* 289 ** ADDHEADER -- add a header entry to the end of the queue. 290 ** 291 ** This bypasses the special checking of chompheader. 292 ** 293 ** Parameters: 294 ** field -- the name of the header field. 295 ** value -- the value of the field. 296 ** hp -- an indirect pointer to the header structure list. 297 ** 298 ** Returns: 299 ** none. 300 ** 301 ** Side Effects: 302 ** adds the field on the list of headers for this envelope. 303 */ 304 305 void 306 addheader(field, value, hdrlist) 307 char *field; 308 char *value; 309 HDR **hdrlist; 310 { 311 register HDR *h; 312 STAB *s; 313 HDR **hp; 314 315 /* find info struct */ 316 s = stab(field, ST_HEADER, ST_FIND); 317 318 /* find current place in list -- keep back pointer? */ 319 for (hp = hdrlist; (h = *hp) != NULL; hp = &h->h_link) 320 { 321 if (strcasecmp(field, h->h_field) == 0) 322 break; 323 } 324 325 /* allocate space for new header */ 326 h = (HDR *) xalloc(sizeof *h); 327 h->h_field = field; 328 h->h_value = newstr(value); 329 h->h_link = *hp; 330 h->h_flags = H_DEFAULT; 331 if (s != NULL) 332 h->h_flags |= s->s_header.hi_flags; 333 clrbitmap(h->h_mflags); 334 *hp = h; 335 } 336 /* 337 ** HVALUE -- return value of a header. 338 ** 339 ** Only "real" fields (i.e., ones that have not been supplied 340 ** as a default) are used. 341 ** 342 ** Parameters: 343 ** field -- the field name. 344 ** header -- the header list. 345 ** 346 ** Returns: 347 ** pointer to the value part. 348 ** NULL if not found. 349 ** 350 ** Side Effects: 351 ** none. 352 */ 353 354 char * 355 hvalue(field, header) 356 char *field; 357 HDR *header; 358 { 359 register HDR *h; 360 361 for (h = header; h != NULL; h = h->h_link) 362 { 363 if (!bitset(H_DEFAULT, h->h_flags) && 364 strcasecmp(h->h_field, field) == 0) 365 return (h->h_value); 366 } 367 return (NULL); 368 } 369 /* 370 ** ISHEADER -- predicate telling if argument is a header. 371 ** 372 ** A line is a header if it has a single word followed by 373 ** optional white space followed by a colon. 374 ** 375 ** Header fields beginning with two dashes, although technically 376 ** permitted by RFC822, are automatically rejected in order 377 ** to make MIME work out. Without this we could have a technically 378 ** legal header such as ``--"foo:bar"'' that would also be a legal 379 ** MIME separator. 380 ** 381 ** Parameters: 382 ** h -- string to check for possible headerness. 383 ** 384 ** Returns: 385 ** TRUE if h is a header. 386 ** FALSE otherwise. 387 ** 388 ** Side Effects: 389 ** none. 390 */ 391 392 bool 393 isheader(h) 394 char *h; 395 { 396 register char *s = h; 397 398 if (s[0] == '-' && s[1] == '-') 399 return FALSE; 400 401 while (*s > ' ' && *s != ':' && *s != '\0') 402 s++; 403 404 if (h == s) 405 return FALSE; 406 407 /* following technically violates RFC822 */ 408 while (isascii(*s) && isspace(*s)) 409 s++; 410 411 return (*s == ':'); 412 } 413 /* 414 ** EATHEADER -- run through the stored header and extract info. 415 ** 416 ** Parameters: 417 ** e -- the envelope to process. 418 ** full -- if set, do full processing (e.g., compute 419 ** message priority). This should not be set 420 ** when reading a queue file because some info 421 ** needed to compute the priority is wrong. 422 ** 423 ** Returns: 424 ** none. 425 ** 426 ** Side Effects: 427 ** Sets a bunch of global variables from information 428 ** in the collected header. 429 ** Aborts the message if the hop count is exceeded. 430 */ 431 432 void 433 eatheader(e, full) 434 register ENVELOPE *e; 435 bool full; 436 { 437 register HDR *h; 438 register char *p; 439 int hopcnt = 0; 440 char *msgid; 441 char buf[MAXLINE]; 442 extern int priencode __P((char *)); 443 444 /* 445 ** Set up macros for possible expansion in headers. 446 */ 447 448 define('f', e->e_sender, e); 449 define('g', e->e_sender, e); 450 if (e->e_origrcpt != NULL && *e->e_origrcpt != '\0') 451 define('u', e->e_origrcpt, e); 452 else 453 define('u', NULL, e); 454 455 /* full name of from person */ 456 p = hvalue("full-name", e->e_header); 457 if (p != NULL) 458 { 459 extern bool rfc822_string __P((char *)); 460 461 if (!rfc822_string(p)) 462 { 463 extern char *addquotes __P((char *)); 464 465 /* 466 ** Quote a full name with special characters 467 ** as a comment so crackaddr() doesn't destroy 468 ** the name portion of the address. 469 */ 470 p = addquotes(p); 471 } 472 define('x', p, e); 473 } 474 475 if (tTd(32, 1)) 476 printf("----- collected header -----\n"); 477 msgid = NULL; 478 for (h = e->e_header; h != NULL; h = h->h_link) 479 { 480 if (tTd(32, 1)) 481 printf("%s: ", h->h_field); 482 if (h->h_value == NULL) 483 { 484 if (tTd(32, 1)) 485 printf("<NULL>\n"); 486 continue; 487 } 488 489 /* do early binding */ 490 if (bitset(H_DEFAULT, h->h_flags)) 491 { 492 if (tTd(32, 1)) 493 { 494 printf("("); 495 xputs(h->h_value); 496 printf(") "); 497 } 498 expand(h->h_value, buf, sizeof buf, e); 499 if (buf[0] != '\0') 500 { 501 if (bitset(H_FROM, h->h_flags)) 502 { 503 extern char *crackaddr __P((char *)); 504 505 expand(crackaddr(buf), buf, sizeof buf, e); 506 } 507 h->h_value = newstr(buf); 508 h->h_flags &= ~H_DEFAULT; 509 } 510 } 511 512 if (tTd(32, 1)) 513 { 514 xputs(h->h_value); 515 printf("\n"); 516 } 517 518 /* count the number of times it has been processed */ 519 if (bitset(H_TRACE, h->h_flags)) 520 hopcnt++; 521 522 /* send to this person if we so desire */ 523 if (GrabTo && bitset(H_RCPT, h->h_flags) && 524 !bitset(H_DEFAULT, h->h_flags) && 525 (!bitset(EF_RESENT, e->e_flags) || bitset(H_RESENT, h->h_flags))) 526 { 527 #if 0 528 int saveflags = e->e_flags; 529 #endif 530 531 (void) sendtolist(h->h_value, NULLADDR, 532 &e->e_sendqueue, 0, e); 533 534 #if 0 535 /* 536 ** Change functionality so a fatal error on an 537 ** address doesn't affect the entire envelope. 538 */ 539 540 /* delete fatal errors generated by this address */ 541 if (!bitset(EF_FATALERRS, saveflags)) 542 e->e_flags &= ~EF_FATALERRS; 543 #endif 544 } 545 546 /* save the message-id for logging */ 547 p = "resent-message-id"; 548 if (!bitset(EF_RESENT, e->e_flags)) 549 p += 7; 550 if (strcasecmp(h->h_field, p) == 0) 551 { 552 msgid = h->h_value; 553 while (isascii(*msgid) && isspace(*msgid)) 554 msgid++; 555 } 556 } 557 if (tTd(32, 1)) 558 printf("----------------------------\n"); 559 560 /* if we are just verifying (that is, sendmail -t -bv), drop out now */ 561 if (OpMode == MD_VERIFY) 562 return; 563 564 /* store hop count */ 565 if (hopcnt > e->e_hopcount) 566 e->e_hopcount = hopcnt; 567 568 /* message priority */ 569 p = hvalue("precedence", e->e_header); 570 if (p != NULL) 571 e->e_class = priencode(p); 572 if (e->e_class < 0) 573 e->e_timeoutclass = TOC_NONURGENT; 574 else if (e->e_class > 0) 575 e->e_timeoutclass = TOC_URGENT; 576 if (full) 577 { 578 e->e_msgpriority = e->e_msgsize 579 - e->e_class * WkClassFact 580 + e->e_nrcpts * WkRecipFact; 581 } 582 583 /* message timeout priority */ 584 p = hvalue("priority", e->e_header); 585 if (p != NULL) 586 { 587 /* (this should be in the configuration file) */ 588 if (strcasecmp(p, "urgent") == 0) 589 e->e_timeoutclass = TOC_URGENT; 590 else if (strcasecmp(p, "normal") == 0) 591 e->e_timeoutclass = TOC_NORMAL; 592 else if (strcasecmp(p, "non-urgent") == 0) 593 e->e_timeoutclass = TOC_NONURGENT; 594 } 595 596 /* date message originated */ 597 p = hvalue("posted-date", e->e_header); 598 if (p == NULL) 599 p = hvalue("date", e->e_header); 600 if (p != NULL) 601 define('a', p, e); 602 603 /* check to see if this is a MIME message */ 604 if ((e->e_bodytype != NULL && 605 strcasecmp(e->e_bodytype, "8BITMIME") == 0) || 606 hvalue("MIME-Version", e->e_header) != NULL) 607 { 608 e->e_flags |= EF_IS_MIME; 609 if (HasEightBits) 610 e->e_bodytype = "8BITMIME"; 611 } 612 else if ((p = hvalue("Content-Type", e->e_header)) != NULL) 613 { 614 /* this may be an RFC 1049 message */ 615 p = strpbrk(p, ";/"); 616 if (p == NULL || *p == ';') 617 { 618 /* yep, it is */ 619 e->e_flags |= EF_DONT_MIME; 620 } 621 } 622 623 /* 624 ** From person in antiquated ARPANET mode 625 ** required by UK Grey Book e-mail gateways (sigh) 626 */ 627 628 if (OpMode == MD_ARPAFTP) 629 { 630 register struct hdrinfo *hi; 631 632 for (hi = HdrInfo; hi->hi_field != NULL; hi++) 633 { 634 if (bitset(H_FROM, hi->hi_flags) && 635 (!bitset(H_RESENT, hi->hi_flags) || 636 bitset(EF_RESENT, e->e_flags)) && 637 (p = hvalue(hi->hi_field, e->e_header)) != NULL) 638 break; 639 } 640 if (hi->hi_field != NULL) 641 { 642 if (tTd(32, 2)) 643 printf("eatheader: setsender(*%s == %s)\n", 644 hi->hi_field, p); 645 setsender(p, e, NULL, '\0', TRUE); 646 } 647 } 648 649 /* 650 ** Log collection information. 651 */ 652 653 if (bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4) 654 logsender(e, msgid); 655 e->e_flags &= ~EF_LOGSENDER; 656 } 657 /* 658 ** LOGSENDER -- log sender information 659 ** 660 ** Parameters: 661 ** e -- the envelope to log 662 ** msgid -- the message id 663 ** 664 ** Returns: 665 ** none 666 */ 667 668 void 669 logsender(e, msgid) 670 register ENVELOPE *e; 671 char *msgid; 672 { 673 char *name; 674 register char *sbp; 675 register char *p; 676 int l; 677 char hbuf[MAXNAME + 1]; 678 char sbuf[MAXLINE + 1]; 679 char mbuf[MAXNAME + 1]; 680 681 /* don't allow newlines in the message-id */ 682 if (msgid != NULL) 683 { 684 l = strlen(msgid); 685 if (l > sizeof mbuf - 1) 686 l = sizeof mbuf - 1; 687 bcopy(msgid, mbuf, l); 688 mbuf[l] = '\0'; 689 p = mbuf; 690 while ((p = strchr(p, '\n')) != NULL) 691 *p++ = ' '; 692 } 693 694 if (bitset(EF_RESPONSE, e->e_flags)) 695 name = "[RESPONSE]"; 696 else if ((name = macvalue('_', e)) != NULL) 697 ; 698 else if (RealHostName == NULL) 699 name = "localhost"; 700 else if (RealHostName[0] == '[') 701 name = RealHostName; 702 else 703 { 704 name = hbuf; 705 (void) snprintf(hbuf, sizeof hbuf, "%.80s", RealHostName); 706 if (RealHostAddr.sa.sa_family != 0) 707 { 708 p = &hbuf[strlen(hbuf)]; 709 (void) snprintf(p, SPACELEFT(hbuf, p), " (%.100s)", 710 anynet_ntoa(&RealHostAddr)); 711 } 712 } 713 714 /* some versions of syslog only take 5 printf args */ 715 # if (SYSLOG_BUFSIZE) >= 256 716 sbp = sbuf; 717 snprintf(sbp, SPACELEFT(sbuf, sbp), 718 "from=%.200s, size=%ld, class=%d, pri=%ld, nrcpts=%d", 719 e->e_from.q_paddr == NULL ? "<NONE>" : e->e_from.q_paddr, 720 e->e_msgsize, e->e_class, e->e_msgpriority, e->e_nrcpts); 721 sbp += strlen(sbp); 722 if (msgid != NULL) 723 { 724 snprintf(sbp, SPACELEFT(sbuf, sbp), ", msgid=%.100s", mbuf); 725 sbp += strlen(sbp); 726 } 727 if (e->e_bodytype != NULL) 728 { 729 (void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", bodytype=%.20s", 730 e->e_bodytype); 731 sbp += strlen(sbp); 732 } 733 p = macvalue('r', e); 734 if (p != NULL) 735 (void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", proto=%.20s", p); 736 sm_syslog(LOG_INFO, e->e_id, 737 "%.850s, relay=%.100s", 738 sbuf, name); 739 740 # else /* short syslog buffer */ 741 742 sm_syslog(LOG_INFO, e->e_id, 743 "from=%s", 744 e->e_from.q_paddr == NULL ? "<NONE>" 745 : shortenstring(e->e_from.q_paddr, 83)); 746 sm_syslog(LOG_INFO, e->e_id, 747 "size=%ld, class=%ld, pri=%ld, nrcpts=%d", 748 e->e_msgsize, e->e_class, e->e_msgpriority, e->e_nrcpts); 749 if (msgid != NULL) 750 sm_syslog(LOG_INFO, e->e_id, 751 "msgid=%s", 752 shortenstring(mbuf, 83)); 753 sbp = sbuf; 754 *sbp = '\0'; 755 if (e->e_bodytype != NULL) 756 { 757 snprintf(sbp, SPACELEFT(sbuf, sbp), "bodytype=%.20s, ", e->e_bodytype); 758 sbp += strlen(sbp); 759 } 760 p = macvalue('r', e); 761 if (p != NULL) 762 { 763 snprintf(sbp, SPACELEFT(sbuf, sbp), "proto=%.20s, ", p); 764 sbp += strlen(sbp); 765 } 766 sm_syslog(LOG_INFO, e->e_id, 767 "%.400srelay=%.100s", sbuf, name); 768 # endif 769 } 770 /* 771 ** PRIENCODE -- encode external priority names into internal values. 772 ** 773 ** Parameters: 774 ** p -- priority in ascii. 775 ** 776 ** Returns: 777 ** priority as a numeric level. 778 ** 779 ** Side Effects: 780 ** none. 781 */ 782 783 int 784 priencode(p) 785 char *p; 786 { 787 register int i; 788 789 for (i = 0; i < NumPriorities; i++) 790 { 791 if (!strcasecmp(p, Priorities[i].pri_name)) 792 return (Priorities[i].pri_val); 793 } 794 795 /* unknown priority */ 796 return (0); 797 } 798 /* 799 ** CRACKADDR -- parse an address and turn it into a macro 800 ** 801 ** This doesn't actually parse the address -- it just extracts 802 ** it and replaces it with "$g". The parse is totally ad hoc 803 ** and isn't even guaranteed to leave something syntactically 804 ** identical to what it started with. However, it does leave 805 ** something semantically identical. 806 ** 807 ** This algorithm has been cleaned up to handle a wider range 808 ** of cases -- notably quoted and backslash escaped strings. 809 ** This modification makes it substantially better at preserving 810 ** the original syntax. 811 ** 812 ** Parameters: 813 ** addr -- the address to be cracked. 814 ** 815 ** Returns: 816 ** a pointer to the new version. 817 ** 818 ** Side Effects: 819 ** none. 820 ** 821 ** Warning: 822 ** The return value is saved in local storage and should 823 ** be copied if it is to be reused. 824 */ 825 826 char * 827 crackaddr(addr) 828 register char *addr; 829 { 830 register char *p; 831 register char c; 832 int cmtlev; 833 int realcmtlev; 834 int anglelev, realanglelev; 835 int copylev; 836 int bracklev; 837 bool qmode; 838 bool realqmode; 839 bool skipping; 840 bool putgmac = FALSE; 841 bool quoteit = FALSE; 842 bool gotangle = FALSE; 843 bool gotcolon = FALSE; 844 register char *bp; 845 char *buflim; 846 char *bufhead; 847 char *addrhead; 848 static char buf[MAXNAME + 1]; 849 850 if (tTd(33, 1)) 851 printf("crackaddr(%s)\n", addr); 852 853 /* strip leading spaces */ 854 while (*addr != '\0' && isascii(*addr) && isspace(*addr)) 855 addr++; 856 857 /* 858 ** Start by assuming we have no angle brackets. This will be 859 ** adjusted later if we find them. 860 */ 861 862 bp = bufhead = buf; 863 buflim = &buf[sizeof buf - 7]; 864 p = addrhead = addr; 865 copylev = anglelev = realanglelev = cmtlev = realcmtlev = 0; 866 bracklev = 0; 867 qmode = realqmode = FALSE; 868 869 while ((c = *p++) != '\0') 870 { 871 /* 872 ** If the buffer is overful, go into a special "skipping" 873 ** mode that tries to keep legal syntax but doesn't actually 874 ** output things. 875 */ 876 877 skipping = bp >= buflim; 878 879 if (copylev > 0 && !skipping) 880 *bp++ = c; 881 882 /* check for backslash escapes */ 883 if (c == '\\') 884 { 885 /* arrange to quote the address */ 886 if (cmtlev <= 0 && !qmode) 887 quoteit = TRUE; 888 889 if ((c = *p++) == '\0') 890 { 891 /* too far */ 892 p--; 893 goto putg; 894 } 895 if (copylev > 0 && !skipping) 896 *bp++ = c; 897 goto putg; 898 } 899 900 /* check for quoted strings */ 901 if (c == '"' && cmtlev <= 0) 902 { 903 qmode = !qmode; 904 if (copylev > 0 && !skipping) 905 realqmode = !realqmode; 906 continue; 907 } 908 if (qmode) 909 goto putg; 910 911 /* check for comments */ 912 if (c == '(') 913 { 914 cmtlev++; 915 916 /* allow space for closing paren */ 917 if (!skipping) 918 { 919 buflim--; 920 realcmtlev++; 921 if (copylev++ <= 0) 922 { 923 if (bp != bufhead) 924 *bp++ = ' '; 925 *bp++ = c; 926 } 927 } 928 } 929 if (cmtlev > 0) 930 { 931 if (c == ')') 932 { 933 cmtlev--; 934 copylev--; 935 if (!skipping) 936 { 937 realcmtlev--; 938 buflim++; 939 } 940 } 941 continue; 942 } 943 else if (c == ')') 944 { 945 /* syntax error: unmatched ) */ 946 if (copylev > 0 && !skipping) 947 bp--; 948 } 949 950 /* count nesting on [ ... ] (for IPv6 domain literals) */ 951 if (c == '[') 952 bracklev++; 953 else if (c == ']') 954 bracklev--; 955 956 /* check for group: list; syntax */ 957 if (c == ':' && anglelev <= 0 && bracklev <= 0 && 958 !gotcolon && !ColonOkInAddr) 959 { 960 register char *q; 961 962 /* 963 ** Check for DECnet phase IV ``::'' (host::user) 964 ** or ** DECnet phase V ``:.'' syntaxes. The latter 965 ** covers ``user@DEC:.tay.myhost'' and 966 ** ``DEC:.tay.myhost::user'' syntaxes (bletch). 967 */ 968 969 if (*p == ':' || *p == '.') 970 { 971 if (cmtlev <= 0 && !qmode) 972 quoteit = TRUE; 973 if (copylev > 0 && !skipping) 974 { 975 *bp++ = c; 976 *bp++ = *p; 977 } 978 p++; 979 goto putg; 980 } 981 982 gotcolon = TRUE; 983 984 bp = bufhead; 985 if (quoteit) 986 { 987 *bp++ = '"'; 988 989 /* back up over the ':' and any spaces */ 990 --p; 991 while (isascii(*--p) && isspace(*p)) 992 continue; 993 p++; 994 } 995 for (q = addrhead; q < p; ) 996 { 997 c = *q++; 998 if (bp < buflim) 999 { 1000 if (quoteit && c == '"') 1001 *bp++ = '\\'; 1002 *bp++ = c; 1003 } 1004 } 1005 if (quoteit) 1006 { 1007 if (bp == &bufhead[1]) 1008 bp--; 1009 else 1010 *bp++ = '"'; 1011 while ((c = *p++) != ':') 1012 { 1013 if (bp < buflim) 1014 *bp++ = c; 1015 } 1016 *bp++ = c; 1017 } 1018 1019 /* any trailing white space is part of group: */ 1020 while (isascii(*p) && isspace(*p) && bp < buflim) 1021 *bp++ = *p++; 1022 copylev = 0; 1023 putgmac = quoteit = FALSE; 1024 bufhead = bp; 1025 addrhead = p; 1026 continue; 1027 } 1028 1029 if (c == ';' && copylev <= 0 && !ColonOkInAddr) 1030 { 1031 if (bp < buflim) 1032 *bp++ = c; 1033 } 1034 1035 /* check for characters that may have to be quoted */ 1036 if (strchr(MustQuoteChars, c) != NULL) 1037 { 1038 /* 1039 ** If these occur as the phrase part of a <> 1040 ** construct, but are not inside of () or already 1041 ** quoted, they will have to be quoted. Note that 1042 ** now (but don't actually do the quoting). 1043 */ 1044 1045 if (cmtlev <= 0 && !qmode) 1046 quoteit = TRUE; 1047 } 1048 1049 /* check for angle brackets */ 1050 if (c == '<') 1051 { 1052 register char *q; 1053 1054 /* assume first of two angles is bogus */ 1055 if (gotangle) 1056 quoteit = TRUE; 1057 gotangle = TRUE; 1058 1059 /* oops -- have to change our mind */ 1060 anglelev = 1; 1061 if (!skipping) 1062 realanglelev = 1; 1063 1064 bp = bufhead; 1065 if (quoteit) 1066 { 1067 *bp++ = '"'; 1068 1069 /* back up over the '<' and any spaces */ 1070 --p; 1071 while (isascii(*--p) && isspace(*p)) 1072 continue; 1073 p++; 1074 } 1075 for (q = addrhead; q < p; ) 1076 { 1077 c = *q++; 1078 if (bp < buflim) 1079 { 1080 if (quoteit && c == '"') 1081 *bp++ = '\\'; 1082 *bp++ = c; 1083 } 1084 } 1085 if (quoteit) 1086 { 1087 if (bp == &buf[1]) 1088 bp--; 1089 else 1090 *bp++ = '"'; 1091 while ((c = *p++) != '<') 1092 { 1093 if (bp < buflim) 1094 *bp++ = c; 1095 } 1096 *bp++ = c; 1097 } 1098 copylev = 0; 1099 putgmac = quoteit = FALSE; 1100 continue; 1101 } 1102 1103 if (c == '>') 1104 { 1105 if (anglelev > 0) 1106 { 1107 anglelev--; 1108 if (!skipping) 1109 { 1110 realanglelev--; 1111 buflim++; 1112 } 1113 } 1114 else if (!skipping) 1115 { 1116 /* syntax error: unmatched > */ 1117 if (copylev > 0) 1118 bp--; 1119 quoteit = TRUE; 1120 continue; 1121 } 1122 if (copylev++ <= 0) 1123 *bp++ = c; 1124 continue; 1125 } 1126 1127 /* must be a real address character */ 1128 putg: 1129 if (copylev <= 0 && !putgmac) 1130 { 1131 if (bp > bufhead && bp[-1] == ')') 1132 *bp++ = ' '; 1133 *bp++ = MACROEXPAND; 1134 *bp++ = 'g'; 1135 putgmac = TRUE; 1136 } 1137 } 1138 1139 /* repair any syntactic damage */ 1140 if (realqmode) 1141 *bp++ = '"'; 1142 while (realcmtlev-- > 0) 1143 *bp++ = ')'; 1144 while (realanglelev-- > 0) 1145 *bp++ = '>'; 1146 *bp++ = '\0'; 1147 1148 if (tTd(33, 1)) 1149 { 1150 printf("crackaddr=>`"); 1151 xputs(buf); 1152 printf("'\n"); 1153 } 1154 1155 return (buf); 1156 } 1157 /* 1158 ** PUTHEADER -- put the header part of a message from the in-core copy 1159 ** 1160 ** Parameters: 1161 ** mci -- the connection information. 1162 ** h -- the header to put. 1163 ** e -- envelope to use. 1164 ** flags -- MIME conversion flags. 1165 ** 1166 ** Returns: 1167 ** none. 1168 ** 1169 ** Side Effects: 1170 ** none. 1171 */ 1172 1173 /* 1174 * Macro for fast max (not available in e.g. DG/UX, 386/ix). 1175 */ 1176 #ifndef MAX 1177 # define MAX(a,b) (((a)>(b))?(a):(b)) 1178 #endif 1179 1180 void 1181 putheader(mci, hdr, e, flags) 1182 register MCI *mci; 1183 HDR *hdr; 1184 register ENVELOPE *e; 1185 int flags; 1186 { 1187 register HDR *h; 1188 char buf[MAX(MAXLINE,BUFSIZ)]; 1189 char obuf[MAXLINE]; 1190 1191 if (tTd(34, 1)) 1192 printf("--- putheader, mailer = %s ---\n", 1193 mci->mci_mailer->m_name); 1194 1195 /* 1196 ** If we're in MIME mode, we're not really in the header of the 1197 ** message, just the header of one of the parts of the body of 1198 ** the message. Therefore MCIF_INHEADER should not be turned on. 1199 */ 1200 1201 if (!bitset(MCIF_INMIME, mci->mci_flags)) 1202 mci->mci_flags |= MCIF_INHEADER; 1203 1204 for (h = hdr; h != NULL; h = h->h_link) 1205 { 1206 register char *p = h->h_value; 1207 extern bool bitintersect __P((BITMAP, BITMAP)); 1208 1209 if (tTd(34, 11)) 1210 { 1211 printf(" %s: ", h->h_field); 1212 xputs(p); 1213 } 1214 1215 #if _FFR_MAX_MIME_HEADER_LENGTH 1216 /* heuristic shortening of MIME fields to avoid MUA overflows */ 1217 if (MaxMimeFieldLength > 0 && 1218 wordinclass(h->h_field, 1219 macid("{checkMIMEFieldHeaders}", NULL))) 1220 { 1221 extern bool fix_mime_header __P((char *)); 1222 1223 if (fix_mime_header(h->h_value)) 1224 { 1225 sm_syslog(LOG_ALERT, e->e_id, 1226 "Truncated MIME %s header due to field size (possible attack)", 1227 h->h_field); 1228 if (tTd(34, 11)) 1229 printf(" truncated MIME %s header due to field size (possible attack)\n", 1230 h->h_field); 1231 } 1232 } 1233 1234 if (MaxMimeHeaderLength > 0 && 1235 wordinclass(h->h_field, 1236 macid("{checkMIMETextHeaders}", NULL))) 1237 { 1238 if (strlen(h->h_value) > MaxMimeHeaderLength) 1239 { 1240 h->h_value[MaxMimeHeaderLength - 1] = '\0'; 1241 sm_syslog(LOG_ALERT, e->e_id, 1242 "Truncated long MIME %s header (possible attack)", 1243 h->h_field); 1244 if (tTd(34, 11)) 1245 printf(" truncated long MIME %s header (possible attack)\n", 1246 h->h_field); 1247 } 1248 } 1249 1250 if (MaxMimeHeaderLength > 0 && 1251 wordinclass(h->h_field, 1252 macid("{checkMIMEHeaders}", NULL))) 1253 { 1254 extern bool shorten_rfc822_string __P((char *, int)); 1255 1256 if (shorten_rfc822_string(h->h_value, MaxMimeHeaderLength)) 1257 { 1258 sm_syslog(LOG_ALERT, e->e_id, 1259 "Truncated long MIME %s header (possible attack)", 1260 h->h_field); 1261 if (tTd(34, 11)) 1262 printf(" truncated long MIME %s header (possible attack)\n", 1263 h->h_field); 1264 } 1265 } 1266 #endif 1267 1268 /* 1269 ** Suppress Content-Transfer-Encoding: if we are MIMEing 1270 ** and we are potentially converting from 8 bit to 7 bit 1271 ** MIME. If converting, add a new CTE header in 1272 ** mime8to7(). 1273 */ 1274 if (bitset(H_CTE, h->h_flags) && 1275 bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, 1276 mci->mci_flags) && 1277 !bitset(M87F_NO8TO7, flags)) 1278 { 1279 if (tTd(34, 11)) 1280 printf(" (skipped (content-transfer-encoding))\n"); 1281 continue; 1282 } 1283 1284 if (bitset(MCIF_INMIME, mci->mci_flags)) 1285 { 1286 if (tTd(34, 11)) 1287 printf("\n"); 1288 put_vanilla_header(h, p, mci); 1289 continue; 1290 } 1291 1292 if (bitset(H_CHECK|H_ACHECK, h->h_flags) && 1293 !bitintersect(h->h_mflags, mci->mci_mailer->m_flags)) 1294 { 1295 if (tTd(34, 11)) 1296 printf(" (skipped)\n"); 1297 continue; 1298 } 1299 1300 /* handle Resent-... headers specially */ 1301 if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 1302 { 1303 if (tTd(34, 11)) 1304 printf(" (skipped (resent))\n"); 1305 continue; 1306 } 1307 1308 /* suppress return receipts if requested */ 1309 if (bitset(H_RECEIPTTO, h->h_flags) && 1310 #if _FFR_DSN_RRT_OPTION 1311 (RrtImpliesDsn || bitset(EF_NORECEIPT, e->e_flags))) 1312 #else 1313 bitset(EF_NORECEIPT, e->e_flags)) 1314 #endif 1315 { 1316 if (tTd(34, 11)) 1317 printf(" (skipped (receipt))\n"); 1318 continue; 1319 } 1320 1321 /* macro expand value if generated internally */ 1322 if (bitset(H_DEFAULT, h->h_flags)) 1323 { 1324 expand(p, buf, sizeof buf, e); 1325 p = buf; 1326 if (*p == '\0') 1327 { 1328 if (tTd(34, 11)) 1329 printf(" (skipped -- null value)\n"); 1330 continue; 1331 } 1332 } 1333 1334 if (bitset(H_BCC, h->h_flags)) 1335 { 1336 /* Bcc: field -- either truncate or delete */ 1337 if (bitset(EF_DELETE_BCC, e->e_flags)) 1338 { 1339 if (tTd(34, 11)) 1340 printf(" (skipped -- bcc)\n"); 1341 } 1342 else 1343 { 1344 /* no other recipient headers: truncate value */ 1345 (void) snprintf(obuf, sizeof obuf, "%s:", 1346 h->h_field); 1347 putline(obuf, mci); 1348 } 1349 continue; 1350 } 1351 1352 if (tTd(34, 11)) 1353 printf("\n"); 1354 1355 if (bitset(H_FROM|H_RCPT, h->h_flags)) 1356 { 1357 /* address field */ 1358 bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 1359 1360 if (bitset(H_FROM, h->h_flags)) 1361 oldstyle = FALSE; 1362 commaize(h, p, oldstyle, mci, e); 1363 } 1364 else 1365 { 1366 put_vanilla_header(h, p, mci); 1367 } 1368 } 1369 1370 /* 1371 ** If we are converting this to a MIME message, add the 1372 ** MIME headers. 1373 */ 1374 1375 #if MIME8TO7 1376 if (bitset(MM_MIME8BIT, MimeMode) && 1377 bitset(EF_HAS8BIT, e->e_flags) && 1378 !bitset(EF_DONT_MIME, e->e_flags) && 1379 !bitnset(M_8BITS, mci->mci_mailer->m_flags) && 1380 !bitset(MCIF_CVT8TO7|MCIF_CVT7TO8, mci->mci_flags)) 1381 { 1382 if (hvalue("MIME-Version", e->e_header) == NULL) 1383 putline("MIME-Version: 1.0", mci); 1384 if (hvalue("Content-Type", e->e_header) == NULL) 1385 { 1386 snprintf(obuf, sizeof obuf, 1387 "Content-Type: text/plain; charset=%s", 1388 defcharset(e)); 1389 putline(obuf, mci); 1390 } 1391 if (hvalue("Content-Transfer-Encoding", e->e_header) == NULL) 1392 putline("Content-Transfer-Encoding: 8bit", mci); 1393 } 1394 #endif 1395 } 1396 /* 1397 ** PUT_VANILLA_HEADER -- output a fairly ordinary header 1398 ** 1399 ** Parameters: 1400 ** h -- the structure describing this header 1401 ** v -- the value of this header 1402 ** mci -- the connection info for output 1403 ** 1404 ** Returns: 1405 ** none. 1406 */ 1407 1408 void 1409 put_vanilla_header(h, v, mci) 1410 HDR *h; 1411 char *v; 1412 MCI *mci; 1413 { 1414 register char *nlp; 1415 register char *obp; 1416 int putflags; 1417 char obuf[MAXLINE]; 1418 1419 putflags = PXLF_HEADER; 1420 #if _FFR_7BITHDRS 1421 if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags)) 1422 putflags |= PXLF_STRIP8BIT; 1423 #endif 1424 (void) snprintf(obuf, sizeof obuf, "%.200s: ", h->h_field); 1425 obp = obuf + strlen(obuf); 1426 while ((nlp = strchr(v, '\n')) != NULL) 1427 { 1428 int l; 1429 1430 l = nlp - v; 1431 if (SPACELEFT(obuf, obp) - 1 < l) 1432 l = SPACELEFT(obuf, obp) - 1; 1433 1434 snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v); 1435 putxline(obuf, strlen(obuf), mci, putflags); 1436 v += l + 1; 1437 obp = obuf; 1438 if (*v != ' ' && *v != '\t') 1439 *obp++ = ' '; 1440 } 1441 snprintf(obp, SPACELEFT(obuf, obp), "%.*s", 1442 (int)(sizeof obuf - (obp - obuf) - 1), v); 1443 putxline(obuf, strlen(obuf), mci, putflags); 1444 } 1445 /* 1446 ** COMMAIZE -- output a header field, making a comma-translated list. 1447 ** 1448 ** Parameters: 1449 ** h -- the header field to output. 1450 ** p -- the value to put in it. 1451 ** oldstyle -- TRUE if this is an old style header. 1452 ** mci -- the connection information. 1453 ** e -- the envelope containing the message. 1454 ** 1455 ** Returns: 1456 ** none. 1457 ** 1458 ** Side Effects: 1459 ** outputs "p" to file "fp". 1460 */ 1461 1462 void 1463 commaize(h, p, oldstyle, mci, e) 1464 register HDR *h; 1465 register char *p; 1466 bool oldstyle; 1467 register MCI *mci; 1468 register ENVELOPE *e; 1469 { 1470 register char *obp; 1471 int opos; 1472 int omax; 1473 bool firstone = TRUE; 1474 int putflags = PXLF_HEADER; 1475 char obuf[MAXLINE + 3]; 1476 1477 /* 1478 ** Output the address list translated by the 1479 ** mailer and with commas. 1480 */ 1481 1482 if (tTd(14, 2)) 1483 printf("commaize(%s: %s)\n", h->h_field, p); 1484 1485 #if _FFR_7BITHDRS 1486 if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags)) 1487 putflags |= PXLF_STRIP8BIT; 1488 #endif 1489 1490 obp = obuf; 1491 (void) snprintf(obp, SPACELEFT(obuf, obp), "%.200s: ", h->h_field); 1492 opos = strlen(h->h_field) + 2; 1493 if (opos > 202) 1494 opos = 202; 1495 obp += opos; 1496 omax = mci->mci_mailer->m_linelimit - 2; 1497 if (omax < 0 || omax > 78) 1498 omax = 78; 1499 1500 /* 1501 ** Run through the list of values. 1502 */ 1503 1504 while (*p != '\0') 1505 { 1506 register char *name; 1507 register int c; 1508 char savechar; 1509 int flags; 1510 auto int stat; 1511 1512 /* 1513 ** Find the end of the name. New style names 1514 ** end with a comma, old style names end with 1515 ** a space character. However, spaces do not 1516 ** necessarily delimit an old-style name -- at 1517 ** signs mean keep going. 1518 */ 1519 1520 /* find end of name */ 1521 while ((isascii(*p) && isspace(*p)) || *p == ',') 1522 p++; 1523 name = p; 1524 for (;;) 1525 { 1526 auto char *oldp; 1527 char pvpbuf[PSBUFSIZE]; 1528 1529 (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf, 1530 sizeof pvpbuf, &oldp, NULL); 1531 p = oldp; 1532 1533 /* look to see if we have an at sign */ 1534 while (*p != '\0' && isascii(*p) && isspace(*p)) 1535 p++; 1536 1537 if (*p != '@') 1538 { 1539 p = oldp; 1540 break; 1541 } 1542 p += *p == '@' ? 1 : 2; 1543 while (*p != '\0' && isascii(*p) && isspace(*p)) 1544 p++; 1545 } 1546 /* at the end of one complete name */ 1547 1548 /* strip off trailing white space */ 1549 while (p >= name && 1550 ((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0')) 1551 p--; 1552 if (++p == name) 1553 continue; 1554 savechar = *p; 1555 *p = '\0'; 1556 1557 /* translate the name to be relative */ 1558 flags = RF_HEADERADDR|RF_ADDDOMAIN; 1559 if (bitset(H_FROM, h->h_flags)) 1560 flags |= RF_SENDERADDR; 1561 #if USERDB 1562 else if (e->e_from.q_mailer != NULL && 1563 bitnset(M_UDBRECIPIENT, e->e_from.q_mailer->m_flags)) 1564 { 1565 extern char *udbsender __P((char *)); 1566 char *q; 1567 1568 q = udbsender(name); 1569 if (q != NULL) 1570 name = q; 1571 } 1572 #endif 1573 stat = EX_OK; 1574 name = remotename(name, mci->mci_mailer, flags, &stat, e); 1575 if (*name == '\0') 1576 { 1577 *p = savechar; 1578 continue; 1579 } 1580 name = denlstring(name, FALSE, TRUE); 1581 1582 /* output the name with nice formatting */ 1583 opos += strlen(name); 1584 if (!firstone) 1585 opos += 2; 1586 if (opos > omax && !firstone) 1587 { 1588 snprintf(obp, SPACELEFT(obuf, obp), ",\n"); 1589 putxline(obuf, strlen(obuf), mci, putflags); 1590 obp = obuf; 1591 (void) strcpy(obp, " "); 1592 opos = strlen(obp); 1593 obp += opos; 1594 opos += strlen(name); 1595 } 1596 else if (!firstone) 1597 { 1598 snprintf(obp, SPACELEFT(obuf, obp), ", "); 1599 obp += 2; 1600 } 1601 1602 while ((c = *name++) != '\0' && obp < &obuf[MAXLINE]) 1603 *obp++ = c; 1604 firstone = FALSE; 1605 *p = savechar; 1606 } 1607 *obp = '\0'; 1608 putxline(obuf, strlen(obuf), mci, putflags); 1609 } 1610 /* 1611 ** COPYHEADER -- copy header list 1612 ** 1613 ** This routine is the equivalent of newstr for header lists 1614 ** 1615 ** Parameters: 1616 ** header -- list of header structures to copy. 1617 ** 1618 ** Returns: 1619 ** a copy of 'header'. 1620 ** 1621 ** Side Effects: 1622 ** none. 1623 */ 1624 1625 HDR * 1626 copyheader(header) 1627 register HDR *header; 1628 { 1629 register HDR *newhdr; 1630 HDR *ret; 1631 register HDR **tail = &ret; 1632 1633 while (header != NULL) 1634 { 1635 newhdr = (HDR *) xalloc(sizeof(HDR)); 1636 STRUCTCOPY(*header, *newhdr); 1637 *tail = newhdr; 1638 tail = &newhdr->h_link; 1639 header = header->h_link; 1640 } 1641 *tail = NULL; 1642 1643 return ret; 1644 } 1645 /* 1646 ** FIX_MIME_HEADER -- possibly truncate/rebalance parameters in a MIME header 1647 ** 1648 ** Run through all of the parameters of a MIME header and 1649 ** possibly truncate and rebalance the parameter according 1650 ** to MaxMimeFieldLength. 1651 ** 1652 ** Parameters: 1653 ** string -- the full header 1654 ** 1655 ** Returns: 1656 ** TRUE if the header was modified, FALSE otherwise 1657 ** 1658 ** Side Effects: 1659 ** string modified in place 1660 */ 1661 1662 bool 1663 fix_mime_header(string) 1664 char *string; 1665 { 1666 bool modified = FALSE; 1667 char *begin = string; 1668 char *end; 1669 extern char *find_character __P((char *, char)); 1670 extern bool shorten_rfc822_string __P((char *, int)); 1671 1672 if (string == NULL || *string == '\0') 1673 return FALSE; 1674 1675 /* Split on each ';' */ 1676 while ((end = find_character(begin, ';')) != NULL) 1677 { 1678 char save = *end; 1679 char *bp; 1680 1681 *end = '\0'; 1682 1683 /* Shorten individual parameter */ 1684 if (shorten_rfc822_string(begin, MaxMimeFieldLength)) 1685 modified = TRUE; 1686 1687 /* Collapse the possibly shortened string with rest */ 1688 bp = begin + strlen(begin); 1689 if (bp != end) 1690 { 1691 char *ep = end; 1692 1693 *end = save; 1694 end = bp; 1695 1696 /* copy character by character due to overlap */ 1697 while (*ep != '\0') 1698 *bp++ = *ep++; 1699 *bp = '\0'; 1700 } 1701 else 1702 *end = save; 1703 if (*end == '\0') 1704 break; 1705 1706 /* Move past ';' */ 1707 begin = end + 1; 1708 } 1709 return modified; 1710 } 1711