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