1 /* 2 * Copyright (c) 1988 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that this notice is preserved and that due credit is given 7 * to the University of California at Berkeley. The name of the University 8 * may not be used to endorse or promote products derived from this 9 * software without specific prior written permission. This software 10 * is provided ``as is'' without express or implied warranty. 11 * 12 * Sendmail 13 * Copyright (c) 1983 Eric P. Allman 14 * Berkeley, California 15 */ 16 17 #ifndef lint 18 static char sccsid[] = "@(#)headers.c 5.9 (Berkeley) 03/13/88"; 19 #endif /* not lint */ 20 21 # include <errno.h> 22 # include "sendmail.h" 23 24 /* 25 ** CHOMPHEADER -- process and save a header line. 26 ** 27 ** Called by collect and by readcf to deal with header lines. 28 ** 29 ** Parameters: 30 ** line -- header as a text line. 31 ** def -- if set, this is a default value. 32 ** 33 ** Returns: 34 ** flags for this header. 35 ** 36 ** Side Effects: 37 ** The header is saved on the header list. 38 ** Contents of 'line' are destroyed. 39 */ 40 41 chompheader(line, def) 42 char *line; 43 bool def; 44 { 45 register char *p; 46 register HDR *h; 47 HDR **hp; 48 char *fname; 49 char *fvalue; 50 struct hdrinfo *hi; 51 bool cond = FALSE; 52 BITMAP mopts; 53 extern char *crackaddr(); 54 55 # ifdef DEBUG 56 if (tTd(31, 6)) 57 printf("chompheader: %s\n", line); 58 # endif DEBUG 59 60 /* strip off options */ 61 clrbitmap(mopts); 62 p = line; 63 if (*p == '?') 64 { 65 /* have some */ 66 register char *q = index(p + 1, *p); 67 68 if (q != NULL) 69 { 70 *q++ = '\0'; 71 while (*++p != '\0') 72 setbitn(*p, mopts); 73 p = q; 74 } 75 else 76 syserr("chompheader: syntax error, line \"%s\"", line); 77 cond = TRUE; 78 } 79 80 /* find canonical name */ 81 fname = p; 82 p = index(p, ':'); 83 if (p == NULL) 84 { 85 syserr("chompheader: syntax error, line \"%s\"", line); 86 return (0); 87 } 88 fvalue = &p[1]; 89 while (isspace(*--p)) 90 continue; 91 *++p = '\0'; 92 makelower(fname); 93 94 /* strip field value on front */ 95 if (*fvalue == ' ') 96 fvalue++; 97 98 /* see if it is a known type */ 99 for (hi = HdrInfo; hi->hi_field != NULL; hi++) 100 { 101 if (strcmp(hi->hi_field, fname) == 0) 102 break; 103 } 104 105 /* see if this is a resent message */ 106 if (!def && bitset(H_RESENT, hi->hi_flags)) 107 CurEnv->e_flags |= EF_RESENT; 108 109 /* if this means "end of header" quit now */ 110 if (bitset(H_EOH, hi->hi_flags)) 111 return (hi->hi_flags); 112 113 /* drop explicit From: if same as what we would generate -- for MH */ 114 p = "resent-from"; 115 if (!bitset(EF_RESENT, CurEnv->e_flags)) 116 p += 7; 117 if (!def && !QueueRun && strcmp(fname, p) == 0) 118 { 119 if (CurEnv->e_from.q_paddr != NULL && 120 strcmp(fvalue, CurEnv->e_from.q_paddr) == 0) 121 return (hi->hi_flags); 122 } 123 124 /* delete default value for this header */ 125 for (hp = &CurEnv->e_header; (h = *hp) != NULL; hp = &h->h_link) 126 { 127 if (strcmp(fname, h->h_field) == 0 && 128 bitset(H_DEFAULT, h->h_flags) && 129 !bitset(H_FORCE, h->h_flags)) 130 h->h_value = NULL; 131 } 132 133 /* create a new node */ 134 h = (HDR *) xalloc(sizeof *h); 135 h->h_field = newstr(fname); 136 h->h_value = NULL; 137 h->h_link = NULL; 138 bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts); 139 *hp = h; 140 h->h_flags = hi->hi_flags; 141 if (def) 142 h->h_flags |= H_DEFAULT; 143 if (cond) 144 h->h_flags |= H_CHECK; 145 if (h->h_value != NULL) 146 free((char *) h->h_value); 147 h->h_value = newstr(fvalue); 148 149 /* hack to see if this is a new format message */ 150 if (!def && bitset(H_RCPT|H_FROM, h->h_flags) && 151 (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL || 152 index(fvalue, '<') != NULL || index(fvalue, ';') != NULL)) 153 { 154 CurEnv->e_flags &= ~EF_OLDSTYLE; 155 } 156 157 return (h->h_flags); 158 } 159 /* 160 ** ADDHEADER -- add a header entry to the end of the queue. 161 ** 162 ** This bypasses the special checking of chompheader. 163 ** 164 ** Parameters: 165 ** field -- the name of the header field. 166 ** value -- the value of the field. It must be lower-cased. 167 ** e -- the envelope to add them to. 168 ** 169 ** Returns: 170 ** none. 171 ** 172 ** Side Effects: 173 ** adds the field on the list of headers for this envelope. 174 */ 175 176 addheader(field, value, e) 177 char *field; 178 char *value; 179 ENVELOPE *e; 180 { 181 register HDR *h; 182 register struct hdrinfo *hi; 183 HDR **hp; 184 185 /* find info struct */ 186 for (hi = HdrInfo; hi->hi_field != NULL; hi++) 187 { 188 if (strcmp(field, hi->hi_field) == 0) 189 break; 190 } 191 192 /* find current place in list -- keep back pointer? */ 193 for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) 194 { 195 if (strcmp(field, h->h_field) == 0) 196 break; 197 } 198 199 /* allocate space for new header */ 200 h = (HDR *) xalloc(sizeof *h); 201 h->h_field = field; 202 h->h_value = newstr(value); 203 h->h_link = *hp; 204 h->h_flags = hi->hi_flags | H_DEFAULT; 205 clrbitmap(h->h_mflags); 206 *hp = h; 207 } 208 /* 209 ** HVALUE -- return value of a header. 210 ** 211 ** Only "real" fields (i.e., ones that have not been supplied 212 ** as a default) are used. 213 ** 214 ** Parameters: 215 ** field -- the field name. 216 ** 217 ** Returns: 218 ** pointer to the value part. 219 ** NULL if not found. 220 ** 221 ** Side Effects: 222 ** none. 223 */ 224 225 char * 226 hvalue(field) 227 char *field; 228 { 229 register HDR *h; 230 231 for (h = CurEnv->e_header; h != NULL; h = h->h_link) 232 { 233 if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0) 234 return (h->h_value); 235 } 236 return (NULL); 237 } 238 /* 239 ** ISHEADER -- predicate telling if argument is a header. 240 ** 241 ** A line is a header if it has a single word followed by 242 ** optional white space followed by a colon. 243 ** 244 ** Parameters: 245 ** s -- string to check for possible headerness. 246 ** 247 ** Returns: 248 ** TRUE if s is a header. 249 ** FALSE otherwise. 250 ** 251 ** Side Effects: 252 ** none. 253 */ 254 255 bool 256 isheader(s) 257 register char *s; 258 { 259 while (*s > ' ' && *s != ':' && *s != '\0') 260 s++; 261 262 /* following technically violates RFC822 */ 263 while (isspace(*s)) 264 s++; 265 266 return (*s == ':'); 267 } 268 /* 269 ** EATHEADER -- run through the stored header and extract info. 270 ** 271 ** Parameters: 272 ** e -- the envelope to process. 273 ** 274 ** Returns: 275 ** none. 276 ** 277 ** Side Effects: 278 ** Sets a bunch of global variables from information 279 ** in the collected header. 280 ** Aborts the message if the hop count is exceeded. 281 */ 282 283 eatheader(e) 284 register ENVELOPE *e; 285 { 286 register HDR *h; 287 register char *p; 288 int hopcnt = 0; 289 290 #ifdef DEBUG 291 if (tTd(32, 1)) 292 printf("----- collected header -----\n"); 293 #endif DEBUG 294 for (h = e->e_header; h != NULL; h = h->h_link) 295 { 296 #ifdef DEBUG 297 extern char *capitalize(); 298 299 if (tTd(32, 1)) 300 printf("%s: %s\n", capitalize(h->h_field), h->h_value); 301 #endif DEBUG 302 /* count the number of times it has been processed */ 303 if (bitset(H_TRACE, h->h_flags)) 304 hopcnt++; 305 306 /* send to this person if we so desire */ 307 if (GrabTo && bitset(H_RCPT, h->h_flags) && 308 !bitset(H_DEFAULT, h->h_flags) && 309 (!bitset(EF_RESENT, CurEnv->e_flags) || bitset(H_RESENT, h->h_flags))) 310 { 311 sendtolist(h->h_value, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 312 } 313 314 /* log the message-id */ 315 #ifdef LOG 316 if (!QueueRun && LogLevel > 8 && h->h_value != NULL && 317 strcmp(h->h_field, "message-id") == 0) 318 { 319 char buf[MAXNAME]; 320 321 p = h->h_value; 322 if (bitset(H_DEFAULT, h->h_flags)) 323 { 324 expand(p, buf, &buf[sizeof buf], e); 325 p = buf; 326 } 327 syslog(LOG_INFO, "%s: message-id=%s", e->e_id, p); 328 } 329 #endif LOG 330 } 331 #ifdef DEBUG 332 if (tTd(32, 1)) 333 printf("----------------------------\n"); 334 #endif DEBUG 335 336 /* store hop count */ 337 if (hopcnt > e->e_hopcount) 338 e->e_hopcount = hopcnt; 339 340 /* message priority */ 341 p = hvalue("precedence"); 342 if (p != NULL) 343 e->e_class = priencode(p); 344 if (!QueueRun) 345 e->e_msgpriority = e->e_msgsize 346 - e->e_class * WkClassFact 347 + e->e_nrcpts * WkRecipFact; 348 349 /* return receipt to */ 350 p = hvalue("return-receipt-to"); 351 if (p != NULL) 352 e->e_receiptto = p; 353 354 /* errors to */ 355 p = hvalue("errors-to"); 356 if (p != NULL) 357 sendtolist(p, (ADDRESS *) NULL, &e->e_errorqueue); 358 359 /* from person */ 360 if (OpMode == MD_ARPAFTP) 361 { 362 register struct hdrinfo *hi = HdrInfo; 363 364 for (p = NULL; p == NULL && hi->hi_field != NULL; hi++) 365 { 366 if (bitset(H_FROM, hi->hi_flags)) 367 p = hvalue(hi->hi_field); 368 } 369 if (p != NULL) 370 setsender(p); 371 } 372 373 /* full name of from person */ 374 p = hvalue("full-name"); 375 if (p != NULL) 376 define('x', p, e); 377 378 /* date message originated */ 379 p = hvalue("posted-date"); 380 if (p == NULL) 381 p = hvalue("date"); 382 if (p != NULL) 383 { 384 define('a', p, e); 385 /* we don't have a good way to do canonical conversion .... 386 define('d', newstr(arpatounix(p)), e); 387 .... so we will ignore the problem for the time being */ 388 } 389 390 /* 391 ** Log collection information. 392 */ 393 394 # ifdef LOG 395 if (!QueueRun && LogLevel > 1) 396 { 397 syslog(LOG_INFO, "%s: from=%s, size=%ld, class=%d\n", 398 CurEnv->e_id, CurEnv->e_from.q_paddr, CurEnv->e_msgsize, 399 CurEnv->e_class); 400 } 401 # endif LOG 402 } 403 /* 404 ** PRIENCODE -- encode external priority names into internal values. 405 ** 406 ** Parameters: 407 ** p -- priority in ascii. 408 ** 409 ** Returns: 410 ** priority as a numeric level. 411 ** 412 ** Side Effects: 413 ** none. 414 */ 415 416 priencode(p) 417 char *p; 418 { 419 register int i; 420 421 for (i = 0; i < NumPriorities; i++) 422 { 423 if (!strcasecmp(p, Priorities[i].pri_name)) 424 return (Priorities[i].pri_val); 425 } 426 427 /* unknown priority */ 428 return (0); 429 } 430 /* 431 ** CRACKADDR -- parse an address and turn it into a macro 432 ** 433 ** This doesn't actually parse the address -- it just extracts 434 ** it and replaces it with "$g". The parse is totally ad hoc 435 ** and isn't even guaranteed to leave something syntactically 436 ** identical to what it started with. However, it does leave 437 ** something semantically identical. 438 ** 439 ** The process is kind of strange. There are a number of 440 ** interesting cases: 441 ** 1. comment <address> comment ==> comment <$g> comment 442 ** 2. address ==> address 443 ** 3. address (comment) ==> $g (comment) 444 ** 4. (comment) address ==> (comment) $g 445 ** And then there are the hard cases.... 446 ** 5. add (comment) ress ==> $g (comment) 447 ** 6. comment <address (comment)> ==> comment <$g (comment)> 448 ** 7. .... etc .... 449 ** 450 ** Parameters: 451 ** addr -- the address to be cracked. 452 ** 453 ** Returns: 454 ** a pointer to the new version. 455 ** 456 ** Side Effects: 457 ** none. 458 ** 459 ** Warning: 460 ** The return value is saved in local storage and should 461 ** be copied if it is to be reused. 462 */ 463 464 char * 465 crackaddr(addr) 466 register char *addr; 467 { 468 register char *p; 469 register int i; 470 static char buf[MAXNAME]; 471 char *rhs; 472 bool gotaddr; 473 register char *bp; 474 475 # ifdef DEBUG 476 if (tTd(33, 1)) 477 printf("crackaddr(%s)\n", addr); 478 # endif DEBUG 479 480 (void) strcpy(buf, ""); 481 rhs = NULL; 482 483 /* strip leading spaces */ 484 while (*addr != '\0' && isspace(*addr)) 485 addr++; 486 487 /* 488 ** See if we have anything in angle brackets. If so, that is 489 ** the address part, and the rest is the comment. 490 */ 491 492 p = index(addr, '<'); 493 if (p != NULL) 494 { 495 /* copy the beginning of the addr field to the buffer */ 496 *p = '\0'; 497 (void) strcpy(buf, addr); 498 (void) strcat(buf, "<"); 499 *p++ = '<'; 500 501 /* skip spaces */ 502 while (isspace(*p)) 503 p++; 504 505 /* find the matching right angle bracket */ 506 addr = p; 507 for (i = 0; *p != '\0'; p++) 508 { 509 switch (*p) 510 { 511 case '<': 512 i++; 513 break; 514 515 case '>': 516 i--; 517 break; 518 } 519 if (i < 0) 520 break; 521 } 522 523 /* p now points to the closing quote (or a null byte) */ 524 if (*p != '\0') 525 { 526 /* make rhs point to the extra stuff at the end */ 527 rhs = p; 528 *p++ = '\0'; 529 } 530 } 531 532 /* 533 ** Now parse the real address part. "addr" points to the (null 534 ** terminated) version of what we are inerested in; rhs points 535 ** to the extra stuff at the end of the line, if any. 536 */ 537 538 p = addr; 539 540 /* now strip out comments */ 541 bp = &buf[strlen(buf)]; 542 gotaddr = FALSE; 543 for (; *p != '\0'; p++) 544 { 545 if (*p == '(') 546 { 547 /* copy to matching close paren */ 548 *bp++ = *p++; 549 for (i = 0; *p != '\0'; p++) 550 { 551 *bp++ = *p; 552 switch (*p) 553 { 554 case '(': 555 i++; 556 break; 557 558 case ')': 559 i--; 560 break; 561 } 562 if (i < 0) 563 break; 564 } 565 continue; 566 } 567 568 /* 569 ** If this is the first "real" character we have seen, 570 ** then we put the "$g" in the buffer now. 571 */ 572 573 if (isspace(*p)) 574 *bp++ = *p; 575 else if (!gotaddr) 576 { 577 (void) strcpy(bp, "\001g"); 578 bp += 2; 579 gotaddr = TRUE; 580 } 581 } 582 583 /* hack, hack.... strip trailing blanks */ 584 do 585 { 586 *bp-- = '\0'; 587 } while (isspace(*bp)); 588 bp++; 589 590 /* put any right hand side back on */ 591 if (rhs != NULL) 592 { 593 *rhs = '>'; 594 (void) strcpy(bp, rhs); 595 } 596 597 # ifdef DEBUG 598 if (tTd(33, 1)) 599 printf("crackaddr=>`%s'\n", buf); 600 # endif DEBUG 601 602 return (buf); 603 } 604 /* 605 ** PUTHEADER -- put the header part of a message from the in-core copy 606 ** 607 ** Parameters: 608 ** fp -- file to put it on. 609 ** m -- mailer to use. 610 ** e -- envelope to use. 611 ** 612 ** Returns: 613 ** none. 614 ** 615 ** Side Effects: 616 ** none. 617 */ 618 619 putheader(fp, m, e) 620 register FILE *fp; 621 register MAILER *m; 622 register ENVELOPE *e; 623 { 624 char buf[BUFSIZ]; 625 register HDR *h; 626 extern char *arpadate(); 627 extern char *capitalize(); 628 char obuf[MAXLINE]; 629 630 for (h = e->e_header; h != NULL; h = h->h_link) 631 { 632 register char *p; 633 extern bool bitintersect(); 634 635 if (bitset(H_CHECK|H_ACHECK, h->h_flags) && 636 !bitintersect(h->h_mflags, m->m_flags)) 637 continue; 638 639 /* handle Resent-... headers specially */ 640 if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 641 continue; 642 643 p = h->h_value; 644 if (bitset(H_DEFAULT, h->h_flags)) 645 { 646 /* macro expand value if generated internally */ 647 expand(p, buf, &buf[sizeof buf], e); 648 p = buf; 649 if (p == NULL || *p == '\0') 650 continue; 651 } 652 653 if (bitset(H_FROM|H_RCPT, h->h_flags)) 654 { 655 /* address field */ 656 bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 657 658 if (bitset(H_FROM, h->h_flags)) 659 oldstyle = FALSE; 660 commaize(h, p, fp, oldstyle, m); 661 } 662 else 663 { 664 /* vanilla header line */ 665 register char *nlp; 666 667 (void) sprintf(obuf, "%s: ", capitalize(h->h_field)); 668 while ((nlp = index(p, '\n')) != NULL) 669 { 670 *nlp = '\0'; 671 (void) strcat(obuf, p); 672 *nlp = '\n'; 673 putline(obuf, fp, m); 674 p = ++nlp; 675 obuf[0] = '\0'; 676 } 677 (void) strcat(obuf, p); 678 putline(obuf, fp, m); 679 } 680 } 681 } 682 /* 683 ** COMMAIZE -- output a header field, making a comma-translated list. 684 ** 685 ** Parameters: 686 ** h -- the header field to output. 687 ** p -- the value to put in it. 688 ** fp -- file to put it to. 689 ** oldstyle -- TRUE if this is an old style header. 690 ** m -- a pointer to the mailer descriptor. If NULL, 691 ** don't transform the name at all. 692 ** 693 ** Returns: 694 ** none. 695 ** 696 ** Side Effects: 697 ** outputs "p" to file "fp". 698 */ 699 700 commaize(h, p, fp, oldstyle, m) 701 register HDR *h; 702 register char *p; 703 FILE *fp; 704 bool oldstyle; 705 register MAILER *m; 706 { 707 register char *obp; 708 int opos; 709 bool firstone = TRUE; 710 char obuf[MAXLINE + 3]; 711 712 /* 713 ** Output the address list translated by the 714 ** mailer and with commas. 715 */ 716 717 # ifdef DEBUG 718 if (tTd(14, 2)) 719 printf("commaize(%s: %s)\n", h->h_field, p); 720 # endif DEBUG 721 722 obp = obuf; 723 (void) sprintf(obp, "%s: ", capitalize(h->h_field)); 724 opos = strlen(h->h_field) + 2; 725 obp += opos; 726 727 /* 728 ** Run through the list of values. 729 */ 730 731 while (*p != '\0') 732 { 733 register char *name; 734 char savechar; 735 extern char *remotename(); 736 extern char *DelimChar; /* defined in prescan */ 737 738 /* 739 ** Find the end of the name. New style names 740 ** end with a comma, old style names end with 741 ** a space character. However, spaces do not 742 ** necessarily delimit an old-style name -- at 743 ** signs mean keep going. 744 */ 745 746 /* find end of name */ 747 while (isspace(*p) || *p == ',') 748 p++; 749 name = p; 750 for (;;) 751 { 752 char *oldp; 753 char pvpbuf[PSBUFSIZE]; 754 extern bool isatword(); 755 extern char **prescan(); 756 757 (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf); 758 p = DelimChar; 759 760 /* look to see if we have an at sign */ 761 oldp = p; 762 while (*p != '\0' && isspace(*p)) 763 p++; 764 765 if (*p != '@' && !isatword(p)) 766 { 767 p = oldp; 768 break; 769 } 770 p += *p == '@' ? 1 : 2; 771 while (*p != '\0' && isspace(*p)) 772 p++; 773 } 774 /* at the end of one complete name */ 775 776 /* strip off trailing white space */ 777 while (p >= name && (isspace(*p) || *p == ',' || *p == '\0')) 778 p--; 779 if (++p == name) 780 continue; 781 savechar = *p; 782 *p = '\0'; 783 784 /* translate the name to be relative */ 785 name = remotename(name, m, bitset(H_FROM, h->h_flags), FALSE); 786 if (*name == '\0') 787 { 788 *p = savechar; 789 continue; 790 } 791 792 /* output the name with nice formatting */ 793 opos += qstrlen(name); 794 if (!firstone) 795 opos += 2; 796 if (opos > 78 && !firstone) 797 { 798 (void) strcpy(obp, ",\n"); 799 putline(obuf, fp, m); 800 obp = obuf; 801 (void) sprintf(obp, " "); 802 opos = strlen(obp); 803 obp += opos; 804 opos += qstrlen(name); 805 } 806 else if (!firstone) 807 { 808 (void) sprintf(obp, ", "); 809 obp += 2; 810 } 811 812 /* strip off quote bits as we output */ 813 while (*name != '\0' && obp < &obuf[MAXLINE]) 814 { 815 if (bitset(0200, *name)) 816 *obp++ = '\\'; 817 *obp++ = *name++ & ~0200; 818 } 819 firstone = FALSE; 820 *p = savechar; 821 } 822 (void) strcpy(obp, "\n"); 823 putline(obuf, fp, m); 824 } 825 /* 826 ** ISATWORD -- tell if the word we are pointing to is "at". 827 ** 828 ** Parameters: 829 ** p -- word to check. 830 ** 831 ** Returns: 832 ** TRUE -- if p is the word at. 833 ** FALSE -- otherwise. 834 ** 835 ** Side Effects: 836 ** none. 837 */ 838 839 bool 840 isatword(p) 841 register char *p; 842 { 843 extern char lower(); 844 845 if (lower(p[0]) == 'a' && lower(p[1]) == 't' && 846 p[2] != '\0' && isspace(p[2])) 847 return (TRUE); 848 return (FALSE); 849 } 850