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