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