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