xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 68105)
122709Sdist /*
234921Sbostic  * Copyright (c) 1983 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*68105Seric static char sccsid[] = "@(#)readcf.c	8.55 (Berkeley) 12/29/94";
1133731Sbostic #endif /* not lint */
1222709Sdist 
133313Seric # include "sendmail.h"
1464133Seric # include <pwd.h>
1564133Seric # include <grp.h>
1666334Seric #if NAMED_BIND
1757207Seric # include <resolv.h>
1857207Seric #endif
193308Seric 
203308Seric /*
213308Seric **  READCF -- read control file.
223308Seric **
233308Seric **	This routine reads the control file and builds the internal
243308Seric **	form.
253308Seric **
264432Seric **	The file is formatted as a sequence of lines, each taken
274432Seric **	atomically.  The first character of each line describes how
284432Seric **	the line is to be interpreted.  The lines are:
294432Seric **		Dxval		Define macro x to have value val.
304432Seric **		Cxword		Put word into class x.
314432Seric **		Fxfile [fmt]	Read file for lines to put into
324432Seric **				class x.  Use scanf string 'fmt'
334432Seric **				or "%s" if not present.  Fmt should
344432Seric **				only produce one string-valued result.
354432Seric **		Hname: value	Define header with field-name 'name'
364432Seric **				and value as specified; this will be
374432Seric **				macro expanded immediately before
384432Seric **				use.
394432Seric **		Sn		Use rewriting set n.
404432Seric **		Rlhs rhs	Rewrite addresses that match lhs to
414432Seric **				be rhs.
4224944Seric **		Mn arg=val...	Define mailer.  n is the internal name.
4324944Seric **				Args specify mailer parameters.
448252Seric **		Oxvalue		Set option x to value.
458252Seric **		Pname=value	Set precedence name to value.
4664718Seric **		Vversioncode[/vendorcode]
4764718Seric **				Version level/vendor name of
4864718Seric **				configuration syntax.
4953654Seric **		Kmapname mapclass arguments....
5053654Seric **				Define keyed lookup of a given class.
5153654Seric **				Arguments are class dependent.
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 
6655012Seric readcf(cfname, safe, e)
673308Seric 	char *cfname;
6854973Seric 	bool safe;
6955012Seric 	register ENVELOPE *e;
703308Seric {
713308Seric 	FILE *cf;
728547Seric 	int ruleset = 0;
738547Seric 	char *q;
749350Seric 	struct rewrite *rwp = NULL;
7557135Seric 	char *bp;
7664718Seric 	auto char *ep;
7757589Seric 	int nfuzzy;
7864133Seric 	char *file;
7964133Seric 	bool optional;
8067769Seric 	int mid;
813308Seric 	char buf[MAXLINE];
823308Seric 	register char *p;
833308Seric 	extern char **copyplist();
8452647Seric 	struct stat statb;
855909Seric 	char exbuf[MAXLINE];
8665066Seric 	char pvpbuf[MAXLINE + MAXATOM];
8767826Seric 	static char *null_list[1] = { NULL };
8810709Seric 	extern char *munchstring();
8953654Seric 	extern void makemapentry();
903308Seric 
9152647Seric 	FileName = cfname;
9252647Seric 	LineNumber = 0;
9352647Seric 
943308Seric 	cf = fopen(cfname, "r");
953308Seric 	if (cf == NULL)
963308Seric 	{
9752647Seric 		syserr("cannot open");
983308Seric 		exit(EX_OSFILE);
993308Seric 	}
1003308Seric 
10152647Seric 	if (fstat(fileno(cf), &statb) < 0)
10252647Seric 	{
10352647Seric 		syserr("cannot fstat");
10452647Seric 		exit(EX_OSFILE);
10552647Seric 	}
10652647Seric 
10752647Seric 	if (!S_ISREG(statb.st_mode))
10852647Seric 	{
10952647Seric 		syserr("not a plain file");
11052647Seric 		exit(EX_OSFILE);
11152647Seric 	}
11252647Seric 
11352647Seric 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
11452647Seric 	{
11553037Seric 		if (OpMode == MD_DAEMON || OpMode == MD_FREEZE)
11653037Seric 			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
11753037Seric 				FileName);
11853037Seric #ifdef LOG
11953037Seric 		if (LogLevel > 0)
12053037Seric 			syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
12153037Seric 				FileName);
12253037Seric #endif
12352647Seric 	}
12452647Seric 
12559254Seric #ifdef XLA
12659254Seric 	xla_zero();
12759254Seric #endif
12859254Seric 
12957135Seric 	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
1303308Seric 	{
13157135Seric 		if (bp[0] == '#')
13257135Seric 		{
13357135Seric 			if (bp != buf)
13457135Seric 				free(bp);
13552637Seric 			continue;
13657135Seric 		}
13752637Seric 
13867769Seric 		/* do macro expansion mappings */
13957135Seric 		for (p = bp; *p != '\0'; p++)
14016157Seric 		{
14157135Seric 			if (*p == '#' && p > bp && ConfigLevel >= 3)
14252647Seric 			{
14352647Seric 				/* this is an on-line comment */
14452647Seric 				register char *e;
14552647Seric 
14658050Seric 				switch (*--p & 0377)
14752647Seric 				{
14858050Seric 				  case MACROEXPAND:
14952647Seric 					/* it's from $# -- let it go through */
15052647Seric 					p++;
15152647Seric 					break;
15252647Seric 
15352647Seric 				  case '\\':
15452647Seric 					/* it's backslash escaped */
15552647Seric 					(void) strcpy(p, p + 1);
15652647Seric 					break;
15752647Seric 
15852647Seric 				  default:
15952647Seric 					/* delete preceeding white space */
16058050Seric 					while (isascii(*p) && isspace(*p) && p > bp)
16152647Seric 						p--;
16256795Seric 					if ((e = strchr(++p, '\n')) != NULL)
16352647Seric 						(void) strcpy(p, e);
16452647Seric 					else
16552647Seric 						p[0] = p[1] = '\0';
16652647Seric 					break;
16752647Seric 				}
16852647Seric 				continue;
16952647Seric 			}
17052647Seric 
17167769Seric 			if (*p != '$' || p[1] == '\0')
17216157Seric 				continue;
17316157Seric 
17416157Seric 			if (p[1] == '$')
17516157Seric 			{
17616157Seric 				/* actual dollar sign.... */
17723111Seric 				(void) strcpy(p, p + 1);
17816157Seric 				continue;
17916157Seric 			}
18016157Seric 
18116157Seric 			/* convert to macro expansion character */
18267769Seric 			*p++ = MACROEXPAND;
18367769Seric 
18467769Seric 			/* convert macro name to code */
18567769Seric 			*p = macid(p, &ep);
18667769Seric 			if (ep != p)
18767769Seric 				strcpy(p + 1, ep);
18816157Seric 		}
18916157Seric 
19016157Seric 		/* interpret this line */
19164718Seric 		errno = 0;
19257135Seric 		switch (bp[0])
1933308Seric 		{
1943308Seric 		  case '\0':
1953308Seric 		  case '#':		/* comment */
1963308Seric 			break;
1973308Seric 
1983308Seric 		  case 'R':		/* rewriting rule */
19957135Seric 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
2003308Seric 				continue;
2013308Seric 
2023308Seric 			if (*p == '\0')
2035909Seric 			{
20465821Seric 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
2055909Seric 				break;
2065909Seric 			}
2075909Seric 
2085909Seric 			/* allocate space for the rule header */
2095909Seric 			if (rwp == NULL)
2105909Seric 			{
2115909Seric 				RewriteRules[ruleset] = rwp =
2125909Seric 					(struct rewrite *) xalloc(sizeof *rwp);
2135909Seric 			}
2143308Seric 			else
2153308Seric 			{
2165909Seric 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
2175909Seric 				rwp = rwp->r_next;
2185909Seric 			}
2195909Seric 			rwp->r_next = NULL;
2203308Seric 
2215909Seric 			/* expand and save the LHS */
2225909Seric 			*p = '\0';
22357135Seric 			expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e);
22465066Seric 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
22565066Seric 					     sizeof pvpbuf, NULL);
22657589Seric 			nfuzzy = 0;
2275909Seric 			if (rwp->r_lhs != NULL)
22857589Seric 			{
22957589Seric 				register char **ap;
23057589Seric 
2315909Seric 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
23257589Seric 
23357589Seric 				/* count the number of fuzzy matches in LHS */
23457589Seric 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
23557589Seric 				{
23658148Seric 					char *botch;
23758148Seric 
23858148Seric 					botch = NULL;
23958050Seric 					switch (**ap & 0377)
24057589Seric 					{
24157589Seric 					  case MATCHZANY:
24257589Seric 					  case MATCHANY:
24357589Seric 					  case MATCHONE:
24457589Seric 					  case MATCHCLASS:
24557589Seric 					  case MATCHNCLASS:
24657589Seric 						nfuzzy++;
24758148Seric 						break;
24858148Seric 
24958148Seric 					  case MATCHREPL:
25058148Seric 						botch = "$0-$9";
25158148Seric 						break;
25258148Seric 
25358148Seric 					  case CANONNET:
25458148Seric 						botch = "$#";
25558148Seric 						break;
25658148Seric 
25758148Seric 					  case CANONUSER:
25858148Seric 						botch = "$:";
25958148Seric 						break;
26058148Seric 
26158148Seric 					  case CALLSUBR:
26258148Seric 						botch = "$>";
26358148Seric 						break;
26458148Seric 
26558148Seric 					  case CONDIF:
26658148Seric 						botch = "$?";
26758148Seric 						break;
26858148Seric 
26958148Seric 					  case CONDELSE:
27058148Seric 						botch = "$|";
27158148Seric 						break;
27258148Seric 
27358148Seric 					  case CONDFI:
27458148Seric 						botch = "$.";
27558148Seric 						break;
27658148Seric 
27758148Seric 					  case HOSTBEGIN:
27858148Seric 						botch = "$[";
27958148Seric 						break;
28058148Seric 
28158148Seric 					  case HOSTEND:
28258148Seric 						botch = "$]";
28358148Seric 						break;
28458148Seric 
28558148Seric 					  case LOOKUPBEGIN:
28658148Seric 						botch = "$(";
28758148Seric 						break;
28858148Seric 
28958148Seric 					  case LOOKUPEND:
29058148Seric 						botch = "$)";
29158148Seric 						break;
29257589Seric 					}
29358148Seric 					if (botch != NULL)
29458148Seric 						syserr("Inappropriate use of %s on LHS",
29558148Seric 							botch);
29657589Seric 				}
29757589Seric 			}
29856678Seric 			else
29967826Seric 			{
30056678Seric 				syserr("R line: null LHS");
30167826Seric 				rwp->r_lhs = null_list;
30267826Seric 			}
3035909Seric 
3045909Seric 			/* expand and save the RHS */
3055909Seric 			while (*++p == '\t')
3065909Seric 				continue;
3077231Seric 			q = p;
3087231Seric 			while (*p != '\0' && *p != '\t')
3097231Seric 				p++;
3107231Seric 			*p = '\0';
31155012Seric 			expand(q, exbuf, &exbuf[sizeof exbuf], e);
31265066Seric 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
31365066Seric 					     sizeof pvpbuf, NULL);
3145909Seric 			if (rwp->r_rhs != NULL)
31557589Seric 			{
31657589Seric 				register char **ap;
31757589Seric 
3185909Seric 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
31957589Seric 
32057589Seric 				/* check no out-of-bounds replacements */
32157589Seric 				nfuzzy += '0';
32257589Seric 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
32357589Seric 				{
32458148Seric 					char *botch;
32558148Seric 
32658148Seric 					botch = NULL;
32758148Seric 					switch (**ap & 0377)
32857589Seric 					{
32958148Seric 					  case MATCHREPL:
33058148Seric 						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
33158148Seric 						{
33258148Seric 							syserr("replacement $%c out of bounds",
33358148Seric 								(*ap)[1]);
33458148Seric 						}
33558148Seric 						break;
33658148Seric 
33758148Seric 					  case MATCHZANY:
33858148Seric 						botch = "$*";
33958148Seric 						break;
34058148Seric 
34158148Seric 					  case MATCHANY:
34258148Seric 						botch = "$+";
34358148Seric 						break;
34458148Seric 
34558148Seric 					  case MATCHONE:
34658148Seric 						botch = "$-";
34758148Seric 						break;
34858148Seric 
34958148Seric 					  case MATCHCLASS:
35058148Seric 						botch = "$=";
35158148Seric 						break;
35258148Seric 
35358148Seric 					  case MATCHNCLASS:
35458148Seric 						botch = "$~";
35558148Seric 						break;
35657589Seric 					}
35758148Seric 					if (botch != NULL)
35858148Seric 						syserr("Inappropriate use of %s on RHS",
35958148Seric 							botch);
36057589Seric 				}
36157589Seric 			}
36256678Seric 			else
36367826Seric 			{
36456678Seric 				syserr("R line: null RHS");
36567826Seric 				rwp->r_rhs = null_list;
36667826Seric 			}
3673308Seric 			break;
3683308Seric 
3694072Seric 		  case 'S':		/* select rewriting set */
37064440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
37164440Seric 				continue;
37264440Seric 			if (!isascii(*p) || !isdigit(*p))
37364440Seric 			{
37464440Seric 				syserr("invalid argument to S line: \"%.20s\"",
37564440Seric 					&bp[1]);
37664440Seric 				break;
37764440Seric 			}
37864440Seric 			ruleset = atoi(p);
3798056Seric 			if (ruleset >= MAXRWSETS || ruleset < 0)
3808056Seric 			{
3819381Seric 				syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
3828056Seric 				ruleset = 0;
3838056Seric 			}
3844072Seric 			rwp = NULL;
3854072Seric 			break;
3864072Seric 
3873308Seric 		  case 'D':		/* macro definition */
38867769Seric 			mid = macid(&bp[1], &ep);
38967769Seric 			p = munchstring(ep, NULL);
39067769Seric 			define(mid, newstr(p), e);
3913308Seric 			break;
3923308Seric 
3933387Seric 		  case 'H':		/* required header line */
39457135Seric 			(void) chompheader(&bp[1], TRUE, e);
3953387Seric 			break;
3963387Seric 
3974061Seric 		  case 'C':		/* word class */
3984432Seric 			/* scan the list of words and set class for all */
39967769Seric 			mid = macid(&bp[1], &ep);
40067769Seric 			expand(ep, exbuf, &exbuf[sizeof exbuf], e);
40164121Seric 			for (p = exbuf; *p != '\0'; )
4024061Seric 			{
4034061Seric 				register char *wd;
4044061Seric 				char delim;
4054061Seric 
40658050Seric 				while (*p != '\0' && isascii(*p) && isspace(*p))
4074061Seric 					p++;
4084061Seric 				wd = p;
40958050Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4104061Seric 					p++;
4114061Seric 				delim = *p;
4124061Seric 				*p = '\0';
4134061Seric 				if (wd[0] != '\0')
41467769Seric 					setclass(mid, wd);
4154061Seric 				*p = delim;
4164061Seric 			}
4174061Seric 			break;
4184061Seric 
41959272Seric 		  case 'F':		/* word class from file */
42067769Seric 			mid = macid(&bp[1], &ep);
42167769Seric 			for (p = ep; isascii(*p) && isspace(*p); )
42264133Seric 				p++;
42364133Seric 			if (p[0] == '-' && p[1] == 'o')
42464133Seric 			{
42564133Seric 				optional = TRUE;
42664133Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
42764133Seric 					p++;
42864133Seric 				while (isascii(*p) && isspace(*p))
42967615Seric 					p++;
43064133Seric 			}
43164133Seric 			else
43264133Seric 				optional = FALSE;
43364133Seric 			file = p;
43464133Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
43564133Seric 				p++;
43659272Seric 			if (*p == '\0')
43759272Seric 				p = "%s";
43859272Seric 			else
43959272Seric 			{
44059272Seric 				*p = '\0';
44159272Seric 				while (isascii(*++p) && isspace(*p))
44259272Seric 					continue;
44359272Seric 			}
44464133Seric 			fileclass(bp[1], file, p, safe, optional);
44559272Seric 			break;
44659272Seric 
44759156Seric #ifdef XLA
44859156Seric 		  case 'L':		/* extended load average description */
44959156Seric 			xla_init(&bp[1]);
45059156Seric 			break;
45159156Seric #endif
45259156Seric 
4534096Seric 		  case 'M':		/* define mailer */
45457135Seric 			makemailer(&bp[1]);
4554096Seric 			break;
4564096Seric 
4578252Seric 		  case 'O':		/* set option */
45858734Seric 			setoption(bp[1], &bp[2], safe, FALSE, e);
4598252Seric 			break;
4608252Seric 
4618252Seric 		  case 'P':		/* set precedence */
4628252Seric 			if (NumPriorities >= MAXPRIORITIES)
4638252Seric 			{
4648547Seric 				toomany('P', MAXPRIORITIES);
4658252Seric 				break;
4668252Seric 			}
46757135Seric 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
4688252Seric 				continue;
4698252Seric 			if (*p == '\0')
4708252Seric 				goto badline;
4718252Seric 			*p = '\0';
47257135Seric 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
4738252Seric 			Priorities[NumPriorities].pri_val = atoi(++p);
4748252Seric 			NumPriorities++;
4758252Seric 			break;
4768252Seric 
4778547Seric 		  case 'T':		/* trusted user(s) */
47858161Seric 			/* this option is obsolete, but will be ignored */
4798547Seric 			break;
4808547Seric 
48152645Seric 		  case 'V':		/* configuration syntax version */
48264440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
48364440Seric 				continue;
48464440Seric 			if (!isascii(*p) || !isdigit(*p))
48564440Seric 			{
48664440Seric 				syserr("invalid argument to V line: \"%.20s\"",
48764440Seric 					&bp[1]);
48864440Seric 				break;
48964440Seric 			}
49064718Seric 			ConfigLevel = strtol(p, &ep, 10);
49164279Seric 			if (ConfigLevel >= 5)
49264279Seric 			{
49364279Seric 				/* level 5 configs have short name in $w */
49464279Seric 				p = macvalue('w', e);
49564279Seric 				if (p != NULL && (p = strchr(p, '.')) != NULL)
49664279Seric 					*p = '\0';
49764279Seric 			}
49864718Seric 			if (*ep++ == '/')
49964718Seric 			{
50064718Seric 				/* extract vendor code */
50164718Seric 				for (p = ep; isascii(*p) && isalpha(*p); )
50264718Seric 					p++;
50364718Seric 				*p = '\0';
50464718Seric 
50564718Seric 				if (!setvendor(ep))
50664718Seric 					syserr("invalid V line vendor code: \"%s\"",
50764718Seric 						ep);
50864718Seric 			}
50952645Seric 			break;
51052645Seric 
51153654Seric 		  case 'K':
51257135Seric 			makemapentry(&bp[1]);
51353654Seric 			break;
51453654Seric 
5153308Seric 		  default:
5164061Seric 		  badline:
51757135Seric 			syserr("unknown control line \"%s\"", bp);
5183308Seric 		}
51957135Seric 		if (bp != buf)
52057135Seric 			free(bp);
5213308Seric 	}
52252637Seric 	if (ferror(cf))
52352637Seric 	{
52452647Seric 		syserr("I/O read error", cfname);
52552637Seric 		exit(EX_OSFILE);
52652637Seric 	}
52752637Seric 	fclose(cf);
5289381Seric 	FileName = NULL;
52956836Seric 
53067730Seric 	/* initialize host maps from local service tables */
53167730Seric 	inithostmaps();
53267905Seric 
53367905Seric 	/* determine if we need to do special name-server frotz */
53467905Seric 	{
53567905Seric 		int nmaps;
53667905Seric 		char *maptype[MAXMAPSTACK];
53767905Seric 		short mapreturn[MAXMAPACTIONS];
53867905Seric 
53967905Seric 		nmaps = switch_map_find("hosts", maptype, mapreturn);
54067905Seric 		UseNameServer = FALSE;
54167905Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
54267905Seric 		{
54367905Seric 			register int mapno;
54467905Seric 
54567905Seric 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
54667905Seric 			{
54767905Seric 				if (strcmp(maptype[mapno], "dns") == 0)
54867905Seric 					UseNameServer = TRUE;
54967905Seric 			}
55067905Seric 		}
55167905Seric 	}
5524096Seric }
5534096Seric /*
5548547Seric **  TOOMANY -- signal too many of some option
5558547Seric **
5568547Seric **	Parameters:
5578547Seric **		id -- the id of the error line
5588547Seric **		maxcnt -- the maximum possible values
5598547Seric **
5608547Seric **	Returns:
5618547Seric **		none.
5628547Seric **
5638547Seric **	Side Effects:
5648547Seric **		gives a syserr.
5658547Seric */
5668547Seric 
5678547Seric toomany(id, maxcnt)
5688547Seric 	char id;
5698547Seric 	int maxcnt;
5708547Seric {
5719381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
5728547Seric }
5738547Seric /*
5744432Seric **  FILECLASS -- read members of a class from a file
5754432Seric **
5764432Seric **	Parameters:
5774432Seric **		class -- class to define.
5784432Seric **		filename -- name of file to read.
5794432Seric **		fmt -- scanf string to use for match.
58064133Seric **		safe -- if set, this is a safe read.
58164133Seric **		optional -- if set, it is not an error for the file to
58264133Seric **			not exist.
5834432Seric **
5844432Seric **	Returns:
5854432Seric **		none
5864432Seric **
5874432Seric **	Side Effects:
5884432Seric **
5894432Seric **		puts all lines in filename that match a scanf into
5904432Seric **			the named class.
5914432Seric */
5924432Seric 
59364133Seric fileclass(class, filename, fmt, safe, optional)
5944432Seric 	int class;
5954432Seric 	char *filename;
5964432Seric 	char *fmt;
59754973Seric 	bool safe;
59864133Seric 	bool optional;
5994432Seric {
60025808Seric 	FILE *f;
60154973Seric 	struct stat stbuf;
6024432Seric 	char buf[MAXLINE];
6034432Seric 
60466101Seric 	if (tTd(37, 2))
60566101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
60666101Seric 
60766031Seric 	if (filename[0] == '|')
60866031Seric 	{
60966031Seric 		syserr("fileclass: pipes (F%c%s) not supported due to security problems",
61066031Seric 			class, filename);
61166031Seric 		return;
61266031Seric 	}
61354973Seric 	if (stat(filename, &stbuf) < 0)
61454973Seric 	{
61566101Seric 		if (tTd(37, 2))
61666101Seric 			printf("  cannot stat (%s)\n", errstring(errno));
61764133Seric 		if (!optional)
61864133Seric 			syserr("fileclass: cannot stat %s", filename);
61954973Seric 		return;
62054973Seric 	}
62154973Seric 	if (!S_ISREG(stbuf.st_mode))
62254973Seric 	{
62354973Seric 		syserr("fileclass: %s not a regular file", filename);
62454973Seric 		return;
62554973Seric 	}
62654973Seric 	if (!safe && access(filename, R_OK) < 0)
62754973Seric 	{
62854973Seric 		syserr("fileclass: access denied on %s", filename);
62954973Seric 		return;
63054973Seric 	}
63154973Seric 	f = fopen(filename, "r");
6324432Seric 	if (f == NULL)
6334432Seric 	{
63454973Seric 		syserr("fileclass: cannot open %s", filename);
6354432Seric 		return;
6364432Seric 	}
6374432Seric 
6384432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
6394432Seric 	{
6404432Seric 		register STAB *s;
64125808Seric 		register char *p;
64225808Seric # ifdef SCANF
6434432Seric 		char wordbuf[MAXNAME+1];
6444432Seric 
6454432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
6464432Seric 			continue;
64725808Seric 		p = wordbuf;
64856795Seric # else /* SCANF */
64925808Seric 		p = buf;
65056795Seric # endif /* SCANF */
65125808Seric 
65225808Seric 		/*
65325808Seric 		**  Break up the match into words.
65425808Seric 		*/
65525808Seric 
65625808Seric 		while (*p != '\0')
65725808Seric 		{
65825808Seric 			register char *q;
65925808Seric 
66025808Seric 			/* strip leading spaces */
66158050Seric 			while (isascii(*p) && isspace(*p))
66225808Seric 				p++;
66325808Seric 			if (*p == '\0')
66425808Seric 				break;
66525808Seric 
66625808Seric 			/* find the end of the word */
66725808Seric 			q = p;
66858050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
66925808Seric 				p++;
67025808Seric 			if (*p != '\0')
67125808Seric 				*p++ = '\0';
67225808Seric 
67325808Seric 			/* enter the word in the symbol table */
67466101Seric 			setclass(class, q);
67525808Seric 		}
6764432Seric 	}
6774432Seric 
67854973Seric 	(void) fclose(f);
6794432Seric }
6804432Seric /*
6814096Seric **  MAKEMAILER -- define a new mailer.
6824096Seric **
6834096Seric **	Parameters:
68410327Seric **		line -- description of mailer.  This is in labeled
68510327Seric **			fields.  The fields are:
68667998Seric **			   A -- the argv for this mailer
68767998Seric **			   C -- the character set for MIME conversions
68867998Seric **			   D -- the directory to run in
68967998Seric **			   E -- the eol string
69067998Seric **			   F -- the flags associated with the mailer
69167998Seric **			   L -- the maximum line length
69267998Seric **			   M -- the maximum message size
69310327Seric **			   P -- the path to the mailer
69467998Seric **			   R -- the recipient rewriting set
69510327Seric **			   S -- the sender rewriting set
69667998Seric **			   T -- the mailer type (for DSNs)
69767998Seric **			   U -- the uid to run as
69810327Seric **			The first word is the canonical name of the mailer.
6994096Seric **
7004096Seric **	Returns:
7014096Seric **		none.
7024096Seric **
7034096Seric **	Side Effects:
7044096Seric **		enters the mailer into the mailer table.
7054096Seric */
7063308Seric 
70721066Seric makemailer(line)
7084096Seric 	char *line;
7094096Seric {
7104096Seric 	register char *p;
7118067Seric 	register struct mailer *m;
7128067Seric 	register STAB *s;
7138067Seric 	int i;
71410327Seric 	char fcode;
71558020Seric 	auto char *endp;
7164096Seric 	extern int NextMailer;
71710327Seric 	extern char **makeargv();
71810327Seric 	extern char *munchstring();
71910701Seric 	extern long atol();
7204096Seric 
72110327Seric 	/* allocate a mailer and set up defaults */
72210327Seric 	m = (struct mailer *) xalloc(sizeof *m);
72310327Seric 	bzero((char *) m, sizeof *m);
72410327Seric 	m->m_eol = "\n";
72567604Seric 	m->m_uid = m->m_gid = 0;
72610327Seric 
72710327Seric 	/* collect the mailer name */
72858050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
72910327Seric 		continue;
73010327Seric 	if (*p != '\0')
73110327Seric 		*p++ = '\0';
73210327Seric 	m->m_name = newstr(line);
73310327Seric 
73410327Seric 	/* now scan through and assign info from the fields */
73510327Seric 	while (*p != '\0')
73610327Seric 	{
73758333Seric 		auto char *delimptr;
73858333Seric 
73958050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
74010327Seric 			p++;
74110327Seric 
74210327Seric 		/* p now points to field code */
74310327Seric 		fcode = *p;
74410327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
74510327Seric 			p++;
74610327Seric 		if (*p++ != '=')
74710327Seric 		{
74852637Seric 			syserr("mailer %s: `=' expected", m->m_name);
74910327Seric 			return;
75010327Seric 		}
75158050Seric 		while (isascii(*p) && isspace(*p))
75210327Seric 			p++;
75310327Seric 
75410327Seric 		/* p now points to the field body */
75558333Seric 		p = munchstring(p, &delimptr);
75610327Seric 
75710327Seric 		/* install the field into the mailer struct */
75810327Seric 		switch (fcode)
75910327Seric 		{
76010327Seric 		  case 'P':		/* pathname */
76110327Seric 			m->m_mailer = newstr(p);
76210327Seric 			break;
76310327Seric 
76410327Seric 		  case 'F':		/* flags */
76510687Seric 			for (; *p != '\0'; p++)
76658050Seric 				if (!(isascii(*p) && isspace(*p)))
76752637Seric 					setbitn(*p, m->m_flags);
76810327Seric 			break;
76910327Seric 
77010327Seric 		  case 'S':		/* sender rewriting ruleset */
77110327Seric 		  case 'R':		/* recipient rewriting ruleset */
77258020Seric 			i = strtol(p, &endp, 10);
77310327Seric 			if (i < 0 || i >= MAXRWSETS)
77410327Seric 			{
77510327Seric 				syserr("invalid rewrite set, %d max", MAXRWSETS);
77610327Seric 				return;
77710327Seric 			}
77810327Seric 			if (fcode == 'S')
77958020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
78010327Seric 			else
78158020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
78258020Seric 
78358020Seric 			p = endp;
78459985Seric 			if (*p++ == '/')
78558020Seric 			{
78658020Seric 				i = strtol(p, NULL, 10);
78758020Seric 				if (i < 0 || i >= MAXRWSETS)
78858020Seric 				{
78958020Seric 					syserr("invalid rewrite set, %d max",
79058020Seric 						MAXRWSETS);
79158020Seric 					return;
79258020Seric 				}
79358020Seric 				if (fcode == 'S')
79458020Seric 					m->m_sh_rwset = i;
79558020Seric 				else
79658020Seric 					m->m_rh_rwset = i;
79758020Seric 			}
79810327Seric 			break;
79910327Seric 
80010327Seric 		  case 'E':		/* end of line string */
80110327Seric 			m->m_eol = newstr(p);
80210327Seric 			break;
80310327Seric 
80410327Seric 		  case 'A':		/* argument vector */
80510327Seric 			m->m_argv = makeargv(p);
80610327Seric 			break;
80710701Seric 
80810701Seric 		  case 'M':		/* maximum message size */
80910701Seric 			m->m_maxsize = atol(p);
81010701Seric 			break;
81152106Seric 
81252106Seric 		  case 'L':		/* maximum line length */
81352106Seric 			m->m_linelimit = atoi(p);
81452106Seric 			break;
81558935Seric 
81658935Seric 		  case 'D':		/* working directory */
81758935Seric 			m->m_execdir = newstr(p);
81858935Seric 			break;
81967604Seric 
82067896Seric 		  case 'C':		/* default charset */
82167896Seric 			m->m_defcharset = newstr(p);
82267896Seric 			break;
82367896Seric 
82467990Seric 		  case 'T':		/* MTS Type */
82567990Seric 			m->m_mtstype = newstr(p);
82667990Seric 			break;
82767990Seric 
82867604Seric 		  case 'U':		/* user id */
82967604Seric 			if (isascii(*p) && !isdigit(*p))
83067604Seric 			{
83167604Seric 				char *q = p;
83267604Seric 				struct passwd *pw;
83367604Seric 
83467604Seric 				while (isascii(*p) && isalnum(*p))
83567604Seric 					p++;
83667604Seric 				while (isascii(*p) && isspace(*p))
83767604Seric 					*p++ = '\0';
83867604Seric 				if (*p != '\0')
83967604Seric 					*p++ = '\0';
84067604Seric 				pw = getpwnam(q);
84167604Seric 				if (pw == NULL)
84267604Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
84367604Seric 				else
84467604Seric 				{
84567604Seric 					m->m_uid = pw->pw_uid;
84667604Seric 					m->m_gid = pw->pw_gid;
84767604Seric 				}
84867604Seric 			}
84967604Seric 			else
85067604Seric 			{
85167604Seric 				auto char *q;
85267604Seric 
85367604Seric 				m->m_uid = strtol(p, &q, 0);
85467604Seric 				p = q;
85567604Seric 			}
85667604Seric 			while (isascii(*p) && isspace(*p))
85767604Seric 				p++;
85867604Seric 			if (*p == '\0')
85967604Seric 				break;
86067604Seric 			if (isascii(*p) && !isdigit(*p))
86167604Seric 			{
86267604Seric 				char *q = p;
86367604Seric 				struct group *gr;
86467604Seric 
86567604Seric 				while (isascii(*p) && isalnum(*p))
86667604Seric 					p++;
86767604Seric 				*p++ = '\0';
86867604Seric 				gr = getgrnam(q);
86967604Seric 				if (gr == NULL)
87067604Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
87167604Seric 				else
87267604Seric 					m->m_gid = gr->gr_gid;
87367604Seric 			}
87467604Seric 			else
87567604Seric 			{
87667604Seric 				m->m_gid = strtol(p, NULL, 0);
87767604Seric 			}
87867604Seric 			break;
87910327Seric 		}
88010327Seric 
88158333Seric 		p = delimptr;
88210327Seric 	}
88310327Seric 
88458321Seric 	/* do some rationality checking */
88558321Seric 	if (m->m_argv == NULL)
88658321Seric 	{
88758321Seric 		syserr("M%s: A= argument required", m->m_name);
88858321Seric 		return;
88958321Seric 	}
89058321Seric 	if (m->m_mailer == NULL)
89158321Seric 	{
89258321Seric 		syserr("M%s: P= argument required", m->m_name);
89358321Seric 		return;
89458321Seric 	}
89558321Seric 
8964096Seric 	if (NextMailer >= MAXMAILERS)
8974096Seric 	{
8989381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
8994096Seric 		return;
9004096Seric 	}
90157402Seric 
90267998Seric 	/* do some heuristic cleanup for back compatibility */
90367998Seric 	if (bitnset(M_LIMITS, m->m_flags))
90467998Seric 	{
90567998Seric 		if (m->m_linelimit == 0)
90667998Seric 			m->m_linelimit = SMTPLINELIM;
90767998Seric 		if (ConfigLevel < 2)
90867998Seric 			setbitn(M_7BITS, m->m_flags);
90967998Seric 	}
91067998Seric 
91167998Seric 	if (ConfigLevel < 6 && m->m_mtstype == NULL &&
91267998Seric 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
91368092Seric 	     strcmp(m->m_mailer, "[TCP]") == 0))
91467998Seric 		m->m_mtstype = "Internet";
91567998Seric 
91667998Seric 	/* enter the mailer into the symbol table */
91710327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
91857402Seric 	if (s->s_mailer != NULL)
91957402Seric 	{
92057402Seric 		i = s->s_mailer->m_mno;
92157402Seric 		free(s->s_mailer);
92257402Seric 	}
92357402Seric 	else
92457402Seric 	{
92557402Seric 		i = NextMailer++;
92657402Seric 	}
92757402Seric 	Mailer[i] = s->s_mailer = m;
92857454Seric 	m->m_mno = i;
92910327Seric }
93010327Seric /*
93110327Seric **  MUNCHSTRING -- translate a string into internal form.
93210327Seric **
93310327Seric **	Parameters:
93410327Seric **		p -- the string to munch.
93558333Seric **		delimptr -- if non-NULL, set to the pointer of the
93658333Seric **			field delimiter character.
93710327Seric **
93810327Seric **	Returns:
93910327Seric **		the munched string.
94010327Seric */
9414096Seric 
94210327Seric char *
94358333Seric munchstring(p, delimptr)
94410327Seric 	register char *p;
94558333Seric 	char **delimptr;
94610327Seric {
94710327Seric 	register char *q;
94810327Seric 	bool backslash = FALSE;
94910327Seric 	bool quotemode = FALSE;
95010327Seric 	static char buf[MAXLINE];
9514096Seric 
95210327Seric 	for (q = buf; *p != '\0'; p++)
9534096Seric 	{
95410327Seric 		if (backslash)
95510327Seric 		{
95610327Seric 			/* everything is roughly literal */
95710357Seric 			backslash = FALSE;
95810327Seric 			switch (*p)
95910327Seric 			{
96010327Seric 			  case 'r':		/* carriage return */
96110327Seric 				*q++ = '\r';
96210327Seric 				continue;
96310327Seric 
96410327Seric 			  case 'n':		/* newline */
96510327Seric 				*q++ = '\n';
96610327Seric 				continue;
96710327Seric 
96810327Seric 			  case 'f':		/* form feed */
96910327Seric 				*q++ = '\f';
97010327Seric 				continue;
97110327Seric 
97210327Seric 			  case 'b':		/* backspace */
97310327Seric 				*q++ = '\b';
97410327Seric 				continue;
97510327Seric 			}
97610327Seric 			*q++ = *p;
97710327Seric 		}
97810327Seric 		else
97910327Seric 		{
98010327Seric 			if (*p == '\\')
98110327Seric 				backslash = TRUE;
98210327Seric 			else if (*p == '"')
98310327Seric 				quotemode = !quotemode;
98410327Seric 			else if (quotemode || *p != ',')
98510327Seric 				*q++ = *p;
98610327Seric 			else
98710327Seric 				break;
98810327Seric 		}
9894096Seric 	}
9904096Seric 
99158333Seric 	if (delimptr != NULL)
99258333Seric 		*delimptr = p;
99310327Seric 	*q++ = '\0';
99410327Seric 	return (buf);
99510327Seric }
99610327Seric /*
99710327Seric **  MAKEARGV -- break up a string into words
99810327Seric **
99910327Seric **	Parameters:
100010327Seric **		p -- the string to break up.
100110327Seric **
100210327Seric **	Returns:
100310327Seric **		a char **argv (dynamically allocated)
100410327Seric **
100510327Seric **	Side Effects:
100610327Seric **		munges p.
100710327Seric */
10084096Seric 
100910327Seric char **
101010327Seric makeargv(p)
101110327Seric 	register char *p;
101210327Seric {
101310327Seric 	char *q;
101410327Seric 	int i;
101510327Seric 	char **avp;
101610327Seric 	char *argv[MAXPV + 1];
101710327Seric 
101810327Seric 	/* take apart the words */
101910327Seric 	i = 0;
102010327Seric 	while (*p != '\0' && i < MAXPV)
10214096Seric 	{
102210327Seric 		q = p;
102358050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
102410327Seric 			p++;
102558050Seric 		while (isascii(*p) && isspace(*p))
102610327Seric 			*p++ = '\0';
102710327Seric 		argv[i++] = newstr(q);
10284096Seric 	}
102910327Seric 	argv[i++] = NULL;
10304096Seric 
103110327Seric 	/* now make a copy of the argv */
103210327Seric 	avp = (char **) xalloc(sizeof *avp * i);
103316893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
103410327Seric 
103510327Seric 	return (avp);
10363308Seric }
10373308Seric /*
10383308Seric **  PRINTRULES -- print rewrite rules (for debugging)
10393308Seric **
10403308Seric **	Parameters:
10413308Seric **		none.
10423308Seric **
10433308Seric **	Returns:
10443308Seric **		none.
10453308Seric **
10463308Seric **	Side Effects:
10473308Seric **		prints rewrite rules.
10483308Seric */
10493308Seric 
10503308Seric printrules()
10513308Seric {
10523308Seric 	register struct rewrite *rwp;
10534072Seric 	register int ruleset;
10543308Seric 
10554072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
10563308Seric 	{
10574072Seric 		if (RewriteRules[ruleset] == NULL)
10584072Seric 			continue;
10598067Seric 		printf("\n----Rule Set %d:", ruleset);
10603308Seric 
10614072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
10623308Seric 		{
10638067Seric 			printf("\nLHS:");
10648067Seric 			printav(rwp->r_lhs);
10658067Seric 			printf("RHS:");
10668067Seric 			printav(rwp->r_rhs);
10673308Seric 		}
10683308Seric 	}
10693308Seric }
107067994Seric /*
107167994Seric **  PRINTMAILER -- print mailer structure (for debugging)
107267994Seric **
107367994Seric **	Parameters:
107467994Seric **		m -- the mailer to print
107567994Seric **
107667994Seric **	Returns:
107767994Seric **		none.
107867994Seric */
10794319Seric 
108067994Seric printmailer(m)
108167994Seric 	register MAILER *m;
108267994Seric {
108367994Seric 	int j;
108467994Seric 
108567994Seric 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
108667994Seric 		m->m_mno, m->m_name,
108767994Seric 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
108867994Seric 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
108967994Seric 		m->m_uid, m->m_gid);
109067994Seric 	for (j = '\0'; j <= '\177'; j++)
109167994Seric 		if (bitnset(j, m->m_flags))
109267994Seric 			(void) putchar(j);
109367994Seric 	printf(" L=%d E=", m->m_linelimit);
109467994Seric 	xputs(m->m_eol);
109567994Seric 	if (m->m_defcharset != NULL)
109667994Seric 		printf(" C=%s", m->m_defcharset);
109767994Seric 	if (m->m_mtstype != NULL)
109867994Seric 		printf(" T=%s", m->m_mtstype);
109967994Seric 	if (m->m_argv != NULL)
110067994Seric 	{
110167994Seric 		char **a = m->m_argv;
110267994Seric 
110367994Seric 		printf(" A=");
110467994Seric 		while (*a != NULL)
110567994Seric 		{
110667994Seric 			if (a != m->m_argv)
110767994Seric 				printf(" ");
110867994Seric 			xputs(*a++);
110967994Seric 		}
111067994Seric 	}
111167994Seric 	printf("\n");
111267994Seric }
11134096Seric /*
11148256Seric **  SETOPTION -- set global processing option
11158256Seric **
11168256Seric **	Parameters:
11178256Seric **		opt -- option name.
11188256Seric **		val -- option value (as a text string).
111921755Seric **		safe -- set if this came from a configuration file.
112021755Seric **			Some options (if set from the command line) will
112121755Seric **			reset the user id to avoid security problems.
11228269Seric **		sticky -- if set, don't let other setoptions override
11238269Seric **			this value.
112458734Seric **		e -- the main envelope.
11258256Seric **
11268256Seric **	Returns:
11278256Seric **		none.
11288256Seric **
11298256Seric **	Side Effects:
11308256Seric **		Sets options as implied by the arguments.
11318256Seric */
11328256Seric 
113310687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
11348269Seric 
113557207Seric 
113666334Seric #if NAMED_BIND
113757207Seric 
113857207Seric struct resolverflags
113957207Seric {
114057207Seric 	char	*rf_name;	/* name of the flag */
114157207Seric 	long	rf_bits;	/* bits to set/clear */
114257207Seric } ResolverFlags[] =
114357207Seric {
114457207Seric 	"debug",	RES_DEBUG,
114557207Seric 	"aaonly",	RES_AAONLY,
114657207Seric 	"usevc",	RES_USEVC,
114757207Seric 	"primary",	RES_PRIMARY,
114857207Seric 	"igntc",	RES_IGNTC,
114957207Seric 	"recurse",	RES_RECURSE,
115057207Seric 	"defnames",	RES_DEFNAMES,
115157207Seric 	"stayopen",	RES_STAYOPEN,
115257207Seric 	"dnsrch",	RES_DNSRCH,
115365583Seric 	"true",		0,		/* to avoid error on old syntax */
115457207Seric 	NULL,		0
115557207Seric };
115657207Seric 
115757207Seric #endif
115857207Seric 
115967614Seric struct optioninfo
116067614Seric {
116167614Seric 	char	*o_name;	/* long name of option */
116267787Seric 	u_char	o_code;		/* short name of option */
116367614Seric 	bool	o_safe;		/* safe for random people to use */
116467614Seric } OptionTab[] =
116567614Seric {
116667707Seric 	"SevenBitInput",	'7',		TRUE,
116767707Seric 	"EightBitMode",		'8',		TRUE,
116867707Seric 	"AliasFile",		'A',		FALSE,
116967707Seric 	"AliasWait",		'a',		FALSE,
117067707Seric 	"BlankSub",		'B',		FALSE,
117167707Seric 	"MinFreeBlocks",	'b',		TRUE,
117267707Seric 	"CheckpointInterval",	'C',		TRUE,
117367707Seric 	"HoldExpensive",	'c',		FALSE,
117467707Seric 	"AutoRebuildAliases",	'D',		FALSE,
117567707Seric 	"DeliveryMode",		'd',		TRUE,
117667707Seric 	"ErrorHeader",		'E',		FALSE,
117767707Seric 	"ErrorMode",		'e',		TRUE,
117867707Seric 	"TempFileMode",		'F',		FALSE,
117967707Seric 	"SaveFromLine",		'f',		FALSE,
118067707Seric 	"MatchGECOS",		'G',		FALSE,
118167707Seric 	"HelpFile",		'H',		FALSE,
118267707Seric 	"MaxHopCount",		'h',		FALSE,
118367707Seric 	"NameServerOptions",	'I',		FALSE,
118467707Seric 	"IgnoreDots",		'i',		TRUE,
118567707Seric 	"ForwardPath",		'J',		FALSE,
118667707Seric 	"SendMimeErrors",	'j',		TRUE,
118767707Seric 	"ConnectionCacheSize",	'k',		FALSE,
118867707Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
118967707Seric 	"UseErrorsTo",		'l',		FALSE,
119067707Seric 	"LogLevel",		'L',		FALSE,
119167707Seric 	"MeToo",		'm',		TRUE,
119267707Seric 	"CheckAliases",		'n',		FALSE,
119367707Seric 	"OldStyleHeaders",	'o',		TRUE,
119467707Seric 	"DaemonPortOptions",	'O',		FALSE,
119567707Seric 	"PrivacyOptions",	'p',		TRUE,
119667707Seric 	"PostmasterCopy",	'P',		FALSE,
119767707Seric 	"QueueFactor",		'q',		FALSE,
119867707Seric 	"QueueDirectory",	'Q',		FALSE,
119967707Seric 	"DontPruneRoutes",	'R',		FALSE,
120067711Seric 	"Timeouts",		'r',		TRUE,
120167707Seric 	"StatusFile",		'S',		FALSE,
120267707Seric 	"SuperSafe",		's',		TRUE,
120367707Seric 	"QueueTimeout",		'T',		FALSE,
120467707Seric 	"TimeZoneSpec",		't',		FALSE,
120567707Seric 	"UserDatabaseSpec",	'U',		FALSE,
120667707Seric 	"DefaultUser",		'u',		FALSE,
120767707Seric 	"FallbackMXhost",	'V',		FALSE,
120867707Seric 	"Verbose",		'v',		TRUE,
120967707Seric 	"TryNullMXList",	'w',		TRUE,
121067707Seric 	"QueueLA",		'x',		FALSE,
121167707Seric 	"RefuseLA",		'X',		FALSE,
121267707Seric 	"RecipientFactor",	'y',		FALSE,
121367707Seric 	"ForkQueueRuns",	'Y',		FALSE,
121467707Seric 	"ClassFactor",		'z',		FALSE,
121567707Seric 	"TimeFactor",		'Z',		FALSE,
121667707Seric #define O_BSP		0x80
121767707Seric 	"BrokenSmtpPeers",	O_BSP,		TRUE,
1218*68105Seric #define O_QUEUESORTORD,	0x81
1219*68105Seric 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
122067707Seric #define O_DNICE		0x82
122167707Seric 	"DeliveryNiceness",	O_DNICE,	TRUE,
122267707Seric #define O_MQA		0x83
122367707Seric 	"MinQueueAge",		O_MQA,		TRUE,
122467707Seric #define O_MHSA		0x84
122567707Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
122667813Seric #define O_DEFCHARSET	0x85
122767813Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
122867848Seric #define O_SSFILE	0x86
122967848Seric 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
123068034Seric #define O_DIALDELAY	0x87
123168034Seric 	"DialDelay",		O_DIALDELAY,	TRUE,
123267707Seric 
123367707Seric 	NULL,			'\0',		FALSE,
123467614Seric };
123567614Seric 
123667614Seric 
123767614Seric 
123858734Seric setoption(opt, val, safe, sticky, e)
123967614Seric 	u_char opt;
12408256Seric 	char *val;
124121755Seric 	bool safe;
12428269Seric 	bool sticky;
124358734Seric 	register ENVELOPE *e;
12448256Seric {
124557207Seric 	register char *p;
124667614Seric 	register struct optioninfo *o;
124767903Seric 	char *subopt;
12488265Seric 	extern bool atobool();
124912633Seric 	extern time_t convtime();
125014879Seric 	extern int QueueLA;
125114879Seric 	extern int RefuseLA;
125264718Seric 	extern bool Warn_Q_option;
12538256Seric 
125467736Seric 	errno = 0;
125567614Seric 	if (opt == ' ')
125667614Seric 	{
125767614Seric 		/* full word options */
125867736Seric 		struct optioninfo *sel;
125967614Seric 
126067614Seric 		p = strchr(val, '=');
126167614Seric 		if (p == NULL)
126267614Seric 			p = &val[strlen(val)];
126367614Seric 		while (*--p == ' ')
126467614Seric 			continue;
126567614Seric 		while (*++p == ' ')
126667614Seric 			*p = '\0';
126767731Seric 		if (p == val)
126867731Seric 		{
126967731Seric 			syserr("readcf: null option name");
127067731Seric 			return;
127167731Seric 		}
127267614Seric 		if (*p == '=')
127367614Seric 			*p++ = '\0';
127467614Seric 		while (*p == ' ')
127567614Seric 			p++;
127667903Seric 		subopt = strchr(val, '.');
127767903Seric 		if (subopt != NULL)
127867903Seric 			*subopt++ = '\0';
127967736Seric 		sel = NULL;
128067614Seric 		for (o = OptionTab; o->o_name != NULL; o++)
128167614Seric 		{
128267736Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
128367736Seric 				continue;
128467736Seric 			if (strlen(o->o_name) == strlen(val))
128567736Seric 			{
128667736Seric 				/* completely specified -- this must be it */
128767736Seric 				sel = NULL;
128867614Seric 				break;
128967736Seric 			}
129067736Seric 			if (sel != NULL)
129167736Seric 				break;
129267736Seric 			sel = o;
129367614Seric 		}
129467736Seric 		if (sel != NULL && o->o_name == NULL)
129567736Seric 			o = sel;
129667736Seric 		else if (o->o_name == NULL)
129767787Seric 		{
129867614Seric 			syserr("readcf: unknown option name %s", val);
129967787Seric 			return;
130067787Seric 		}
130167736Seric 		else if (sel != NULL)
130267736Seric 		{
130367736Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
130467736Seric 				val, sel->o_name, o->o_name);
130567736Seric 			return;
130667736Seric 		}
130767736Seric 		if (strlen(val) != strlen(o->o_name))
130867736Seric 		{
130967736Seric 			bool oldVerbose = Verbose;
131067736Seric 
131167736Seric 			Verbose = TRUE;
131267736Seric 			message("Option %s used as abbreviation for %s",
131367736Seric 				val, o->o_name);
131467736Seric 			Verbose = oldVerbose;
131567736Seric 		}
131667614Seric 		opt = o->o_code;
131767614Seric 		val = p;
131867614Seric 	}
131967614Seric 	else
132067614Seric 	{
132167614Seric 		for (o = OptionTab; o->o_name != NULL; o++)
132267614Seric 		{
132367614Seric 			if (o->o_code == opt)
132467614Seric 				break;
132567614Seric 		}
132667903Seric 		subopt = NULL;
132767614Seric 	}
132867614Seric 
13298256Seric 	if (tTd(37, 1))
133067731Seric 	{
133167731Seric 		printf(isascii(opt) && isprint(opt) ?
133267903Seric 			    "setoption %s (%c).%s=%s" :
133367903Seric 			    "setoption %s (0x%x).%s=%s",
133467614Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
133567903Seric 			opt,
133667903Seric 			subopt == NULL ? "" : subopt,
133767903Seric 			val);
133867731Seric 	}
13398256Seric 
13408256Seric 	/*
13418269Seric 	**  See if this option is preset for us.
13428256Seric 	*/
13438256Seric 
134459731Seric 	if (!sticky && bitnset(opt, StickyOpt))
13458269Seric 	{
13469341Seric 		if (tTd(37, 1))
13479341Seric 			printf(" (ignored)\n");
13488269Seric 		return;
13498269Seric 	}
13508269Seric 
135121755Seric 	/*
135221755Seric 	**  Check to see if this option can be specified by this user.
135321755Seric 	*/
135421755Seric 
135563787Seric 	if (!safe && RealUid == 0)
135621755Seric 		safe = TRUE;
135767614Seric 	if (!safe && !o->o_safe)
135821755Seric 	{
135939111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
136021755Seric 		{
136136582Sbostic 			if (tTd(37, 1))
136236582Sbostic 				printf(" (unsafe)");
136363787Seric 			if (RealUid != geteuid())
136436582Sbostic 			{
136551210Seric 				if (tTd(37, 1))
136651210Seric 					printf("(Resetting uid)");
136763787Seric 				(void) setgid(RealGid);
136863787Seric 				(void) setuid(RealUid);
136936582Sbostic 			}
137021755Seric 		}
137121755Seric 	}
137251210Seric 	if (tTd(37, 1))
137317985Seric 		printf("\n");
13748269Seric 
137567614Seric 	switch (opt & 0xff)
13768256Seric 	{
137759709Seric 	  case '7':		/* force seven-bit input */
137867546Seric 		SevenBitInput = atobool(val);
137952106Seric 		break;
138052106Seric 
138167546Seric 	  case '8':		/* handling of 8-bit input */
138267546Seric 		switch (*val)
138367546Seric 		{
138467547Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
138567546Seric 			MimeMode = 0;
138667546Seric 			break;
138767546Seric 
138867547Seric 		  case 'm':		/* convert 8-bit, convert MIME */
138967546Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
139067546Seric 			break;
139167546Seric 
139267547Seric 		  case 'j':		/* "just send 8" */
139367546Seric 			MimeMode = MM_PASS8BIT;
139467546Seric 			break;
139567546Seric 
139667546Seric 		  case 'p':		/* pass 8 bit, convert MIME */
139767546Seric 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
139867546Seric 			break;
139967546Seric 
140067546Seric 		  case 's':		/* strict adherence */
140167546Seric 			MimeMode = MM_CVTMIME;
140267546Seric 			break;
140367546Seric 
140467547Seric 		  case 'a':		/* encode 8 bit if available */
140567546Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
140667546Seric 			break;
140767546Seric 
140867547Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
140967547Seric 			MimeMode = MM_MIME8BIT;
141067547Seric 			break;
141167547Seric 
141267546Seric 		  default:
141367546Seric 			syserr("Unknown 8-bit mode %c", *val);
141467546Seric 			exit(EX_USAGE);
141567546Seric 		}
141667546Seric 		break;
141767546Seric 
14188256Seric 	  case 'A':		/* set default alias file */
14199381Seric 		if (val[0] == '\0')
142059672Seric 			setalias("aliases");
14219381Seric 		else
142259672Seric 			setalias(val);
14238256Seric 		break;
14248256Seric 
142517474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
142617474Seric 		if (val[0] == '\0')
142764796Seric 			SafeAlias = 5 * 60;		/* five minutes */
142817474Seric 		else
142964796Seric 			SafeAlias = convtime(val, 'm');
143017474Seric 		break;
143117474Seric 
143216843Seric 	  case 'B':		/* substitution for blank character */
143316843Seric 		SpaceSub = val[0];
143416843Seric 		if (SpaceSub == '\0')
143516843Seric 			SpaceSub = ' ';
143616843Seric 		break;
143716843Seric 
143859283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
143959283Seric 		p = strchr(val, '/');
144059283Seric 		if (p != NULL)
144159283Seric 		{
144259283Seric 			*p++ = '\0';
144359283Seric 			MaxMessageSize = atol(p);
144459283Seric 		}
144558082Seric 		MinBlocksFree = atol(val);
144658082Seric 		break;
144758082Seric 
14489284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
14499381Seric 		NoConnect = atobool(val);
14509284Seric 		break;
14519284Seric 
145251305Seric 	  case 'C':		/* checkpoint every N addresses */
145351305Seric 		CheckpointInterval = atoi(val);
145424944Seric 		break;
145524944Seric 
14569284Seric 	  case 'd':		/* delivery mode */
14579284Seric 		switch (*val)
14588269Seric 		{
14599284Seric 		  case '\0':
146058734Seric 			e->e_sendmode = SM_DELIVER;
14618269Seric 			break;
14628269Seric 
146310755Seric 		  case SM_QUEUE:	/* queue only */
146410755Seric #ifndef QUEUE
146510755Seric 			syserr("need QUEUE to set -odqueue");
146656795Seric #endif /* QUEUE */
146710755Seric 			/* fall through..... */
146810755Seric 
14699284Seric 		  case SM_DELIVER:	/* do everything */
14709284Seric 		  case SM_FORK:		/* fork after verification */
147158734Seric 			e->e_sendmode = *val;
14728269Seric 			break;
14738269Seric 
14748269Seric 		  default:
14759284Seric 			syserr("Unknown delivery mode %c", *val);
14768269Seric 			exit(EX_USAGE);
14778269Seric 		}
14788269Seric 		break;
14798269Seric 
14809146Seric 	  case 'D':		/* rebuild alias database as needed */
14819381Seric 		AutoRebuild = atobool(val);
14829146Seric 		break;
14839146Seric 
148455372Seric 	  case 'E':		/* error message header/header file */
148555379Seric 		if (*val != '\0')
148655379Seric 			ErrMsgFile = newstr(val);
148755372Seric 		break;
148855372Seric 
14898269Seric 	  case 'e':		/* set error processing mode */
14908269Seric 		switch (*val)
14918269Seric 		{
14929381Seric 		  case EM_QUIET:	/* be silent about it */
14939381Seric 		  case EM_MAIL:		/* mail back */
14949381Seric 		  case EM_BERKNET:	/* do berknet error processing */
14959381Seric 		  case EM_WRITE:	/* write back (or mail) */
14969381Seric 		  case EM_PRINT:	/* print errors normally (default) */
149758734Seric 			e->e_errormode = *val;
14988269Seric 			break;
14998269Seric 		}
15008269Seric 		break;
15018269Seric 
15029049Seric 	  case 'F':		/* file mode */
150317975Seric 		FileMode = atooct(val) & 0777;
15049049Seric 		break;
15059049Seric 
15068269Seric 	  case 'f':		/* save Unix-style From lines on front */
15079381Seric 		SaveFrom = atobool(val);
15088269Seric 		break;
15098269Seric 
151053735Seric 	  case 'G':		/* match recipients against GECOS field */
151153735Seric 		MatchGecos = atobool(val);
151253735Seric 		break;
151353735Seric 
15148256Seric 	  case 'g':		/* default gid */
151567823Seric   g_opt:
151664133Seric 		if (isascii(*val) && isdigit(*val))
151764133Seric 			DefGid = atoi(val);
151864133Seric 		else
151964133Seric 		{
152064133Seric 			register struct group *gr;
152164133Seric 
152264133Seric 			DefGid = -1;
152364133Seric 			gr = getgrnam(val);
152464133Seric 			if (gr == NULL)
152567823Seric 				syserr("readcf: option %c: unknown group %s",
152667823Seric 					opt, val);
152764133Seric 			else
152864133Seric 				DefGid = gr->gr_gid;
152964133Seric 		}
15308256Seric 		break;
15318256Seric 
15328256Seric 	  case 'H':		/* help file */
15339381Seric 		if (val[0] == '\0')
15348269Seric 			HelpFile = "sendmail.hf";
15359381Seric 		else
15369381Seric 			HelpFile = newstr(val);
15378256Seric 		break;
15388256Seric 
153951305Seric 	  case 'h':		/* maximum hop count */
154051305Seric 		MaxHopCount = atoi(val);
154151305Seric 		break;
154251305Seric 
154335651Seric 	  case 'I':		/* use internet domain name server */
154466334Seric #if NAMED_BIND
154557207Seric 		for (p = val; *p != 0; )
154657207Seric 		{
154757207Seric 			bool clearmode;
154857207Seric 			char *q;
154957207Seric 			struct resolverflags *rfp;
155057207Seric 
155157207Seric 			while (*p == ' ')
155257207Seric 				p++;
155357207Seric 			if (*p == '\0')
155457207Seric 				break;
155557207Seric 			clearmode = FALSE;
155657207Seric 			if (*p == '-')
155757207Seric 				clearmode = TRUE;
155857207Seric 			else if (*p != '+')
155957207Seric 				p--;
156057207Seric 			p++;
156157207Seric 			q = p;
156258050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
156357207Seric 				p++;
156457207Seric 			if (*p != '\0')
156557207Seric 				*p++ = '\0';
156657207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
156757207Seric 			{
156857207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
156957207Seric 					break;
157057207Seric 			}
157164923Seric 			if (rfp->rf_name == NULL)
157264923Seric 				syserr("readcf: I option value %s unrecognized", q);
157364923Seric 			else if (clearmode)
157457207Seric 				_res.options &= ~rfp->rf_bits;
157557207Seric 			else
157657207Seric 				_res.options |= rfp->rf_bits;
157757207Seric 		}
157857207Seric 		if (tTd(8, 2))
157957207Seric 			printf("_res.options = %x\n", _res.options);
158057207Seric #else
158157207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
158257207Seric #endif
158335651Seric 		break;
158435651Seric 
15858269Seric 	  case 'i':		/* ignore dot lines in message */
15869381Seric 		IgnrDot = atobool(val);
15878269Seric 		break;
15888269Seric 
158959730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
159059730Seric 		SendMIMEErrors = atobool(val);
159159730Seric 		break;
159259730Seric 
159357136Seric 	  case 'J':		/* .forward search path */
159457136Seric 		ForwardPath = newstr(val);
159557136Seric 		break;
159657136Seric 
159754967Seric 	  case 'k':		/* connection cache size */
159854967Seric 		MaxMciCache = atoi(val);
159956215Seric 		if (MaxMciCache < 0)
160056215Seric 			MaxMciCache = 0;
160154967Seric 		break;
160254967Seric 
160354967Seric 	  case 'K':		/* connection cache timeout */
160458796Seric 		MciCacheTimeout = convtime(val, 'm');
160554967Seric 		break;
160654967Seric 
160761104Seric 	  case 'l':		/* use Errors-To: header */
160861104Seric 		UseErrorsTo = atobool(val);
160961104Seric 		break;
161061104Seric 
16118256Seric 	  case 'L':		/* log level */
161264140Seric 		if (safe || LogLevel < atoi(val))
161364140Seric 			LogLevel = atoi(val);
16148256Seric 		break;
16158256Seric 
16168269Seric 	  case 'M':		/* define macro */
16179381Seric 		define(val[0], newstr(&val[1]), CurEnv);
161816878Seric 		sticky = FALSE;
16198269Seric 		break;
16208269Seric 
16218269Seric 	  case 'm':		/* send to me too */
16229381Seric 		MeToo = atobool(val);
16238269Seric 		break;
16248269Seric 
162525820Seric 	  case 'n':		/* validate RHS in newaliases */
162625820Seric 		CheckAliases = atobool(val);
162725820Seric 		break;
162825820Seric 
162961104Seric 	    /* 'N' available -- was "net name" */
163061104Seric 
163158851Seric 	  case 'O':		/* daemon options */
163258851Seric 		setdaemonoptions(val);
163358851Seric 		break;
163458851Seric 
16358269Seric 	  case 'o':		/* assume old style headers */
16369381Seric 		if (atobool(val))
16379341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
16389341Seric 		else
16399341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
16408269Seric 		break;
16418269Seric 
164258082Seric 	  case 'p':		/* select privacy level */
164358082Seric 		p = val;
164458082Seric 		for (;;)
164558082Seric 		{
164658082Seric 			register struct prival *pv;
164758082Seric 			extern struct prival PrivacyValues[];
164858082Seric 
164958082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
165058082Seric 				p++;
165158082Seric 			if (*p == '\0')
165258082Seric 				break;
165358082Seric 			val = p;
165458082Seric 			while (isascii(*p) && isalnum(*p))
165558082Seric 				p++;
165658082Seric 			if (*p != '\0')
165758082Seric 				*p++ = '\0';
165858082Seric 
165958082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
166058082Seric 			{
166158082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
166258082Seric 					break;
166358082Seric 			}
166458886Seric 			if (pv->pv_name == NULL)
166558886Seric 				syserr("readcf: Op line: %s unrecognized", val);
166658082Seric 			PrivacyFlags |= pv->pv_flag;
166758082Seric 		}
166858082Seric 		break;
166958082Seric 
167024944Seric 	  case 'P':		/* postmaster copy address for returned mail */
167124944Seric 		PostMasterCopy = newstr(val);
167224944Seric 		break;
167324944Seric 
167424944Seric 	  case 'q':		/* slope of queue only function */
167524944Seric 		QueueFactor = atoi(val);
167624944Seric 		break;
167724944Seric 
16788256Seric 	  case 'Q':		/* queue directory */
16799381Seric 		if (val[0] == '\0')
16808269Seric 			QueueDir = "mqueue";
16819381Seric 		else
16829381Seric 			QueueDir = newstr(val);
168358789Seric 		if (RealUid != 0 && !safe)
168464718Seric 			Warn_Q_option = TRUE;
16858256Seric 		break;
16868256Seric 
168758148Seric 	  case 'R':		/* don't prune routes */
168858148Seric 		DontPruneRoutes = atobool(val);
168958148Seric 		break;
169058148Seric 
16918256Seric 	  case 'r':		/* read timeout */
169267903Seric 		if (subopt == NULL)
169367903Seric 			inittimeouts(val);
169467903Seric 		else
169567903Seric 			settimeout(subopt, val);
16968256Seric 		break;
16978256Seric 
16988256Seric 	  case 'S':		/* status file */
16999381Seric 		if (val[0] == '\0')
17008269Seric 			StatFile = "sendmail.st";
17019381Seric 		else
17029381Seric 			StatFile = newstr(val);
17038256Seric 		break;
17048256Seric 
17058265Seric 	  case 's':		/* be super safe, even if expensive */
17069381Seric 		SuperSafe = atobool(val);
17078256Seric 		break;
17088256Seric 
17098256Seric 	  case 'T':		/* queue timeout */
171058737Seric 		p = strchr(val, '/');
171158737Seric 		if (p != NULL)
171258737Seric 		{
171358737Seric 			*p++ = '\0';
171467903Seric 			settimeout("queuewarn", p);
171558737Seric 		}
171667903Seric 		settimeout("queuereturn", val);
171754967Seric 		break;
17188256Seric 
17198265Seric 	  case 't':		/* time zone name */
172052106Seric 		TimeZoneSpec = newstr(val);
17218265Seric 		break;
17228265Seric 
172350556Seric 	  case 'U':		/* location of user database */
172451360Seric 		UdbSpec = newstr(val);
172550556Seric 		break;
172650556Seric 
17278256Seric 	  case 'u':		/* set default uid */
172867823Seric 		for (p = val; *p != '\0'; p++)
172967823Seric 		{
173067823Seric 			if (*p == '.' || *p == '/' || *p == ':')
173167823Seric 			{
173267823Seric 				*p++ = '\0';
173367823Seric 				break;
173467823Seric 			}
173567823Seric 		}
173664133Seric 		if (isascii(*val) && isdigit(*val))
173764133Seric 			DefUid = atoi(val);
173864133Seric 		else
173964133Seric 		{
174064133Seric 			register struct passwd *pw;
174164133Seric 
174264133Seric 			DefUid = -1;
174364133Seric 			pw = getpwnam(val);
174464133Seric 			if (pw == NULL)
174564133Seric 				syserr("readcf: option u: unknown user %s", val);
174664133Seric 			else
174767823Seric 			{
174864133Seric 				DefUid = pw->pw_uid;
174967823Seric 				DefGid = pw->pw_gid;
175067823Seric 			}
175164133Seric 		}
175240973Sbostic 		setdefuser();
17538256Seric 
175467823Seric 		/* handle the group if it is there */
175567823Seric 		if (*p == '\0')
175667823Seric 			break;
175767823Seric 		val = p;
175867823Seric 		goto g_opt;
175967823Seric 
176058851Seric 	  case 'V':		/* fallback MX host */
176158851Seric 		FallBackMX = newstr(val);
176258851Seric 		break;
176358851Seric 
17648269Seric 	  case 'v':		/* run in verbose mode */
17659381Seric 		Verbose = atobool(val);
17668256Seric 		break;
17678256Seric 
176863837Seric 	  case 'w':		/* if we are best MX, try host directly */
176963837Seric 		TryNullMXList = atobool(val);
177063837Seric 		break;
177161104Seric 
177261104Seric 	    /* 'W' available -- was wizard password */
177361104Seric 
177414879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
177514879Seric 		QueueLA = atoi(val);
177614879Seric 		break;
177714879Seric 
177814879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
177914879Seric 		RefuseLA = atoi(val);
178014879Seric 		break;
178114879Seric 
178224981Seric 	  case 'y':		/* work recipient factor */
178324981Seric 		WkRecipFact = atoi(val);
178424981Seric 		break;
178524981Seric 
178624981Seric 	  case 'Y':		/* fork jobs during queue runs */
178724952Seric 		ForkQueueRuns = atobool(val);
178824952Seric 		break;
178924952Seric 
179024981Seric 	  case 'z':		/* work message class factor */
179124981Seric 		WkClassFact = atoi(val);
179224981Seric 		break;
179324981Seric 
179424981Seric 	  case 'Z':		/* work time factor */
179524981Seric 		WkTimeFact = atoi(val);
179624981Seric 		break;
179724981Seric 
179867614Seric 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
179967614Seric 		BrokenSmtpPeers = atobool(val);
180067614Seric 		break;
180167614Seric 
1802*68105Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
1803*68105Seric 		switch (*val)
1804*68105Seric 		{
1805*68105Seric 		  case 'h':	/* Host first */
1806*68105Seric 		  case 'H':
1807*68105Seric 			QueueSortOrder = QS_BYHOST;
1808*68105Seric 			break;
1809*68105Seric 
1810*68105Seric 		  case 'p':	/* Priority order */
1811*68105Seric 		  case 'P':
1812*68105Seric 			QueueSortOrder = QS_BYPRIORITY;
1813*68105Seric 			break;
1814*68105Seric 
1815*68105Seric 		  default:
1816*68105Seric 			syserr("Invalid queue sort order \"%s\"", val);
1817*68105Seric 		}
181867614Seric 		break;
181967614Seric 
182067707Seric 	  case O_DNICE:		/* delivery nice value */
182167707Seric 		DeliveryNiceness = atoi(val);
182267707Seric 		break;
182367707Seric 
182467707Seric 	  case O_MQA:		/* minimum queue age between deliveries */
182567707Seric 		MinQueueAge = convtime(val, 'm');
182667707Seric 		break;
182767707Seric 
182867707Seric 	  case O_MHSA:		/* maximum age of cached host status */
182967707Seric 		MaxHostStatAge = convtime(val, 'm');
183067707Seric 		break;
183167707Seric 
183267813Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
183367814Seric 		DefaultCharSet = newstr(val);
183467813Seric 		break;
183567813Seric 
183667848Seric 	  case O_SSFILE:	/* service switch file */
183767848Seric 		ServiceSwitchFile = newstr(val);
183867848Seric 		break;
183967848Seric 
184068034Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
184168034Seric 		DialDelay = convtime(val, 's');
184268034Seric 		break;
184368034Seric 
18448256Seric 	  default:
18458256Seric 		break;
18468256Seric 	}
184716878Seric 	if (sticky)
184816878Seric 		setbitn(opt, StickyOpt);
18499188Seric 	return;
18508256Seric }
185110687Seric /*
185210687Seric **  SETCLASS -- set a word into a class
185310687Seric **
185410687Seric **	Parameters:
185510687Seric **		class -- the class to put the word in.
185610687Seric **		word -- the word to enter
185710687Seric **
185810687Seric **	Returns:
185910687Seric **		none.
186010687Seric **
186110687Seric **	Side Effects:
186210687Seric **		puts the word into the symbol table.
186310687Seric */
186410687Seric 
186510687Seric setclass(class, word)
186610687Seric 	int class;
186710687Seric 	char *word;
186810687Seric {
186910687Seric 	register STAB *s;
187010687Seric 
187157943Seric 	if (tTd(37, 8))
187264326Seric 		printf("setclass(%c, %s)\n", class, word);
187310687Seric 	s = stab(word, ST_CLASS, ST_ENTER);
187410687Seric 	setbitn(class, s->s_class);
187510687Seric }
187653654Seric /*
187753654Seric **  MAKEMAPENTRY -- create a map entry
187853654Seric **
187953654Seric **	Parameters:
188053654Seric **		line -- the config file line
188153654Seric **
188253654Seric **	Returns:
188353654Seric **		TRUE if it successfully entered the map entry.
188453654Seric **		FALSE otherwise (usually syntax error).
188553654Seric **
188653654Seric **	Side Effects:
188753654Seric **		Enters the map into the dictionary.
188853654Seric */
188953654Seric 
189053654Seric void
189153654Seric makemapentry(line)
189253654Seric 	char *line;
189353654Seric {
189453654Seric 	register char *p;
189553654Seric 	char *mapname;
189653654Seric 	char *classname;
189764078Seric 	register STAB *s;
189853654Seric 	STAB *class;
189953654Seric 
190058050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
190153654Seric 		continue;
190258050Seric 	if (!(isascii(*p) && isalnum(*p)))
190353654Seric 	{
190453654Seric 		syserr("readcf: config K line: no map name");
190553654Seric 		return;
190653654Seric 	}
190753654Seric 
190853654Seric 	mapname = p;
190967848Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
191053654Seric 		continue;
191153654Seric 	if (*p != '\0')
191253654Seric 		*p++ = '\0';
191358050Seric 	while (isascii(*p) && isspace(*p))
191453654Seric 		p++;
191558050Seric 	if (!(isascii(*p) && isalnum(*p)))
191653654Seric 	{
191753654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
191853654Seric 		return;
191953654Seric 	}
192053654Seric 	classname = p;
192158050Seric 	while (isascii(*++p) && isalnum(*p))
192253654Seric 		continue;
192353654Seric 	if (*p != '\0')
192453654Seric 		*p++ = '\0';
192558050Seric 	while (isascii(*p) && isspace(*p))
192653654Seric 		p++;
192753654Seric 
192853654Seric 	/* look up the class */
192953654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
193053654Seric 	if (class == NULL)
193153654Seric 	{
193253654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
193353654Seric 		return;
193453654Seric 	}
193553654Seric 
193653654Seric 	/* enter the map */
193764078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
193864078Seric 	s->s_map.map_class = &class->s_mapclass;
193964078Seric 	s->s_map.map_mname = newstr(mapname);
194053654Seric 
194164078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
194264078Seric 		s->s_map.map_mflags |= MF_VALID;
194364078Seric 
194464078Seric 	if (tTd(37, 5))
194564078Seric 	{
194664078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
194764078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
194864078Seric 			s->s_map.map_mflags,
194964078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
195064078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
195164078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
195264078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
195364078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
195464078Seric 	}
195553654Seric }
195658112Seric /*
195767903Seric **  INITTIMEOUTS -- parse and set timeout values
195858112Seric **
195958112Seric **	Parameters:
196058112Seric **		val -- a pointer to the values.  If NULL, do initial
196158112Seric **			settings.
196258112Seric **
196358112Seric **	Returns:
196458112Seric **		none.
196558112Seric **
196658112Seric **	Side Effects:
196758112Seric **		Initializes the TimeOuts structure
196858112Seric */
196958112Seric 
197064255Seric #define SECONDS
197158112Seric #define MINUTES	* 60
197258112Seric #define HOUR	* 3600
197358112Seric 
197467903Seric inittimeouts(val)
197558112Seric 	register char *val;
197658112Seric {
197758112Seric 	register char *p;
197858671Seric 	extern time_t convtime();
197958112Seric 
198058112Seric 	if (val == NULL)
198158112Seric 	{
198258112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
198358112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
198458112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
198558112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
198658112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
198758112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
198858112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
198958112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
199058112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
199158112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
199258112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
199364255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
199467711Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
199558112Seric 		return;
199658112Seric 	}
199758112Seric 
199858112Seric 	for (;; val = p)
199958112Seric 	{
200058112Seric 		while (isascii(*val) && isspace(*val))
200158112Seric 			val++;
200258112Seric 		if (*val == '\0')
200358112Seric 			break;
200458112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
200558112Seric 			continue;
200658112Seric 		if (*p != '\0')
200758112Seric 			*p++ = '\0';
200858112Seric 
200958112Seric 		if (isascii(*val) && isdigit(*val))
201058112Seric 		{
201158112Seric 			/* old syntax -- set everything */
201258796Seric 			TimeOuts.to_mail = convtime(val, 'm');
201358112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
201458112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
201558112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
201658112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
201758112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
201858112Seric 			continue;
201958112Seric 		}
202058112Seric 		else
202158112Seric 		{
202267711Seric 			register char *q = strchr(val, ':');
202358112Seric 
202467711Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
202558112Seric 			{
202658112Seric 				/* syntax error */
202758112Seric 				continue;
202858112Seric 			}
202958112Seric 			*q++ = '\0';
203067903Seric 			settimeout(val, q);
203167903Seric 		}
203267903Seric 	}
203367903Seric }
203467903Seric /*
203567903Seric **  SETTIMEOUT -- set an individual timeout
203667903Seric **
203767903Seric **	Parameters:
203867903Seric **		name -- the name of the timeout.
203967903Seric **		val -- the value of the timeout.
204067903Seric **
204167903Seric **	Returns:
204267903Seric **		none.
204367903Seric */
204458112Seric 
204567903Seric settimeout(name, val)
204667903Seric 	char *name;
204767903Seric 	char *val;
204867903Seric {
204967903Seric 	register char *p;
205067903Seric 	time_t to;
205167903Seric 	extern time_t convtime();
205267903Seric 
205367903Seric 	to = convtime(val, 'm');
205467903Seric 	p = strchr(name, '.');
205567903Seric 	if (p != NULL)
205667903Seric 		*p++ = '\0';
205767903Seric 
205867903Seric 	if (strcasecmp(name, "initial") == 0)
205967903Seric 		TimeOuts.to_initial = to;
206067903Seric 	else if (strcasecmp(name, "mail") == 0)
206167903Seric 		TimeOuts.to_mail = to;
206267903Seric 	else if (strcasecmp(name, "rcpt") == 0)
206367903Seric 		TimeOuts.to_rcpt = to;
206467903Seric 	else if (strcasecmp(name, "datainit") == 0)
206567903Seric 		TimeOuts.to_datainit = to;
206667903Seric 	else if (strcasecmp(name, "datablock") == 0)
206767903Seric 		TimeOuts.to_datablock = to;
206867903Seric 	else if (strcasecmp(name, "datafinal") == 0)
206967903Seric 		TimeOuts.to_datafinal = to;
207067903Seric 	else if (strcasecmp(name, "command") == 0)
207167903Seric 		TimeOuts.to_nextcommand = to;
207267903Seric 	else if (strcasecmp(name, "rset") == 0)
207367903Seric 		TimeOuts.to_rset = to;
207467903Seric 	else if (strcasecmp(name, "helo") == 0)
207567903Seric 		TimeOuts.to_helo = to;
207667903Seric 	else if (strcasecmp(name, "quit") == 0)
207767903Seric 		TimeOuts.to_quit = to;
207867903Seric 	else if (strcasecmp(name, "misc") == 0)
207967903Seric 		TimeOuts.to_miscshort = to;
208067903Seric 	else if (strcasecmp(name, "ident") == 0)
208167903Seric 		TimeOuts.to_ident = to;
208267903Seric 	else if (strcasecmp(name, "fileopen") == 0)
208367903Seric 		TimeOuts.to_fileopen = to;
208467903Seric 	else if (strcasecmp(name, "queuewarn") == 0)
208567903Seric 	{
208667903Seric 		to = convtime(val, 'h');
208767946Seric 		if (p == NULL || strcmp(p, "*") == 0)
208867903Seric 		{
208967903Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
209067903Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
209167903Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
209258112Seric 		}
209367903Seric 		else if (strcasecmp(p, "normal") == 0)
209467903Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
209567903Seric 		else if (strcasecmp(p, "urgent") == 0)
209667903Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
209767903Seric 		else if (strcasecmp(p, "non-urgent") == 0)
209867903Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
209967903Seric 		else
210067903Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
210158112Seric 	}
210267903Seric 	else if (strcasecmp(name, "queuereturn") == 0)
210367903Seric 	{
210467903Seric 		to = convtime(val, 'd');
210567903Seric 		if (p == NULL || strcmp(p, "*") == 0)
210667903Seric 		{
210767903Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
210867903Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
210967903Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
211067903Seric 		}
211167903Seric 		else if (strcasecmp(p, "normal") == 0)
211267903Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
211367903Seric 		else if (strcasecmp(p, "urgent") == 0)
211467903Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
211567903Seric 		else if (strcasecmp(p, "non-urgent") == 0)
211667903Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
211767903Seric 		else
211867903Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
211967903Seric 	}
212067903Seric 	else
212167903Seric 		syserr("settimeout: invalid timeout %s", name);
212258112Seric }
2123