1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)readcf.c 6.7 (Berkeley) 02/16/93"; 11 #endif /* not lint */ 12 13 # include "sendmail.h" 14 # include <sys/stat.h> 15 # include <unistd.h> 16 #ifdef NAMED_BIND 17 # include <arpa/nameser.h> 18 # include <resolv.h> 19 #endif 20 21 /* System 5 compatibility */ 22 #ifndef S_ISREG 23 #define S_ISREG(foo) ((foo & S_IFREG) == S_IFREG) 24 #endif 25 #ifndef S_IWGRP 26 #define S_IWGRP 020 27 #endif 28 #ifndef S_IWOTH 29 #define S_IWOTH 002 30 #endif 31 32 /* 33 ** READCF -- read control file. 34 ** 35 ** This routine reads the control file and builds the internal 36 ** form. 37 ** 38 ** The file is formatted as a sequence of lines, each taken 39 ** atomically. The first character of each line describes how 40 ** the line is to be interpreted. The lines are: 41 ** Dxval Define macro x to have value val. 42 ** Cxword Put word into class x. 43 ** Fxfile [fmt] Read file for lines to put into 44 ** class x. Use scanf string 'fmt' 45 ** or "%s" if not present. Fmt should 46 ** only produce one string-valued result. 47 ** Hname: value Define header with field-name 'name' 48 ** and value as specified; this will be 49 ** macro expanded immediately before 50 ** use. 51 ** Sn Use rewriting set n. 52 ** Rlhs rhs Rewrite addresses that match lhs to 53 ** be rhs. 54 ** Mn arg=val... Define mailer. n is the internal name. 55 ** Args specify mailer parameters. 56 ** Oxvalue Set option x to value. 57 ** Pname=value Set precedence name to value. 58 ** Vversioncode Version level of configuration syntax. 59 ** Kmapname mapclass arguments.... 60 ** Define keyed lookup of a given class. 61 ** Arguments are class dependent. 62 ** 63 ** Parameters: 64 ** cfname -- control file name. 65 ** safe -- TRUE if this is the system config file; 66 ** FALSE otherwise. 67 ** e -- the main envelope. 68 ** 69 ** Returns: 70 ** none. 71 ** 72 ** Side Effects: 73 ** Builds several internal tables. 74 */ 75 76 readcf(cfname, safe, e) 77 char *cfname; 78 bool safe; 79 register ENVELOPE *e; 80 { 81 FILE *cf; 82 int ruleset = 0; 83 char *q; 84 char **pv; 85 struct rewrite *rwp = NULL; 86 char *bp; 87 int nfuzzy; 88 char buf[MAXLINE]; 89 register char *p; 90 extern char **prescan(); 91 extern char **copyplist(); 92 struct stat statb; 93 char exbuf[MAXLINE]; 94 char pvpbuf[PSBUFSIZE]; 95 extern char *fgetfolded(); 96 extern char *munchstring(); 97 extern void makemapentry(); 98 99 FileName = cfname; 100 LineNumber = 0; 101 102 cf = fopen(cfname, "r"); 103 if (cf == NULL) 104 { 105 syserr("cannot open"); 106 exit(EX_OSFILE); 107 } 108 109 if (fstat(fileno(cf), &statb) < 0) 110 { 111 syserr("cannot fstat"); 112 exit(EX_OSFILE); 113 } 114 115 if (!S_ISREG(statb.st_mode)) 116 { 117 syserr("not a plain file"); 118 exit(EX_OSFILE); 119 } 120 121 if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) 122 { 123 if (OpMode == MD_DAEMON || OpMode == MD_FREEZE) 124 fprintf(stderr, "%s: WARNING: dangerous write permissions\n", 125 FileName); 126 #ifdef LOG 127 if (LogLevel > 0) 128 syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions", 129 FileName); 130 #endif 131 } 132 133 while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) 134 { 135 if (bp[0] == '#') 136 { 137 if (bp != buf) 138 free(bp); 139 continue; 140 } 141 142 /* map $ into \001 (ASCII SOH) for macro expansion */ 143 for (p = bp; *p != '\0'; p++) 144 { 145 if (*p == '#' && p > bp && ConfigLevel >= 3) 146 { 147 /* this is an on-line comment */ 148 register char *e; 149 150 switch (*--p) 151 { 152 case '\001': 153 /* it's from $# -- let it go through */ 154 p++; 155 break; 156 157 case '\\': 158 /* it's backslash escaped */ 159 (void) strcpy(p, p + 1); 160 break; 161 162 default: 163 /* delete preceeding white space */ 164 while (isspace(*p) && p > bp) 165 p--; 166 if ((e = strchr(++p, '\n')) != NULL) 167 (void) strcpy(p, e); 168 else 169 p[0] = p[1] = '\0'; 170 break; 171 } 172 continue; 173 } 174 175 if (*p != '$') 176 continue; 177 178 if (p[1] == '$') 179 { 180 /* actual dollar sign.... */ 181 (void) strcpy(p, p + 1); 182 continue; 183 } 184 185 /* convert to macro expansion character */ 186 *p = '\001'; 187 } 188 189 /* interpret this line */ 190 switch (bp[0]) 191 { 192 case '\0': 193 case '#': /* comment */ 194 break; 195 196 case 'R': /* rewriting rule */ 197 for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) 198 continue; 199 200 if (*p == '\0') 201 { 202 syserr("invalid rewrite line \"%s\"", bp); 203 break; 204 } 205 206 /* allocate space for the rule header */ 207 if (rwp == NULL) 208 { 209 RewriteRules[ruleset] = rwp = 210 (struct rewrite *) xalloc(sizeof *rwp); 211 } 212 else 213 { 214 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 215 rwp = rwp->r_next; 216 } 217 rwp->r_next = NULL; 218 219 /* expand and save the LHS */ 220 *p = '\0'; 221 expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e); 222 rwp->r_lhs = prescan(exbuf, '\t', pvpbuf); 223 nfuzzy = 0; 224 if (rwp->r_lhs != NULL) 225 { 226 register char **ap; 227 228 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 229 230 /* count the number of fuzzy matches in LHS */ 231 for (ap = rwp->r_lhs; *ap != NULL; ap++) 232 { 233 switch (**ap) 234 { 235 case MATCHZANY: 236 case MATCHANY: 237 case MATCHONE: 238 case MATCHCLASS: 239 case MATCHNCLASS: 240 nfuzzy++; 241 } 242 } 243 } 244 else 245 syserr("R line: null LHS"); 246 247 /* expand and save the RHS */ 248 while (*++p == '\t') 249 continue; 250 q = p; 251 while (*p != '\0' && *p != '\t') 252 p++; 253 *p = '\0'; 254 expand(q, exbuf, &exbuf[sizeof exbuf], e); 255 rwp->r_rhs = prescan(exbuf, '\t', pvpbuf); 256 if (rwp->r_rhs != NULL) 257 { 258 register char **ap; 259 260 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 261 262 /* check no out-of-bounds replacements */ 263 nfuzzy += '0'; 264 for (ap = rwp->r_rhs; *ap != NULL; ap++) 265 { 266 if (**ap != MATCHREPL) 267 continue; 268 if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) 269 { 270 syserr("replacement $%c out of bounds", 271 (*ap)[1]); 272 } 273 } 274 } 275 else 276 syserr("R line: null RHS"); 277 break; 278 279 case 'S': /* select rewriting set */ 280 ruleset = atoi(&bp[1]); 281 if (ruleset >= MAXRWSETS || ruleset < 0) 282 { 283 syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS); 284 ruleset = 0; 285 } 286 rwp = NULL; 287 break; 288 289 case 'D': /* macro definition */ 290 define(bp[1], newstr(munchstring(&bp[2])), e); 291 break; 292 293 case 'H': /* required header line */ 294 (void) chompheader(&bp[1], TRUE, e); 295 break; 296 297 case 'C': /* word class */ 298 case 'F': /* word class from file */ 299 /* read list of words from argument or file */ 300 if (bp[0] == 'F') 301 { 302 /* read from file */ 303 for (p = &bp[2]; *p != '\0' && !isspace(*p); p++) 304 continue; 305 if (*p == '\0') 306 p = "%s"; 307 else 308 { 309 *p = '\0'; 310 while (isspace(*++p)) 311 continue; 312 } 313 fileclass(bp[1], &bp[2], p, safe); 314 break; 315 } 316 317 /* scan the list of words and set class for all */ 318 for (p = &bp[2]; *p != '\0'; ) 319 { 320 register char *wd; 321 char delim; 322 323 while (*p != '\0' && isspace(*p)) 324 p++; 325 wd = p; 326 while (*p != '\0' && !isspace(*p)) 327 p++; 328 delim = *p; 329 *p = '\0'; 330 if (wd[0] != '\0') 331 setclass(bp[1], wd); 332 *p = delim; 333 } 334 break; 335 336 case 'M': /* define mailer */ 337 makemailer(&bp[1]); 338 break; 339 340 case 'O': /* set option */ 341 setoption(bp[1], &bp[2], safe, FALSE); 342 break; 343 344 case 'P': /* set precedence */ 345 if (NumPriorities >= MAXPRIORITIES) 346 { 347 toomany('P', MAXPRIORITIES); 348 break; 349 } 350 for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) 351 continue; 352 if (*p == '\0') 353 goto badline; 354 *p = '\0'; 355 Priorities[NumPriorities].pri_name = newstr(&bp[1]); 356 Priorities[NumPriorities].pri_val = atoi(++p); 357 NumPriorities++; 358 break; 359 360 case 'T': /* trusted user(s) */ 361 p = &bp[1]; 362 while (*p != '\0') 363 { 364 while (isspace(*p)) 365 p++; 366 q = p; 367 while (*p != '\0' && !isspace(*p)) 368 p++; 369 if (*p != '\0') 370 *p++ = '\0'; 371 if (*q == '\0') 372 continue; 373 for (pv = TrustedUsers; *pv != NULL; pv++) 374 continue; 375 if (pv >= &TrustedUsers[MAXTRUST]) 376 { 377 toomany('T', MAXTRUST); 378 break; 379 } 380 *pv = newstr(q); 381 } 382 break; 383 384 case 'V': /* configuration syntax version */ 385 ConfigLevel = atoi(&bp[1]); 386 break; 387 388 case 'K': 389 makemapentry(&bp[1]); 390 break; 391 392 default: 393 badline: 394 syserr("unknown control line \"%s\"", bp); 395 } 396 if (bp != buf) 397 free(bp); 398 } 399 if (ferror(cf)) 400 { 401 syserr("I/O read error", cfname); 402 exit(EX_OSFILE); 403 } 404 fclose(cf); 405 FileName = NULL; 406 407 if (stab("host", ST_MAP, ST_FIND) == NULL) 408 { 409 /* user didn't initialize: set up host map */ 410 strcpy(buf, "host host"); 411 if (ConfigLevel >= 2) 412 strcat(buf, " -a."); 413 makemapentry(buf); 414 } 415 } 416 /* 417 ** TOOMANY -- signal too many of some option 418 ** 419 ** Parameters: 420 ** id -- the id of the error line 421 ** maxcnt -- the maximum possible values 422 ** 423 ** Returns: 424 ** none. 425 ** 426 ** Side Effects: 427 ** gives a syserr. 428 */ 429 430 toomany(id, maxcnt) 431 char id; 432 int maxcnt; 433 { 434 syserr("too many %c lines, %d max", id, maxcnt); 435 } 436 /* 437 ** FILECLASS -- read members of a class from a file 438 ** 439 ** Parameters: 440 ** class -- class to define. 441 ** filename -- name of file to read. 442 ** fmt -- scanf string to use for match. 443 ** 444 ** Returns: 445 ** none 446 ** 447 ** Side Effects: 448 ** 449 ** puts all lines in filename that match a scanf into 450 ** the named class. 451 */ 452 453 fileclass(class, filename, fmt, safe) 454 int class; 455 char *filename; 456 char *fmt; 457 bool safe; 458 { 459 FILE *f; 460 struct stat stbuf; 461 char buf[MAXLINE]; 462 463 if (stat(filename, &stbuf) < 0) 464 { 465 syserr("fileclass: cannot stat %s", filename); 466 return; 467 } 468 if (!S_ISREG(stbuf.st_mode)) 469 { 470 syserr("fileclass: %s not a regular file", filename); 471 return; 472 } 473 if (!safe && access(filename, R_OK) < 0) 474 { 475 syserr("fileclass: access denied on %s", filename); 476 return; 477 } 478 f = fopen(filename, "r"); 479 if (f == NULL) 480 { 481 syserr("fileclass: cannot open %s", filename); 482 return; 483 } 484 485 while (fgets(buf, sizeof buf, f) != NULL) 486 { 487 register STAB *s; 488 register char *p; 489 # ifdef SCANF 490 char wordbuf[MAXNAME+1]; 491 492 if (sscanf(buf, fmt, wordbuf) != 1) 493 continue; 494 p = wordbuf; 495 # else /* SCANF */ 496 p = buf; 497 # endif /* SCANF */ 498 499 /* 500 ** Break up the match into words. 501 */ 502 503 while (*p != '\0') 504 { 505 register char *q; 506 507 /* strip leading spaces */ 508 while (isspace(*p)) 509 p++; 510 if (*p == '\0') 511 break; 512 513 /* find the end of the word */ 514 q = p; 515 while (*p != '\0' && !isspace(*p)) 516 p++; 517 if (*p != '\0') 518 *p++ = '\0'; 519 520 /* enter the word in the symbol table */ 521 s = stab(q, ST_CLASS, ST_ENTER); 522 setbitn(class, s->s_class); 523 } 524 } 525 526 (void) fclose(f); 527 } 528 /* 529 ** MAKEMAILER -- define a new mailer. 530 ** 531 ** Parameters: 532 ** line -- description of mailer. This is in labeled 533 ** fields. The fields are: 534 ** P -- the path to the mailer 535 ** F -- the flags associated with the mailer 536 ** A -- the argv for this mailer 537 ** S -- the sender rewriting set 538 ** R -- the recipient rewriting set 539 ** E -- the eol string 540 ** The first word is the canonical name of the mailer. 541 ** 542 ** Returns: 543 ** none. 544 ** 545 ** Side Effects: 546 ** enters the mailer into the mailer table. 547 */ 548 549 makemailer(line) 550 char *line; 551 { 552 register char *p; 553 register struct mailer *m; 554 register STAB *s; 555 int i; 556 char fcode; 557 auto char *endp; 558 extern int NextMailer; 559 extern char **makeargv(); 560 extern char *munchstring(); 561 extern char *DelimChar; 562 extern long atol(); 563 564 /* allocate a mailer and set up defaults */ 565 m = (struct mailer *) xalloc(sizeof *m); 566 bzero((char *) m, sizeof *m); 567 m->m_eol = "\n"; 568 569 /* collect the mailer name */ 570 for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++) 571 continue; 572 if (*p != '\0') 573 *p++ = '\0'; 574 m->m_name = newstr(line); 575 576 /* now scan through and assign info from the fields */ 577 while (*p != '\0') 578 { 579 while (*p != '\0' && (*p == ',' || isspace(*p))) 580 p++; 581 582 /* p now points to field code */ 583 fcode = *p; 584 while (*p != '\0' && *p != '=' && *p != ',') 585 p++; 586 if (*p++ != '=') 587 { 588 syserr("mailer %s: `=' expected", m->m_name); 589 return; 590 } 591 while (isspace(*p)) 592 p++; 593 594 /* p now points to the field body */ 595 p = munchstring(p); 596 597 /* install the field into the mailer struct */ 598 switch (fcode) 599 { 600 case 'P': /* pathname */ 601 m->m_mailer = newstr(p); 602 break; 603 604 case 'F': /* flags */ 605 for (; *p != '\0'; p++) 606 if (!isspace(*p)) 607 setbitn(*p, m->m_flags); 608 break; 609 610 case 'S': /* sender rewriting ruleset */ 611 case 'R': /* recipient rewriting ruleset */ 612 i = strtol(p, &endp, 10); 613 if (i < 0 || i >= MAXRWSETS) 614 { 615 syserr("invalid rewrite set, %d max", MAXRWSETS); 616 return; 617 } 618 if (fcode == 'S') 619 m->m_sh_rwset = m->m_se_rwset = i; 620 else 621 m->m_rh_rwset = m->m_re_rwset = i; 622 623 p = endp; 624 if (*p == '/') 625 { 626 i = strtol(p, NULL, 10); 627 if (i < 0 || i >= MAXRWSETS) 628 { 629 syserr("invalid rewrite set, %d max", 630 MAXRWSETS); 631 return; 632 } 633 if (fcode == 'S') 634 m->m_sh_rwset = i; 635 else 636 m->m_rh_rwset = i; 637 } 638 break; 639 640 case 'E': /* end of line string */ 641 m->m_eol = newstr(p); 642 break; 643 644 case 'A': /* argument vector */ 645 m->m_argv = makeargv(p); 646 break; 647 648 case 'M': /* maximum message size */ 649 m->m_maxsize = atol(p); 650 break; 651 652 case 'L': /* maximum line length */ 653 m->m_linelimit = atoi(p); 654 break; 655 } 656 657 p = DelimChar; 658 } 659 660 /* do some heuristic cleanup for back compatibility */ 661 if (bitnset(M_LIMITS, m->m_flags)) 662 { 663 if (m->m_linelimit == 0) 664 m->m_linelimit = SMTPLINELIM; 665 if (ConfigLevel < 2) 666 setbitn(M_7BITS, m->m_flags); 667 } 668 669 if (NextMailer >= MAXMAILERS) 670 { 671 syserr("too many mailers defined (%d max)", MAXMAILERS); 672 return; 673 } 674 675 s = stab(m->m_name, ST_MAILER, ST_ENTER); 676 if (s->s_mailer != NULL) 677 { 678 i = s->s_mailer->m_mno; 679 free(s->s_mailer); 680 } 681 else 682 { 683 i = NextMailer++; 684 } 685 Mailer[i] = s->s_mailer = m; 686 m->m_mno = i; 687 } 688 /* 689 ** MUNCHSTRING -- translate a string into internal form. 690 ** 691 ** Parameters: 692 ** p -- the string to munch. 693 ** 694 ** Returns: 695 ** the munched string. 696 ** 697 ** Side Effects: 698 ** Sets "DelimChar" to point to the string that caused us 699 ** to stop. 700 */ 701 702 char * 703 munchstring(p) 704 register char *p; 705 { 706 register char *q; 707 bool backslash = FALSE; 708 bool quotemode = FALSE; 709 static char buf[MAXLINE]; 710 extern char *DelimChar; 711 712 for (q = buf; *p != '\0'; p++) 713 { 714 if (backslash) 715 { 716 /* everything is roughly literal */ 717 backslash = FALSE; 718 switch (*p) 719 { 720 case 'r': /* carriage return */ 721 *q++ = '\r'; 722 continue; 723 724 case 'n': /* newline */ 725 *q++ = '\n'; 726 continue; 727 728 case 'f': /* form feed */ 729 *q++ = '\f'; 730 continue; 731 732 case 'b': /* backspace */ 733 *q++ = '\b'; 734 continue; 735 } 736 *q++ = *p; 737 } 738 else 739 { 740 if (*p == '\\') 741 backslash = TRUE; 742 else if (*p == '"') 743 quotemode = !quotemode; 744 else if (quotemode || *p != ',') 745 *q++ = *p; 746 else 747 break; 748 } 749 } 750 751 DelimChar = p; 752 *q++ = '\0'; 753 return (buf); 754 } 755 /* 756 ** MAKEARGV -- break up a string into words 757 ** 758 ** Parameters: 759 ** p -- the string to break up. 760 ** 761 ** Returns: 762 ** a char **argv (dynamically allocated) 763 ** 764 ** Side Effects: 765 ** munges p. 766 */ 767 768 char ** 769 makeargv(p) 770 register char *p; 771 { 772 char *q; 773 int i; 774 char **avp; 775 char *argv[MAXPV + 1]; 776 777 /* take apart the words */ 778 i = 0; 779 while (*p != '\0' && i < MAXPV) 780 { 781 q = p; 782 while (*p != '\0' && !isspace(*p)) 783 p++; 784 while (isspace(*p)) 785 *p++ = '\0'; 786 argv[i++] = newstr(q); 787 } 788 argv[i++] = NULL; 789 790 /* now make a copy of the argv */ 791 avp = (char **) xalloc(sizeof *avp * i); 792 bcopy((char *) argv, (char *) avp, sizeof *avp * i); 793 794 return (avp); 795 } 796 /* 797 ** PRINTRULES -- print rewrite rules (for debugging) 798 ** 799 ** Parameters: 800 ** none. 801 ** 802 ** Returns: 803 ** none. 804 ** 805 ** Side Effects: 806 ** prints rewrite rules. 807 */ 808 809 printrules() 810 { 811 register struct rewrite *rwp; 812 register int ruleset; 813 814 for (ruleset = 0; ruleset < 10; ruleset++) 815 { 816 if (RewriteRules[ruleset] == NULL) 817 continue; 818 printf("\n----Rule Set %d:", ruleset); 819 820 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 821 { 822 printf("\nLHS:"); 823 printav(rwp->r_lhs); 824 printf("RHS:"); 825 printav(rwp->r_rhs); 826 } 827 } 828 } 829 830 /* 831 ** SETOPTION -- set global processing option 832 ** 833 ** Parameters: 834 ** opt -- option name. 835 ** val -- option value (as a text string). 836 ** safe -- set if this came from a configuration file. 837 ** Some options (if set from the command line) will 838 ** reset the user id to avoid security problems. 839 ** sticky -- if set, don't let other setoptions override 840 ** this value. 841 ** 842 ** Returns: 843 ** none. 844 ** 845 ** Side Effects: 846 ** Sets options as implied by the arguments. 847 */ 848 849 static BITMAP StickyOpt; /* set if option is stuck */ 850 851 852 #ifdef NAMED_BIND 853 854 struct resolverflags 855 { 856 char *rf_name; /* name of the flag */ 857 long rf_bits; /* bits to set/clear */ 858 } ResolverFlags[] = 859 { 860 "debug", RES_DEBUG, 861 "aaonly", RES_AAONLY, 862 "usevc", RES_USEVC, 863 "primary", RES_PRIMARY, 864 "igntc", RES_IGNTC, 865 "recurse", RES_RECURSE, 866 "defnames", RES_DEFNAMES, 867 "stayopen", RES_STAYOPEN, 868 "dnsrch", RES_DNSRCH, 869 NULL, 0 870 }; 871 872 #endif 873 874 setoption(opt, val, safe, sticky) 875 char opt; 876 char *val; 877 bool safe; 878 bool sticky; 879 { 880 register char *p; 881 extern bool atobool(); 882 extern time_t convtime(); 883 extern int QueueLA; 884 extern int RefuseLA; 885 extern bool trusteduser(); 886 extern char *username(); 887 888 if (tTd(37, 1)) 889 printf("setoption %c=%s", opt, val); 890 891 /* 892 ** See if this option is preset for us. 893 */ 894 895 if (bitnset(opt, StickyOpt)) 896 { 897 if (tTd(37, 1)) 898 printf(" (ignored)\n"); 899 return; 900 } 901 902 /* 903 ** Check to see if this option can be specified by this user. 904 */ 905 906 if (!safe && getuid() == 0) 907 safe = TRUE; 908 if (!safe && strchr("deEiLmorsvC8", opt) == NULL) 909 { 910 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 911 { 912 if (tTd(37, 1)) 913 printf(" (unsafe)"); 914 if (getuid() != geteuid()) 915 { 916 if (tTd(37, 1)) 917 printf("(Resetting uid)"); 918 (void) setgid(getgid()); 919 (void) setuid(getuid()); 920 } 921 } 922 } 923 if (tTd(37, 1)) 924 printf("\n"); 925 926 switch (opt) 927 { 928 case '8': /* allow eight-bit input */ 929 EightBit = atobool(val); 930 break; 931 932 case 'A': /* set default alias file */ 933 if (val[0] == '\0') 934 AliasFile = "aliases"; 935 else 936 AliasFile = newstr(val); 937 break; 938 939 case 'a': /* look N minutes for "@:@" in alias file */ 940 if (val[0] == '\0') 941 SafeAlias = 5; 942 else 943 SafeAlias = atoi(val); 944 break; 945 946 case 'B': /* substitution for blank character */ 947 SpaceSub = val[0]; 948 if (SpaceSub == '\0') 949 SpaceSub = ' '; 950 break; 951 952 case 'c': /* don't connect to "expensive" mailers */ 953 NoConnect = atobool(val); 954 break; 955 956 case 'C': /* checkpoint every N addresses */ 957 CheckpointInterval = atoi(val); 958 break; 959 960 case 'd': /* delivery mode */ 961 switch (*val) 962 { 963 case '\0': 964 SendMode = SM_DELIVER; 965 break; 966 967 case SM_QUEUE: /* queue only */ 968 #ifndef QUEUE 969 syserr("need QUEUE to set -odqueue"); 970 #endif /* QUEUE */ 971 /* fall through..... */ 972 973 case SM_DELIVER: /* do everything */ 974 case SM_FORK: /* fork after verification */ 975 SendMode = *val; 976 break; 977 978 default: 979 syserr("Unknown delivery mode %c", *val); 980 exit(EX_USAGE); 981 } 982 break; 983 984 case 'D': /* rebuild alias database as needed */ 985 AutoRebuild = atobool(val); 986 break; 987 988 case 'E': /* error message header/header file */ 989 if (*val != '\0') 990 ErrMsgFile = newstr(val); 991 break; 992 993 case 'e': /* set error processing mode */ 994 switch (*val) 995 { 996 case EM_QUIET: /* be silent about it */ 997 case EM_MAIL: /* mail back */ 998 case EM_BERKNET: /* do berknet error processing */ 999 case EM_WRITE: /* write back (or mail) */ 1000 HoldErrs = TRUE; 1001 /* fall through... */ 1002 1003 case EM_PRINT: /* print errors normally (default) */ 1004 ErrorMode = *val; 1005 break; 1006 } 1007 break; 1008 1009 case 'F': /* file mode */ 1010 FileMode = atooct(val) & 0777; 1011 break; 1012 1013 case 'f': /* save Unix-style From lines on front */ 1014 SaveFrom = atobool(val); 1015 break; 1016 1017 case 'G': /* match recipients against GECOS field */ 1018 MatchGecos = atobool(val); 1019 break; 1020 1021 case 'g': /* default gid */ 1022 DefGid = atoi(val); 1023 break; 1024 1025 case 'H': /* help file */ 1026 if (val[0] == '\0') 1027 HelpFile = "sendmail.hf"; 1028 else 1029 HelpFile = newstr(val); 1030 break; 1031 1032 case 'h': /* maximum hop count */ 1033 MaxHopCount = atoi(val); 1034 break; 1035 1036 case 'I': /* use internet domain name server */ 1037 #ifdef NAMED_BIND 1038 UseNameServer = TRUE; 1039 for (p = val; *p != 0; ) 1040 { 1041 bool clearmode; 1042 char *q; 1043 struct resolverflags *rfp; 1044 1045 while (*p == ' ') 1046 p++; 1047 if (*p == '\0') 1048 break; 1049 clearmode = FALSE; 1050 if (*p == '-') 1051 clearmode = TRUE; 1052 else if (*p != '+') 1053 p--; 1054 p++; 1055 q = p; 1056 while (*p != '\0' && !isspace(*p)) 1057 p++; 1058 if (*p != '\0') 1059 *p++ = '\0'; 1060 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1061 { 1062 if (strcasecmp(q, rfp->rf_name) == 0) 1063 break; 1064 } 1065 if (clearmode) 1066 _res.options &= ~rfp->rf_bits; 1067 else 1068 _res.options |= rfp->rf_bits; 1069 } 1070 if (tTd(8, 2)) 1071 printf("_res.options = %x\n", _res.options); 1072 #else 1073 usrerr("name server (I option) specified but BIND not compiled in"); 1074 #endif 1075 break; 1076 1077 case 'i': /* ignore dot lines in message */ 1078 IgnrDot = atobool(val); 1079 break; 1080 1081 case 'J': /* .forward search path */ 1082 ForwardPath = newstr(val); 1083 break; 1084 1085 case 'k': /* connection cache size */ 1086 MaxMciCache = atoi(val); 1087 if (MaxMciCache < 0) 1088 MaxMciCache = 0; 1089 break; 1090 1091 case 'K': /* connection cache timeout */ 1092 MciCacheTimeout = convtime(val); 1093 break; 1094 1095 case 'L': /* log level */ 1096 LogLevel = atoi(val); 1097 break; 1098 1099 case 'M': /* define macro */ 1100 define(val[0], newstr(&val[1]), CurEnv); 1101 sticky = FALSE; 1102 break; 1103 1104 case 'm': /* send to me too */ 1105 MeToo = atobool(val); 1106 break; 1107 1108 case 'n': /* validate RHS in newaliases */ 1109 CheckAliases = atobool(val); 1110 break; 1111 1112 case 'o': /* assume old style headers */ 1113 if (atobool(val)) 1114 CurEnv->e_flags |= EF_OLDSTYLE; 1115 else 1116 CurEnv->e_flags &= ~EF_OLDSTYLE; 1117 break; 1118 1119 case 'P': /* postmaster copy address for returned mail */ 1120 PostMasterCopy = newstr(val); 1121 break; 1122 1123 case 'q': /* slope of queue only function */ 1124 QueueFactor = atoi(val); 1125 break; 1126 1127 case 'Q': /* queue directory */ 1128 if (val[0] == '\0') 1129 QueueDir = "mqueue"; 1130 else 1131 QueueDir = newstr(val); 1132 break; 1133 1134 case 'r': /* read timeout */ 1135 ReadTimeout = convtime(val); 1136 break; 1137 1138 case 'S': /* status file */ 1139 if (val[0] == '\0') 1140 StatFile = "sendmail.st"; 1141 else 1142 StatFile = newstr(val); 1143 break; 1144 1145 case 's': /* be super safe, even if expensive */ 1146 SuperSafe = atobool(val); 1147 break; 1148 1149 case 'T': /* queue timeout */ 1150 TimeOut = convtime(val); 1151 break; 1152 1153 case 't': /* time zone name */ 1154 TimeZoneSpec = newstr(val); 1155 break; 1156 1157 case 'U': /* location of user database */ 1158 UdbSpec = newstr(val); 1159 break; 1160 1161 case 'u': /* set default uid */ 1162 DefUid = atoi(val); 1163 setdefuser(); 1164 break; 1165 1166 case 'v': /* run in verbose mode */ 1167 Verbose = atobool(val); 1168 break; 1169 1170 case 'x': /* load avg at which to auto-queue msgs */ 1171 QueueLA = atoi(val); 1172 break; 1173 1174 case 'X': /* load avg at which to auto-reject connections */ 1175 RefuseLA = atoi(val); 1176 break; 1177 1178 case 'y': /* work recipient factor */ 1179 WkRecipFact = atoi(val); 1180 break; 1181 1182 case 'Y': /* fork jobs during queue runs */ 1183 ForkQueueRuns = atobool(val); 1184 break; 1185 1186 case 'z': /* work message class factor */ 1187 WkClassFact = atoi(val); 1188 break; 1189 1190 case 'Z': /* work time factor */ 1191 WkTimeFact = atoi(val); 1192 break; 1193 1194 default: 1195 break; 1196 } 1197 if (sticky) 1198 setbitn(opt, StickyOpt); 1199 return; 1200 } 1201 /* 1202 ** SETCLASS -- set a word into a class 1203 ** 1204 ** Parameters: 1205 ** class -- the class to put the word in. 1206 ** word -- the word to enter 1207 ** 1208 ** Returns: 1209 ** none. 1210 ** 1211 ** Side Effects: 1212 ** puts the word into the symbol table. 1213 */ 1214 1215 setclass(class, word) 1216 int class; 1217 char *word; 1218 { 1219 register STAB *s; 1220 1221 if (tTd(37, 8)) 1222 printf("%s added to class %c\n", word, class); 1223 s = stab(word, ST_CLASS, ST_ENTER); 1224 setbitn(class, s->s_class); 1225 } 1226 /* 1227 ** MAKEMAPENTRY -- create a map entry 1228 ** 1229 ** Parameters: 1230 ** line -- the config file line 1231 ** 1232 ** Returns: 1233 ** TRUE if it successfully entered the map entry. 1234 ** FALSE otherwise (usually syntax error). 1235 ** 1236 ** Side Effects: 1237 ** Enters the map into the dictionary. 1238 */ 1239 1240 void 1241 makemapentry(line) 1242 char *line; 1243 { 1244 register char *p; 1245 char *mapname; 1246 char *classname; 1247 register STAB *map; 1248 STAB *class; 1249 1250 for (p = line; isspace(*p); p++) 1251 continue; 1252 if (!isalnum(*p)) 1253 { 1254 syserr("readcf: config K line: no map name"); 1255 return; 1256 } 1257 1258 mapname = p; 1259 while (isalnum(*++p)) 1260 continue; 1261 if (*p != '\0') 1262 *p++ = '\0'; 1263 while (isspace(*p)) 1264 p++; 1265 if (!isalnum(*p)) 1266 { 1267 syserr("readcf: config K line, map %s: no map class", mapname); 1268 return; 1269 } 1270 classname = p; 1271 while (isalnum(*++p)) 1272 continue; 1273 if (*p != '\0') 1274 *p++ = '\0'; 1275 while (isspace(*p)) 1276 p++; 1277 1278 /* look up the class */ 1279 class = stab(classname, ST_MAPCLASS, ST_FIND); 1280 if (class == NULL) 1281 { 1282 syserr("readcf: map %s: class %s not available", mapname, classname); 1283 return; 1284 } 1285 1286 /* enter the map */ 1287 map = stab(mapname, ST_MAP, ST_ENTER); 1288 map->s_map.map_class = &class->s_mapclass; 1289 1290 if ((*class->s_mapclass.map_init)(&map->s_map, mapname, p)) 1291 map->s_map.map_flags |= MF_VALID; 1292 } 1293