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