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