1 # include <errno.h> 2 # include "sendmail.h" 3 4 SCCSID(@(#)headers.c 3.26 08/22/82); 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 extern bool isheader(); 31 char *fname; 32 char *fvalue; 33 struct hdrinfo *hi; 34 u_long mopts; 35 extern u_long mfencode(); 36 extern char *crackfrom(); 37 38 # ifdef DEBUG 39 if (tTd(31, 6)) 40 printf("chompheader: %s\n", line); 41 # endif DEBUG 42 43 /* strip off trailing newline */ 44 p = rindex(line, '\n'); 45 if (p != NULL) 46 *p = '\0'; 47 48 /* strip off options */ 49 mopts = 0; 50 p = line; 51 if (*p == '?') 52 { 53 /* have some */ 54 register char *q = index(p + 1, *p); 55 56 if (q != NULL) 57 { 58 *q++ = '\0'; 59 mopts = mfencode(p + 1); 60 p = q; 61 } 62 else 63 syserr("chompheader: syntax error, line \"%s\"", line); 64 } 65 66 /* find canonical name */ 67 fname = p; 68 p = index(p, ':'); 69 fvalue = &p[1]; 70 while (isspace(*--p)) 71 continue; 72 *++p = '\0'; 73 makelower(fname); 74 75 /* strip field value on front */ 76 if (*fvalue == ' ') 77 fvalue++; 78 79 /* search header list for this header */ 80 for (hp = &CurEnv->e_header, h = CurEnv->e_header; h != NULL; hp = &h->h_link, h = h->h_link) 81 { 82 if (strcmp(fname, h->h_field) == 0 && bitset(H_DEFAULT, h->h_flags)) 83 break; 84 } 85 86 /* see if it is a known type */ 87 for (hi = HdrInfo; hi->hi_field != NULL; hi++) 88 { 89 if (strcmp(hi->hi_field, fname) == 0) 90 break; 91 } 92 93 /* if this means "end of header" quit now */ 94 if (bitset(H_EOH, hi->hi_flags)) 95 return (hi->hi_flags); 96 97 /* count Received: lines to avoid loops (simulate hop counts) */ 98 if (strcmp(fname, "received") == 0) 99 HopCount++; 100 101 /* create/fill in a new node */ 102 if (h == NULL || bitset(H_FORCE, h->h_flags)) 103 { 104 /* create a new node */ 105 h = (HDR *) xalloc(sizeof *h); 106 h->h_field = newstr(fname); 107 h->h_value = NULL; 108 h->h_link = *hp; 109 h->h_flags = hi->hi_flags; 110 h->h_mflags = mopts | hi->hi_mflags; 111 *hp = h; 112 } 113 if (def) 114 h->h_flags |= H_DEFAULT; 115 else if (mopts == 0) 116 h->h_flags &= ~H_CHECK; 117 if (h->h_value != NULL) 118 free(h->h_value); 119 if (!def && strcmp(fname, "from") == 0) 120 { 121 /* turn it into a macro -- will be expanded later */ 122 h->h_value = newstr(crackfrom(fvalue)); 123 h->h_flags |= H_DEFAULT; 124 } 125 else 126 h->h_value = newstr(fvalue); 127 if (!def && GrabTo && bitset(H_RCPT, h->h_flags)) 128 sendto(h->h_value, 0, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 129 130 /* hack to see if this is a new format message */ 131 if (bitset(H_RCPT, h->h_flags) && 132 (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL || 133 index(fvalue, '<') != NULL)) 134 CurEnv->e_oldstyle = FALSE; 135 136 return (h->h_flags); 137 } 138 /* 139 ** ADDHEADER -- add a header entry to the end of the queue. 140 ** 141 ** This bypasses the special checking of chompheader. 142 ** 143 ** Parameters: 144 ** field -- the name of the header field. 145 ** value -- the value of the field. It must be lower-cased. 146 ** e -- the envelope to add them to. 147 ** 148 ** Returns: 149 ** none. 150 ** 151 ** Side Effects: 152 ** adds the field on the list of headers for this envelope. 153 */ 154 155 addheader(field, value, e) 156 char *field; 157 char *value; 158 ENVELOPE *e; 159 { 160 register HDR *h; 161 register struct hdrinfo *hi; 162 HDR **hp; 163 164 /* find info struct */ 165 for (hi = HdrInfo; hi->hi_field != NULL; hi++) 166 { 167 if (strcmp(field, hi->hi_field) == 0) 168 break; 169 } 170 171 /* find current place in list -- keep back pointer? */ 172 for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link) 173 { 174 if (strcmp(field, h->h_field) == 0) 175 break; 176 } 177 178 /* allocate space for new header */ 179 h = (HDR *) xalloc(sizeof *h); 180 h->h_field = field; 181 h->h_value = newstr(value); 182 h->h_link = *hp; 183 h->h_flags = hi->hi_flags | H_DEFAULT; 184 h->h_mflags = hi->hi_mflags; 185 *hp = h; 186 } 187 /* 188 ** HVALUE -- return value of a header. 189 ** 190 ** Only "real" fields (i.e., ones that have not been supplied 191 ** as a default) are used. 192 ** 193 ** Parameters: 194 ** field -- the field name. 195 ** 196 ** Returns: 197 ** pointer to the value part. 198 ** NULL if not found. 199 ** 200 ** Side Effects: 201 ** sets the H_USED bit in the header if found. 202 */ 203 204 char * 205 hvalue(field) 206 char *field; 207 { 208 register HDR *h; 209 210 for (h = CurEnv->e_header; h != NULL; h = h->h_link) 211 { 212 if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0) 213 { 214 h->h_flags |= H_USED; 215 return (h->h_value); 216 } 217 } 218 return (NULL); 219 } 220 /* 221 ** HRVALUE -- return pointer to header descriptor. 222 ** 223 ** Like hvalue except returns header descriptor block and isn't 224 ** picky about "real" headers. 225 ** 226 ** Parameters: 227 ** field -- name of field we are interested in. 228 ** 229 ** Returns: 230 ** pointer to header descriptor. 231 ** 232 ** Side Effects: 233 ** none. 234 */ 235 236 HDR * 237 hrvalue(field) 238 char *field; 239 { 240 register HDR *h; 241 242 for (h = CurEnv->e_header; h != NULL; h = h->h_link) 243 { 244 if (strcmp(h->h_field, field) == 0) 245 return (h); 246 } 247 return (NULL); 248 } 249 /* 250 ** ISHEADER -- predicate telling if argument is a header. 251 ** 252 ** A line is a header if it has a single word followed by 253 ** optional white space followed by a colon. 254 ** 255 ** Parameters: 256 ** s -- string to check for possible headerness. 257 ** 258 ** Returns: 259 ** TRUE if s is a header. 260 ** FALSE otherwise. 261 ** 262 ** Side Effects: 263 ** none. 264 ** 265 ** Bugs: 266 ** According to RFC733, there should be a newline 267 ** permitted after the word but before the colon. 268 ** We don't seem to support that..... 269 */ 270 271 bool 272 isheader(s) 273 register char *s; 274 { 275 if (!isalnum(*s)) 276 return (FALSE); 277 while (!isspace(*s) && *s != ':') 278 s++; 279 while (isspace(*s)) 280 s++; 281 return (*s == ':'); 282 } 283 /* 284 ** GETXPART -- extract the "signature" part of an address line. 285 ** 286 ** Try to extract the full name from a general address 287 ** field. We take anything which is a comment as a 288 ** first choice. Failing in that, we see if there is 289 ** a "machine readable" name (in <angle brackets>); if 290 ** so we take anything preceeding that clause. 291 ** 292 ** If we blow it here it's not all that serious. 293 ** 294 ** Parameters: 295 ** p -- line to crack. 296 ** 297 ** Returns: 298 ** signature part. 299 ** NULL if no signature part. 300 ** 301 ** Side Effects: 302 ** none. 303 */ 304 305 char * 306 getxpart(p) 307 register char *p; 308 { 309 register char *q; 310 register char *rval = NULL; 311 312 q = index(p, '('); 313 if (q != NULL) 314 { 315 int parenlev = 0; 316 317 for (p = q; *p != '\0'; p++) 318 { 319 if (*p == '(') 320 parenlev++; 321 else if (*p == ')' && --parenlev <= 0) 322 break; 323 } 324 if (*p == ')') 325 { 326 *p = '\0'; 327 if (*++q != '\0') 328 rval = newstr(q); 329 *p = ')'; 330 } 331 } 332 else if ((q = index(p, '<')) != NULL) 333 { 334 char savec; 335 336 while (*--q == ' ') 337 continue; 338 while (isspace(*p)) 339 p++; 340 savec = *++q; 341 *q = '\0'; 342 if (*p != '\0') 343 rval = newstr(p); 344 *q = savec; 345 } 346 347 return (rval); 348 } 349 /* 350 ** EATHEADER -- run through the stored header and extract info. 351 ** 352 ** Parameters: 353 ** none. 354 ** 355 ** Returns: 356 ** none. 357 ** 358 ** Side Effects: 359 ** Sets a bunch of global variables from information 360 ** in the collected header. 361 */ 362 363 eatheader() 364 { 365 register HDR *h; 366 register char *p; 367 char buf[MAXLINE]; 368 char *msgid; 369 370 # ifdef DEBUG 371 if (tTd(32, 2)) 372 { 373 extern char *capitalize(); 374 375 printf("----- collected header -----\n"); 376 for (h = CurEnv->e_header; h != NULL; h = h->h_link) 377 printf("%s: %s\n", capitalize(h->h_field), h->h_value); 378 printf("----------------------------\n"); 379 } 380 # endif DEBUG 381 382 /* message id */ 383 h = hrvalue("message-id"); 384 if (h == NULL) 385 msgid = NULL; 386 else if (bitset(H_DEFAULT, h->h_flags)) 387 { 388 (void) expand(h->h_value, buf, &buf[sizeof buf - 1], CurEnv); 389 msgid = buf; 390 } 391 else 392 msgid = h->h_value; 393 if (msgid != NULL) 394 { 395 # ifdef DEBUG 396 if (tTd(32, 1)) 397 printf("Message-Id: %s\n", msgid); 398 # endif DEBUG 399 # ifdef LOG 400 if (LogLevel > 1) 401 syslog(LOG_INFO, "%s: messageid=%s", CurEnv->e_id, msgid); 402 # endif LOG 403 } 404 405 /* message priority */ 406 if (!QueueRun) 407 { 408 /* adjust total priority by message priority */ 409 CurEnv->e_msgpriority = CurEnv->e_msgsize; 410 p = hvalue("priority"); 411 if (p != NULL) 412 CurEnv->e_class = priencode(p); 413 else 414 CurEnv->e_class = PRI_NORMAL; 415 CurEnv->e_msgpriority -= CurEnv->e_class * WKPRIFACT; 416 } 417 418 /* special handling */ 419 p = hvalue("special-handling"); 420 if (p != NULL) 421 spechandling(p); 422 423 /* from person */ 424 p = hvalue("sender"); 425 if (p == NULL) 426 p = CurEnv->e_origfrom; 427 if (ArpaMode) 428 setfrom(p, (char *) NULL); 429 430 /* full name of from person */ 431 p = hvalue("full-name"); 432 if (p != NULL) 433 define('x', p); 434 435 /* date message originated */ 436 p = hvalue("posted-date"); 437 if (p == NULL) 438 p = hvalue("date"); 439 if (p != NULL) 440 { 441 define('a', p); 442 /* we don't have a good way to do canonical conversion .... 443 define('d', newstr(arpatounix(p))); 444 .... so we will ignore the problem for the time being */ 445 } 446 } 447 /* 448 ** PRIENCODE -- encode external priority names into internal values. 449 ** 450 ** Parameters: 451 ** p -- priority in ascii. 452 ** 453 ** Returns: 454 ** priority as a numeric level. 455 ** 456 ** Side Effects: 457 ** none. 458 */ 459 460 struct prio 461 { 462 char *pri_name; /* external name of priority */ 463 int pri_val; /* internal value for same */ 464 }; 465 466 static struct prio Prio[] = 467 { 468 "alert", PRI_ALERT, 469 "quick", PRI_QUICK, 470 "first-class", PRI_FIRSTCL, 471 "normal", PRI_NORMAL, 472 "second-class", PRI_SECONDCL, 473 "third-class", PRI_THIRDCL, 474 "junk", PRI_JUNK, 475 NULL, PRI_NORMAL, 476 }; 477 478 priencode(p) 479 char *p; 480 { 481 register struct prio *pl; 482 extern bool sameword(); 483 484 for (pl = Prio; pl->pri_name != NULL; pl++) 485 { 486 if (sameword(p, pl->pri_name)) 487 break; 488 } 489 return (pl->pri_val); 490 } 491 /* 492 ** SPECHANDLE -- do special handling 493 ** 494 ** Parameters: 495 ** p -- pointer to list of special handling words. 496 ** 497 ** Returns: 498 ** none. 499 ** 500 ** Side Effects: 501 ** Sets flags as indicated by p. 502 */ 503 504 struct handling 505 { 506 char *han_name; /* word to get this magic */ 507 int han_what; /* what to do, see below */ 508 }; 509 510 /* modes for han_what */ 511 # define HAN_NONE 0 /* nothing special */ 512 # define HAN_RRECEIPT 1 /* give return receipt */ 513 514 struct handling Handling[] = 515 { 516 "return-receipt-requested", HAN_RRECEIPT, 517 NULL, HAN_NONE 518 }; 519 520 spechandling(p) 521 register char *p; 522 { 523 register char *w; 524 register struct handling *h; 525 extern bool sameword(); 526 527 while (*p != '\0') 528 { 529 /* collect a word to compare to */ 530 while (*p != '\0' && (*p == ',' || isspace(*p))) 531 p++; 532 if (*p == '\0') 533 break; 534 w = p; 535 while (*p != '\0' && *p != ',' && !isspace(*p)) 536 p++; 537 if (*p != '\0') 538 *p++ = '\0'; 539 540 /* scan the special handling table */ 541 for (h = Handling; h->han_name != NULL; h++) 542 if (sameword(h->han_name, w)) 543 break; 544 545 /* see if we can do anything interesting */ 546 switch (h->han_what) 547 { 548 case HAN_NONE: /* nothing to be done */ 549 break; 550 551 case HAN_RRECEIPT: /* give return receipt */ 552 CurEnv->e_retreceipt = TRUE; 553 # ifdef DEBUG 554 if (tTd(30, 3)) 555 printf(">>> Return receipt requested\n"); 556 # endif DEBUG 557 break; 558 559 default: 560 syserr("spechandling: handling %d (%s)", h->han_what, w); 561 } 562 } 563 } 564 /* 565 ** CRACKFROM -- parse the from line and turn it into a macro 566 ** 567 ** This doesn't actually parse the address -- it just extracts 568 ** it and replaces it with "$g". The parse is totally ad hoc 569 ** and isn't even guaranteed to leave something syntactically 570 ** identical to what it started with. However, it does leave 571 ** something semantically identical. 572 ** 573 ** The process is kind of strange. There are a number of 574 ** interesting cases: 575 ** 1. comment <address> comment ==> comment <$g> comment 576 ** 2. address ==> address 577 ** 3. address (comment) ==> $g (comment) 578 ** 4. (comment) address ==> (comment) $g 579 ** And then there are the hard cases.... 580 ** 5. add (comment) ress ==> $g (comment) 581 ** 6. comment <address (comment)> ==> comment <$g (comment)> 582 ** 7. .... etc .... 583 ** 584 ** Parameters: 585 ** from -- the value part of the from line. 586 ** 587 ** Returns: 588 ** a pointer to the new version. 589 ** 590 ** Side Effects: 591 ** The $f and $x macros may be defined. 592 ** 593 ** Warning: 594 ** The return value is saved in local storage and should 595 ** be copied if it is to be reused. 596 */ 597 598 char * 599 crackfrom(from) 600 register char *from; 601 { 602 register char *p; 603 register int i; 604 static char buf[MAXNAME]; 605 char *rhs; 606 bool gotaddr; 607 register char *bp; 608 609 # ifdef DEBUG 610 if (tTd(33, 1)) 611 printf("crackfrom(%s)\n", from); 612 # endif DEBUG 613 614 strcpy(buf, ""); 615 rhs = NULL; 616 617 /* 618 ** See if we have anything in angle brackets. If so, that is 619 ** the address part, and the rest is the comment. 620 */ 621 622 p = index(from, '<'); 623 if (p != NULL) 624 { 625 /* copy the beginning of the from field to the buffer */ 626 *p = '\0'; 627 strcpy(buf, from); 628 strcat(buf, "<"); 629 *p = '<'; 630 631 /* find the matching right angle bracket */ 632 from = ++p; 633 for (i = 0; *p != '\0'; p++) 634 { 635 switch (*p) 636 { 637 case '<': 638 i++; 639 break; 640 641 case '>': 642 i--; 643 break; 644 } 645 if (i < 0) 646 break; 647 } 648 649 /* p now points to the closing quote (or a null byte) */ 650 if (*p != '\0') 651 { 652 /* make rhs point to the extra stuff at the end */ 653 rhs = p; 654 *p++ = '\0'; 655 } 656 } 657 658 /* 659 ** Now parse the real address part. from points to the (null 660 ** terminated) version of what we are inerested in; rhs points 661 ** to the extra stuff at the end of the line, if any. 662 */ 663 664 p = from; 665 666 /* now strip out comments */ 667 bp = &buf[strlen(buf)]; 668 gotaddr = FALSE; 669 for (; *p != '\0'; p++) 670 { 671 if (*p == '(') 672 { 673 /* copy to matching close paren */ 674 *bp++ = *p++; 675 for (i = 0; *p != '\0'; p++) 676 { 677 *bp++ = *p; 678 switch (*p) 679 { 680 case '(': 681 i++; 682 break; 683 684 case ')': 685 i--; 686 break; 687 } 688 if (i < 0) 689 break; 690 } 691 continue; 692 } 693 694 /* 695 ** If this is the first "real" character we have seen, 696 ** then we put the "$g" in the buffer now. 697 */ 698 699 if (isspace(*p)) 700 *bp++ = *p; 701 else if (!gotaddr) 702 { 703 strcpy(bp, "$g"); 704 bp += 2; 705 gotaddr = TRUE; 706 } 707 } 708 709 /* 710 ** If there is a tag at the end, insert it. 711 */ 712 713 *bp = '\0'; 714 if (rhs != NULL) 715 { 716 *rhs = '>'; 717 strcpy(bp, rhs); 718 } 719 720 # ifdef DEBUG 721 if (tTd(33, 1)) 722 printf("crackfrom=>%s\n", buf); 723 # endif DEBUG 724 725 return (buf); 726 } 727