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