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