1 # include <errno.h> 2 # include "sendmail.h" 3 4 SCCSID(@(#)headers.c 3.51 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 (strcmp(h->h_field, "message-id") == 0 && LogLevel > 8) 280 { 281 char buf[MAXNAME]; 282 283 p = h->h_value; 284 if (bitset(H_DEFAULT, h->h_flags)) 285 { 286 expand(p, buf, &buf[sizeof buf], e); 287 p = buf; 288 } 289 syslog(LOG_INFO, "%s: message-id=%s", e->e_id, p); 290 } 291 #endif LOG 292 } 293 #ifdef DEBUG 294 if (tTd(32, 1)) 295 printf("----------------------------\n"); 296 #endif DEBUG 297 298 /* store hop count */ 299 if (hopcnt > e->e_hopcount) 300 e->e_hopcount = hopcnt; 301 302 /* message priority */ 303 p = hvalue("precedence"); 304 if (p != NULL) 305 e->e_class = priencode(p); 306 if (!QueueRun) 307 e->e_msgpriority = e->e_msgsize - e->e_class * WKPRIFACT; 308 309 /* return receipt to */ 310 p = hvalue("return-receipt-to"); 311 if (p != NULL) 312 e->e_receiptto = p; 313 314 /* errors to */ 315 p = hvalue("errors-to"); 316 if (p != NULL) 317 sendtolist(p, (ADDRESS *) NULL, &e->e_errorqueue); 318 319 /* from person */ 320 if (OpMode == MD_ARPAFTP) 321 { 322 register struct hdrinfo *hi = HdrInfo; 323 324 for (p = NULL; p == NULL && hi->hi_field != NULL; hi++) 325 { 326 if (bitset(H_FROM, hi->hi_flags)) 327 p = hvalue(hi->hi_field); 328 } 329 if (p != NULL) 330 setsender(p); 331 } 332 333 /* full name of from person */ 334 p = hvalue("full-name"); 335 if (p != NULL) 336 define('x', p, e); 337 338 /* date message originated */ 339 p = hvalue("posted-date"); 340 if (p == NULL) 341 p = hvalue("date"); 342 if (p != NULL) 343 { 344 define('a', p, e); 345 /* we don't have a good way to do canonical conversion .... 346 define('d', newstr(arpatounix(p)), e); 347 .... so we will ignore the problem for the time being */ 348 } 349 350 /* 351 ** Log collection information. 352 */ 353 354 # ifdef LOG 355 if (LogLevel > 1) 356 { 357 syslog(LOG_INFO, "%s: from=%s, size=%ld, class=%d\n", 358 CurEnv->e_id, CurEnv->e_from.q_paddr, CurEnv->e_msgsize, 359 CurEnv->e_class); 360 } 361 # endif LOG 362 } 363 /* 364 ** PRIENCODE -- encode external priority names into internal values. 365 ** 366 ** Parameters: 367 ** p -- priority in ascii. 368 ** 369 ** Returns: 370 ** priority as a numeric level. 371 ** 372 ** Side Effects: 373 ** none. 374 */ 375 376 priencode(p) 377 char *p; 378 { 379 register int i; 380 extern bool sameword(); 381 382 for (i = 0; i < NumPriorities; i++) 383 { 384 if (sameword(p, Priorities[i].pri_name)) 385 return (Priorities[i].pri_val); 386 } 387 388 /* unknown priority */ 389 return (0); 390 } 391 /* 392 ** CRACKADDR -- parse an address and turn it into a macro 393 ** 394 ** This doesn't actually parse the address -- it just extracts 395 ** it and replaces it with "$g". The parse is totally ad hoc 396 ** and isn't even guaranteed to leave something syntactically 397 ** identical to what it started with. However, it does leave 398 ** something semantically identical. 399 ** 400 ** The process is kind of strange. There are a number of 401 ** interesting cases: 402 ** 1. comment <address> comment ==> comment <$g> comment 403 ** 2. address ==> address 404 ** 3. address (comment) ==> $g (comment) 405 ** 4. (comment) address ==> (comment) $g 406 ** And then there are the hard cases.... 407 ** 5. add (comment) ress ==> $g (comment) 408 ** 6. comment <address (comment)> ==> comment <$g (comment)> 409 ** 7. .... etc .... 410 ** 411 ** Parameters: 412 ** addr -- the address to be cracked. 413 ** 414 ** Returns: 415 ** a pointer to the new version. 416 ** 417 ** Side Effects: 418 ** none. 419 ** 420 ** Warning: 421 ** The return value is saved in local storage and should 422 ** be copied if it is to be reused. 423 */ 424 425 char * 426 crackaddr(addr) 427 register char *addr; 428 { 429 register char *p; 430 register int i; 431 static char buf[MAXNAME]; 432 char *rhs; 433 bool gotaddr; 434 register char *bp; 435 436 # ifdef DEBUG 437 if (tTd(33, 1)) 438 printf("crackaddr(%s)\n", addr); 439 # endif DEBUG 440 441 strcpy(buf, ""); 442 rhs = NULL; 443 444 /* strip leading spaces */ 445 while (*addr != '\0' && isspace(*addr)) 446 addr++; 447 448 /* 449 ** See if we have anything in angle brackets. If so, that is 450 ** the address part, and the rest is the comment. 451 */ 452 453 p = index(addr, '<'); 454 if (p != NULL) 455 { 456 /* copy the beginning of the addr field to the buffer */ 457 *p = '\0'; 458 strcpy(buf, addr); 459 strcat(buf, "<"); 460 *p++ = '<'; 461 462 /* skip spaces */ 463 while (isspace(*p)) 464 p++; 465 466 /* find the matching right angle bracket */ 467 addr = p; 468 for (i = 0; *p != '\0'; p++) 469 { 470 switch (*p) 471 { 472 case '<': 473 i++; 474 break; 475 476 case '>': 477 i--; 478 break; 479 } 480 if (i < 0) 481 break; 482 } 483 484 /* p now points to the closing quote (or a null byte) */ 485 if (*p != '\0') 486 { 487 /* make rhs point to the extra stuff at the end */ 488 rhs = p; 489 *p++ = '\0'; 490 } 491 } 492 493 /* 494 ** Now parse the real address part. "addr" points to the (null 495 ** terminated) version of what we are inerested in; rhs points 496 ** to the extra stuff at the end of the line, if any. 497 */ 498 499 p = addr; 500 501 /* now strip out comments */ 502 bp = &buf[strlen(buf)]; 503 gotaddr = FALSE; 504 for (; *p != '\0'; p++) 505 { 506 if (*p == '(') 507 { 508 /* copy to matching close paren */ 509 *bp++ = *p++; 510 for (i = 0; *p != '\0'; p++) 511 { 512 *bp++ = *p; 513 switch (*p) 514 { 515 case '(': 516 i++; 517 break; 518 519 case ')': 520 i--; 521 break; 522 } 523 if (i < 0) 524 break; 525 } 526 continue; 527 } 528 529 /* 530 ** If this is the first "real" character we have seen, 531 ** then we put the "$g" in the buffer now. 532 */ 533 534 if (isspace(*p)) 535 *bp++ = *p; 536 else if (!gotaddr) 537 { 538 strcpy(bp, "$g"); 539 bp += 2; 540 gotaddr = TRUE; 541 } 542 } 543 544 /* hack, hack.... strip trailing blanks */ 545 do 546 { 547 *bp-- = '\0'; 548 } while (isspace(*bp)); 549 bp++; 550 551 /* put any right hand side back on */ 552 if (rhs != NULL) 553 { 554 *rhs = '>'; 555 strcpy(bp, rhs); 556 } 557 558 # ifdef DEBUG 559 if (tTd(33, 1)) 560 printf("crackaddr=>`%s'\n", buf); 561 # endif DEBUG 562 563 return (buf); 564 } 565 /* 566 ** PUTHEADER -- put the header part of a message from the in-core copy 567 ** 568 ** Parameters: 569 ** fp -- file to put it on. 570 ** m -- mailer to use. 571 ** e -- envelope to use. 572 ** 573 ** Returns: 574 ** none. 575 ** 576 ** Side Effects: 577 ** none. 578 */ 579 580 putheader(fp, m, e) 581 register FILE *fp; 582 register MAILER *m; 583 register ENVELOPE *e; 584 { 585 char buf[BUFSIZ]; 586 register HDR *h; 587 extern char *arpadate(); 588 extern char *capitalize(); 589 char obuf[MAXLINE]; 590 591 for (h = e->e_header; h != NULL; h = h->h_link) 592 { 593 register char *p; 594 extern bool bitintersect(); 595 596 if (bitset(H_CHECK|H_ACHECK, h->h_flags) && 597 !bitintersect(h->h_mflags, m->m_flags)) 598 continue; 599 600 p = h->h_value; 601 if (bitset(H_DEFAULT, h->h_flags)) 602 { 603 /* macro expand value if generated internally */ 604 expand(p, buf, &buf[sizeof buf], e); 605 p = buf; 606 if (p == NULL || *p == '\0') 607 continue; 608 } 609 610 if (bitset(H_FROM|H_RCPT, h->h_flags)) 611 { 612 /* address field */ 613 bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 614 615 if (bitset(H_FROM, h->h_flags)) 616 oldstyle = FALSE; 617 commaize(h, p, fp, oldstyle, m); 618 } 619 else 620 { 621 /* vanilla header line */ 622 (void) sprintf(obuf, "%s: %s\n", capitalize(h->h_field), p); 623 putline(obuf, fp, m); 624 } 625 } 626 } 627 /* 628 ** COMMAIZE -- output a header field, making a comma-translated list. 629 ** 630 ** Parameters: 631 ** h -- the header field to output. 632 ** p -- the value to put in it. 633 ** fp -- file to put it to. 634 ** oldstyle -- TRUE if this is an old style header. 635 ** m -- a pointer to the mailer descriptor. If NULL, 636 ** don't transform the name at all. 637 ** 638 ** Returns: 639 ** none. 640 ** 641 ** Side Effects: 642 ** outputs "p" to file "fp". 643 */ 644 645 commaize(h, p, fp, oldstyle, m) 646 register HDR *h; 647 register char *p; 648 FILE *fp; 649 bool oldstyle; 650 register MAILER *m; 651 { 652 register char *obp; 653 int opos; 654 bool firstone = TRUE; 655 char obuf[MAXLINE + 3]; 656 657 /* 658 ** Output the address list translated by the 659 ** mailer and with commas. 660 */ 661 662 # ifdef DEBUG 663 if (tTd(14, 2)) 664 printf("commaize(%s: %s)\n", h->h_field, p); 665 # endif DEBUG 666 667 obp = obuf; 668 (void) sprintf(obp, "%s: ", capitalize(h->h_field)); 669 opos = strlen(h->h_field) + 2; 670 obp += opos; 671 672 /* 673 ** Run through the list of values. 674 */ 675 676 while (*p != '\0') 677 { 678 register char *name; 679 char savechar; 680 extern char *remotename(); 681 extern char *DelimChar; /* defined in prescan */ 682 683 /* 684 ** Find the end of the name. New style names 685 ** end with a comma, old style names end with 686 ** a space character. However, spaces do not 687 ** necessarily delimit an old-style name -- at 688 ** signs mean keep going. 689 */ 690 691 /* find end of name */ 692 while (isspace(*p) || *p == ',') 693 p++; 694 name = p; 695 for (;;) 696 { 697 char *oldp; 698 extern bool isatword(); 699 extern char **prescan(); 700 701 (void) prescan(p, oldstyle ? ' ' : ','); 702 p = DelimChar; 703 704 /* look to see if we have an at sign */ 705 oldp = p; 706 while (*p != '\0' && isspace(*p)) 707 p++; 708 709 if (*p != '@' && !isatword(p)) 710 { 711 p = oldp; 712 break; 713 } 714 p += *p == '@' ? 1 : 2; 715 while (*p != '\0' && isspace(*p)) 716 p++; 717 } 718 /* at the end of one complete name */ 719 720 /* strip off trailing white space */ 721 while (p >= name && (isspace(*p) || *p == ',' || *p == '\0')) 722 p--; 723 if (++p == name) 724 continue; 725 savechar = *p; 726 *p = '\0'; 727 728 /* translate the name to be relative */ 729 name = remotename(name, m, bitset(H_FROM, h->h_flags), FALSE); 730 if (*name == '\0') 731 { 732 *p = savechar; 733 continue; 734 } 735 736 /* output the name with nice formatting */ 737 opos += qstrlen(name); 738 if (!firstone) 739 opos += 2; 740 if (opos > 78 && !firstone) 741 { 742 (void) strcpy(obp, ",\n"); 743 putline(obuf, fp, m); 744 obp = obuf; 745 (void) sprintf(obp, " "); 746 opos = strlen(obp); 747 obp += opos; 748 opos += qstrlen(name); 749 } 750 else if (!firstone) 751 { 752 (void) sprintf(obp, ", "); 753 obp += 2; 754 } 755 756 /* strip off quote bits as we output */ 757 while (*name != '\0' && obp < &obuf[MAXLINE]) 758 { 759 if (bitset(0200, *name)) 760 *obp++ = '\\'; 761 *obp++ = *name++ & ~0200; 762 } 763 firstone = FALSE; 764 *p = savechar; 765 } 766 (void) strcpy(obp, "\n"); 767 putline(obuf, fp, m); 768 } 769 /* 770 ** ISATWORD -- tell if the word we are pointing to is "at". 771 ** 772 ** Parameters: 773 ** p -- word to check. 774 ** 775 ** Returns: 776 ** TRUE -- if p is the word at. 777 ** FALSE -- otherwise. 778 ** 779 ** Side Effects: 780 ** none. 781 */ 782 783 bool 784 isatword(p) 785 register char *p; 786 { 787 extern char lower(); 788 789 if (lower(p[0]) == 'a' && lower(p[1]) == 't' && 790 p[2] != '\0' && isspace(p[2])) 791 return (TRUE); 792 return (FALSE); 793 } 794