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