122709Sdist /* 268839Seric * Copyright (c) 1983, 1995 Eric P. Allman 362530Sbostic * Copyright (c) 1988, 1993 462530Sbostic * The Regents of the University of California. All rights reserved. 533731Sbostic * 642829Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822709Sdist 922709Sdist #ifndef lint 10*69789Seric static char sccsid[] = "@(#)readcf.c 8.99 (Berkeley) 05/31/95"; 1133731Sbostic #endif /* not lint */ 1222709Sdist 133313Seric # include "sendmail.h" 1464133Seric # include <grp.h> 1566334Seric #if NAMED_BIND 1657207Seric # include <resolv.h> 1757207Seric #endif 183308Seric 193308Seric /* 203308Seric ** READCF -- read control file. 213308Seric ** 223308Seric ** This routine reads the control file and builds the internal 233308Seric ** form. 243308Seric ** 254432Seric ** The file is formatted as a sequence of lines, each taken 264432Seric ** atomically. The first character of each line describes how 274432Seric ** the line is to be interpreted. The lines are: 284432Seric ** Dxval Define macro x to have value val. 294432Seric ** Cxword Put word into class x. 304432Seric ** Fxfile [fmt] Read file for lines to put into 314432Seric ** class x. Use scanf string 'fmt' 324432Seric ** or "%s" if not present. Fmt should 334432Seric ** only produce one string-valued result. 344432Seric ** Hname: value Define header with field-name 'name' 354432Seric ** and value as specified; this will be 364432Seric ** macro expanded immediately before 374432Seric ** use. 384432Seric ** Sn Use rewriting set n. 394432Seric ** Rlhs rhs Rewrite addresses that match lhs to 404432Seric ** be rhs. 4124944Seric ** Mn arg=val... Define mailer. n is the internal name. 4224944Seric ** Args specify mailer parameters. 438252Seric ** Oxvalue Set option x to value. 448252Seric ** Pname=value Set precedence name to value. 4564718Seric ** Vversioncode[/vendorcode] 4664718Seric ** Version level/vendor name of 4764718Seric ** configuration syntax. 4853654Seric ** Kmapname mapclass arguments.... 4953654Seric ** Define keyed lookup of a given class. 5053654Seric ** Arguments are class dependent. 5169476Seric ** Eenvar=value Set the environment value to the given value. 524432Seric ** 533308Seric ** Parameters: 543308Seric ** cfname -- control file name. 5554973Seric ** safe -- TRUE if this is the system config file; 5654973Seric ** FALSE otherwise. 5755012Seric ** e -- the main envelope. 583308Seric ** 593308Seric ** Returns: 603308Seric ** none. 613308Seric ** 623308Seric ** Side Effects: 633308Seric ** Builds several internal tables. 643308Seric */ 653308Seric 6669748Seric void 6755012Seric readcf(cfname, safe, e) 683308Seric char *cfname; 6954973Seric bool safe; 7055012Seric register ENVELOPE *e; 713308Seric { 723308Seric FILE *cf; 738547Seric int ruleset = 0; 748547Seric char *q; 759350Seric struct rewrite *rwp = NULL; 7657135Seric char *bp; 7764718Seric auto char *ep; 7857589Seric int nfuzzy; 7964133Seric char *file; 8064133Seric bool optional; 8168481Seric int mid; 823308Seric char buf[MAXLINE]; 833308Seric register char *p; 843308Seric extern char **copyplist(); 8552647Seric struct stat statb; 865909Seric char exbuf[MAXLINE]; 8765066Seric char pvpbuf[MAXLINE + MAXATOM]; 8868481Seric static char *null_list[1] = { NULL }; 8969748Seric extern char *munchstring __P((char *, char **)); 9069748Seric extern void fileclass __P((int, char *, char *, bool, bool)); 9169748Seric extern void toomany __P((int, int)); 923308Seric 9352647Seric FileName = cfname; 9452647Seric LineNumber = 0; 9552647Seric 963308Seric cf = fopen(cfname, "r"); 973308Seric if (cf == NULL) 983308Seric { 9952647Seric syserr("cannot open"); 1003308Seric exit(EX_OSFILE); 1013308Seric } 1023308Seric 10352647Seric if (fstat(fileno(cf), &statb) < 0) 10452647Seric { 10552647Seric syserr("cannot fstat"); 10652647Seric exit(EX_OSFILE); 10752647Seric } 10852647Seric 10952647Seric if (!S_ISREG(statb.st_mode)) 11052647Seric { 11152647Seric syserr("not a plain file"); 11252647Seric exit(EX_OSFILE); 11352647Seric } 11452647Seric 11552647Seric if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) 11652647Seric { 11769686Seric if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS) 11853037Seric fprintf(stderr, "%s: WARNING: dangerous write permissions\n", 11953037Seric FileName); 12053037Seric #ifdef LOG 12153037Seric if (LogLevel > 0) 12253037Seric syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions", 12353037Seric FileName); 12453037Seric #endif 12552647Seric } 12652647Seric 12759254Seric #ifdef XLA 12859254Seric xla_zero(); 12959254Seric #endif 13059254Seric 13157135Seric while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) 1323308Seric { 13357135Seric if (bp[0] == '#') 13457135Seric { 13557135Seric if (bp != buf) 13657135Seric free(bp); 13752637Seric continue; 13857135Seric } 13952637Seric 14068481Seric /* do macro expansion mappings */ 14157135Seric for (p = bp; *p != '\0'; p++) 14216157Seric { 14357135Seric if (*p == '#' && p > bp && ConfigLevel >= 3) 14452647Seric { 14552647Seric /* this is an on-line comment */ 14652647Seric register char *e; 14752647Seric 14858050Seric switch (*--p & 0377) 14952647Seric { 15058050Seric case MACROEXPAND: 15152647Seric /* it's from $# -- let it go through */ 15252647Seric p++; 15352647Seric break; 15452647Seric 15552647Seric case '\\': 15652647Seric /* it's backslash escaped */ 15752647Seric (void) strcpy(p, p + 1); 15852647Seric break; 15952647Seric 16052647Seric default: 16152647Seric /* delete preceeding white space */ 16258050Seric while (isascii(*p) && isspace(*p) && p > bp) 16352647Seric p--; 16456795Seric if ((e = strchr(++p, '\n')) != NULL) 16552647Seric (void) strcpy(p, e); 16652647Seric else 16752647Seric p[0] = p[1] = '\0'; 16852647Seric break; 16952647Seric } 17052647Seric continue; 17152647Seric } 17252647Seric 17368481Seric if (*p != '$' || p[1] == '\0') 17416157Seric continue; 17516157Seric 17616157Seric if (p[1] == '$') 17716157Seric { 17816157Seric /* actual dollar sign.... */ 17923111Seric (void) strcpy(p, p + 1); 18016157Seric continue; 18116157Seric } 18216157Seric 18316157Seric /* convert to macro expansion character */ 18468481Seric *p++ = MACROEXPAND; 18568481Seric 18668481Seric /* convert macro name to code */ 18768481Seric *p = macid(p, &ep); 18868481Seric if (ep != p) 18968481Seric strcpy(p + 1, ep); 19016157Seric } 19116157Seric 19216157Seric /* interpret this line */ 19364718Seric errno = 0; 19457135Seric switch (bp[0]) 1953308Seric { 1963308Seric case '\0': 1973308Seric case '#': /* comment */ 1983308Seric break; 1993308Seric 2003308Seric case 'R': /* rewriting rule */ 20157135Seric for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) 2023308Seric continue; 2033308Seric 2043308Seric if (*p == '\0') 2055909Seric { 20665821Seric syserr("invalid rewrite line \"%s\" (tab expected)", bp); 2075909Seric break; 2085909Seric } 2095909Seric 2105909Seric /* allocate space for the rule header */ 2115909Seric if (rwp == NULL) 2125909Seric { 2135909Seric RewriteRules[ruleset] = rwp = 2145909Seric (struct rewrite *) xalloc(sizeof *rwp); 2155909Seric } 2163308Seric else 2173308Seric { 2185909Seric rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 2195909Seric rwp = rwp->r_next; 2205909Seric } 2215909Seric rwp->r_next = NULL; 2223308Seric 2235909Seric /* expand and save the LHS */ 2245909Seric *p = '\0'; 22568529Seric expand(&bp[1], exbuf, sizeof exbuf, e); 22665066Seric rwp->r_lhs = prescan(exbuf, '\t', pvpbuf, 22768711Seric sizeof pvpbuf, NULL, NULL); 22857589Seric nfuzzy = 0; 2295909Seric if (rwp->r_lhs != NULL) 23057589Seric { 23157589Seric register char **ap; 23257589Seric 2335909Seric rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 23457589Seric 23557589Seric /* count the number of fuzzy matches in LHS */ 23657589Seric for (ap = rwp->r_lhs; *ap != NULL; ap++) 23757589Seric { 23858148Seric char *botch; 23958148Seric 24058148Seric botch = NULL; 24158050Seric switch (**ap & 0377) 24257589Seric { 24357589Seric case MATCHZANY: 24457589Seric case MATCHANY: 24557589Seric case MATCHONE: 24657589Seric case MATCHCLASS: 24757589Seric case MATCHNCLASS: 24857589Seric nfuzzy++; 24958148Seric break; 25058148Seric 25158148Seric case MATCHREPL: 25258148Seric botch = "$0-$9"; 25358148Seric break; 25458148Seric 25558148Seric case CANONNET: 25658148Seric botch = "$#"; 25758148Seric break; 25858148Seric 25958148Seric case CANONUSER: 26058148Seric botch = "$:"; 26158148Seric break; 26258148Seric 26358148Seric case CALLSUBR: 26458148Seric botch = "$>"; 26558148Seric break; 26658148Seric 26758148Seric case CONDIF: 26858148Seric botch = "$?"; 26958148Seric break; 27058148Seric 27158148Seric case CONDELSE: 27258148Seric botch = "$|"; 27358148Seric break; 27458148Seric 27558148Seric case CONDFI: 27658148Seric botch = "$."; 27758148Seric break; 27858148Seric 27958148Seric case HOSTBEGIN: 28058148Seric botch = "$["; 28158148Seric break; 28258148Seric 28358148Seric case HOSTEND: 28458148Seric botch = "$]"; 28558148Seric break; 28658148Seric 28758148Seric case LOOKUPBEGIN: 28858148Seric botch = "$("; 28958148Seric break; 29058148Seric 29158148Seric case LOOKUPEND: 29258148Seric botch = "$)"; 29358148Seric break; 29457589Seric } 29558148Seric if (botch != NULL) 29658148Seric syserr("Inappropriate use of %s on LHS", 29758148Seric botch); 29857589Seric } 29957589Seric } 30056678Seric else 30168481Seric { 30256678Seric syserr("R line: null LHS"); 30368481Seric rwp->r_lhs = null_list; 30468481Seric } 3055909Seric 3065909Seric /* expand and save the RHS */ 3075909Seric while (*++p == '\t') 3085909Seric continue; 3097231Seric q = p; 3107231Seric while (*p != '\0' && *p != '\t') 3117231Seric p++; 3127231Seric *p = '\0'; 31368529Seric expand(q, exbuf, sizeof exbuf, e); 31465066Seric rwp->r_rhs = prescan(exbuf, '\t', pvpbuf, 31568711Seric sizeof pvpbuf, NULL, NULL); 3165909Seric if (rwp->r_rhs != NULL) 31757589Seric { 31857589Seric register char **ap; 31957589Seric 3205909Seric rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 32157589Seric 32257589Seric /* check no out-of-bounds replacements */ 32357589Seric nfuzzy += '0'; 32457589Seric for (ap = rwp->r_rhs; *ap != NULL; ap++) 32557589Seric { 32658148Seric char *botch; 32758148Seric 32858148Seric botch = NULL; 32958148Seric switch (**ap & 0377) 33057589Seric { 33158148Seric case MATCHREPL: 33258148Seric if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) 33358148Seric { 33458148Seric syserr("replacement $%c out of bounds", 33558148Seric (*ap)[1]); 33658148Seric } 33758148Seric break; 33858148Seric 33958148Seric case MATCHZANY: 34058148Seric botch = "$*"; 34158148Seric break; 34258148Seric 34358148Seric case MATCHANY: 34458148Seric botch = "$+"; 34558148Seric break; 34658148Seric 34758148Seric case MATCHONE: 34858148Seric botch = "$-"; 34958148Seric break; 35058148Seric 35158148Seric case MATCHCLASS: 35258148Seric botch = "$="; 35358148Seric break; 35458148Seric 35558148Seric case MATCHNCLASS: 35658148Seric botch = "$~"; 35758148Seric break; 35857589Seric } 35958148Seric if (botch != NULL) 36058148Seric syserr("Inappropriate use of %s on RHS", 36158148Seric botch); 36257589Seric } 36357589Seric } 36456678Seric else 36568481Seric { 36656678Seric syserr("R line: null RHS"); 36768481Seric rwp->r_rhs = null_list; 36868481Seric } 3693308Seric break; 3703308Seric 3714072Seric case 'S': /* select rewriting set */ 37269783Seric ruleset = strtorwset(&bp[1], NULL, ST_ENTER); 373*69789Seric rwp = RewriteRules[ruleset]; 374*69789Seric if (rwp != NULL) 375*69789Seric { 376*69789Seric while (rwp->r_next != NULL) 377*69789Seric rwp = rwp->r_next; 378*69789Seric fprintf(stderr, "WARNING: Ruleset %s redefined\n", 379*69789Seric &bp[1]); 380*69789Seric } 3814072Seric break; 3824072Seric 3833308Seric case 'D': /* macro definition */ 38468481Seric mid = macid(&bp[1], &ep); 38568481Seric p = munchstring(ep, NULL); 38668481Seric define(mid, newstr(p), e); 3873308Seric break; 3883308Seric 3893387Seric case 'H': /* required header line */ 39068717Seric (void) chompheader(&bp[1], TRUE, NULL, e); 3913387Seric break; 3923387Seric 3934061Seric case 'C': /* word class */ 39468481Seric case 'T': /* trusted user (set class `t') */ 39568481Seric if (bp[0] == 'C') 3964061Seric { 39768481Seric mid = macid(&bp[1], &ep); 39868529Seric expand(ep, exbuf, sizeof exbuf, e); 39968481Seric p = exbuf; 40068481Seric } 40168481Seric else 40268481Seric { 40368481Seric mid = 't'; 40468481Seric p = &bp[1]; 40568481Seric } 40668481Seric while (*p != '\0') 40768481Seric { 4084061Seric register char *wd; 4094061Seric char delim; 4104061Seric 41158050Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 4124061Seric p++; 4134061Seric wd = p; 41458050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 4154061Seric p++; 4164061Seric delim = *p; 4174061Seric *p = '\0'; 4184061Seric if (wd[0] != '\0') 41968481Seric setclass(mid, wd); 4204061Seric *p = delim; 4214061Seric } 4224061Seric break; 4234061Seric 42459272Seric case 'F': /* word class from file */ 42568481Seric mid = macid(&bp[1], &ep); 42668481Seric for (p = ep; isascii(*p) && isspace(*p); ) 42764133Seric p++; 42864133Seric if (p[0] == '-' && p[1] == 'o') 42964133Seric { 43064133Seric optional = TRUE; 43164133Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 43264133Seric p++; 43364133Seric while (isascii(*p) && isspace(*p)) 43468481Seric p++; 43564133Seric } 43664133Seric else 43764133Seric optional = FALSE; 43864133Seric file = p; 43964133Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 44064133Seric p++; 44159272Seric if (*p == '\0') 44259272Seric p = "%s"; 44359272Seric else 44459272Seric { 44559272Seric *p = '\0'; 44659272Seric while (isascii(*++p) && isspace(*p)) 44759272Seric continue; 44859272Seric } 44964133Seric fileclass(bp[1], file, p, safe, optional); 45059272Seric break; 45159272Seric 45259156Seric #ifdef XLA 45359156Seric case 'L': /* extended load average description */ 45459156Seric xla_init(&bp[1]); 45559156Seric break; 45659156Seric #endif 45759156Seric 4584096Seric case 'M': /* define mailer */ 45957135Seric makemailer(&bp[1]); 4604096Seric break; 4614096Seric 4628252Seric case 'O': /* set option */ 46358734Seric setoption(bp[1], &bp[2], safe, FALSE, e); 4648252Seric break; 4658252Seric 4668252Seric case 'P': /* set precedence */ 4678252Seric if (NumPriorities >= MAXPRIORITIES) 4688252Seric { 4698547Seric toomany('P', MAXPRIORITIES); 4708252Seric break; 4718252Seric } 47257135Seric for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) 4738252Seric continue; 4748252Seric if (*p == '\0') 4758252Seric goto badline; 4768252Seric *p = '\0'; 47757135Seric Priorities[NumPriorities].pri_name = newstr(&bp[1]); 4788252Seric Priorities[NumPriorities].pri_val = atoi(++p); 4798252Seric NumPriorities++; 4808252Seric break; 4818252Seric 48252645Seric case 'V': /* configuration syntax version */ 48364440Seric for (p = &bp[1]; isascii(*p) && isspace(*p); p++) 48464440Seric continue; 48564440Seric if (!isascii(*p) || !isdigit(*p)) 48664440Seric { 48764440Seric syserr("invalid argument to V line: \"%.20s\"", 48864440Seric &bp[1]); 48964440Seric break; 49064440Seric } 49164718Seric ConfigLevel = strtol(p, &ep, 10); 49268805Seric 49368805Seric /* 49468805Seric ** Do heuristic tweaking for back compatibility. 49568805Seric */ 49668805Seric 49764279Seric if (ConfigLevel >= 5) 49864279Seric { 49964279Seric /* level 5 configs have short name in $w */ 50064279Seric p = macvalue('w', e); 50164279Seric if (p != NULL && (p = strchr(p, '.')) != NULL) 50264279Seric *p = '\0'; 50364279Seric } 50468805Seric if (ConfigLevel >= 6) 50568805Seric { 50668805Seric ColonOkInAddr = FALSE; 50768805Seric } 50868805Seric 50968805Seric /* 51068805Seric ** Look for vendor code. 51168805Seric */ 51268805Seric 51364718Seric if (*ep++ == '/') 51464718Seric { 51564718Seric /* extract vendor code */ 51664718Seric for (p = ep; isascii(*p) && isalpha(*p); ) 51764718Seric p++; 51864718Seric *p = '\0'; 51964718Seric 52064718Seric if (!setvendor(ep)) 52164718Seric syserr("invalid V line vendor code: \"%s\"", 52264718Seric ep); 52364718Seric } 52452645Seric break; 52552645Seric 52653654Seric case 'K': 52769774Seric (void) makemapentry(&bp[1]); 52853654Seric break; 52953654Seric 53069476Seric case 'E': 53169476Seric p = strchr(bp, '='); 53269476Seric if (p != NULL) 53369476Seric *p++ = '\0'; 53469476Seric setuserenv(&bp[1], p); 53569476Seric break; 53669476Seric 5373308Seric default: 5384061Seric badline: 53957135Seric syserr("unknown control line \"%s\"", bp); 5403308Seric } 54157135Seric if (bp != buf) 54257135Seric free(bp); 5433308Seric } 54452637Seric if (ferror(cf)) 54552637Seric { 54652647Seric syserr("I/O read error", cfname); 54752637Seric exit(EX_OSFILE); 54852637Seric } 54952637Seric fclose(cf); 5509381Seric FileName = NULL; 55156836Seric 55268481Seric /* initialize host maps from local service tables */ 55368481Seric inithostmaps(); 55468481Seric 55568481Seric /* determine if we need to do special name-server frotz */ 55667905Seric { 55768481Seric int nmaps; 55868481Seric char *maptype[MAXMAPSTACK]; 55968481Seric short mapreturn[MAXMAPACTIONS]; 56068481Seric 56168481Seric nmaps = switch_map_find("hosts", maptype, mapreturn); 56268481Seric UseNameServer = FALSE; 56368481Seric if (nmaps > 0 && nmaps <= MAXMAPSTACK) 56468481Seric { 56568481Seric register int mapno; 56668481Seric 56768481Seric for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++) 56868481Seric { 56968481Seric if (strcmp(maptype[mapno], "dns") == 0) 57068481Seric UseNameServer = TRUE; 57168481Seric } 57268481Seric } 57368481Seric 57468481Seric #ifdef HESIOD 57568481Seric nmaps = switch_map_find("passwd", maptype, mapreturn); 57668481Seric UseHesiod = FALSE; 57768481Seric if (nmaps > 0 && nmaps <= MAXMAPSTACK) 57868481Seric { 57968481Seric register int mapno; 58068481Seric 58168481Seric for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++) 58268481Seric { 58368481Seric if (strcmp(maptype[mapno], "hesiod") == 0) 58468481Seric UseHesiod = TRUE; 58568481Seric } 58668481Seric } 58768204Seric #endif 58867905Seric } 5894096Seric } 5904096Seric /* 5918547Seric ** TOOMANY -- signal too many of some option 5928547Seric ** 5938547Seric ** Parameters: 5948547Seric ** id -- the id of the error line 5958547Seric ** maxcnt -- the maximum possible values 5968547Seric ** 5978547Seric ** Returns: 5988547Seric ** none. 5998547Seric ** 6008547Seric ** Side Effects: 6018547Seric ** gives a syserr. 6028547Seric */ 6038547Seric 60469748Seric void 6058547Seric toomany(id, maxcnt) 60669748Seric int id; 6078547Seric int maxcnt; 6088547Seric { 6099381Seric syserr("too many %c lines, %d max", id, maxcnt); 6108547Seric } 6118547Seric /* 6124432Seric ** FILECLASS -- read members of a class from a file 6134432Seric ** 6144432Seric ** Parameters: 6154432Seric ** class -- class to define. 6164432Seric ** filename -- name of file to read. 6174432Seric ** fmt -- scanf string to use for match. 61864133Seric ** safe -- if set, this is a safe read. 61964133Seric ** optional -- if set, it is not an error for the file to 62064133Seric ** not exist. 6214432Seric ** 6224432Seric ** Returns: 6234432Seric ** none 6244432Seric ** 6254432Seric ** Side Effects: 6264432Seric ** 6274432Seric ** puts all lines in filename that match a scanf into 6284432Seric ** the named class. 6294432Seric */ 6304432Seric 63169748Seric void 63264133Seric fileclass(class, filename, fmt, safe, optional) 6334432Seric int class; 6344432Seric char *filename; 6354432Seric char *fmt; 63654973Seric bool safe; 63764133Seric bool optional; 6384432Seric { 63925808Seric FILE *f; 64068513Seric int sff; 64169453Seric int pid; 64269453Seric register char *p; 6434432Seric char buf[MAXLINE]; 6444432Seric 64566101Seric if (tTd(37, 2)) 64666101Seric printf("fileclass(%s, fmt=%s)\n", filename, fmt); 64766101Seric 64866031Seric if (filename[0] == '|') 64966031Seric { 65069453Seric auto int fd; 65169453Seric int i; 65269453Seric char *argv[MAXPV + 1]; 65369453Seric 65469453Seric i = 0; 65569453Seric for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t")) 65669453Seric { 65769453Seric if (i >= MAXPV) 65869453Seric break; 65969453Seric argv[i++] = p; 66069453Seric } 66169453Seric argv[i] = NULL; 66269453Seric pid = prog_open(argv, &fd, CurEnv); 66369453Seric if (pid < 0) 66469453Seric f = NULL; 66569453Seric else 66669453Seric f = fdopen(fd, "r"); 66766031Seric } 66869453Seric else 66969453Seric { 67069453Seric pid = -1; 67169453Seric sff = SFF_REGONLY; 67269453Seric if (safe) 67369453Seric sff |= SFF_OPENASROOT; 67469453Seric f = safefopen(filename, O_RDONLY, 0, sff); 67569453Seric } 67668602Seric if (f == NULL) 67754973Seric { 67868602Seric if (!optional) 67968602Seric syserr("fileclass: cannot open %s", filename); 6804432Seric return; 6814432Seric } 6824432Seric 6834432Seric while (fgets(buf, sizeof buf, f) != NULL) 6844432Seric { 68525808Seric register char *p; 68625808Seric # ifdef SCANF 6874432Seric char wordbuf[MAXNAME+1]; 6884432Seric 6894432Seric if (sscanf(buf, fmt, wordbuf) != 1) 6904432Seric continue; 69125808Seric p = wordbuf; 69256795Seric # else /* SCANF */ 69325808Seric p = buf; 69456795Seric # endif /* SCANF */ 69525808Seric 69625808Seric /* 69725808Seric ** Break up the match into words. 69825808Seric */ 69925808Seric 70025808Seric while (*p != '\0') 70125808Seric { 70225808Seric register char *q; 70325808Seric 70425808Seric /* strip leading spaces */ 70558050Seric while (isascii(*p) && isspace(*p)) 70625808Seric p++; 70725808Seric if (*p == '\0') 70825808Seric break; 70925808Seric 71025808Seric /* find the end of the word */ 71125808Seric q = p; 71258050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 71325808Seric p++; 71425808Seric if (*p != '\0') 71525808Seric *p++ = '\0'; 71625808Seric 71725808Seric /* enter the word in the symbol table */ 71866101Seric setclass(class, q); 71925808Seric } 7204432Seric } 7214432Seric 72254973Seric (void) fclose(f); 72369453Seric if (pid > 0) 72469453Seric (void) waitfor(pid); 7254432Seric } 7264432Seric /* 7274096Seric ** MAKEMAILER -- define a new mailer. 7284096Seric ** 7294096Seric ** Parameters: 73010327Seric ** line -- description of mailer. This is in labeled 73110327Seric ** fields. The fields are: 73268481Seric ** A -- the argv for this mailer 73368481Seric ** C -- the character set for MIME conversions 73468481Seric ** D -- the directory to run in 73568481Seric ** E -- the eol string 73668481Seric ** F -- the flags associated with the mailer 73768481Seric ** L -- the maximum line length 73868481Seric ** M -- the maximum message size 73968816Seric ** N -- the niceness at which to run 74068479Seric ** P -- the path to the mailer 74168481Seric ** R -- the recipient rewriting set 74268479Seric ** S -- the sender rewriting set 74368481Seric ** T -- the mailer type (for DSNs) 74468481Seric ** U -- the uid to run as 74510327Seric ** The first word is the canonical name of the mailer. 7464096Seric ** 7474096Seric ** Returns: 7484096Seric ** none. 7494096Seric ** 7504096Seric ** Side Effects: 7514096Seric ** enters the mailer into the mailer table. 7524096Seric */ 7533308Seric 75469748Seric void 75521066Seric makemailer(line) 7564096Seric char *line; 7574096Seric { 7584096Seric register char *p; 7598067Seric register struct mailer *m; 7608067Seric register STAB *s; 7618067Seric int i; 76210327Seric char fcode; 76358020Seric auto char *endp; 7644096Seric extern int NextMailer; 76510327Seric extern char **makeargv(); 76610327Seric extern char *munchstring(); 7674096Seric 76810327Seric /* allocate a mailer and set up defaults */ 76910327Seric m = (struct mailer *) xalloc(sizeof *m); 77010327Seric bzero((char *) m, sizeof *m); 77110327Seric m->m_eol = "\n"; 77268481Seric m->m_uid = m->m_gid = 0; 77310327Seric 77410327Seric /* collect the mailer name */ 77558050Seric for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) 77610327Seric continue; 77710327Seric if (*p != '\0') 77810327Seric *p++ = '\0'; 77910327Seric m->m_name = newstr(line); 78010327Seric 78110327Seric /* now scan through and assign info from the fields */ 78210327Seric while (*p != '\0') 78310327Seric { 78458333Seric auto char *delimptr; 78558333Seric 78658050Seric while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) 78710327Seric p++; 78810327Seric 78910327Seric /* p now points to field code */ 79010327Seric fcode = *p; 79110327Seric while (*p != '\0' && *p != '=' && *p != ',') 79210327Seric p++; 79310327Seric if (*p++ != '=') 79410327Seric { 79552637Seric syserr("mailer %s: `=' expected", m->m_name); 79610327Seric return; 79710327Seric } 79858050Seric while (isascii(*p) && isspace(*p)) 79910327Seric p++; 80010327Seric 80110327Seric /* p now points to the field body */ 80258333Seric p = munchstring(p, &delimptr); 80310327Seric 80410327Seric /* install the field into the mailer struct */ 80510327Seric switch (fcode) 80610327Seric { 80710327Seric case 'P': /* pathname */ 80810327Seric m->m_mailer = newstr(p); 80910327Seric break; 81010327Seric 81110327Seric case 'F': /* flags */ 81210687Seric for (; *p != '\0'; p++) 81358050Seric if (!(isascii(*p) && isspace(*p))) 81452637Seric setbitn(*p, m->m_flags); 81510327Seric break; 81610327Seric 81710327Seric case 'S': /* sender rewriting ruleset */ 81810327Seric case 'R': /* recipient rewriting ruleset */ 81969783Seric i = strtorwset(p, &endp, ST_ENTER); 82069783Seric if (i < 0) 82110327Seric return; 82210327Seric if (fcode == 'S') 82358020Seric m->m_sh_rwset = m->m_se_rwset = i; 82410327Seric else 82558020Seric m->m_rh_rwset = m->m_re_rwset = i; 82658020Seric 82758020Seric p = endp; 82859985Seric if (*p++ == '/') 82958020Seric { 83069783Seric i = strtorwset(p, NULL); 83169783Seric if (i < 0) 83258020Seric return; 83358020Seric if (fcode == 'S') 83458020Seric m->m_sh_rwset = i; 83558020Seric else 83658020Seric m->m_rh_rwset = i; 83758020Seric } 83810327Seric break; 83910327Seric 84010327Seric case 'E': /* end of line string */ 84110327Seric m->m_eol = newstr(p); 84210327Seric break; 84310327Seric 84410327Seric case 'A': /* argument vector */ 84510327Seric m->m_argv = makeargv(p); 84610327Seric break; 84710701Seric 84810701Seric case 'M': /* maximum message size */ 84910701Seric m->m_maxsize = atol(p); 85010701Seric break; 85152106Seric 85252106Seric case 'L': /* maximum line length */ 85352106Seric m->m_linelimit = atoi(p); 85452106Seric break; 85558935Seric 85668816Seric case 'N': /* run niceness */ 85768816Seric m->m_nice = atoi(p); 85868816Seric break; 85968816Seric 86058935Seric case 'D': /* working directory */ 86158935Seric m->m_execdir = newstr(p); 86258935Seric break; 86368481Seric 86468481Seric case 'C': /* default charset */ 86568481Seric m->m_defcharset = newstr(p); 86668481Seric break; 86768481Seric 86869720Seric case 'T': /* MTA-Name/Address/Diagnostic types */ 86968481Seric m->m_mtatype = newstr(p); 87068481Seric p = strchr(m->m_mtatype, '/'); 87168481Seric if (p != NULL) 87268481Seric { 87368481Seric *p++ = '\0'; 87468481Seric if (*p == '\0') 87568481Seric p = NULL; 87668481Seric } 87768481Seric if (p == NULL) 87868481Seric m->m_addrtype = m->m_mtatype; 87968481Seric else 88068481Seric { 88168481Seric m->m_addrtype = p; 88268481Seric p = strchr(p, '/'); 88368481Seric } 88468481Seric if (p != NULL) 88568481Seric { 88668481Seric *p++ = '\0'; 88768481Seric if (*p == '\0') 88868481Seric p = NULL; 88968481Seric } 89068481Seric if (p == NULL) 89168481Seric m->m_diagtype = m->m_mtatype; 89268481Seric else 89368481Seric m->m_diagtype = p; 89468481Seric break; 89568481Seric 89668481Seric case 'U': /* user id */ 89768481Seric if (isascii(*p) && !isdigit(*p)) 89868481Seric { 89968481Seric char *q = p; 90068481Seric struct passwd *pw; 90168481Seric 90268481Seric while (isascii(*p) && isalnum(*p)) 90368481Seric p++; 90468481Seric while (isascii(*p) && isspace(*p)) 90568481Seric *p++ = '\0'; 90668481Seric if (*p != '\0') 90768481Seric *p++ = '\0'; 90868693Seric pw = sm_getpwnam(q); 90968481Seric if (pw == NULL) 91068481Seric syserr("readcf: mailer U= flag: unknown user %s", q); 91168481Seric else 91268481Seric { 91368481Seric m->m_uid = pw->pw_uid; 91468481Seric m->m_gid = pw->pw_gid; 91568481Seric } 91668481Seric } 91768481Seric else 91868481Seric { 91968481Seric auto char *q; 92068481Seric 92168481Seric m->m_uid = strtol(p, &q, 0); 92268481Seric p = q; 92368481Seric } 92468481Seric while (isascii(*p) && isspace(*p)) 92568481Seric p++; 92668481Seric if (*p == '\0') 92768481Seric break; 92868481Seric if (isascii(*p) && !isdigit(*p)) 92968481Seric { 93068481Seric char *q = p; 93168481Seric struct group *gr; 93268481Seric 93368481Seric while (isascii(*p) && isalnum(*p)) 93468481Seric p++; 93568481Seric *p++ = '\0'; 93668481Seric gr = getgrnam(q); 93768481Seric if (gr == NULL) 93868481Seric syserr("readcf: mailer U= flag: unknown group %s", q); 93968481Seric else 94068481Seric m->m_gid = gr->gr_gid; 94168481Seric } 94268481Seric else 94368481Seric { 94468481Seric m->m_gid = strtol(p, NULL, 0); 94568481Seric } 94668481Seric break; 94710327Seric } 94810327Seric 94958333Seric p = delimptr; 95010327Seric } 95110327Seric 95258321Seric /* do some rationality checking */ 95358321Seric if (m->m_argv == NULL) 95458321Seric { 95558321Seric syserr("M%s: A= argument required", m->m_name); 95658321Seric return; 95758321Seric } 95858321Seric if (m->m_mailer == NULL) 95958321Seric { 96058321Seric syserr("M%s: P= argument required", m->m_name); 96158321Seric return; 96258321Seric } 96358321Seric 9644096Seric if (NextMailer >= MAXMAILERS) 9654096Seric { 9669381Seric syserr("too many mailers defined (%d max)", MAXMAILERS); 9674096Seric return; 9684096Seric } 96957402Seric 97068481Seric /* do some heuristic cleanup for back compatibility */ 97168481Seric if (bitnset(M_LIMITS, m->m_flags)) 97268481Seric { 97368481Seric if (m->m_linelimit == 0) 97468481Seric m->m_linelimit = SMTPLINELIM; 97568481Seric if (ConfigLevel < 2) 97668481Seric setbitn(M_7BITS, m->m_flags); 97768481Seric } 97868481Seric 97968481Seric if (ConfigLevel < 6 && 98068481Seric (strcmp(m->m_mailer, "[IPC]") == 0 || 98168481Seric strcmp(m->m_mailer, "[TCP]") == 0)) 98268481Seric { 98368481Seric if (m->m_mtatype == NULL) 98468481Seric m->m_mtatype = "dns"; 98568481Seric if (m->m_addrtype == NULL) 98668481Seric m->m_addrtype = "rfc822"; 98768481Seric if (m->m_diagtype == NULL) 98868481Seric m->m_diagtype = "smtp"; 98968481Seric } 99068481Seric 99168481Seric /* enter the mailer into the symbol table */ 99210327Seric s = stab(m->m_name, ST_MAILER, ST_ENTER); 99357402Seric if (s->s_mailer != NULL) 99457402Seric { 99557402Seric i = s->s_mailer->m_mno; 99657402Seric free(s->s_mailer); 99757402Seric } 99857402Seric else 99957402Seric { 100057402Seric i = NextMailer++; 100157402Seric } 100257402Seric Mailer[i] = s->s_mailer = m; 100357454Seric m->m_mno = i; 100410327Seric } 100510327Seric /* 100610327Seric ** MUNCHSTRING -- translate a string into internal form. 100710327Seric ** 100810327Seric ** Parameters: 100910327Seric ** p -- the string to munch. 101058333Seric ** delimptr -- if non-NULL, set to the pointer of the 101158333Seric ** field delimiter character. 101210327Seric ** 101310327Seric ** Returns: 101410327Seric ** the munched string. 101510327Seric */ 10164096Seric 101710327Seric char * 101858333Seric munchstring(p, delimptr) 101910327Seric register char *p; 102058333Seric char **delimptr; 102110327Seric { 102210327Seric register char *q; 102310327Seric bool backslash = FALSE; 102410327Seric bool quotemode = FALSE; 102510327Seric static char buf[MAXLINE]; 10264096Seric 102710327Seric for (q = buf; *p != '\0'; p++) 10284096Seric { 102910327Seric if (backslash) 103010327Seric { 103110327Seric /* everything is roughly literal */ 103210357Seric backslash = FALSE; 103310327Seric switch (*p) 103410327Seric { 103510327Seric case 'r': /* carriage return */ 103610327Seric *q++ = '\r'; 103710327Seric continue; 103810327Seric 103910327Seric case 'n': /* newline */ 104010327Seric *q++ = '\n'; 104110327Seric continue; 104210327Seric 104310327Seric case 'f': /* form feed */ 104410327Seric *q++ = '\f'; 104510327Seric continue; 104610327Seric 104710327Seric case 'b': /* backspace */ 104810327Seric *q++ = '\b'; 104910327Seric continue; 105010327Seric } 105110327Seric *q++ = *p; 105210327Seric } 105310327Seric else 105410327Seric { 105510327Seric if (*p == '\\') 105610327Seric backslash = TRUE; 105710327Seric else if (*p == '"') 105810327Seric quotemode = !quotemode; 105910327Seric else if (quotemode || *p != ',') 106010327Seric *q++ = *p; 106110327Seric else 106210327Seric break; 106310327Seric } 10644096Seric } 10654096Seric 106658333Seric if (delimptr != NULL) 106758333Seric *delimptr = p; 106810327Seric *q++ = '\0'; 106910327Seric return (buf); 107010327Seric } 107110327Seric /* 107210327Seric ** MAKEARGV -- break up a string into words 107310327Seric ** 107410327Seric ** Parameters: 107510327Seric ** p -- the string to break up. 107610327Seric ** 107710327Seric ** Returns: 107810327Seric ** a char **argv (dynamically allocated) 107910327Seric ** 108010327Seric ** Side Effects: 108110327Seric ** munges p. 108210327Seric */ 10834096Seric 108410327Seric char ** 108510327Seric makeargv(p) 108610327Seric register char *p; 108710327Seric { 108810327Seric char *q; 108910327Seric int i; 109010327Seric char **avp; 109110327Seric char *argv[MAXPV + 1]; 109210327Seric 109310327Seric /* take apart the words */ 109410327Seric i = 0; 109510327Seric while (*p != '\0' && i < MAXPV) 10964096Seric { 109710327Seric q = p; 109858050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 109910327Seric p++; 110058050Seric while (isascii(*p) && isspace(*p)) 110110327Seric *p++ = '\0'; 110210327Seric argv[i++] = newstr(q); 11034096Seric } 110410327Seric argv[i++] = NULL; 11054096Seric 110610327Seric /* now make a copy of the argv */ 110710327Seric avp = (char **) xalloc(sizeof *avp * i); 110816893Seric bcopy((char *) argv, (char *) avp, sizeof *avp * i); 110910327Seric 111010327Seric return (avp); 11113308Seric } 11123308Seric /* 11133308Seric ** PRINTRULES -- print rewrite rules (for debugging) 11143308Seric ** 11153308Seric ** Parameters: 11163308Seric ** none. 11173308Seric ** 11183308Seric ** Returns: 11193308Seric ** none. 11203308Seric ** 11213308Seric ** Side Effects: 11223308Seric ** prints rewrite rules. 11233308Seric */ 11243308Seric 112569748Seric void 11263308Seric printrules() 11273308Seric { 11283308Seric register struct rewrite *rwp; 11294072Seric register int ruleset; 11303308Seric 11314072Seric for (ruleset = 0; ruleset < 10; ruleset++) 11323308Seric { 11334072Seric if (RewriteRules[ruleset] == NULL) 11344072Seric continue; 11358067Seric printf("\n----Rule Set %d:", ruleset); 11363308Seric 11374072Seric for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 11383308Seric { 11398067Seric printf("\nLHS:"); 11408067Seric printav(rwp->r_lhs); 11418067Seric printf("RHS:"); 11428067Seric printav(rwp->r_rhs); 11433308Seric } 11443308Seric } 11453308Seric } 114668481Seric /* 114768481Seric ** PRINTMAILER -- print mailer structure (for debugging) 114868481Seric ** 114968481Seric ** Parameters: 115068481Seric ** m -- the mailer to print 115168481Seric ** 115268481Seric ** Returns: 115368481Seric ** none. 115468481Seric */ 11554319Seric 115669748Seric void 115768481Seric printmailer(m) 115868481Seric register MAILER *m; 115968481Seric { 116068481Seric int j; 116168481Seric 116268481Seric printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=", 116368481Seric m->m_mno, m->m_name, 116468481Seric m->m_mailer, m->m_se_rwset, m->m_sh_rwset, 116568481Seric m->m_re_rwset, m->m_rh_rwset, m->m_maxsize, 116668481Seric m->m_uid, m->m_gid); 116768481Seric for (j = '\0'; j <= '\177'; j++) 116868481Seric if (bitnset(j, m->m_flags)) 116968481Seric (void) putchar(j); 117068481Seric printf(" L=%d E=", m->m_linelimit); 117168481Seric xputs(m->m_eol); 117268481Seric if (m->m_defcharset != NULL) 117368481Seric printf(" C=%s", m->m_defcharset); 117468481Seric printf(" T=%s/%s/%s", 117568481Seric m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype, 117668481Seric m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype, 117768481Seric m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype); 117868481Seric if (m->m_argv != NULL) 117968481Seric { 118068481Seric char **a = m->m_argv; 118168481Seric 118268481Seric printf(" A="); 118368481Seric while (*a != NULL) 118468481Seric { 118568481Seric if (a != m->m_argv) 118668481Seric printf(" "); 118768481Seric xputs(*a++); 118868481Seric } 118968481Seric } 119068481Seric printf("\n"); 119168481Seric } 11924096Seric /* 11938256Seric ** SETOPTION -- set global processing option 11948256Seric ** 11958256Seric ** Parameters: 11968256Seric ** opt -- option name. 11978256Seric ** val -- option value (as a text string). 119821755Seric ** safe -- set if this came from a configuration file. 119921755Seric ** Some options (if set from the command line) will 120021755Seric ** reset the user id to avoid security problems. 12018269Seric ** sticky -- if set, don't let other setoptions override 12028269Seric ** this value. 120358734Seric ** e -- the main envelope. 12048256Seric ** 12058256Seric ** Returns: 12068256Seric ** none. 12078256Seric ** 12088256Seric ** Side Effects: 12098256Seric ** Sets options as implied by the arguments. 12108256Seric */ 12118256Seric 121210687Seric static BITMAP StickyOpt; /* set if option is stuck */ 121369748Seric extern void settimeout __P((char *, char *)); 12148269Seric 121557207Seric 121666334Seric #if NAMED_BIND 121757207Seric 121857207Seric struct resolverflags 121957207Seric { 122057207Seric char *rf_name; /* name of the flag */ 122157207Seric long rf_bits; /* bits to set/clear */ 122257207Seric } ResolverFlags[] = 122357207Seric { 122457207Seric "debug", RES_DEBUG, 122557207Seric "aaonly", RES_AAONLY, 122657207Seric "usevc", RES_USEVC, 122757207Seric "primary", RES_PRIMARY, 122857207Seric "igntc", RES_IGNTC, 122957207Seric "recurse", RES_RECURSE, 123057207Seric "defnames", RES_DEFNAMES, 123157207Seric "stayopen", RES_STAYOPEN, 123257207Seric "dnsrch", RES_DNSRCH, 123365583Seric "true", 0, /* to avoid error on old syntax */ 123457207Seric NULL, 0 123557207Seric }; 123657207Seric 123757207Seric #endif 123857207Seric 123968481Seric struct optioninfo 124068481Seric { 124168481Seric char *o_name; /* long name of option */ 124268481Seric u_char o_code; /* short name of option */ 124368481Seric bool o_safe; /* safe for random people to use */ 124468481Seric } OptionTab[] = 124568481Seric { 124668481Seric "SevenBitInput", '7', TRUE, 124769480Seric #if MIME8TO7 124868481Seric "EightBitMode", '8', TRUE, 124969480Seric #endif 125068481Seric "AliasFile", 'A', FALSE, 125168481Seric "AliasWait", 'a', FALSE, 125268481Seric "BlankSub", 'B', FALSE, 125368481Seric "MinFreeBlocks", 'b', TRUE, 125468481Seric "CheckpointInterval", 'C', TRUE, 125568481Seric "HoldExpensive", 'c', FALSE, 125668481Seric "AutoRebuildAliases", 'D', FALSE, 125768481Seric "DeliveryMode", 'd', TRUE, 125868481Seric "ErrorHeader", 'E', FALSE, 125968481Seric "ErrorMode", 'e', TRUE, 126068481Seric "TempFileMode", 'F', FALSE, 126168481Seric "SaveFromLine", 'f', FALSE, 126268481Seric "MatchGECOS", 'G', FALSE, 126368481Seric "HelpFile", 'H', FALSE, 126468481Seric "MaxHopCount", 'h', FALSE, 126568569Seric "ResolverOptions", 'I', FALSE, 126668481Seric "IgnoreDots", 'i', TRUE, 126768481Seric "ForwardPath", 'J', FALSE, 126868481Seric "SendMimeErrors", 'j', TRUE, 126968481Seric "ConnectionCacheSize", 'k', FALSE, 127068481Seric "ConnectionCacheTimeout", 'K', FALSE, 127168481Seric "UseErrorsTo", 'l', FALSE, 127268481Seric "LogLevel", 'L', FALSE, 127368481Seric "MeToo", 'm', TRUE, 127468481Seric "CheckAliases", 'n', FALSE, 127568481Seric "OldStyleHeaders", 'o', TRUE, 127668481Seric "DaemonPortOptions", 'O', FALSE, 127768481Seric "PrivacyOptions", 'p', TRUE, 127868481Seric "PostmasterCopy", 'P', FALSE, 127968481Seric "QueueFactor", 'q', FALSE, 128068481Seric "QueueDirectory", 'Q', FALSE, 128168481Seric "DontPruneRoutes", 'R', FALSE, 128268481Seric "Timeout", 'r', TRUE, 128368481Seric "StatusFile", 'S', FALSE, 128468481Seric "SuperSafe", 's', TRUE, 128568481Seric "QueueTimeout", 'T', FALSE, 128668481Seric "TimeZoneSpec", 't', FALSE, 128768481Seric "UserDatabaseSpec", 'U', FALSE, 128868481Seric "DefaultUser", 'u', FALSE, 128968481Seric "FallbackMXhost", 'V', FALSE, 129068481Seric "Verbose", 'v', TRUE, 129168481Seric "TryNullMXList", 'w', TRUE, 129268481Seric "QueueLA", 'x', FALSE, 129368481Seric "RefuseLA", 'X', FALSE, 129468481Seric "RecipientFactor", 'y', FALSE, 129568569Seric "ForkEachJob", 'Y', FALSE, 129668481Seric "ClassFactor", 'z', FALSE, 129768569Seric "RetryFactor", 'Z', FALSE, 129868481Seric #define O_QUEUESORTORD 0x81 129968481Seric "QueueSortOrder", O_QUEUESORTORD, TRUE, 130069401Seric #define O_HOSTSFILE 0x82 130169401Seric "HostsFile", O_HOSTSFILE, FALSE, 130268481Seric #define O_MQA 0x83 130368481Seric "MinQueueAge", O_MQA, TRUE, 130468481Seric #define O_MHSA 0x84 130568481Seric /* 130668481Seric "MaxHostStatAge", O_MHSA, TRUE, 130768481Seric */ 130868481Seric #define O_DEFCHARSET 0x85 130968481Seric "DefaultCharSet", O_DEFCHARSET, TRUE, 131068481Seric #define O_SSFILE 0x86 131168481Seric "ServiceSwitchFile", O_SSFILE, FALSE, 131268481Seric #define O_DIALDELAY 0x87 131368481Seric "DialDelay", O_DIALDELAY, TRUE, 131468481Seric #define O_NORCPTACTION 0x88 131568481Seric "NoRecipientAction", O_NORCPTACTION, TRUE, 131668490Seric #define O_SAFEFILEENV 0x89 131768490Seric "SafeFileEnvironment", O_SAFEFILEENV, FALSE, 131868569Seric #define O_MAXMSGSIZE 0x8a 131968569Seric "MaxMessageSize", O_MAXMSGSIZE, FALSE, 132068756Seric #define O_COLONOKINADDR 0x8b 132168756Seric "ColonOkInAddr", O_COLONOKINADDR, TRUE, 132269724Seric #define O_MAXQUEUERUN 0x8c 132369724Seric "MaxQueueRunSize", O_MAXQUEUERUN, TRUE, 132468481Seric 132568481Seric NULL, '\0', FALSE, 132668481Seric }; 132768481Seric 132868481Seric 132968481Seric 133069748Seric void 133158734Seric setoption(opt, val, safe, sticky, e) 133269748Seric int opt; 13338256Seric char *val; 133421755Seric bool safe; 13358269Seric bool sticky; 133658734Seric register ENVELOPE *e; 13378256Seric { 133857207Seric register char *p; 133968481Seric register struct optioninfo *o; 134068481Seric char *subopt; 13418265Seric extern bool atobool(); 134212633Seric extern time_t convtime(); 134314879Seric extern int QueueLA; 134414879Seric extern int RefuseLA; 134564718Seric extern bool Warn_Q_option; 13468256Seric 134768481Seric errno = 0; 134868481Seric if (opt == ' ') 134968481Seric { 135068481Seric /* full word options */ 135168481Seric struct optioninfo *sel; 135268481Seric 135368481Seric p = strchr(val, '='); 135468481Seric if (p == NULL) 135568481Seric p = &val[strlen(val)]; 135668481Seric while (*--p == ' ') 135768481Seric continue; 135868481Seric while (*++p == ' ') 135968481Seric *p = '\0'; 136068481Seric if (p == val) 136168481Seric { 136268481Seric syserr("readcf: null option name"); 136368481Seric return; 136468481Seric } 136568481Seric if (*p == '=') 136668481Seric *p++ = '\0'; 136768481Seric while (*p == ' ') 136868481Seric p++; 136968481Seric subopt = strchr(val, '.'); 137068481Seric if (subopt != NULL) 137168481Seric *subopt++ = '\0'; 137268481Seric sel = NULL; 137368481Seric for (o = OptionTab; o->o_name != NULL; o++) 137468481Seric { 137568481Seric if (strncasecmp(o->o_name, val, strlen(val)) != 0) 137668481Seric continue; 137768481Seric if (strlen(o->o_name) == strlen(val)) 137868481Seric { 137968481Seric /* completely specified -- this must be it */ 138068481Seric sel = NULL; 138168481Seric break; 138268481Seric } 138368481Seric if (sel != NULL) 138468481Seric break; 138568481Seric sel = o; 138668481Seric } 138768481Seric if (sel != NULL && o->o_name == NULL) 138868481Seric o = sel; 138968481Seric else if (o->o_name == NULL) 139068481Seric { 139168481Seric syserr("readcf: unknown option name %s", val); 139268481Seric return; 139368481Seric } 139468481Seric else if (sel != NULL) 139568481Seric { 139668481Seric syserr("readcf: ambiguous option name %s (matches %s and %s)", 139768481Seric val, sel->o_name, o->o_name); 139868481Seric return; 139968481Seric } 140068481Seric if (strlen(val) != strlen(o->o_name)) 140168481Seric { 140268481Seric bool oldVerbose = Verbose; 140368481Seric 140468481Seric Verbose = TRUE; 140568481Seric message("Option %s used as abbreviation for %s", 140668481Seric val, o->o_name); 140768481Seric Verbose = oldVerbose; 140868481Seric } 140968481Seric opt = o->o_code; 141068481Seric val = p; 141168481Seric } 141268481Seric else 141368481Seric { 141468481Seric for (o = OptionTab; o->o_name != NULL; o++) 141568481Seric { 141668481Seric if (o->o_code == opt) 141768481Seric break; 141868481Seric } 141968481Seric subopt = NULL; 142068481Seric } 142168481Seric 14228256Seric if (tTd(37, 1)) 142368481Seric { 142468481Seric printf(isascii(opt) && isprint(opt) ? 142568481Seric "setoption %s (%c).%s=%s" : 142668481Seric "setoption %s (0x%x).%s=%s", 142768481Seric o->o_name == NULL ? "<unknown>" : o->o_name, 142868481Seric opt, 142968481Seric subopt == NULL ? "" : subopt, 143068481Seric val); 143168481Seric } 14328256Seric 14338256Seric /* 14348269Seric ** See if this option is preset for us. 14358256Seric */ 14368256Seric 143759731Seric if (!sticky && bitnset(opt, StickyOpt)) 14388269Seric { 14399341Seric if (tTd(37, 1)) 14409341Seric printf(" (ignored)\n"); 14418269Seric return; 14428269Seric } 14438269Seric 144421755Seric /* 144521755Seric ** Check to see if this option can be specified by this user. 144621755Seric */ 144721755Seric 144863787Seric if (!safe && RealUid == 0) 144921755Seric safe = TRUE; 145068481Seric if (!safe && !o->o_safe) 145121755Seric { 145239111Srick if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 145321755Seric { 145436582Sbostic if (tTd(37, 1)) 145536582Sbostic printf(" (unsafe)"); 145663787Seric if (RealUid != geteuid()) 145736582Sbostic { 145851210Seric if (tTd(37, 1)) 145951210Seric printf("(Resetting uid)"); 146063787Seric (void) setgid(RealGid); 146163787Seric (void) setuid(RealUid); 146236582Sbostic } 146321755Seric } 146421755Seric } 146551210Seric if (tTd(37, 1)) 146617985Seric printf("\n"); 14678269Seric 146868481Seric switch (opt & 0xff) 14698256Seric { 147059709Seric case '7': /* force seven-bit input */ 147168481Seric SevenBitInput = atobool(val); 147252106Seric break; 147352106Seric 147469480Seric #if MIME8TO7 147568481Seric case '8': /* handling of 8-bit input */ 147668481Seric switch (*val) 147768481Seric { 147868481Seric case 'm': /* convert 8-bit, convert MIME */ 147968481Seric MimeMode = MM_CVTMIME|MM_MIME8BIT; 148068481Seric break; 148168481Seric 148268481Seric case 'p': /* pass 8 bit, convert MIME */ 148368856Seric MimeMode = MM_CVTMIME|MM_PASS8BIT; 148468481Seric break; 148568481Seric 148668481Seric case 's': /* strict adherence */ 148768481Seric MimeMode = MM_CVTMIME; 148868481Seric break; 148968481Seric 149068856Seric #if 0 149168856Seric case 'r': /* reject 8-bit, don't convert MIME */ 149268856Seric MimeMode = 0; 149368856Seric break; 149468856Seric 149568856Seric case 'j': /* "just send 8" */ 149668856Seric MimeMode = MM_PASS8BIT; 149768856Seric break; 149868856Seric 149968481Seric case 'a': /* encode 8 bit if available */ 150068481Seric MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 150168481Seric break; 150268481Seric 150368481Seric case 'c': /* convert 8 bit to MIME, never 7 bit */ 150468481Seric MimeMode = MM_MIME8BIT; 150568481Seric break; 150668856Seric #endif 150768481Seric 150868481Seric default: 150968481Seric syserr("Unknown 8-bit mode %c", *val); 151068481Seric exit(EX_USAGE); 151168481Seric } 151268481Seric break; 151369480Seric #endif 151468481Seric 15158256Seric case 'A': /* set default alias file */ 15169381Seric if (val[0] == '\0') 151759672Seric setalias("aliases"); 15189381Seric else 151959672Seric setalias(val); 15208256Seric break; 15218256Seric 152217474Seric case 'a': /* look N minutes for "@:@" in alias file */ 152317474Seric if (val[0] == '\0') 152464796Seric SafeAlias = 5 * 60; /* five minutes */ 152517474Seric else 152664796Seric SafeAlias = convtime(val, 'm'); 152717474Seric break; 152817474Seric 152916843Seric case 'B': /* substitution for blank character */ 153016843Seric SpaceSub = val[0]; 153116843Seric if (SpaceSub == '\0') 153216843Seric SpaceSub = ' '; 153316843Seric break; 153416843Seric 153559283Seric case 'b': /* min blocks free on queue fs/max msg size */ 153659283Seric p = strchr(val, '/'); 153759283Seric if (p != NULL) 153859283Seric { 153959283Seric *p++ = '\0'; 154059283Seric MaxMessageSize = atol(p); 154159283Seric } 154258082Seric MinBlocksFree = atol(val); 154358082Seric break; 154458082Seric 15459284Seric case 'c': /* don't connect to "expensive" mailers */ 15469381Seric NoConnect = atobool(val); 15479284Seric break; 15489284Seric 154951305Seric case 'C': /* checkpoint every N addresses */ 155051305Seric CheckpointInterval = atoi(val); 155124944Seric break; 155224944Seric 15539284Seric case 'd': /* delivery mode */ 15549284Seric switch (*val) 15558269Seric { 15569284Seric case '\0': 155758734Seric e->e_sendmode = SM_DELIVER; 15588269Seric break; 15598269Seric 156010755Seric case SM_QUEUE: /* queue only */ 156110755Seric #ifndef QUEUE 156210755Seric syserr("need QUEUE to set -odqueue"); 156356795Seric #endif /* QUEUE */ 156410755Seric /* fall through..... */ 156510755Seric 15669284Seric case SM_DELIVER: /* do everything */ 15679284Seric case SM_FORK: /* fork after verification */ 156858734Seric e->e_sendmode = *val; 15698269Seric break; 15708269Seric 15718269Seric default: 15729284Seric syserr("Unknown delivery mode %c", *val); 15738269Seric exit(EX_USAGE); 15748269Seric } 15758269Seric break; 15768269Seric 15779146Seric case 'D': /* rebuild alias database as needed */ 15789381Seric AutoRebuild = atobool(val); 15799146Seric break; 15809146Seric 158155372Seric case 'E': /* error message header/header file */ 158255379Seric if (*val != '\0') 158355379Seric ErrMsgFile = newstr(val); 158455372Seric break; 158555372Seric 15868269Seric case 'e': /* set error processing mode */ 15878269Seric switch (*val) 15888269Seric { 15899381Seric case EM_QUIET: /* be silent about it */ 15909381Seric case EM_MAIL: /* mail back */ 15919381Seric case EM_BERKNET: /* do berknet error processing */ 15929381Seric case EM_WRITE: /* write back (or mail) */ 15939381Seric case EM_PRINT: /* print errors normally (default) */ 159458734Seric e->e_errormode = *val; 15958269Seric break; 15968269Seric } 15978269Seric break; 15988269Seric 15999049Seric case 'F': /* file mode */ 160017975Seric FileMode = atooct(val) & 0777; 16019049Seric break; 16029049Seric 16038269Seric case 'f': /* save Unix-style From lines on front */ 16049381Seric SaveFrom = atobool(val); 16058269Seric break; 16068269Seric 160753735Seric case 'G': /* match recipients against GECOS field */ 160853735Seric MatchGecos = atobool(val); 160953735Seric break; 161053735Seric 16118256Seric case 'g': /* default gid */ 161268481Seric g_opt: 161364133Seric if (isascii(*val) && isdigit(*val)) 161464133Seric DefGid = atoi(val); 161564133Seric else 161664133Seric { 161764133Seric register struct group *gr; 161864133Seric 161964133Seric DefGid = -1; 162064133Seric gr = getgrnam(val); 162164133Seric if (gr == NULL) 162268481Seric syserr("readcf: option %c: unknown group %s", 162368481Seric opt, val); 162464133Seric else 162564133Seric DefGid = gr->gr_gid; 162664133Seric } 16278256Seric break; 16288256Seric 16298256Seric case 'H': /* help file */ 16309381Seric if (val[0] == '\0') 16318269Seric HelpFile = "sendmail.hf"; 16329381Seric else 16339381Seric HelpFile = newstr(val); 16348256Seric break; 16358256Seric 163651305Seric case 'h': /* maximum hop count */ 163751305Seric MaxHopCount = atoi(val); 163851305Seric break; 163951305Seric 164035651Seric case 'I': /* use internet domain name server */ 164166334Seric #if NAMED_BIND 164257207Seric for (p = val; *p != 0; ) 164357207Seric { 164457207Seric bool clearmode; 164557207Seric char *q; 164657207Seric struct resolverflags *rfp; 164757207Seric 164857207Seric while (*p == ' ') 164957207Seric p++; 165057207Seric if (*p == '\0') 165157207Seric break; 165257207Seric clearmode = FALSE; 165357207Seric if (*p == '-') 165457207Seric clearmode = TRUE; 165557207Seric else if (*p != '+') 165657207Seric p--; 165757207Seric p++; 165857207Seric q = p; 165958050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 166057207Seric p++; 166157207Seric if (*p != '\0') 166257207Seric *p++ = '\0'; 166368759Seric if (strcasecmp(q, "HasWildcardMX") == 0) 166468759Seric { 166568759Seric NoMXforCanon = !clearmode; 166668759Seric continue; 166768759Seric } 166857207Seric for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 166957207Seric { 167057207Seric if (strcasecmp(q, rfp->rf_name) == 0) 167157207Seric break; 167257207Seric } 167364923Seric if (rfp->rf_name == NULL) 167464923Seric syserr("readcf: I option value %s unrecognized", q); 167564923Seric else if (clearmode) 167657207Seric _res.options &= ~rfp->rf_bits; 167757207Seric else 167857207Seric _res.options |= rfp->rf_bits; 167957207Seric } 168057207Seric if (tTd(8, 2)) 168168759Seric printf("_res.options = %x, HasWildcardMX = %d\n", 168268759Seric _res.options, !NoMXforCanon); 168357207Seric #else 168457207Seric usrerr("name server (I option) specified but BIND not compiled in"); 168557207Seric #endif 168635651Seric break; 168735651Seric 16888269Seric case 'i': /* ignore dot lines in message */ 16899381Seric IgnrDot = atobool(val); 16908269Seric break; 16918269Seric 169259730Seric case 'j': /* send errors in MIME (RFC 1341) format */ 169359730Seric SendMIMEErrors = atobool(val); 169459730Seric break; 169559730Seric 169657136Seric case 'J': /* .forward search path */ 169757136Seric ForwardPath = newstr(val); 169857136Seric break; 169957136Seric 170054967Seric case 'k': /* connection cache size */ 170154967Seric MaxMciCache = atoi(val); 170256215Seric if (MaxMciCache < 0) 170356215Seric MaxMciCache = 0; 170454967Seric break; 170554967Seric 170654967Seric case 'K': /* connection cache timeout */ 170758796Seric MciCacheTimeout = convtime(val, 'm'); 170854967Seric break; 170954967Seric 171061104Seric case 'l': /* use Errors-To: header */ 171161104Seric UseErrorsTo = atobool(val); 171261104Seric break; 171361104Seric 17148256Seric case 'L': /* log level */ 171564140Seric if (safe || LogLevel < atoi(val)) 171664140Seric LogLevel = atoi(val); 17178256Seric break; 17188256Seric 17198269Seric case 'M': /* define macro */ 172068267Seric p = newstr(&val[1]); 172168267Seric if (!safe) 172268267Seric cleanstrcpy(p, p, MAXNAME); 172368267Seric define(val[0], p, CurEnv); 172416878Seric sticky = FALSE; 17258269Seric break; 17268269Seric 17278269Seric case 'm': /* send to me too */ 17289381Seric MeToo = atobool(val); 17298269Seric break; 17308269Seric 173125820Seric case 'n': /* validate RHS in newaliases */ 173225820Seric CheckAliases = atobool(val); 173325820Seric break; 173425820Seric 173561104Seric /* 'N' available -- was "net name" */ 173661104Seric 173758851Seric case 'O': /* daemon options */ 173858851Seric setdaemonoptions(val); 173958851Seric break; 174058851Seric 17418269Seric case 'o': /* assume old style headers */ 17429381Seric if (atobool(val)) 17439341Seric CurEnv->e_flags |= EF_OLDSTYLE; 17449341Seric else 17459341Seric CurEnv->e_flags &= ~EF_OLDSTYLE; 17468269Seric break; 17478269Seric 174858082Seric case 'p': /* select privacy level */ 174958082Seric p = val; 175058082Seric for (;;) 175158082Seric { 175258082Seric register struct prival *pv; 175358082Seric extern struct prival PrivacyValues[]; 175458082Seric 175558082Seric while (isascii(*p) && (isspace(*p) || ispunct(*p))) 175658082Seric p++; 175758082Seric if (*p == '\0') 175858082Seric break; 175958082Seric val = p; 176058082Seric while (isascii(*p) && isalnum(*p)) 176158082Seric p++; 176258082Seric if (*p != '\0') 176358082Seric *p++ = '\0'; 176458082Seric 176558082Seric for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 176658082Seric { 176758082Seric if (strcasecmp(val, pv->pv_name) == 0) 176858082Seric break; 176958082Seric } 177058886Seric if (pv->pv_name == NULL) 177158886Seric syserr("readcf: Op line: %s unrecognized", val); 177258082Seric PrivacyFlags |= pv->pv_flag; 177358082Seric } 177468479Seric sticky = FALSE; 177558082Seric break; 177658082Seric 177724944Seric case 'P': /* postmaster copy address for returned mail */ 177824944Seric PostMasterCopy = newstr(val); 177924944Seric break; 178024944Seric 178124944Seric case 'q': /* slope of queue only function */ 178224944Seric QueueFactor = atoi(val); 178324944Seric break; 178424944Seric 17858256Seric case 'Q': /* queue directory */ 17869381Seric if (val[0] == '\0') 17878269Seric QueueDir = "mqueue"; 17889381Seric else 17899381Seric QueueDir = newstr(val); 179058789Seric if (RealUid != 0 && !safe) 179164718Seric Warn_Q_option = TRUE; 17928256Seric break; 17938256Seric 179458148Seric case 'R': /* don't prune routes */ 179558148Seric DontPruneRoutes = atobool(val); 179658148Seric break; 179758148Seric 17988256Seric case 'r': /* read timeout */ 179968481Seric if (subopt == NULL) 180068481Seric inittimeouts(val); 180168481Seric else 180268481Seric settimeout(subopt, val); 18038256Seric break; 18048256Seric 18058256Seric case 'S': /* status file */ 18069381Seric if (val[0] == '\0') 18078269Seric StatFile = "sendmail.st"; 18089381Seric else 18099381Seric StatFile = newstr(val); 18108256Seric break; 18118256Seric 18128265Seric case 's': /* be super safe, even if expensive */ 18139381Seric SuperSafe = atobool(val); 18148256Seric break; 18158256Seric 18168256Seric case 'T': /* queue timeout */ 181758737Seric p = strchr(val, '/'); 181858737Seric if (p != NULL) 181958737Seric { 182058737Seric *p++ = '\0'; 182168481Seric settimeout("queuewarn", p); 182258737Seric } 182368481Seric settimeout("queuereturn", val); 182454967Seric break; 18258256Seric 18268265Seric case 't': /* time zone name */ 182752106Seric TimeZoneSpec = newstr(val); 18288265Seric break; 18298265Seric 183050556Seric case 'U': /* location of user database */ 183151360Seric UdbSpec = newstr(val); 183250556Seric break; 183350556Seric 18348256Seric case 'u': /* set default uid */ 183568481Seric for (p = val; *p != '\0'; p++) 183668481Seric { 183768481Seric if (*p == '.' || *p == '/' || *p == ':') 183868481Seric { 183968481Seric *p++ = '\0'; 184068481Seric break; 184168481Seric } 184268481Seric } 184364133Seric if (isascii(*val) && isdigit(*val)) 184464133Seric DefUid = atoi(val); 184564133Seric else 184664133Seric { 184764133Seric register struct passwd *pw; 184864133Seric 184964133Seric DefUid = -1; 185068693Seric pw = sm_getpwnam(val); 185164133Seric if (pw == NULL) 185264133Seric syserr("readcf: option u: unknown user %s", val); 185364133Seric else 185468481Seric { 185564133Seric DefUid = pw->pw_uid; 185668481Seric DefGid = pw->pw_gid; 185768481Seric } 185864133Seric } 185940973Sbostic setdefuser(); 18608256Seric 186168481Seric /* handle the group if it is there */ 186268481Seric if (*p == '\0') 186368481Seric break; 186468481Seric val = p; 186568481Seric goto g_opt; 186668481Seric 186758851Seric case 'V': /* fallback MX host */ 186858851Seric FallBackMX = newstr(val); 186958851Seric break; 187058851Seric 18718269Seric case 'v': /* run in verbose mode */ 18729381Seric Verbose = atobool(val); 18738256Seric break; 18748256Seric 187563837Seric case 'w': /* if we are best MX, try host directly */ 187663837Seric TryNullMXList = atobool(val); 187763837Seric break; 187861104Seric 187961104Seric /* 'W' available -- was wizard password */ 188061104Seric 188114879Seric case 'x': /* load avg at which to auto-queue msgs */ 188214879Seric QueueLA = atoi(val); 188314879Seric break; 188414879Seric 188514879Seric case 'X': /* load avg at which to auto-reject connections */ 188614879Seric RefuseLA = atoi(val); 188714879Seric break; 188814879Seric 188924981Seric case 'y': /* work recipient factor */ 189024981Seric WkRecipFact = atoi(val); 189124981Seric break; 189224981Seric 189324981Seric case 'Y': /* fork jobs during queue runs */ 189424952Seric ForkQueueRuns = atobool(val); 189524952Seric break; 189624952Seric 189724981Seric case 'z': /* work message class factor */ 189824981Seric WkClassFact = atoi(val); 189924981Seric break; 190024981Seric 190124981Seric case 'Z': /* work time factor */ 190224981Seric WkTimeFact = atoi(val); 190324981Seric break; 190424981Seric 190568481Seric case O_QUEUESORTORD: /* queue sorting order */ 190668481Seric switch (*val) 190768481Seric { 190868481Seric case 'h': /* Host first */ 190968481Seric case 'H': 191068481Seric QueueSortOrder = QS_BYHOST; 191168481Seric break; 191268481Seric 191368481Seric case 'p': /* Priority order */ 191468481Seric case 'P': 191568481Seric QueueSortOrder = QS_BYPRIORITY; 191668481Seric break; 191768481Seric 191868481Seric default: 191968481Seric syserr("Invalid queue sort order \"%s\"", val); 192068481Seric } 192168481Seric break; 192268481Seric 192369401Seric case O_HOSTSFILE: /* pathname of /etc/hosts file */ 192469401Seric HostsFile = newstr(val); 192569401Seric break; 192669401Seric 192768481Seric case O_MQA: /* minimum queue age between deliveries */ 192868481Seric MinQueueAge = convtime(val, 'm'); 192968481Seric break; 193068481Seric 193168481Seric case O_MHSA: /* maximum age of cached host status */ 193268481Seric MaxHostStatAge = convtime(val, 'm'); 193368481Seric break; 193468481Seric 193568481Seric case O_DEFCHARSET: /* default character set for mimefying */ 193668481Seric DefaultCharSet = newstr(denlstring(val, TRUE, TRUE)); 193768481Seric break; 193868481Seric 193968481Seric case O_SSFILE: /* service switch file */ 194068481Seric ServiceSwitchFile = newstr(val); 194168481Seric break; 194268481Seric 194368481Seric case O_DIALDELAY: /* delay for dial-on-demand operation */ 194468481Seric DialDelay = convtime(val, 's'); 194568481Seric break; 194668481Seric 194768481Seric case O_NORCPTACTION: /* what to do if no recipient */ 194868481Seric if (strcasecmp(val, "none") == 0) 194968481Seric NoRecipientAction = NRA_NO_ACTION; 195068481Seric else if (strcasecmp(val, "add-to") == 0) 195168481Seric NoRecipientAction = NRA_ADD_TO; 195268481Seric else if (strcasecmp(val, "add-apparently-to") == 0) 195368481Seric NoRecipientAction = NRA_ADD_APPARENTLY_TO; 195468481Seric else if (strcasecmp(val, "add-bcc") == 0) 195568481Seric NoRecipientAction = NRA_ADD_BCC; 195668481Seric else if (strcasecmp(val, "add-to-undisclosed") == 0) 195768481Seric NoRecipientAction = NRA_ADD_TO_UNDISCLOSED; 195868481Seric else 195968481Seric syserr("Invalid NoRecipientAction: %s", val); 196068481Seric 196168490Seric case O_SAFEFILEENV: /* chroot() environ for writing to files */ 196268490Seric SafeFileEnv = newstr(val); 196368490Seric break; 196468490Seric 196568569Seric case O_MAXMSGSIZE: /* maximum message size */ 196669748Seric MaxMessageSize = atol(val); 196768569Seric break; 196868569Seric 196968756Seric case O_COLONOKINADDR: /* old style handling of colon addresses */ 197069748Seric ColonOkInAddr = atobool(val); 197168756Seric break; 197268756Seric 197369724Seric case O_MAXQUEUERUN: /* max # of jobs in a single queue run */ 197469748Seric MaxQueueRun = atol(val); 197569724Seric break; 197669724Seric 19778256Seric default: 197868481Seric if (tTd(37, 1)) 197968481Seric { 198068481Seric if (isascii(opt) && isprint(opt)) 198168481Seric printf("Warning: option %c unknown\n", opt); 198268481Seric else 198368481Seric printf("Warning: option 0x%x unknown\n", opt); 198468481Seric } 19858256Seric break; 19868256Seric } 198716878Seric if (sticky) 198816878Seric setbitn(opt, StickyOpt); 19898256Seric } 199010687Seric /* 199168481Seric ** SETCLASS -- set a string into a class 199210687Seric ** 199310687Seric ** Parameters: 199468481Seric ** class -- the class to put the string in. 199568481Seric ** str -- the string to enter 199610687Seric ** 199710687Seric ** Returns: 199810687Seric ** none. 199910687Seric ** 200010687Seric ** Side Effects: 200110687Seric ** puts the word into the symbol table. 200210687Seric */ 200310687Seric 200469748Seric void 200568481Seric setclass(class, str) 200610687Seric int class; 200768481Seric char *str; 200810687Seric { 200910687Seric register STAB *s; 201010687Seric 201157943Seric if (tTd(37, 8)) 201268481Seric printf("setclass(%c, %s)\n", class, str); 201368481Seric s = stab(str, ST_CLASS, ST_ENTER); 201410687Seric setbitn(class, s->s_class); 201510687Seric } 201653654Seric /* 201753654Seric ** MAKEMAPENTRY -- create a map entry 201853654Seric ** 201953654Seric ** Parameters: 202053654Seric ** line -- the config file line 202153654Seric ** 202253654Seric ** Returns: 202369774Seric ** A pointer to the map that has been created. 202469774Seric ** NULL if there was a syntax error. 202553654Seric ** 202653654Seric ** Side Effects: 202753654Seric ** Enters the map into the dictionary. 202853654Seric */ 202953654Seric 203069774Seric MAP * 203153654Seric makemapentry(line) 203253654Seric char *line; 203353654Seric { 203453654Seric register char *p; 203553654Seric char *mapname; 203653654Seric char *classname; 203764078Seric register STAB *s; 203853654Seric STAB *class; 203953654Seric 204058050Seric for (p = line; isascii(*p) && isspace(*p); p++) 204153654Seric continue; 204258050Seric if (!(isascii(*p) && isalnum(*p))) 204353654Seric { 204453654Seric syserr("readcf: config K line: no map name"); 204569774Seric return NULL; 204653654Seric } 204753654Seric 204853654Seric mapname = p; 204968481Seric while ((isascii(*++p) && isalnum(*p)) || *p == '.') 205053654Seric continue; 205153654Seric if (*p != '\0') 205253654Seric *p++ = '\0'; 205358050Seric while (isascii(*p) && isspace(*p)) 205453654Seric p++; 205558050Seric if (!(isascii(*p) && isalnum(*p))) 205653654Seric { 205753654Seric syserr("readcf: config K line, map %s: no map class", mapname); 205869774Seric return NULL; 205953654Seric } 206053654Seric classname = p; 206158050Seric while (isascii(*++p) && isalnum(*p)) 206253654Seric continue; 206353654Seric if (*p != '\0') 206453654Seric *p++ = '\0'; 206558050Seric while (isascii(*p) && isspace(*p)) 206653654Seric p++; 206753654Seric 206853654Seric /* look up the class */ 206953654Seric class = stab(classname, ST_MAPCLASS, ST_FIND); 207053654Seric if (class == NULL) 207153654Seric { 207253654Seric syserr("readcf: map %s: class %s not available", mapname, classname); 207369774Seric return NULL; 207453654Seric } 207553654Seric 207653654Seric /* enter the map */ 207764078Seric s = stab(mapname, ST_MAP, ST_ENTER); 207864078Seric s->s_map.map_class = &class->s_mapclass; 207964078Seric s->s_map.map_mname = newstr(mapname); 208053654Seric 208164078Seric if (class->s_mapclass.map_parse(&s->s_map, p)) 208264078Seric s->s_map.map_mflags |= MF_VALID; 208364078Seric 208464078Seric if (tTd(37, 5)) 208564078Seric { 208664078Seric printf("map %s, class %s, flags %x, file %s,\n", 208764078Seric s->s_map.map_mname, s->s_map.map_class->map_cname, 208864078Seric s->s_map.map_mflags, 208964078Seric s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 209064078Seric printf("\tapp %s, domain %s, rebuild %s\n", 209164078Seric s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 209264078Seric s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 209364078Seric s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 209464078Seric } 209569774Seric 209669774Seric return &s->s_map; 209753654Seric } 209858112Seric /* 209969783Seric ** STRTORWSET -- convert string to rewriting set number 210069783Seric ** 210169783Seric ** Parameters: 210269783Seric ** p -- the pointer to the string to decode. 210369783Seric ** endp -- if set, store the trailing delimiter here. 210469783Seric ** stabmode -- ST_ENTER to create this entry, ST_FIND if 210569783Seric ** it must already exist. 210669783Seric ** 210769783Seric ** Returns: 210869783Seric ** The appropriate ruleset number. 210969783Seric ** -1 if it is not valid (error already printed) 211069783Seric */ 211169783Seric 211269783Seric int 211369783Seric strtorwset(p, endp, stabmode) 211469783Seric char *p; 211569783Seric char **endp; 211669783Seric int stabmode; 211769783Seric { 211869783Seric int ruleset; 211969783Seric static int nextruleset = MAXRWSETS; 212069783Seric 212169783Seric while (isascii(*p) && isspace(*p)) 212269783Seric p++; 212369783Seric if (!isascii(*p)) 212469783Seric { 212569783Seric syserr("invalid ruleset name: \"%.20s\"", p); 212669783Seric return -1; 212769783Seric } 212869783Seric if (isdigit(*p)) 212969783Seric { 213069783Seric ruleset = strtol(p, endp, 10); 213169783Seric if (ruleset >= MAXRWSETS / 2 || ruleset < 0) 213269783Seric { 213369783Seric syserr("bad ruleset %d (%d max)", 213469783Seric ruleset, MAXRWSETS / 2); 213569783Seric ruleset = -1; 213669783Seric } 213769783Seric } 213869783Seric else 213969783Seric { 214069783Seric STAB *s; 214169783Seric char delim; 214269783Seric char *q; 214369783Seric 214469783Seric q = p; 214569783Seric while (*p != '\0' && isascii(*p) && 214669783Seric (isalnum(*p) || strchr("-_$", *p) != NULL)) 214769783Seric p++; 214869783Seric while (isascii(*p) && isspace(*p)) 214969783Seric *p++ = '\0'; 215069783Seric delim = *p; 215169783Seric if (delim != '\0') 215269783Seric *p = '\0'; 215369783Seric s = stab(q, ST_RULESET, stabmode); 215469783Seric if (delim != '\0') 215569783Seric *p = delim; 215669783Seric 215769783Seric if (s == NULL) 215869783Seric { 215969783Seric syserr("unknown ruleset %s", q); 216069783Seric return -1; 216169783Seric } 216269783Seric 216369783Seric if (stabmode == ST_ENTER && delim == '=') 216469783Seric { 216569783Seric ruleset = strtol(p, endp, 10); 216669783Seric if (ruleset >= MAXRWSETS / 2 || ruleset < 0) 216769783Seric { 216869783Seric syserr("bad ruleset %s = %d (%d max)", 216969783Seric q, ruleset, MAXRWSETS / 2); 217069783Seric ruleset = -1; 217169783Seric } 217269783Seric } 217369783Seric else 217469783Seric { 217569783Seric if (endp != NULL) 217669783Seric *endp = p; 217769783Seric if (s->s_ruleset > 0) 217869783Seric ruleset = s->s_ruleset; 217969783Seric else if ((ruleset = --nextruleset) < MAXRWSETS / 2) 218069783Seric { 218169783Seric syserr("%s: too many named rulesets (%d max)", 218269783Seric q, MAXRWSETS / 2); 218369783Seric ruleset = -1; 218469783Seric } 218569783Seric } 218669783Seric if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset) 218769783Seric { 218869783Seric syserr("%s: ruleset changed value (old %d, new %d)", 218969783Seric q, ruleset, s->s_ruleset); 219069783Seric ruleset = s->s_ruleset; 219169783Seric } 219269783Seric else if (ruleset > 0) 219369783Seric { 219469783Seric s->s_ruleset = ruleset; 219569783Seric } 219669783Seric } 219769783Seric return ruleset; 219869783Seric } 219969783Seric /* 220068481Seric ** INITTIMEOUTS -- parse and set timeout values 220158112Seric ** 220258112Seric ** Parameters: 220358112Seric ** val -- a pointer to the values. If NULL, do initial 220458112Seric ** settings. 220558112Seric ** 220658112Seric ** Returns: 220758112Seric ** none. 220858112Seric ** 220958112Seric ** Side Effects: 221058112Seric ** Initializes the TimeOuts structure 221158112Seric */ 221258112Seric 221364255Seric #define SECONDS 221458112Seric #define MINUTES * 60 221558112Seric #define HOUR * 3600 221658112Seric 221769748Seric void 221868481Seric inittimeouts(val) 221958112Seric register char *val; 222058112Seric { 222158112Seric register char *p; 222258671Seric extern time_t convtime(); 222358112Seric 222458112Seric if (val == NULL) 222558112Seric { 222658112Seric TimeOuts.to_initial = (time_t) 5 MINUTES; 222758112Seric TimeOuts.to_helo = (time_t) 5 MINUTES; 222858112Seric TimeOuts.to_mail = (time_t) 10 MINUTES; 222958112Seric TimeOuts.to_rcpt = (time_t) 1 HOUR; 223058112Seric TimeOuts.to_datainit = (time_t) 5 MINUTES; 223158112Seric TimeOuts.to_datablock = (time_t) 1 HOUR; 223258112Seric TimeOuts.to_datafinal = (time_t) 1 HOUR; 223358112Seric TimeOuts.to_rset = (time_t) 5 MINUTES; 223458112Seric TimeOuts.to_quit = (time_t) 2 MINUTES; 223558112Seric TimeOuts.to_nextcommand = (time_t) 1 HOUR; 223658112Seric TimeOuts.to_miscshort = (time_t) 2 MINUTES; 223768481Seric #if IDENTPROTO 223864255Seric TimeOuts.to_ident = (time_t) 30 SECONDS; 223968481Seric #else 224068481Seric TimeOuts.to_ident = (time_t) 0 SECONDS; 224168481Seric #endif 224268481Seric TimeOuts.to_fileopen = (time_t) 60 SECONDS; 224358112Seric return; 224458112Seric } 224558112Seric 224658112Seric for (;; val = p) 224758112Seric { 224858112Seric while (isascii(*val) && isspace(*val)) 224958112Seric val++; 225058112Seric if (*val == '\0') 225158112Seric break; 225258112Seric for (p = val; *p != '\0' && *p != ','; p++) 225358112Seric continue; 225458112Seric if (*p != '\0') 225558112Seric *p++ = '\0'; 225658112Seric 225758112Seric if (isascii(*val) && isdigit(*val)) 225858112Seric { 225958112Seric /* old syntax -- set everything */ 226058796Seric TimeOuts.to_mail = convtime(val, 'm'); 226158112Seric TimeOuts.to_rcpt = TimeOuts.to_mail; 226258112Seric TimeOuts.to_datainit = TimeOuts.to_mail; 226358112Seric TimeOuts.to_datablock = TimeOuts.to_mail; 226458112Seric TimeOuts.to_datafinal = TimeOuts.to_mail; 226558112Seric TimeOuts.to_nextcommand = TimeOuts.to_mail; 226658112Seric continue; 226758112Seric } 226858112Seric else 226958112Seric { 227068481Seric register char *q = strchr(val, ':'); 227158112Seric 227268481Seric if (q == NULL && (q = strchr(val, '=')) == NULL) 227358112Seric { 227458112Seric /* syntax error */ 227558112Seric continue; 227658112Seric } 227758112Seric *q++ = '\0'; 227868481Seric settimeout(val, q); 227968481Seric } 228068481Seric } 228168481Seric } 228268481Seric /* 228368481Seric ** SETTIMEOUT -- set an individual timeout 228468481Seric ** 228568481Seric ** Parameters: 228668481Seric ** name -- the name of the timeout. 228768481Seric ** val -- the value of the timeout. 228868481Seric ** 228968481Seric ** Returns: 229068481Seric ** none. 229168481Seric */ 229258112Seric 229369748Seric void 229468481Seric settimeout(name, val) 229568481Seric char *name; 229668481Seric char *val; 229768481Seric { 229868481Seric register char *p; 229968481Seric time_t to; 230068481Seric extern time_t convtime(); 230168481Seric 230268481Seric to = convtime(val, 'm'); 230368481Seric p = strchr(name, '.'); 230468481Seric if (p != NULL) 230568481Seric *p++ = '\0'; 230668481Seric 230768481Seric if (strcasecmp(name, "initial") == 0) 230868481Seric TimeOuts.to_initial = to; 230968481Seric else if (strcasecmp(name, "mail") == 0) 231068481Seric TimeOuts.to_mail = to; 231168481Seric else if (strcasecmp(name, "rcpt") == 0) 231268481Seric TimeOuts.to_rcpt = to; 231368481Seric else if (strcasecmp(name, "datainit") == 0) 231468481Seric TimeOuts.to_datainit = to; 231568481Seric else if (strcasecmp(name, "datablock") == 0) 231668481Seric TimeOuts.to_datablock = to; 231768481Seric else if (strcasecmp(name, "datafinal") == 0) 231868481Seric TimeOuts.to_datafinal = to; 231968481Seric else if (strcasecmp(name, "command") == 0) 232068481Seric TimeOuts.to_nextcommand = to; 232168481Seric else if (strcasecmp(name, "rset") == 0) 232268481Seric TimeOuts.to_rset = to; 232368481Seric else if (strcasecmp(name, "helo") == 0) 232468481Seric TimeOuts.to_helo = to; 232568481Seric else if (strcasecmp(name, "quit") == 0) 232668481Seric TimeOuts.to_quit = to; 232768481Seric else if (strcasecmp(name, "misc") == 0) 232868481Seric TimeOuts.to_miscshort = to; 232968481Seric else if (strcasecmp(name, "ident") == 0) 233068481Seric TimeOuts.to_ident = to; 233168481Seric else if (strcasecmp(name, "fileopen") == 0) 233268481Seric TimeOuts.to_fileopen = to; 233368481Seric else if (strcasecmp(name, "queuewarn") == 0) 233468481Seric { 233568481Seric to = convtime(val, 'h'); 233668481Seric if (p == NULL || strcmp(p, "*") == 0) 233768481Seric { 233868481Seric TimeOuts.to_q_warning[TOC_NORMAL] = to; 233968481Seric TimeOuts.to_q_warning[TOC_URGENT] = to; 234068481Seric TimeOuts.to_q_warning[TOC_NONURGENT] = to; 234158112Seric } 234268481Seric else if (strcasecmp(p, "normal") == 0) 234368481Seric TimeOuts.to_q_warning[TOC_NORMAL] = to; 234468481Seric else if (strcasecmp(p, "urgent") == 0) 234568481Seric TimeOuts.to_q_warning[TOC_URGENT] = to; 234668481Seric else if (strcasecmp(p, "non-urgent") == 0) 234768481Seric TimeOuts.to_q_warning[TOC_NONURGENT] = to; 234868481Seric else 234968481Seric syserr("settimeout: invalid queuewarn subtimeout %s", p); 235058112Seric } 235168481Seric else if (strcasecmp(name, "queuereturn") == 0) 235268481Seric { 235368481Seric to = convtime(val, 'd'); 235468481Seric if (p == NULL || strcmp(p, "*") == 0) 235568481Seric { 235668481Seric TimeOuts.to_q_return[TOC_NORMAL] = to; 235768481Seric TimeOuts.to_q_return[TOC_URGENT] = to; 235868481Seric TimeOuts.to_q_return[TOC_NONURGENT] = to; 235968481Seric } 236068481Seric else if (strcasecmp(p, "normal") == 0) 236168481Seric TimeOuts.to_q_return[TOC_NORMAL] = to; 236268481Seric else if (strcasecmp(p, "urgent") == 0) 236368481Seric TimeOuts.to_q_return[TOC_URGENT] = to; 236468481Seric else if (strcasecmp(p, "non-urgent") == 0) 236568481Seric TimeOuts.to_q_return[TOC_NONURGENT] = to; 236668481Seric else 236768481Seric syserr("settimeout: invalid queuereturn subtimeout %s", p); 236868481Seric } 236968481Seric else 237068481Seric syserr("settimeout: invalid timeout %s", name); 237158112Seric } 2372