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.5 (Berkeley) 01/28/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 extern int NextMailer; 558 extern char **makeargv(); 559 extern char *munchstring(); 560 extern char *DelimChar; 561 extern long atol(); 562 563 /* allocate a mailer and set up defaults */ 564 m = (struct mailer *) xalloc(sizeof *m); 565 bzero((char *) m, sizeof *m); 566 m->m_eol = "\n"; 567 568 /* collect the mailer name */ 569 for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++) 570 continue; 571 if (*p != '\0') 572 *p++ = '\0'; 573 m->m_name = newstr(line); 574 575 /* now scan through and assign info from the fields */ 576 while (*p != '\0') 577 { 578 while (*p != '\0' && (*p == ',' || isspace(*p))) 579 p++; 580 581 /* p now points to field code */ 582 fcode = *p; 583 while (*p != '\0' && *p != '=' && *p != ',') 584 p++; 585 if (*p++ != '=') 586 { 587 syserr("mailer %s: `=' expected", m->m_name); 588 return; 589 } 590 while (isspace(*p)) 591 p++; 592 593 /* p now points to the field body */ 594 p = munchstring(p); 595 596 /* install the field into the mailer struct */ 597 switch (fcode) 598 { 599 case 'P': /* pathname */ 600 m->m_mailer = newstr(p); 601 break; 602 603 case 'F': /* flags */ 604 for (; *p != '\0'; p++) 605 if (!isspace(*p)) 606 setbitn(*p, m->m_flags); 607 break; 608 609 case 'S': /* sender rewriting ruleset */ 610 case 'R': /* recipient rewriting ruleset */ 611 i = atoi(p); 612 if (i < 0 || i >= MAXRWSETS) 613 { 614 syserr("invalid rewrite set, %d max", MAXRWSETS); 615 return; 616 } 617 if (fcode == 'S') 618 m->m_s_rwset = i; 619 else 620 m->m_r_rwset = i; 621 break; 622 623 case 'E': /* end of line string */ 624 m->m_eol = newstr(p); 625 break; 626 627 case 'A': /* argument vector */ 628 m->m_argv = makeargv(p); 629 break; 630 631 case 'M': /* maximum message size */ 632 m->m_maxsize = atol(p); 633 break; 634 635 case 'L': /* maximum line length */ 636 m->m_linelimit = atoi(p); 637 break; 638 } 639 640 p = DelimChar; 641 } 642 643 /* do some heuristic cleanup for back compatibility */ 644 if (bitnset(M_LIMITS, m->m_flags)) 645 { 646 if (m->m_linelimit == 0) 647 m->m_linelimit = SMTPLINELIM; 648 if (ConfigLevel < 2) 649 setbitn(M_7BITS, m->m_flags); 650 } 651 652 if (NextMailer >= MAXMAILERS) 653 { 654 syserr("too many mailers defined (%d max)", MAXMAILERS); 655 return; 656 } 657 658 s = stab(m->m_name, ST_MAILER, ST_ENTER); 659 if (s->s_mailer != NULL) 660 { 661 i = s->s_mailer->m_mno; 662 free(s->s_mailer); 663 } 664 else 665 { 666 i = NextMailer++; 667 } 668 Mailer[i] = s->s_mailer = m; 669 m->m_mno = i; 670 } 671 /* 672 ** MUNCHSTRING -- translate a string into internal form. 673 ** 674 ** Parameters: 675 ** p -- the string to munch. 676 ** 677 ** Returns: 678 ** the munched string. 679 ** 680 ** Side Effects: 681 ** Sets "DelimChar" to point to the string that caused us 682 ** to stop. 683 */ 684 685 char * 686 munchstring(p) 687 register char *p; 688 { 689 register char *q; 690 bool backslash = FALSE; 691 bool quotemode = FALSE; 692 static char buf[MAXLINE]; 693 extern char *DelimChar; 694 695 for (q = buf; *p != '\0'; p++) 696 { 697 if (backslash) 698 { 699 /* everything is roughly literal */ 700 backslash = FALSE; 701 switch (*p) 702 { 703 case 'r': /* carriage return */ 704 *q++ = '\r'; 705 continue; 706 707 case 'n': /* newline */ 708 *q++ = '\n'; 709 continue; 710 711 case 'f': /* form feed */ 712 *q++ = '\f'; 713 continue; 714 715 case 'b': /* backspace */ 716 *q++ = '\b'; 717 continue; 718 } 719 *q++ = *p; 720 } 721 else 722 { 723 if (*p == '\\') 724 backslash = TRUE; 725 else if (*p == '"') 726 quotemode = !quotemode; 727 else if (quotemode || *p != ',') 728 *q++ = *p; 729 else 730 break; 731 } 732 } 733 734 DelimChar = p; 735 *q++ = '\0'; 736 return (buf); 737 } 738 /* 739 ** MAKEARGV -- break up a string into words 740 ** 741 ** Parameters: 742 ** p -- the string to break up. 743 ** 744 ** Returns: 745 ** a char **argv (dynamically allocated) 746 ** 747 ** Side Effects: 748 ** munges p. 749 */ 750 751 char ** 752 makeargv(p) 753 register char *p; 754 { 755 char *q; 756 int i; 757 char **avp; 758 char *argv[MAXPV + 1]; 759 760 /* take apart the words */ 761 i = 0; 762 while (*p != '\0' && i < MAXPV) 763 { 764 q = p; 765 while (*p != '\0' && !isspace(*p)) 766 p++; 767 while (isspace(*p)) 768 *p++ = '\0'; 769 argv[i++] = newstr(q); 770 } 771 argv[i++] = NULL; 772 773 /* now make a copy of the argv */ 774 avp = (char **) xalloc(sizeof *avp * i); 775 bcopy((char *) argv, (char *) avp, sizeof *avp * i); 776 777 return (avp); 778 } 779 /* 780 ** PRINTRULES -- print rewrite rules (for debugging) 781 ** 782 ** Parameters: 783 ** none. 784 ** 785 ** Returns: 786 ** none. 787 ** 788 ** Side Effects: 789 ** prints rewrite rules. 790 */ 791 792 printrules() 793 { 794 register struct rewrite *rwp; 795 register int ruleset; 796 797 for (ruleset = 0; ruleset < 10; ruleset++) 798 { 799 if (RewriteRules[ruleset] == NULL) 800 continue; 801 printf("\n----Rule Set %d:", ruleset); 802 803 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 804 { 805 printf("\nLHS:"); 806 printav(rwp->r_lhs); 807 printf("RHS:"); 808 printav(rwp->r_rhs); 809 } 810 } 811 } 812 813 /* 814 ** SETOPTION -- set global processing option 815 ** 816 ** Parameters: 817 ** opt -- option name. 818 ** val -- option value (as a text string). 819 ** safe -- set if this came from a configuration file. 820 ** Some options (if set from the command line) will 821 ** reset the user id to avoid security problems. 822 ** sticky -- if set, don't let other setoptions override 823 ** this value. 824 ** 825 ** Returns: 826 ** none. 827 ** 828 ** Side Effects: 829 ** Sets options as implied by the arguments. 830 */ 831 832 static BITMAP StickyOpt; /* set if option is stuck */ 833 834 835 #ifdef NAMED_BIND 836 837 struct resolverflags 838 { 839 char *rf_name; /* name of the flag */ 840 long rf_bits; /* bits to set/clear */ 841 } ResolverFlags[] = 842 { 843 "debug", RES_DEBUG, 844 "aaonly", RES_AAONLY, 845 "usevc", RES_USEVC, 846 "primary", RES_PRIMARY, 847 "igntc", RES_IGNTC, 848 "recurse", RES_RECURSE, 849 "defnames", RES_DEFNAMES, 850 "stayopen", RES_STAYOPEN, 851 "dnsrch", RES_DNSRCH, 852 NULL, 0 853 }; 854 855 #endif 856 857 setoption(opt, val, safe, sticky) 858 char opt; 859 char *val; 860 bool safe; 861 bool sticky; 862 { 863 register char *p; 864 extern bool atobool(); 865 extern time_t convtime(); 866 extern int QueueLA; 867 extern int RefuseLA; 868 extern bool trusteduser(); 869 extern char *username(); 870 871 if (tTd(37, 1)) 872 printf("setoption %c=%s", opt, val); 873 874 /* 875 ** See if this option is preset for us. 876 */ 877 878 if (bitnset(opt, StickyOpt)) 879 { 880 if (tTd(37, 1)) 881 printf(" (ignored)\n"); 882 return; 883 } 884 885 /* 886 ** Check to see if this option can be specified by this user. 887 */ 888 889 if (!safe && getuid() == 0) 890 safe = TRUE; 891 if (!safe && strchr("deEiLmorsvC8", opt) == NULL) 892 { 893 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 894 { 895 if (tTd(37, 1)) 896 printf(" (unsafe)"); 897 if (getuid() != geteuid()) 898 { 899 if (tTd(37, 1)) 900 printf("(Resetting uid)"); 901 (void) setgid(getgid()); 902 (void) setuid(getuid()); 903 } 904 } 905 } 906 if (tTd(37, 1)) 907 printf("\n"); 908 909 switch (opt) 910 { 911 case '8': /* allow eight-bit input */ 912 EightBit = atobool(val); 913 break; 914 915 case 'A': /* set default alias file */ 916 if (val[0] == '\0') 917 AliasFile = "aliases"; 918 else 919 AliasFile = newstr(val); 920 break; 921 922 case 'a': /* look N minutes for "@:@" in alias file */ 923 if (val[0] == '\0') 924 SafeAlias = 5; 925 else 926 SafeAlias = atoi(val); 927 break; 928 929 case 'B': /* substitution for blank character */ 930 SpaceSub = val[0]; 931 if (SpaceSub == '\0') 932 SpaceSub = ' '; 933 break; 934 935 case 'c': /* don't connect to "expensive" mailers */ 936 NoConnect = atobool(val); 937 break; 938 939 case 'C': /* checkpoint every N addresses */ 940 CheckpointInterval = atoi(val); 941 break; 942 943 case 'd': /* delivery mode */ 944 switch (*val) 945 { 946 case '\0': 947 SendMode = SM_DELIVER; 948 break; 949 950 case SM_QUEUE: /* queue only */ 951 #ifndef QUEUE 952 syserr("need QUEUE to set -odqueue"); 953 #endif /* QUEUE */ 954 /* fall through..... */ 955 956 case SM_DELIVER: /* do everything */ 957 case SM_FORK: /* fork after verification */ 958 SendMode = *val; 959 break; 960 961 default: 962 syserr("Unknown delivery mode %c", *val); 963 exit(EX_USAGE); 964 } 965 break; 966 967 case 'D': /* rebuild alias database as needed */ 968 AutoRebuild = atobool(val); 969 break; 970 971 case 'E': /* error message header/header file */ 972 if (*val != '\0') 973 ErrMsgFile = newstr(val); 974 break; 975 976 case 'e': /* set error processing mode */ 977 switch (*val) 978 { 979 case EM_QUIET: /* be silent about it */ 980 case EM_MAIL: /* mail back */ 981 case EM_BERKNET: /* do berknet error processing */ 982 case EM_WRITE: /* write back (or mail) */ 983 HoldErrs = TRUE; 984 /* fall through... */ 985 986 case EM_PRINT: /* print errors normally (default) */ 987 ErrorMode = *val; 988 break; 989 } 990 break; 991 992 case 'F': /* file mode */ 993 FileMode = atooct(val) & 0777; 994 break; 995 996 case 'f': /* save Unix-style From lines on front */ 997 SaveFrom = atobool(val); 998 break; 999 1000 case 'G': /* match recipients against GECOS field */ 1001 MatchGecos = atobool(val); 1002 break; 1003 1004 case 'g': /* default gid */ 1005 DefGid = atoi(val); 1006 break; 1007 1008 case 'H': /* help file */ 1009 if (val[0] == '\0') 1010 HelpFile = "sendmail.hf"; 1011 else 1012 HelpFile = newstr(val); 1013 break; 1014 1015 case 'h': /* maximum hop count */ 1016 MaxHopCount = atoi(val); 1017 break; 1018 1019 case 'I': /* use internet domain name server */ 1020 #ifdef NAMED_BIND 1021 UseNameServer = TRUE; 1022 for (p = val; *p != 0; ) 1023 { 1024 bool clearmode; 1025 char *q; 1026 struct resolverflags *rfp; 1027 1028 while (*p == ' ') 1029 p++; 1030 if (*p == '\0') 1031 break; 1032 clearmode = FALSE; 1033 if (*p == '-') 1034 clearmode = TRUE; 1035 else if (*p != '+') 1036 p--; 1037 p++; 1038 q = p; 1039 while (*p != '\0' && !isspace(*p)) 1040 p++; 1041 if (*p != '\0') 1042 *p++ = '\0'; 1043 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1044 { 1045 if (strcasecmp(q, rfp->rf_name) == 0) 1046 break; 1047 } 1048 if (clearmode) 1049 _res.options &= ~rfp->rf_bits; 1050 else 1051 _res.options |= rfp->rf_bits; 1052 } 1053 if (tTd(8, 2)) 1054 printf("_res.options = %x\n", _res.options); 1055 #else 1056 usrerr("name server (I option) specified but BIND not compiled in"); 1057 #endif 1058 break; 1059 1060 case 'i': /* ignore dot lines in message */ 1061 IgnrDot = atobool(val); 1062 break; 1063 1064 case 'J': /* .forward search path */ 1065 ForwardPath = newstr(val); 1066 break; 1067 1068 case 'k': /* connection cache size */ 1069 MaxMciCache = atoi(val); 1070 if (MaxMciCache < 0) 1071 MaxMciCache = 0; 1072 break; 1073 1074 case 'K': /* connection cache timeout */ 1075 MciCacheTimeout = convtime(val); 1076 break; 1077 1078 case 'L': /* log level */ 1079 LogLevel = atoi(val); 1080 break; 1081 1082 case 'M': /* define macro */ 1083 define(val[0], newstr(&val[1]), CurEnv); 1084 sticky = FALSE; 1085 break; 1086 1087 case 'm': /* send to me too */ 1088 MeToo = atobool(val); 1089 break; 1090 1091 case 'n': /* validate RHS in newaliases */ 1092 CheckAliases = atobool(val); 1093 break; 1094 1095 case 'o': /* assume old style headers */ 1096 if (atobool(val)) 1097 CurEnv->e_flags |= EF_OLDSTYLE; 1098 else 1099 CurEnv->e_flags &= ~EF_OLDSTYLE; 1100 break; 1101 1102 case 'P': /* postmaster copy address for returned mail */ 1103 PostMasterCopy = newstr(val); 1104 break; 1105 1106 case 'q': /* slope of queue only function */ 1107 QueueFactor = atoi(val); 1108 break; 1109 1110 case 'Q': /* queue directory */ 1111 if (val[0] == '\0') 1112 QueueDir = "mqueue"; 1113 else 1114 QueueDir = newstr(val); 1115 break; 1116 1117 case 'r': /* read timeout */ 1118 ReadTimeout = convtime(val); 1119 break; 1120 1121 case 'S': /* status file */ 1122 if (val[0] == '\0') 1123 StatFile = "sendmail.st"; 1124 else 1125 StatFile = newstr(val); 1126 break; 1127 1128 case 's': /* be super safe, even if expensive */ 1129 SuperSafe = atobool(val); 1130 break; 1131 1132 case 'T': /* queue timeout */ 1133 TimeOut = convtime(val); 1134 break; 1135 1136 case 't': /* time zone name */ 1137 TimeZoneSpec = newstr(val); 1138 break; 1139 1140 case 'U': /* location of user database */ 1141 UdbSpec = newstr(val); 1142 break; 1143 1144 case 'u': /* set default uid */ 1145 DefUid = atoi(val); 1146 setdefuser(); 1147 break; 1148 1149 case 'v': /* run in verbose mode */ 1150 Verbose = atobool(val); 1151 break; 1152 1153 case 'w': /* we don't have wildcard MX records */ 1154 NoWildcardMX = atobool(val); 1155 break; 1156 1157 case 'x': /* load avg at which to auto-queue msgs */ 1158 QueueLA = atoi(val); 1159 break; 1160 1161 case 'X': /* load avg at which to auto-reject connections */ 1162 RefuseLA = atoi(val); 1163 break; 1164 1165 case 'y': /* work recipient factor */ 1166 WkRecipFact = atoi(val); 1167 break; 1168 1169 case 'Y': /* fork jobs during queue runs */ 1170 ForkQueueRuns = atobool(val); 1171 break; 1172 1173 case 'z': /* work message class factor */ 1174 WkClassFact = atoi(val); 1175 break; 1176 1177 case 'Z': /* work time factor */ 1178 WkTimeFact = atoi(val); 1179 break; 1180 1181 default: 1182 break; 1183 } 1184 if (sticky) 1185 setbitn(opt, StickyOpt); 1186 return; 1187 } 1188 /* 1189 ** SETCLASS -- set a word into a class 1190 ** 1191 ** Parameters: 1192 ** class -- the class to put the word in. 1193 ** word -- the word to enter 1194 ** 1195 ** Returns: 1196 ** none. 1197 ** 1198 ** Side Effects: 1199 ** puts the word into the symbol table. 1200 */ 1201 1202 setclass(class, word) 1203 int class; 1204 char *word; 1205 { 1206 register STAB *s; 1207 1208 s = stab(word, ST_CLASS, ST_ENTER); 1209 setbitn(class, s->s_class); 1210 } 1211 /* 1212 ** MAKEMAPENTRY -- create a map entry 1213 ** 1214 ** Parameters: 1215 ** line -- the config file line 1216 ** 1217 ** Returns: 1218 ** TRUE if it successfully entered the map entry. 1219 ** FALSE otherwise (usually syntax error). 1220 ** 1221 ** Side Effects: 1222 ** Enters the map into the dictionary. 1223 */ 1224 1225 void 1226 makemapentry(line) 1227 char *line; 1228 { 1229 register char *p; 1230 char *mapname; 1231 char *classname; 1232 register STAB *map; 1233 STAB *class; 1234 1235 for (p = line; isspace(*p); p++) 1236 continue; 1237 if (!isalnum(*p)) 1238 { 1239 syserr("readcf: config K line: no map name"); 1240 return; 1241 } 1242 1243 mapname = p; 1244 while (isalnum(*++p)) 1245 continue; 1246 if (*p != '\0') 1247 *p++ = '\0'; 1248 while (isspace(*p)) 1249 p++; 1250 if (!isalnum(*p)) 1251 { 1252 syserr("readcf: config K line, map %s: no map class", mapname); 1253 return; 1254 } 1255 classname = p; 1256 while (isalnum(*++p)) 1257 continue; 1258 if (*p != '\0') 1259 *p++ = '\0'; 1260 while (isspace(*p)) 1261 p++; 1262 1263 /* look up the class */ 1264 class = stab(classname, ST_MAPCLASS, ST_FIND); 1265 if (class == NULL) 1266 { 1267 syserr("readcf: map %s: class %s not available", mapname, classname); 1268 return; 1269 } 1270 1271 /* enter the map */ 1272 map = stab(mapname, ST_MAP, ST_ENTER); 1273 map->s_map.map_class = &class->s_mapclass; 1274 1275 if ((*class->s_mapclass.map_init)(&map->s_map, mapname, p)) 1276 map->s_map.map_flags |= MF_VALID; 1277 } 1278