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