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