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