1 # include <errno.h> 2 # include "sendmail.h" 3 4 SCCSID(@(#)headers.c 3.48 01/15/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 u_long mopts; 35 extern u_long mfencode(); 36 extern char *crackaddr(); 37 38 # ifdef DEBUG 39 if (tTd(31, 6)) 40 printf("chompheader: %s\n", line); 41 # endif DEBUG 42 43 /* strip off options */ 44 mopts = 0; 45 p = line; 46 if (*p == '?') 47 { 48 /* have some */ 49 register char *q = index(p + 1, *p); 50 51 if (q != NULL) 52 { 53 *q++ = '\0'; 54 mopts = mfencode(p + 1); 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 h->h_mflags = 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 h->h_mflags = 0; 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 568 if (bitset(H_CHECK|H_ACHECK, h->h_flags) && 569 !bitset(h->h_mflags, m->m_flags)) 570 continue; 571 572 p = h->h_value; 573 if (bitset(H_DEFAULT, h->h_flags)) 574 { 575 /* macro expand value if generated internally */ 576 expand(p, buf, &buf[sizeof buf], e); 577 p = buf; 578 if (p == NULL || *p == '\0') 579 continue; 580 } 581 582 if (bitset(H_FROM|H_RCPT, h->h_flags)) 583 { 584 /* address field */ 585 bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 586 587 if (bitset(H_FROM, h->h_flags)) 588 oldstyle = FALSE; 589 commaize(h, p, fp, oldstyle, m); 590 } 591 else 592 { 593 /* vanilla header line */ 594 (void) sprintf(obuf, "%s: %s\n", capitalize(h->h_field), p); 595 putline(obuf, fp, m); 596 } 597 } 598 } 599 /* 600 ** COMMAIZE -- output a header field, making a comma-translated list. 601 ** 602 ** Parameters: 603 ** h -- the header field to output. 604 ** p -- the value to put in it. 605 ** fp -- file to put it to. 606 ** oldstyle -- TRUE if this is an old style header. 607 ** m -- a pointer to the mailer descriptor. If NULL, 608 ** don't transform the name at all. 609 ** 610 ** Returns: 611 ** none. 612 ** 613 ** Side Effects: 614 ** outputs "p" to file "fp". 615 */ 616 617 commaize(h, p, fp, oldstyle, m) 618 register HDR *h; 619 register char *p; 620 FILE *fp; 621 bool oldstyle; 622 register MAILER *m; 623 { 624 register char *obp; 625 int opos; 626 bool firstone = TRUE; 627 char obuf[MAXLINE]; 628 629 /* 630 ** Output the address list translated by the 631 ** mailer and with commas. 632 */ 633 634 # ifdef DEBUG 635 if (tTd(14, 2)) 636 printf("commaize(%s: %s)\n", h->h_field, p); 637 # endif DEBUG 638 639 obp = obuf; 640 (void) sprintf(obp, "%s: ", capitalize(h->h_field)); 641 opos = strlen(h->h_field) + 2; 642 obp += opos; 643 644 /* 645 ** Run through the list of values. 646 */ 647 648 while (*p != '\0') 649 { 650 register char *name; 651 char savechar; 652 extern char *remotename(); 653 extern char *DelimChar; /* defined in prescan */ 654 655 /* 656 ** Find the end of the name. New style names 657 ** end with a comma, old style names end with 658 ** a space character. However, spaces do not 659 ** necessarily delimit an old-style name -- at 660 ** signs mean keep going. 661 */ 662 663 /* find end of name */ 664 while (isspace(*p) || *p == ',') 665 p++; 666 name = p; 667 for (;;) 668 { 669 char *oldp; 670 extern bool isatword(); 671 extern char **prescan(); 672 673 (void) prescan(p, oldstyle ? ' ' : ','); 674 p = DelimChar; 675 676 /* look to see if we have an at sign */ 677 oldp = p; 678 while (*p != '\0' && isspace(*p)) 679 p++; 680 681 if (*p != '@' && !isatword(p)) 682 { 683 p = oldp; 684 break; 685 } 686 p += *p == '@' ? 1 : 2; 687 while (*p != '\0' && isspace(*p)) 688 p++; 689 } 690 /* at the end of one complete name */ 691 692 /* strip off trailing white space */ 693 while (p >= name && (isspace(*p) || *p == ',' || *p == '\0')) 694 p--; 695 if (++p == name) 696 continue; 697 savechar = *p; 698 *p = '\0'; 699 700 /* translate the name to be relative */ 701 name = remotename(name, m, bitset(H_FROM, h->h_flags), FALSE); 702 if (*name == '\0') 703 { 704 *p = savechar; 705 continue; 706 } 707 708 /* output the name with nice formatting */ 709 opos += qstrlen(name); 710 if (!firstone) 711 opos += 2; 712 if (opos > 78 && !firstone) 713 { 714 (void) strcpy(obp, ",\n"); 715 putline(obuf, fp, m); 716 obp = obuf; 717 (void) sprintf(obp, " "); 718 opos = strlen(obp); 719 obp += opos; 720 opos += qstrlen(name); 721 } 722 else if (!firstone) 723 { 724 (void) sprintf(obp, ", "); 725 obp += 2; 726 } 727 728 /* strip off quote bits as we output */ 729 while (*name != '\0') 730 { 731 if (bitset(0200, *name)) 732 *obp++ = '\\'; 733 *obp++ = *name++ & ~0200; 734 } 735 firstone = FALSE; 736 *p = savechar; 737 } 738 (void) strcpy(obp, "\n"); 739 putline(obuf, fp, m); 740 } 741 /* 742 ** ISATWORD -- tell if the word we are pointing to is "at". 743 ** 744 ** Parameters: 745 ** p -- word to check. 746 ** 747 ** Returns: 748 ** TRUE -- if p is the word at. 749 ** FALSE -- otherwise. 750 ** 751 ** Side Effects: 752 ** none. 753 */ 754 755 bool 756 isatword(p) 757 register char *p; 758 { 759 extern char lower(); 760 761 if (lower(p[0]) == 'a' && lower(p[1]) == 't' && 762 p[2] != '\0' && isspace(p[2])) 763 return (TRUE); 764 return (FALSE); 765 } 766