xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 68856)
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*68856Seric static char sccsid[] = "@(#)readcf.c	8.87 (Berkeley) 04/22/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.
514432Seric **
523308Seric **	Parameters:
533308Seric **		cfname -- control file name.
5454973Seric **		safe -- TRUE if this is the system config file;
5554973Seric **			FALSE otherwise.
5655012Seric **		e -- the main envelope.
573308Seric **
583308Seric **	Returns:
593308Seric **		none.
603308Seric **
613308Seric **	Side Effects:
623308Seric **		Builds several internal tables.
633308Seric */
643308Seric 
6555012Seric readcf(cfname, safe, e)
663308Seric 	char *cfname;
6754973Seric 	bool safe;
6855012Seric 	register ENVELOPE *e;
693308Seric {
703308Seric 	FILE *cf;
718547Seric 	int ruleset = 0;
7268481Seric 	int nextruleset = MAXRWSETS;
738547Seric 	char *q;
749350Seric 	struct rewrite *rwp = NULL;
7557135Seric 	char *bp;
7664718Seric 	auto char *ep;
7757589Seric 	int nfuzzy;
7864133Seric 	char *file;
7964133Seric 	bool optional;
8068481Seric 	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];
8768481Seric 	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 
13868481Seric 		/* 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 
17168481Seric 			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 */
18268481Seric 			*p++ = MACROEXPAND;
18368481Seric 
18468481Seric 			/* convert macro name to code */
18568481Seric 			*p = macid(p, &ep);
18668481Seric 			if (ep != p)
18768481Seric 				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';
22368529Seric 			expand(&bp[1], exbuf, sizeof exbuf, e);
22465066Seric 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
22568711Seric 					     sizeof pvpbuf, NULL, 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
29968481Seric 			{
30056678Seric 				syserr("R line: null LHS");
30168481Seric 				rwp->r_lhs = null_list;
30268481Seric 			}
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';
31168529Seric 			expand(q, exbuf, sizeof exbuf, e);
31265066Seric 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
31368711Seric 					     sizeof pvpbuf, NULL, 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
36368481Seric 			{
36456678Seric 				syserr("R line: null RHS");
36568481Seric 				rwp->r_rhs = null_list;
36668481Seric 			}
3673308Seric 			break;
3683308Seric 
3694072Seric 		  case 'S':		/* select rewriting set */
37064440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
37164440Seric 				continue;
37268481Seric 			if (!isascii(*p))
37364440Seric 			{
37464440Seric 				syserr("invalid argument to S line: \"%.20s\"",
37564440Seric 					&bp[1]);
37664440Seric 				break;
37764440Seric 			}
37868481Seric 			if (isdigit(*p))
3798056Seric 			{
38068481Seric 				ruleset = atoi(p);
38168481Seric 				if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
38268481Seric 				{
38368481Seric 					syserr("bad ruleset %d (%d max)",
38468481Seric 						ruleset, MAXRWSETS / 2);
38568481Seric 					ruleset = 0;
38668481Seric 				}
3878056Seric 			}
38868481Seric 			else
38968481Seric 			{
39068481Seric 				STAB *s;
39168481Seric 				char delim;
39268481Seric 
39368481Seric 				q = p;
39468481Seric 				while (*p != '\0' && isascii(*p) &&
39568481Seric 				       (isalnum(*p) || strchr("-_$", *p) != NULL))
39668481Seric 					p++;
39768481Seric 				while (isascii(*p) && isspace(*p))
39868481Seric 					*p++ = '\0';
39968481Seric 				delim = *p;
40068481Seric 				if (delim != '\0')
40168481Seric 					*p++ = '\0';
40268481Seric 				s = stab(q, ST_RULESET, ST_ENTER);
40368481Seric 				if (s->s_ruleset != 0)
40468481Seric 					ruleset = s->s_ruleset;
40568481Seric 				else if (delim == '=')
40668481Seric 				{
40768481Seric 					ruleset = atoi(p);
40868481Seric 					if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
40968481Seric 					{
41068481Seric 						syserr("bad ruleset %s = %d (%d max)",
41168481Seric 							q, ruleset, MAXRWSETS / 2);
41268481Seric 						ruleset = 0;
41368481Seric 					}
41468481Seric 				}
41568481Seric 				else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
41668481Seric 				{
41768481Seric 					syserr("%s: too many named rulesets (%d max)",
41868481Seric 						q, MAXRWSETS / 2);
41968481Seric 					ruleset = 0;
42068481Seric 				}
42168481Seric 				s->s_ruleset = ruleset;
42268481Seric 			}
4234072Seric 			rwp = NULL;
4244072Seric 			break;
4254072Seric 
4263308Seric 		  case 'D':		/* macro definition */
42768481Seric 			mid = macid(&bp[1], &ep);
42868481Seric 			p = munchstring(ep, NULL);
42968481Seric 			define(mid, newstr(p), e);
4303308Seric 			break;
4313308Seric 
4323387Seric 		  case 'H':		/* required header line */
43368717Seric 			(void) chompheader(&bp[1], TRUE, NULL, e);
4343387Seric 			break;
4353387Seric 
4364061Seric 		  case 'C':		/* word class */
43768481Seric 		  case 'T':		/* trusted user (set class `t') */
43868481Seric 			if (bp[0] == 'C')
4394061Seric 			{
44068481Seric 				mid = macid(&bp[1], &ep);
44168529Seric 				expand(ep, exbuf, sizeof exbuf, e);
44268481Seric 				p = exbuf;
44368481Seric 			}
44468481Seric 			else
44568481Seric 			{
44668481Seric 				mid = 't';
44768481Seric 				p = &bp[1];
44868481Seric 			}
44968481Seric 			while (*p != '\0')
45068481Seric 			{
4514061Seric 				register char *wd;
4524061Seric 				char delim;
4534061Seric 
45458050Seric 				while (*p != '\0' && isascii(*p) && isspace(*p))
4554061Seric 					p++;
4564061Seric 				wd = p;
45758050Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4584061Seric 					p++;
4594061Seric 				delim = *p;
4604061Seric 				*p = '\0';
4614061Seric 				if (wd[0] != '\0')
46268481Seric 					setclass(mid, wd);
4634061Seric 				*p = delim;
4644061Seric 			}
4654061Seric 			break;
4664061Seric 
46759272Seric 		  case 'F':		/* word class from file */
46868481Seric 			mid = macid(&bp[1], &ep);
46968481Seric 			for (p = ep; isascii(*p) && isspace(*p); )
47064133Seric 				p++;
47164133Seric 			if (p[0] == '-' && p[1] == 'o')
47264133Seric 			{
47364133Seric 				optional = TRUE;
47464133Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
47564133Seric 					p++;
47664133Seric 				while (isascii(*p) && isspace(*p))
47768481Seric 					p++;
47864133Seric 			}
47964133Seric 			else
48064133Seric 				optional = FALSE;
48164133Seric 			file = p;
48264133Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
48364133Seric 				p++;
48459272Seric 			if (*p == '\0')
48559272Seric 				p = "%s";
48659272Seric 			else
48759272Seric 			{
48859272Seric 				*p = '\0';
48959272Seric 				while (isascii(*++p) && isspace(*p))
49059272Seric 					continue;
49159272Seric 			}
49264133Seric 			fileclass(bp[1], file, p, safe, optional);
49359272Seric 			break;
49459272Seric 
49559156Seric #ifdef XLA
49659156Seric 		  case 'L':		/* extended load average description */
49759156Seric 			xla_init(&bp[1]);
49859156Seric 			break;
49959156Seric #endif
50059156Seric 
5014096Seric 		  case 'M':		/* define mailer */
50257135Seric 			makemailer(&bp[1]);
5034096Seric 			break;
5044096Seric 
5058252Seric 		  case 'O':		/* set option */
50658734Seric 			setoption(bp[1], &bp[2], safe, FALSE, e);
5078252Seric 			break;
5088252Seric 
5098252Seric 		  case 'P':		/* set precedence */
5108252Seric 			if (NumPriorities >= MAXPRIORITIES)
5118252Seric 			{
5128547Seric 				toomany('P', MAXPRIORITIES);
5138252Seric 				break;
5148252Seric 			}
51557135Seric 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
5168252Seric 				continue;
5178252Seric 			if (*p == '\0')
5188252Seric 				goto badline;
5198252Seric 			*p = '\0';
52057135Seric 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
5218252Seric 			Priorities[NumPriorities].pri_val = atoi(++p);
5228252Seric 			NumPriorities++;
5238252Seric 			break;
5248252Seric 
52552645Seric 		  case 'V':		/* configuration syntax version */
52664440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
52764440Seric 				continue;
52864440Seric 			if (!isascii(*p) || !isdigit(*p))
52964440Seric 			{
53064440Seric 				syserr("invalid argument to V line: \"%.20s\"",
53164440Seric 					&bp[1]);
53264440Seric 				break;
53364440Seric 			}
53464718Seric 			ConfigLevel = strtol(p, &ep, 10);
53568805Seric 
53668805Seric 			/*
53768805Seric 			**  Do heuristic tweaking for back compatibility.
53868805Seric 			*/
53968805Seric 
54064279Seric 			if (ConfigLevel >= 5)
54164279Seric 			{
54264279Seric 				/* level 5 configs have short name in $w */
54364279Seric 				p = macvalue('w', e);
54464279Seric 				if (p != NULL && (p = strchr(p, '.')) != NULL)
54564279Seric 					*p = '\0';
54664279Seric 			}
54768805Seric 			if (ConfigLevel >= 6)
54868805Seric 			{
54968805Seric 				ColonOkInAddr = FALSE;
55068805Seric 			}
55168805Seric 
55268805Seric 			/*
55368805Seric 			**  Look for vendor code.
55468805Seric 			*/
55568805Seric 
55664718Seric 			if (*ep++ == '/')
55764718Seric 			{
55864718Seric 				/* extract vendor code */
55964718Seric 				for (p = ep; isascii(*p) && isalpha(*p); )
56064718Seric 					p++;
56164718Seric 				*p = '\0';
56264718Seric 
56364718Seric 				if (!setvendor(ep))
56464718Seric 					syserr("invalid V line vendor code: \"%s\"",
56564718Seric 						ep);
56664718Seric 			}
56752645Seric 			break;
56852645Seric 
56953654Seric 		  case 'K':
57057135Seric 			makemapentry(&bp[1]);
57153654Seric 			break;
57253654Seric 
5733308Seric 		  default:
5744061Seric 		  badline:
57557135Seric 			syserr("unknown control line \"%s\"", bp);
5763308Seric 		}
57757135Seric 		if (bp != buf)
57857135Seric 			free(bp);
5793308Seric 	}
58052637Seric 	if (ferror(cf))
58152637Seric 	{
58252647Seric 		syserr("I/O read error", cfname);
58352637Seric 		exit(EX_OSFILE);
58452637Seric 	}
58552637Seric 	fclose(cf);
5869381Seric 	FileName = NULL;
58756836Seric 
58868481Seric 	/* initialize host maps from local service tables */
58968481Seric 	inithostmaps();
59068481Seric 
59168481Seric 	/* determine if we need to do special name-server frotz */
59267905Seric 	{
59368481Seric 		int nmaps;
59468481Seric 		char *maptype[MAXMAPSTACK];
59568481Seric 		short mapreturn[MAXMAPACTIONS];
59668481Seric 
59768481Seric 		nmaps = switch_map_find("hosts", maptype, mapreturn);
59868481Seric 		UseNameServer = FALSE;
59968481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
60068481Seric 		{
60168481Seric 			register int mapno;
60268481Seric 
60368481Seric 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
60468481Seric 			{
60568481Seric 				if (strcmp(maptype[mapno], "dns") == 0)
60668481Seric 					UseNameServer = TRUE;
60768481Seric 			}
60868481Seric 		}
60968481Seric 
61068481Seric #ifdef HESIOD
61168481Seric 		nmaps = switch_map_find("passwd", maptype, mapreturn);
61268481Seric 		UseHesiod = FALSE;
61368481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
61468481Seric 		{
61568481Seric 			register int mapno;
61668481Seric 
61768481Seric 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
61868481Seric 			{
61968481Seric 				if (strcmp(maptype[mapno], "hesiod") == 0)
62068481Seric 					UseHesiod = TRUE;
62168481Seric 			}
62268481Seric 		}
62368204Seric #endif
62467905Seric 	}
6254096Seric }
6264096Seric /*
6278547Seric **  TOOMANY -- signal too many of some option
6288547Seric **
6298547Seric **	Parameters:
6308547Seric **		id -- the id of the error line
6318547Seric **		maxcnt -- the maximum possible values
6328547Seric **
6338547Seric **	Returns:
6348547Seric **		none.
6358547Seric **
6368547Seric **	Side Effects:
6378547Seric **		gives a syserr.
6388547Seric */
6398547Seric 
6408547Seric toomany(id, maxcnt)
6418547Seric 	char id;
6428547Seric 	int maxcnt;
6438547Seric {
6449381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
6458547Seric }
6468547Seric /*
6474432Seric **  FILECLASS -- read members of a class from a file
6484432Seric **
6494432Seric **	Parameters:
6504432Seric **		class -- class to define.
6514432Seric **		filename -- name of file to read.
6524432Seric **		fmt -- scanf string to use for match.
65364133Seric **		safe -- if set, this is a safe read.
65464133Seric **		optional -- if set, it is not an error for the file to
65564133Seric **			not exist.
6564432Seric **
6574432Seric **	Returns:
6584432Seric **		none
6594432Seric **
6604432Seric **	Side Effects:
6614432Seric **
6624432Seric **		puts all lines in filename that match a scanf into
6634432Seric **			the named class.
6644432Seric */
6654432Seric 
66664133Seric fileclass(class, filename, fmt, safe, optional)
6674432Seric 	int class;
6684432Seric 	char *filename;
6694432Seric 	char *fmt;
67054973Seric 	bool safe;
67164133Seric 	bool optional;
6724432Seric {
67325808Seric 	FILE *f;
67468513Seric 	int sff;
6754432Seric 	char buf[MAXLINE];
6764432Seric 
67766101Seric 	if (tTd(37, 2))
67866101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
67966101Seric 
68066031Seric 	if (filename[0] == '|')
68166031Seric 	{
68266031Seric 		syserr("fileclass: pipes (F%c%s) not supported due to security problems",
68366031Seric 			class, filename);
68466031Seric 		return;
68566031Seric 	}
68668513Seric 	sff = SFF_REGONLY;
68768513Seric 	if (safe)
68868513Seric 		sff |= SFF_OPENASROOT;
68968513Seric 	f = safefopen(filename, O_RDONLY, 0, sff);
69068602Seric 	if (f == NULL)
69154973Seric 	{
69268602Seric 		if (!optional)
69368602Seric 			syserr("fileclass: cannot open %s", filename);
6944432Seric 		return;
6954432Seric 	}
6964432Seric 
6974432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
6984432Seric 	{
6994432Seric 		register STAB *s;
70025808Seric 		register char *p;
70125808Seric # ifdef SCANF
7024432Seric 		char wordbuf[MAXNAME+1];
7034432Seric 
7044432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
7054432Seric 			continue;
70625808Seric 		p = wordbuf;
70756795Seric # else /* SCANF */
70825808Seric 		p = buf;
70956795Seric # endif /* SCANF */
71025808Seric 
71125808Seric 		/*
71225808Seric 		**  Break up the match into words.
71325808Seric 		*/
71425808Seric 
71525808Seric 		while (*p != '\0')
71625808Seric 		{
71725808Seric 			register char *q;
71825808Seric 
71925808Seric 			/* strip leading spaces */
72058050Seric 			while (isascii(*p) && isspace(*p))
72125808Seric 				p++;
72225808Seric 			if (*p == '\0')
72325808Seric 				break;
72425808Seric 
72525808Seric 			/* find the end of the word */
72625808Seric 			q = p;
72758050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
72825808Seric 				p++;
72925808Seric 			if (*p != '\0')
73025808Seric 				*p++ = '\0';
73125808Seric 
73225808Seric 			/* enter the word in the symbol table */
73366101Seric 			setclass(class, q);
73425808Seric 		}
7354432Seric 	}
7364432Seric 
73754973Seric 	(void) fclose(f);
7384432Seric }
7394432Seric /*
7404096Seric **  MAKEMAILER -- define a new mailer.
7414096Seric **
7424096Seric **	Parameters:
74310327Seric **		line -- description of mailer.  This is in labeled
74410327Seric **			fields.  The fields are:
74568481Seric **			   A -- the argv for this mailer
74668481Seric **			   C -- the character set for MIME conversions
74768481Seric **			   D -- the directory to run in
74868481Seric **			   E -- the eol string
74968481Seric **			   F -- the flags associated with the mailer
75068481Seric **			   L -- the maximum line length
75168481Seric **			   M -- the maximum message size
75268816Seric **			   N -- the niceness at which to run
75368479Seric **			   P -- the path to the mailer
75468481Seric **			   R -- the recipient rewriting set
75568479Seric **			   S -- the sender rewriting set
75668481Seric **			   T -- the mailer type (for DSNs)
75768481Seric **			   U -- the uid to run as
75810327Seric **			The first word is the canonical name of the mailer.
7594096Seric **
7604096Seric **	Returns:
7614096Seric **		none.
7624096Seric **
7634096Seric **	Side Effects:
7644096Seric **		enters the mailer into the mailer table.
7654096Seric */
7663308Seric 
76721066Seric makemailer(line)
7684096Seric 	char *line;
7694096Seric {
7704096Seric 	register char *p;
7718067Seric 	register struct mailer *m;
7728067Seric 	register STAB *s;
7738067Seric 	int i;
77410327Seric 	char fcode;
77558020Seric 	auto char *endp;
7764096Seric 	extern int NextMailer;
77710327Seric 	extern char **makeargv();
77810327Seric 	extern char *munchstring();
7794096Seric 
78010327Seric 	/* allocate a mailer and set up defaults */
78110327Seric 	m = (struct mailer *) xalloc(sizeof *m);
78210327Seric 	bzero((char *) m, sizeof *m);
78310327Seric 	m->m_eol = "\n";
78468481Seric 	m->m_uid = m->m_gid = 0;
78510327Seric 
78610327Seric 	/* collect the mailer name */
78758050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
78810327Seric 		continue;
78910327Seric 	if (*p != '\0')
79010327Seric 		*p++ = '\0';
79110327Seric 	m->m_name = newstr(line);
79210327Seric 
79310327Seric 	/* now scan through and assign info from the fields */
79410327Seric 	while (*p != '\0')
79510327Seric 	{
79658333Seric 		auto char *delimptr;
79758333Seric 
79858050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
79910327Seric 			p++;
80010327Seric 
80110327Seric 		/* p now points to field code */
80210327Seric 		fcode = *p;
80310327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
80410327Seric 			p++;
80510327Seric 		if (*p++ != '=')
80610327Seric 		{
80752637Seric 			syserr("mailer %s: `=' expected", m->m_name);
80810327Seric 			return;
80910327Seric 		}
81058050Seric 		while (isascii(*p) && isspace(*p))
81110327Seric 			p++;
81210327Seric 
81310327Seric 		/* p now points to the field body */
81458333Seric 		p = munchstring(p, &delimptr);
81510327Seric 
81610327Seric 		/* install the field into the mailer struct */
81710327Seric 		switch (fcode)
81810327Seric 		{
81910327Seric 		  case 'P':		/* pathname */
82010327Seric 			m->m_mailer = newstr(p);
82110327Seric 			break;
82210327Seric 
82310327Seric 		  case 'F':		/* flags */
82410687Seric 			for (; *p != '\0'; p++)
82558050Seric 				if (!(isascii(*p) && isspace(*p)))
82652637Seric 					setbitn(*p, m->m_flags);
82710327Seric 			break;
82810327Seric 
82910327Seric 		  case 'S':		/* sender rewriting ruleset */
83010327Seric 		  case 'R':		/* recipient rewriting ruleset */
83158020Seric 			i = strtol(p, &endp, 10);
83210327Seric 			if (i < 0 || i >= MAXRWSETS)
83310327Seric 			{
83410327Seric 				syserr("invalid rewrite set, %d max", MAXRWSETS);
83510327Seric 				return;
83610327Seric 			}
83710327Seric 			if (fcode == 'S')
83858020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
83910327Seric 			else
84058020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
84158020Seric 
84258020Seric 			p = endp;
84359985Seric 			if (*p++ == '/')
84458020Seric 			{
84558020Seric 				i = strtol(p, NULL, 10);
84658020Seric 				if (i < 0 || i >= MAXRWSETS)
84758020Seric 				{
84858020Seric 					syserr("invalid rewrite set, %d max",
84958020Seric 						MAXRWSETS);
85058020Seric 					return;
85158020Seric 				}
85258020Seric 				if (fcode == 'S')
85358020Seric 					m->m_sh_rwset = i;
85458020Seric 				else
85558020Seric 					m->m_rh_rwset = i;
85658020Seric 			}
85710327Seric 			break;
85810327Seric 
85910327Seric 		  case 'E':		/* end of line string */
86010327Seric 			m->m_eol = newstr(p);
86110327Seric 			break;
86210327Seric 
86310327Seric 		  case 'A':		/* argument vector */
86410327Seric 			m->m_argv = makeargv(p);
86510327Seric 			break;
86610701Seric 
86710701Seric 		  case 'M':		/* maximum message size */
86810701Seric 			m->m_maxsize = atol(p);
86910701Seric 			break;
87052106Seric 
87152106Seric 		  case 'L':		/* maximum line length */
87252106Seric 			m->m_linelimit = atoi(p);
87352106Seric 			break;
87458935Seric 
87568816Seric 		  case 'N':		/* run niceness */
87668816Seric 			m->m_nice = atoi(p);
87768816Seric 			break;
87868816Seric 
87958935Seric 		  case 'D':		/* working directory */
88058935Seric 			m->m_execdir = newstr(p);
88158935Seric 			break;
88268481Seric 
88368481Seric 		  case 'C':		/* default charset */
88468481Seric 			m->m_defcharset = newstr(p);
88568481Seric 			break;
88668481Seric 
88768481Seric 		  case 'T':		/* MTA Type */
88868481Seric 			m->m_mtatype = newstr(p);
88968481Seric 			p = strchr(m->m_mtatype, '/');
89068481Seric 			if (p != NULL)
89168481Seric 			{
89268481Seric 				*p++ = '\0';
89368481Seric 				if (*p == '\0')
89468481Seric 					p = NULL;
89568481Seric 			}
89668481Seric 			if (p == NULL)
89768481Seric 				m->m_addrtype = m->m_mtatype;
89868481Seric 			else
89968481Seric 			{
90068481Seric 				m->m_addrtype = p;
90168481Seric 				p = strchr(p, '/');
90268481Seric 			}
90368481Seric 			if (p != NULL)
90468481Seric 			{
90568481Seric 				*p++ = '\0';
90668481Seric 				if (*p == '\0')
90768481Seric 					p = NULL;
90868481Seric 			}
90968481Seric 			if (p == NULL)
91068481Seric 				m->m_diagtype = m->m_mtatype;
91168481Seric 			else
91268481Seric 				m->m_diagtype = p;
91368481Seric 			break;
91468481Seric 
91568481Seric 		  case 'U':		/* user id */
91668481Seric 			if (isascii(*p) && !isdigit(*p))
91768481Seric 			{
91868481Seric 				char *q = p;
91968481Seric 				struct passwd *pw;
92068481Seric 
92168481Seric 				while (isascii(*p) && isalnum(*p))
92268481Seric 					p++;
92368481Seric 				while (isascii(*p) && isspace(*p))
92468481Seric 					*p++ = '\0';
92568481Seric 				if (*p != '\0')
92668481Seric 					*p++ = '\0';
92768693Seric 				pw = sm_getpwnam(q);
92868481Seric 				if (pw == NULL)
92968481Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
93068481Seric 				else
93168481Seric 				{
93268481Seric 					m->m_uid = pw->pw_uid;
93368481Seric 					m->m_gid = pw->pw_gid;
93468481Seric 				}
93568481Seric 			}
93668481Seric 			else
93768481Seric 			{
93868481Seric 				auto char *q;
93968481Seric 
94068481Seric 				m->m_uid = strtol(p, &q, 0);
94168481Seric 				p = q;
94268481Seric 			}
94368481Seric 			while (isascii(*p) && isspace(*p))
94468481Seric 				p++;
94568481Seric 			if (*p == '\0')
94668481Seric 				break;
94768481Seric 			if (isascii(*p) && !isdigit(*p))
94868481Seric 			{
94968481Seric 				char *q = p;
95068481Seric 				struct group *gr;
95168481Seric 
95268481Seric 				while (isascii(*p) && isalnum(*p))
95368481Seric 					p++;
95468481Seric 				*p++ = '\0';
95568481Seric 				gr = getgrnam(q);
95668481Seric 				if (gr == NULL)
95768481Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
95868481Seric 				else
95968481Seric 					m->m_gid = gr->gr_gid;
96068481Seric 			}
96168481Seric 			else
96268481Seric 			{
96368481Seric 				m->m_gid = strtol(p, NULL, 0);
96468481Seric 			}
96568481Seric 			break;
96610327Seric 		}
96710327Seric 
96858333Seric 		p = delimptr;
96910327Seric 	}
97010327Seric 
97158321Seric 	/* do some rationality checking */
97258321Seric 	if (m->m_argv == NULL)
97358321Seric 	{
97458321Seric 		syserr("M%s: A= argument required", m->m_name);
97558321Seric 		return;
97658321Seric 	}
97758321Seric 	if (m->m_mailer == NULL)
97858321Seric 	{
97958321Seric 		syserr("M%s: P= argument required", m->m_name);
98058321Seric 		return;
98158321Seric 	}
98258321Seric 
9834096Seric 	if (NextMailer >= MAXMAILERS)
9844096Seric 	{
9859381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
9864096Seric 		return;
9874096Seric 	}
98857402Seric 
98968481Seric 	/* do some heuristic cleanup for back compatibility */
99068481Seric 	if (bitnset(M_LIMITS, m->m_flags))
99168481Seric 	{
99268481Seric 		if (m->m_linelimit == 0)
99368481Seric 			m->m_linelimit = SMTPLINELIM;
99468481Seric 		if (ConfigLevel < 2)
99568481Seric 			setbitn(M_7BITS, m->m_flags);
99668481Seric 	}
99768481Seric 
99868481Seric 	if (ConfigLevel < 6 &&
99968481Seric 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
100068481Seric 	     strcmp(m->m_mailer, "[TCP]") == 0))
100168481Seric 	{
100268481Seric 		if (m->m_mtatype == NULL)
100368481Seric 			m->m_mtatype = "dns";
100468481Seric 		if (m->m_addrtype == NULL)
100568481Seric 			m->m_addrtype = "rfc822";
100668481Seric 		if (m->m_diagtype == NULL)
100768481Seric 			m->m_diagtype = "smtp";
100868481Seric 	}
100968481Seric 
101068481Seric 	/* enter the mailer into the symbol table */
101110327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
101257402Seric 	if (s->s_mailer != NULL)
101357402Seric 	{
101457402Seric 		i = s->s_mailer->m_mno;
101557402Seric 		free(s->s_mailer);
101657402Seric 	}
101757402Seric 	else
101857402Seric 	{
101957402Seric 		i = NextMailer++;
102057402Seric 	}
102157402Seric 	Mailer[i] = s->s_mailer = m;
102257454Seric 	m->m_mno = i;
102310327Seric }
102410327Seric /*
102510327Seric **  MUNCHSTRING -- translate a string into internal form.
102610327Seric **
102710327Seric **	Parameters:
102810327Seric **		p -- the string to munch.
102958333Seric **		delimptr -- if non-NULL, set to the pointer of the
103058333Seric **			field delimiter character.
103110327Seric **
103210327Seric **	Returns:
103310327Seric **		the munched string.
103410327Seric */
10354096Seric 
103610327Seric char *
103758333Seric munchstring(p, delimptr)
103810327Seric 	register char *p;
103958333Seric 	char **delimptr;
104010327Seric {
104110327Seric 	register char *q;
104210327Seric 	bool backslash = FALSE;
104310327Seric 	bool quotemode = FALSE;
104410327Seric 	static char buf[MAXLINE];
10454096Seric 
104610327Seric 	for (q = buf; *p != '\0'; p++)
10474096Seric 	{
104810327Seric 		if (backslash)
104910327Seric 		{
105010327Seric 			/* everything is roughly literal */
105110357Seric 			backslash = FALSE;
105210327Seric 			switch (*p)
105310327Seric 			{
105410327Seric 			  case 'r':		/* carriage return */
105510327Seric 				*q++ = '\r';
105610327Seric 				continue;
105710327Seric 
105810327Seric 			  case 'n':		/* newline */
105910327Seric 				*q++ = '\n';
106010327Seric 				continue;
106110327Seric 
106210327Seric 			  case 'f':		/* form feed */
106310327Seric 				*q++ = '\f';
106410327Seric 				continue;
106510327Seric 
106610327Seric 			  case 'b':		/* backspace */
106710327Seric 				*q++ = '\b';
106810327Seric 				continue;
106910327Seric 			}
107010327Seric 			*q++ = *p;
107110327Seric 		}
107210327Seric 		else
107310327Seric 		{
107410327Seric 			if (*p == '\\')
107510327Seric 				backslash = TRUE;
107610327Seric 			else if (*p == '"')
107710327Seric 				quotemode = !quotemode;
107810327Seric 			else if (quotemode || *p != ',')
107910327Seric 				*q++ = *p;
108010327Seric 			else
108110327Seric 				break;
108210327Seric 		}
10834096Seric 	}
10844096Seric 
108558333Seric 	if (delimptr != NULL)
108658333Seric 		*delimptr = p;
108710327Seric 	*q++ = '\0';
108810327Seric 	return (buf);
108910327Seric }
109010327Seric /*
109110327Seric **  MAKEARGV -- break up a string into words
109210327Seric **
109310327Seric **	Parameters:
109410327Seric **		p -- the string to break up.
109510327Seric **
109610327Seric **	Returns:
109710327Seric **		a char **argv (dynamically allocated)
109810327Seric **
109910327Seric **	Side Effects:
110010327Seric **		munges p.
110110327Seric */
11024096Seric 
110310327Seric char **
110410327Seric makeargv(p)
110510327Seric 	register char *p;
110610327Seric {
110710327Seric 	char *q;
110810327Seric 	int i;
110910327Seric 	char **avp;
111010327Seric 	char *argv[MAXPV + 1];
111110327Seric 
111210327Seric 	/* take apart the words */
111310327Seric 	i = 0;
111410327Seric 	while (*p != '\0' && i < MAXPV)
11154096Seric 	{
111610327Seric 		q = p;
111758050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
111810327Seric 			p++;
111958050Seric 		while (isascii(*p) && isspace(*p))
112010327Seric 			*p++ = '\0';
112110327Seric 		argv[i++] = newstr(q);
11224096Seric 	}
112310327Seric 	argv[i++] = NULL;
11244096Seric 
112510327Seric 	/* now make a copy of the argv */
112610327Seric 	avp = (char **) xalloc(sizeof *avp * i);
112716893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
112810327Seric 
112910327Seric 	return (avp);
11303308Seric }
11313308Seric /*
11323308Seric **  PRINTRULES -- print rewrite rules (for debugging)
11333308Seric **
11343308Seric **	Parameters:
11353308Seric **		none.
11363308Seric **
11373308Seric **	Returns:
11383308Seric **		none.
11393308Seric **
11403308Seric **	Side Effects:
11413308Seric **		prints rewrite rules.
11423308Seric */
11433308Seric 
11443308Seric printrules()
11453308Seric {
11463308Seric 	register struct rewrite *rwp;
11474072Seric 	register int ruleset;
11483308Seric 
11494072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
11503308Seric 	{
11514072Seric 		if (RewriteRules[ruleset] == NULL)
11524072Seric 			continue;
11538067Seric 		printf("\n----Rule Set %d:", ruleset);
11543308Seric 
11554072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
11563308Seric 		{
11578067Seric 			printf("\nLHS:");
11588067Seric 			printav(rwp->r_lhs);
11598067Seric 			printf("RHS:");
11608067Seric 			printav(rwp->r_rhs);
11613308Seric 		}
11623308Seric 	}
11633308Seric }
116468481Seric /*
116568481Seric **  PRINTMAILER -- print mailer structure (for debugging)
116668481Seric **
116768481Seric **	Parameters:
116868481Seric **		m -- the mailer to print
116968481Seric **
117068481Seric **	Returns:
117168481Seric **		none.
117268481Seric */
11734319Seric 
117468481Seric printmailer(m)
117568481Seric 	register MAILER *m;
117668481Seric {
117768481Seric 	int j;
117868481Seric 
117968481Seric 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
118068481Seric 		m->m_mno, m->m_name,
118168481Seric 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
118268481Seric 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
118368481Seric 		m->m_uid, m->m_gid);
118468481Seric 	for (j = '\0'; j <= '\177'; j++)
118568481Seric 		if (bitnset(j, m->m_flags))
118668481Seric 			(void) putchar(j);
118768481Seric 	printf(" L=%d E=", m->m_linelimit);
118868481Seric 	xputs(m->m_eol);
118968481Seric 	if (m->m_defcharset != NULL)
119068481Seric 		printf(" C=%s", m->m_defcharset);
119168481Seric 	printf(" T=%s/%s/%s",
119268481Seric 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
119368481Seric 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
119468481Seric 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
119568481Seric 	if (m->m_argv != NULL)
119668481Seric 	{
119768481Seric 		char **a = m->m_argv;
119868481Seric 
119968481Seric 		printf(" A=");
120068481Seric 		while (*a != NULL)
120168481Seric 		{
120268481Seric 			if (a != m->m_argv)
120368481Seric 				printf(" ");
120468481Seric 			xputs(*a++);
120568481Seric 		}
120668481Seric 	}
120768481Seric 	printf("\n");
120868481Seric }
12094096Seric /*
12108256Seric **  SETOPTION -- set global processing option
12118256Seric **
12128256Seric **	Parameters:
12138256Seric **		opt -- option name.
12148256Seric **		val -- option value (as a text string).
121521755Seric **		safe -- set if this came from a configuration file.
121621755Seric **			Some options (if set from the command line) will
121721755Seric **			reset the user id to avoid security problems.
12188269Seric **		sticky -- if set, don't let other setoptions override
12198269Seric **			this value.
122058734Seric **		e -- the main envelope.
12218256Seric **
12228256Seric **	Returns:
12238256Seric **		none.
12248256Seric **
12258256Seric **	Side Effects:
12268256Seric **		Sets options as implied by the arguments.
12278256Seric */
12288256Seric 
122910687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
12308269Seric 
123157207Seric 
123266334Seric #if NAMED_BIND
123357207Seric 
123457207Seric struct resolverflags
123557207Seric {
123657207Seric 	char	*rf_name;	/* name of the flag */
123757207Seric 	long	rf_bits;	/* bits to set/clear */
123857207Seric } ResolverFlags[] =
123957207Seric {
124057207Seric 	"debug",	RES_DEBUG,
124157207Seric 	"aaonly",	RES_AAONLY,
124257207Seric 	"usevc",	RES_USEVC,
124357207Seric 	"primary",	RES_PRIMARY,
124457207Seric 	"igntc",	RES_IGNTC,
124557207Seric 	"recurse",	RES_RECURSE,
124657207Seric 	"defnames",	RES_DEFNAMES,
124757207Seric 	"stayopen",	RES_STAYOPEN,
124857207Seric 	"dnsrch",	RES_DNSRCH,
124965583Seric 	"true",		0,		/* to avoid error on old syntax */
125057207Seric 	NULL,		0
125157207Seric };
125257207Seric 
125357207Seric #endif
125457207Seric 
125568481Seric struct optioninfo
125668481Seric {
125768481Seric 	char	*o_name;	/* long name of option */
125868481Seric 	u_char	o_code;		/* short name of option */
125968481Seric 	bool	o_safe;		/* safe for random people to use */
126068481Seric } OptionTab[] =
126168481Seric {
126268481Seric 	"SevenBitInput",	'7',		TRUE,
126368481Seric 	"EightBitMode",		'8',		TRUE,
126468481Seric 	"AliasFile",		'A',		FALSE,
126568481Seric 	"AliasWait",		'a',		FALSE,
126668481Seric 	"BlankSub",		'B',		FALSE,
126768481Seric 	"MinFreeBlocks",	'b',		TRUE,
126868481Seric 	"CheckpointInterval",	'C',		TRUE,
126968481Seric 	"HoldExpensive",	'c',		FALSE,
127068481Seric 	"AutoRebuildAliases",	'D',		FALSE,
127168481Seric 	"DeliveryMode",		'd',		TRUE,
127268481Seric 	"ErrorHeader",		'E',		FALSE,
127368481Seric 	"ErrorMode",		'e',		TRUE,
127468481Seric 	"TempFileMode",		'F',		FALSE,
127568481Seric 	"SaveFromLine",		'f',		FALSE,
127668481Seric 	"MatchGECOS",		'G',		FALSE,
127768481Seric 	"HelpFile",		'H',		FALSE,
127868481Seric 	"MaxHopCount",		'h',		FALSE,
127968569Seric 	"ResolverOptions",	'I',		FALSE,
128068481Seric 	"IgnoreDots",		'i',		TRUE,
128168481Seric 	"ForwardPath",		'J',		FALSE,
128268481Seric 	"SendMimeErrors",	'j',		TRUE,
128368481Seric 	"ConnectionCacheSize",	'k',		FALSE,
128468481Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
128568481Seric 	"UseErrorsTo",		'l',		FALSE,
128668481Seric 	"LogLevel",		'L',		FALSE,
128768481Seric 	"MeToo",		'm',		TRUE,
128868481Seric 	"CheckAliases",		'n',		FALSE,
128968481Seric 	"OldStyleHeaders",	'o',		TRUE,
129068481Seric 	"DaemonPortOptions",	'O',		FALSE,
129168481Seric 	"PrivacyOptions",	'p',		TRUE,
129268481Seric 	"PostmasterCopy",	'P',		FALSE,
129368481Seric 	"QueueFactor",		'q',		FALSE,
129468481Seric 	"QueueDirectory",	'Q',		FALSE,
129568481Seric 	"DontPruneRoutes",	'R',		FALSE,
129668481Seric 	"Timeout",		'r',		TRUE,
129768481Seric 	"StatusFile",		'S',		FALSE,
129868481Seric 	"SuperSafe",		's',		TRUE,
129968481Seric 	"QueueTimeout",		'T',		FALSE,
130068481Seric 	"TimeZoneSpec",		't',		FALSE,
130168481Seric 	"UserDatabaseSpec",	'U',		FALSE,
130268481Seric 	"DefaultUser",		'u',		FALSE,
130368481Seric 	"FallbackMXhost",	'V',		FALSE,
130468481Seric 	"Verbose",		'v',		TRUE,
130568481Seric 	"TryNullMXList",	'w',		TRUE,
130668481Seric 	"QueueLA",		'x',		FALSE,
130768481Seric 	"RefuseLA",		'X',		FALSE,
130868481Seric 	"RecipientFactor",	'y',		FALSE,
130968569Seric 	"ForkEachJob",		'Y',		FALSE,
131068481Seric 	"ClassFactor",		'z',		FALSE,
131168569Seric 	"RetryFactor",		'Z',		FALSE,
131268481Seric #define O_QUEUESORTORD	0x81
131368481Seric 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
131468481Seric #define O_MQA		0x83
131568481Seric 	"MinQueueAge",		O_MQA,		TRUE,
131668481Seric #define O_MHSA		0x84
131768481Seric /*
131868481Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
131968481Seric */
132068481Seric #define O_DEFCHARSET	0x85
132168481Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
132268481Seric #define O_SSFILE	0x86
132368481Seric 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
132468481Seric #define O_DIALDELAY	0x87
132568481Seric 	"DialDelay",		O_DIALDELAY,	TRUE,
132668481Seric #define O_NORCPTACTION	0x88
132768481Seric 	"NoRecipientAction",	O_NORCPTACTION,	TRUE,
132868490Seric #define O_SAFEFILEENV	0x89
132968490Seric 	"SafeFileEnvironment",	O_SAFEFILEENV,	FALSE,
133068569Seric #define O_MAXMSGSIZE	0x8a
133168569Seric 	"MaxMessageSize",	O_MAXMSGSIZE,	FALSE,
133268756Seric #define O_COLONOKINADDR	0x8b
133368756Seric 	"ColonOkInAddr",	O_COLONOKINADDR, TRUE,
133468481Seric 
133568481Seric 	NULL,			'\0',		FALSE,
133668481Seric };
133768481Seric 
133868481Seric 
133968481Seric 
134058734Seric setoption(opt, val, safe, sticky, e)
134168481Seric 	u_char opt;
13428256Seric 	char *val;
134321755Seric 	bool safe;
13448269Seric 	bool sticky;
134558734Seric 	register ENVELOPE *e;
13468256Seric {
134757207Seric 	register char *p;
134868481Seric 	register struct optioninfo *o;
134968481Seric 	char *subopt;
13508265Seric 	extern bool atobool();
135112633Seric 	extern time_t convtime();
135214879Seric 	extern int QueueLA;
135314879Seric 	extern int RefuseLA;
135464718Seric 	extern bool Warn_Q_option;
13558256Seric 
135668481Seric 	errno = 0;
135768481Seric 	if (opt == ' ')
135868481Seric 	{
135968481Seric 		/* full word options */
136068481Seric 		struct optioninfo *sel;
136168481Seric 
136268481Seric 		p = strchr(val, '=');
136368481Seric 		if (p == NULL)
136468481Seric 			p = &val[strlen(val)];
136568481Seric 		while (*--p == ' ')
136668481Seric 			continue;
136768481Seric 		while (*++p == ' ')
136868481Seric 			*p = '\0';
136968481Seric 		if (p == val)
137068481Seric 		{
137168481Seric 			syserr("readcf: null option name");
137268481Seric 			return;
137368481Seric 		}
137468481Seric 		if (*p == '=')
137568481Seric 			*p++ = '\0';
137668481Seric 		while (*p == ' ')
137768481Seric 			p++;
137868481Seric 		subopt = strchr(val, '.');
137968481Seric 		if (subopt != NULL)
138068481Seric 			*subopt++ = '\0';
138168481Seric 		sel = NULL;
138268481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
138368481Seric 		{
138468481Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
138568481Seric 				continue;
138668481Seric 			if (strlen(o->o_name) == strlen(val))
138768481Seric 			{
138868481Seric 				/* completely specified -- this must be it */
138968481Seric 				sel = NULL;
139068481Seric 				break;
139168481Seric 			}
139268481Seric 			if (sel != NULL)
139368481Seric 				break;
139468481Seric 			sel = o;
139568481Seric 		}
139668481Seric 		if (sel != NULL && o->o_name == NULL)
139768481Seric 			o = sel;
139868481Seric 		else if (o->o_name == NULL)
139968481Seric 		{
140068481Seric 			syserr("readcf: unknown option name %s", val);
140168481Seric 			return;
140268481Seric 		}
140368481Seric 		else if (sel != NULL)
140468481Seric 		{
140568481Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
140668481Seric 				val, sel->o_name, o->o_name);
140768481Seric 			return;
140868481Seric 		}
140968481Seric 		if (strlen(val) != strlen(o->o_name))
141068481Seric 		{
141168481Seric 			bool oldVerbose = Verbose;
141268481Seric 
141368481Seric 			Verbose = TRUE;
141468481Seric 			message("Option %s used as abbreviation for %s",
141568481Seric 				val, o->o_name);
141668481Seric 			Verbose = oldVerbose;
141768481Seric 		}
141868481Seric 		opt = o->o_code;
141968481Seric 		val = p;
142068481Seric 	}
142168481Seric 	else
142268481Seric 	{
142368481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
142468481Seric 		{
142568481Seric 			if (o->o_code == opt)
142668481Seric 				break;
142768481Seric 		}
142868481Seric 		subopt = NULL;
142968481Seric 	}
143068481Seric 
14318256Seric 	if (tTd(37, 1))
143268481Seric 	{
143368481Seric 		printf(isascii(opt) && isprint(opt) ?
143468481Seric 			    "setoption %s (%c).%s=%s" :
143568481Seric 			    "setoption %s (0x%x).%s=%s",
143668481Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
143768481Seric 			opt,
143868481Seric 			subopt == NULL ? "" : subopt,
143968481Seric 			val);
144068481Seric 	}
14418256Seric 
14428256Seric 	/*
14438269Seric 	**  See if this option is preset for us.
14448256Seric 	*/
14458256Seric 
144659731Seric 	if (!sticky && bitnset(opt, StickyOpt))
14478269Seric 	{
14489341Seric 		if (tTd(37, 1))
14499341Seric 			printf(" (ignored)\n");
14508269Seric 		return;
14518269Seric 	}
14528269Seric 
145321755Seric 	/*
145421755Seric 	**  Check to see if this option can be specified by this user.
145521755Seric 	*/
145621755Seric 
145763787Seric 	if (!safe && RealUid == 0)
145821755Seric 		safe = TRUE;
145968481Seric 	if (!safe && !o->o_safe)
146021755Seric 	{
146139111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
146221755Seric 		{
146336582Sbostic 			if (tTd(37, 1))
146436582Sbostic 				printf(" (unsafe)");
146563787Seric 			if (RealUid != geteuid())
146636582Sbostic 			{
146751210Seric 				if (tTd(37, 1))
146851210Seric 					printf("(Resetting uid)");
146963787Seric 				(void) setgid(RealGid);
147063787Seric 				(void) setuid(RealUid);
147136582Sbostic 			}
147221755Seric 		}
147321755Seric 	}
147451210Seric 	if (tTd(37, 1))
147517985Seric 		printf("\n");
14768269Seric 
147768481Seric 	switch (opt & 0xff)
14788256Seric 	{
147959709Seric 	  case '7':		/* force seven-bit input */
148068481Seric 		SevenBitInput = atobool(val);
148152106Seric 		break;
148252106Seric 
148368481Seric 	  case '8':		/* handling of 8-bit input */
148468481Seric 		switch (*val)
148568481Seric 		{
148668481Seric 		  case 'm':		/* convert 8-bit, convert MIME */
148768481Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
148868481Seric 			break;
148968481Seric 
149068481Seric 		  case 'p':		/* pass 8 bit, convert MIME */
1491*68856Seric 			MimeMode = MM_CVTMIME|MM_PASS8BIT;
149268481Seric 			break;
149368481Seric 
149468481Seric 		  case 's':		/* strict adherence */
149568481Seric 			MimeMode = MM_CVTMIME;
149668481Seric 			break;
149768481Seric 
1498*68856Seric #if 0
1499*68856Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
1500*68856Seric 			MimeMode = 0;
1501*68856Seric 			break;
1502*68856Seric 
1503*68856Seric 		  case 'j':		/* "just send 8" */
1504*68856Seric 			MimeMode = MM_PASS8BIT;
1505*68856Seric 			break;
1506*68856Seric 
150768481Seric 		  case 'a':		/* encode 8 bit if available */
150868481Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
150968481Seric 			break;
151068481Seric 
151168481Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
151268481Seric 			MimeMode = MM_MIME8BIT;
151368481Seric 			break;
1514*68856Seric #endif
151568481Seric 
151668481Seric 		  default:
151768481Seric 			syserr("Unknown 8-bit mode %c", *val);
151868481Seric 			exit(EX_USAGE);
151968481Seric 		}
152068481Seric 		break;
152168481Seric 
15228256Seric 	  case 'A':		/* set default alias file */
15239381Seric 		if (val[0] == '\0')
152459672Seric 			setalias("aliases");
15259381Seric 		else
152659672Seric 			setalias(val);
15278256Seric 		break;
15288256Seric 
152917474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
153017474Seric 		if (val[0] == '\0')
153164796Seric 			SafeAlias = 5 * 60;		/* five minutes */
153217474Seric 		else
153364796Seric 			SafeAlias = convtime(val, 'm');
153417474Seric 		break;
153517474Seric 
153616843Seric 	  case 'B':		/* substitution for blank character */
153716843Seric 		SpaceSub = val[0];
153816843Seric 		if (SpaceSub == '\0')
153916843Seric 			SpaceSub = ' ';
154016843Seric 		break;
154116843Seric 
154259283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
154359283Seric 		p = strchr(val, '/');
154459283Seric 		if (p != NULL)
154559283Seric 		{
154659283Seric 			*p++ = '\0';
154759283Seric 			MaxMessageSize = atol(p);
154859283Seric 		}
154958082Seric 		MinBlocksFree = atol(val);
155058082Seric 		break;
155158082Seric 
15529284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
15539381Seric 		NoConnect = atobool(val);
15549284Seric 		break;
15559284Seric 
155651305Seric 	  case 'C':		/* checkpoint every N addresses */
155751305Seric 		CheckpointInterval = atoi(val);
155824944Seric 		break;
155924944Seric 
15609284Seric 	  case 'd':		/* delivery mode */
15619284Seric 		switch (*val)
15628269Seric 		{
15639284Seric 		  case '\0':
156458734Seric 			e->e_sendmode = SM_DELIVER;
15658269Seric 			break;
15668269Seric 
156710755Seric 		  case SM_QUEUE:	/* queue only */
156810755Seric #ifndef QUEUE
156910755Seric 			syserr("need QUEUE to set -odqueue");
157056795Seric #endif /* QUEUE */
157110755Seric 			/* fall through..... */
157210755Seric 
15739284Seric 		  case SM_DELIVER:	/* do everything */
15749284Seric 		  case SM_FORK:		/* fork after verification */
157558734Seric 			e->e_sendmode = *val;
15768269Seric 			break;
15778269Seric 
15788269Seric 		  default:
15799284Seric 			syserr("Unknown delivery mode %c", *val);
15808269Seric 			exit(EX_USAGE);
15818269Seric 		}
15828269Seric 		break;
15838269Seric 
15849146Seric 	  case 'D':		/* rebuild alias database as needed */
15859381Seric 		AutoRebuild = atobool(val);
15869146Seric 		break;
15879146Seric 
158855372Seric 	  case 'E':		/* error message header/header file */
158955379Seric 		if (*val != '\0')
159055379Seric 			ErrMsgFile = newstr(val);
159155372Seric 		break;
159255372Seric 
15938269Seric 	  case 'e':		/* set error processing mode */
15948269Seric 		switch (*val)
15958269Seric 		{
15969381Seric 		  case EM_QUIET:	/* be silent about it */
15979381Seric 		  case EM_MAIL:		/* mail back */
15989381Seric 		  case EM_BERKNET:	/* do berknet error processing */
15999381Seric 		  case EM_WRITE:	/* write back (or mail) */
16009381Seric 		  case EM_PRINT:	/* print errors normally (default) */
160158734Seric 			e->e_errormode = *val;
16028269Seric 			break;
16038269Seric 		}
16048269Seric 		break;
16058269Seric 
16069049Seric 	  case 'F':		/* file mode */
160717975Seric 		FileMode = atooct(val) & 0777;
16089049Seric 		break;
16099049Seric 
16108269Seric 	  case 'f':		/* save Unix-style From lines on front */
16119381Seric 		SaveFrom = atobool(val);
16128269Seric 		break;
16138269Seric 
161453735Seric 	  case 'G':		/* match recipients against GECOS field */
161553735Seric 		MatchGecos = atobool(val);
161653735Seric 		break;
161753735Seric 
16188256Seric 	  case 'g':		/* default gid */
161968481Seric   g_opt:
162064133Seric 		if (isascii(*val) && isdigit(*val))
162164133Seric 			DefGid = atoi(val);
162264133Seric 		else
162364133Seric 		{
162464133Seric 			register struct group *gr;
162564133Seric 
162664133Seric 			DefGid = -1;
162764133Seric 			gr = getgrnam(val);
162864133Seric 			if (gr == NULL)
162968481Seric 				syserr("readcf: option %c: unknown group %s",
163068481Seric 					opt, val);
163164133Seric 			else
163264133Seric 				DefGid = gr->gr_gid;
163364133Seric 		}
16348256Seric 		break;
16358256Seric 
16368256Seric 	  case 'H':		/* help file */
16379381Seric 		if (val[0] == '\0')
16388269Seric 			HelpFile = "sendmail.hf";
16399381Seric 		else
16409381Seric 			HelpFile = newstr(val);
16418256Seric 		break;
16428256Seric 
164351305Seric 	  case 'h':		/* maximum hop count */
164451305Seric 		MaxHopCount = atoi(val);
164551305Seric 		break;
164651305Seric 
164735651Seric 	  case 'I':		/* use internet domain name server */
164866334Seric #if NAMED_BIND
164957207Seric 		for (p = val; *p != 0; )
165057207Seric 		{
165157207Seric 			bool clearmode;
165257207Seric 			char *q;
165357207Seric 			struct resolverflags *rfp;
165457207Seric 
165557207Seric 			while (*p == ' ')
165657207Seric 				p++;
165757207Seric 			if (*p == '\0')
165857207Seric 				break;
165957207Seric 			clearmode = FALSE;
166057207Seric 			if (*p == '-')
166157207Seric 				clearmode = TRUE;
166257207Seric 			else if (*p != '+')
166357207Seric 				p--;
166457207Seric 			p++;
166557207Seric 			q = p;
166658050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
166757207Seric 				p++;
166857207Seric 			if (*p != '\0')
166957207Seric 				*p++ = '\0';
167068759Seric 			if (strcasecmp(q, "HasWildcardMX") == 0)
167168759Seric 			{
167268759Seric 				NoMXforCanon = !clearmode;
167368759Seric 				continue;
167468759Seric 			}
167557207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
167657207Seric 			{
167757207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
167857207Seric 					break;
167957207Seric 			}
168064923Seric 			if (rfp->rf_name == NULL)
168164923Seric 				syserr("readcf: I option value %s unrecognized", q);
168264923Seric 			else if (clearmode)
168357207Seric 				_res.options &= ~rfp->rf_bits;
168457207Seric 			else
168557207Seric 				_res.options |= rfp->rf_bits;
168657207Seric 		}
168757207Seric 		if (tTd(8, 2))
168868759Seric 			printf("_res.options = %x, HasWildcardMX = %d\n",
168968759Seric 				_res.options, !NoMXforCanon);
169057207Seric #else
169157207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
169257207Seric #endif
169335651Seric 		break;
169435651Seric 
16958269Seric 	  case 'i':		/* ignore dot lines in message */
16969381Seric 		IgnrDot = atobool(val);
16978269Seric 		break;
16988269Seric 
169959730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
170059730Seric 		SendMIMEErrors = atobool(val);
170159730Seric 		break;
170259730Seric 
170357136Seric 	  case 'J':		/* .forward search path */
170457136Seric 		ForwardPath = newstr(val);
170557136Seric 		break;
170657136Seric 
170754967Seric 	  case 'k':		/* connection cache size */
170854967Seric 		MaxMciCache = atoi(val);
170956215Seric 		if (MaxMciCache < 0)
171056215Seric 			MaxMciCache = 0;
171154967Seric 		break;
171254967Seric 
171354967Seric 	  case 'K':		/* connection cache timeout */
171458796Seric 		MciCacheTimeout = convtime(val, 'm');
171554967Seric 		break;
171654967Seric 
171761104Seric 	  case 'l':		/* use Errors-To: header */
171861104Seric 		UseErrorsTo = atobool(val);
171961104Seric 		break;
172061104Seric 
17218256Seric 	  case 'L':		/* log level */
172264140Seric 		if (safe || LogLevel < atoi(val))
172364140Seric 			LogLevel = atoi(val);
17248256Seric 		break;
17258256Seric 
17268269Seric 	  case 'M':		/* define macro */
172768267Seric 		p = newstr(&val[1]);
172868267Seric 		if (!safe)
172968267Seric 			cleanstrcpy(p, p, MAXNAME);
173068267Seric 		define(val[0], p, CurEnv);
173116878Seric 		sticky = FALSE;
17328269Seric 		break;
17338269Seric 
17348269Seric 	  case 'm':		/* send to me too */
17359381Seric 		MeToo = atobool(val);
17368269Seric 		break;
17378269Seric 
173825820Seric 	  case 'n':		/* validate RHS in newaliases */
173925820Seric 		CheckAliases = atobool(val);
174025820Seric 		break;
174125820Seric 
174261104Seric 	    /* 'N' available -- was "net name" */
174361104Seric 
174458851Seric 	  case 'O':		/* daemon options */
174558851Seric 		setdaemonoptions(val);
174658851Seric 		break;
174758851Seric 
17488269Seric 	  case 'o':		/* assume old style headers */
17499381Seric 		if (atobool(val))
17509341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
17519341Seric 		else
17529341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
17538269Seric 		break;
17548269Seric 
175558082Seric 	  case 'p':		/* select privacy level */
175658082Seric 		p = val;
175758082Seric 		for (;;)
175858082Seric 		{
175958082Seric 			register struct prival *pv;
176058082Seric 			extern struct prival PrivacyValues[];
176158082Seric 
176258082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
176358082Seric 				p++;
176458082Seric 			if (*p == '\0')
176558082Seric 				break;
176658082Seric 			val = p;
176758082Seric 			while (isascii(*p) && isalnum(*p))
176858082Seric 				p++;
176958082Seric 			if (*p != '\0')
177058082Seric 				*p++ = '\0';
177158082Seric 
177258082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
177358082Seric 			{
177458082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
177558082Seric 					break;
177658082Seric 			}
177758886Seric 			if (pv->pv_name == NULL)
177858886Seric 				syserr("readcf: Op line: %s unrecognized", val);
177958082Seric 			PrivacyFlags |= pv->pv_flag;
178058082Seric 		}
178168479Seric 		sticky = FALSE;
178258082Seric 		break;
178358082Seric 
178424944Seric 	  case 'P':		/* postmaster copy address for returned mail */
178524944Seric 		PostMasterCopy = newstr(val);
178624944Seric 		break;
178724944Seric 
178824944Seric 	  case 'q':		/* slope of queue only function */
178924944Seric 		QueueFactor = atoi(val);
179024944Seric 		break;
179124944Seric 
17928256Seric 	  case 'Q':		/* queue directory */
17939381Seric 		if (val[0] == '\0')
17948269Seric 			QueueDir = "mqueue";
17959381Seric 		else
17969381Seric 			QueueDir = newstr(val);
179758789Seric 		if (RealUid != 0 && !safe)
179864718Seric 			Warn_Q_option = TRUE;
17998256Seric 		break;
18008256Seric 
180158148Seric 	  case 'R':		/* don't prune routes */
180258148Seric 		DontPruneRoutes = atobool(val);
180358148Seric 		break;
180458148Seric 
18058256Seric 	  case 'r':		/* read timeout */
180668481Seric 		if (subopt == NULL)
180768481Seric 			inittimeouts(val);
180868481Seric 		else
180968481Seric 			settimeout(subopt, val);
18108256Seric 		break;
18118256Seric 
18128256Seric 	  case 'S':		/* status file */
18139381Seric 		if (val[0] == '\0')
18148269Seric 			StatFile = "sendmail.st";
18159381Seric 		else
18169381Seric 			StatFile = newstr(val);
18178256Seric 		break;
18188256Seric 
18198265Seric 	  case 's':		/* be super safe, even if expensive */
18209381Seric 		SuperSafe = atobool(val);
18218256Seric 		break;
18228256Seric 
18238256Seric 	  case 'T':		/* queue timeout */
182458737Seric 		p = strchr(val, '/');
182558737Seric 		if (p != NULL)
182658737Seric 		{
182758737Seric 			*p++ = '\0';
182868481Seric 			settimeout("queuewarn", p);
182958737Seric 		}
183068481Seric 		settimeout("queuereturn", val);
183154967Seric 		break;
18328256Seric 
18338265Seric 	  case 't':		/* time zone name */
183452106Seric 		TimeZoneSpec = newstr(val);
18358265Seric 		break;
18368265Seric 
183750556Seric 	  case 'U':		/* location of user database */
183851360Seric 		UdbSpec = newstr(val);
183950556Seric 		break;
184050556Seric 
18418256Seric 	  case 'u':		/* set default uid */
184268481Seric 		for (p = val; *p != '\0'; p++)
184368481Seric 		{
184468481Seric 			if (*p == '.' || *p == '/' || *p == ':')
184568481Seric 			{
184668481Seric 				*p++ = '\0';
184768481Seric 				break;
184868481Seric 			}
184968481Seric 		}
185064133Seric 		if (isascii(*val) && isdigit(*val))
185164133Seric 			DefUid = atoi(val);
185264133Seric 		else
185364133Seric 		{
185464133Seric 			register struct passwd *pw;
185564133Seric 
185664133Seric 			DefUid = -1;
185768693Seric 			pw = sm_getpwnam(val);
185864133Seric 			if (pw == NULL)
185964133Seric 				syserr("readcf: option u: unknown user %s", val);
186064133Seric 			else
186168481Seric 			{
186264133Seric 				DefUid = pw->pw_uid;
186368481Seric 				DefGid = pw->pw_gid;
186468481Seric 			}
186564133Seric 		}
186640973Sbostic 		setdefuser();
18678256Seric 
186868481Seric 		/* handle the group if it is there */
186968481Seric 		if (*p == '\0')
187068481Seric 			break;
187168481Seric 		val = p;
187268481Seric 		goto g_opt;
187368481Seric 
187458851Seric 	  case 'V':		/* fallback MX host */
187558851Seric 		FallBackMX = newstr(val);
187658851Seric 		break;
187758851Seric 
18788269Seric 	  case 'v':		/* run in verbose mode */
18799381Seric 		Verbose = atobool(val);
18808256Seric 		break;
18818256Seric 
188263837Seric 	  case 'w':		/* if we are best MX, try host directly */
188363837Seric 		TryNullMXList = atobool(val);
188463837Seric 		break;
188561104Seric 
188661104Seric 	    /* 'W' available -- was wizard password */
188761104Seric 
188814879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
188914879Seric 		QueueLA = atoi(val);
189014879Seric 		break;
189114879Seric 
189214879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
189314879Seric 		RefuseLA = atoi(val);
189414879Seric 		break;
189514879Seric 
189624981Seric 	  case 'y':		/* work recipient factor */
189724981Seric 		WkRecipFact = atoi(val);
189824981Seric 		break;
189924981Seric 
190024981Seric 	  case 'Y':		/* fork jobs during queue runs */
190124952Seric 		ForkQueueRuns = atobool(val);
190224952Seric 		break;
190324952Seric 
190424981Seric 	  case 'z':		/* work message class factor */
190524981Seric 		WkClassFact = atoi(val);
190624981Seric 		break;
190724981Seric 
190824981Seric 	  case 'Z':		/* work time factor */
190924981Seric 		WkTimeFact = atoi(val);
191024981Seric 		break;
191124981Seric 
191268481Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
191368481Seric 		switch (*val)
191468481Seric 		{
191568481Seric 		  case 'h':	/* Host first */
191668481Seric 		  case 'H':
191768481Seric 			QueueSortOrder = QS_BYHOST;
191868481Seric 			break;
191968481Seric 
192068481Seric 		  case 'p':	/* Priority order */
192168481Seric 		  case 'P':
192268481Seric 			QueueSortOrder = QS_BYPRIORITY;
192368481Seric 			break;
192468481Seric 
192568481Seric 		  default:
192668481Seric 			syserr("Invalid queue sort order \"%s\"", val);
192768481Seric 		}
192868481Seric 		break;
192968481Seric 
193068481Seric 	  case O_MQA:		/* minimum queue age between deliveries */
193168481Seric 		MinQueueAge = convtime(val, 'm');
193268481Seric 		break;
193368481Seric 
193468481Seric 	  case O_MHSA:		/* maximum age of cached host status */
193568481Seric 		MaxHostStatAge = convtime(val, 'm');
193668481Seric 		break;
193768481Seric 
193868481Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
193968481Seric 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
194068481Seric 		break;
194168481Seric 
194268481Seric 	  case O_SSFILE:	/* service switch file */
194368481Seric 		ServiceSwitchFile = newstr(val);
194468481Seric 		break;
194568481Seric 
194668481Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
194768481Seric 		DialDelay = convtime(val, 's');
194868481Seric 		break;
194968481Seric 
195068481Seric 	  case O_NORCPTACTION:	/* what to do if no recipient */
195168481Seric 		if (strcasecmp(val, "none") == 0)
195268481Seric 			NoRecipientAction = NRA_NO_ACTION;
195368481Seric 		else if (strcasecmp(val, "add-to") == 0)
195468481Seric 			NoRecipientAction = NRA_ADD_TO;
195568481Seric 		else if (strcasecmp(val, "add-apparently-to") == 0)
195668481Seric 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
195768481Seric 		else if (strcasecmp(val, "add-bcc") == 0)
195868481Seric 			NoRecipientAction = NRA_ADD_BCC;
195968481Seric 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
196068481Seric 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
196168481Seric 		else
196268481Seric 			syserr("Invalid NoRecipientAction: %s", val);
196368481Seric 
196468490Seric 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
196568490Seric 		SafeFileEnv = newstr(val);
196668490Seric 		break;
196768490Seric 
196868569Seric 	  case O_MAXMSGSIZE:	/* maximum message size */
196968569Seric 		MaxMessageSize = atol(p);
197068569Seric 		break;
197168569Seric 
197268756Seric 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
197368756Seric 		ColonOkInAddr = atobool(p);
197468756Seric 		break;
197568756Seric 
19768256Seric 	  default:
197768481Seric 		if (tTd(37, 1))
197868481Seric 		{
197968481Seric 			if (isascii(opt) && isprint(opt))
198068481Seric 				printf("Warning: option %c unknown\n", opt);
198168481Seric 			else
198268481Seric 				printf("Warning: option 0x%x unknown\n", opt);
198368481Seric 		}
19848256Seric 		break;
19858256Seric 	}
198616878Seric 	if (sticky)
198716878Seric 		setbitn(opt, StickyOpt);
19889188Seric 	return;
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 
200468481Seric setclass(class, str)
200510687Seric 	int class;
200668481Seric 	char *str;
200710687Seric {
200810687Seric 	register STAB *s;
200910687Seric 
201057943Seric 	if (tTd(37, 8))
201168481Seric 		printf("setclass(%c, %s)\n", class, str);
201268481Seric 	s = stab(str, ST_CLASS, ST_ENTER);
201310687Seric 	setbitn(class, s->s_class);
201410687Seric }
201553654Seric /*
201653654Seric **  MAKEMAPENTRY -- create a map entry
201753654Seric **
201853654Seric **	Parameters:
201953654Seric **		line -- the config file line
202053654Seric **
202153654Seric **	Returns:
202253654Seric **		TRUE if it successfully entered the map entry.
202353654Seric **		FALSE otherwise (usually syntax error).
202453654Seric **
202553654Seric **	Side Effects:
202653654Seric **		Enters the map into the dictionary.
202753654Seric */
202853654Seric 
202953654Seric void
203053654Seric makemapentry(line)
203153654Seric 	char *line;
203253654Seric {
203353654Seric 	register char *p;
203453654Seric 	char *mapname;
203553654Seric 	char *classname;
203664078Seric 	register STAB *s;
203753654Seric 	STAB *class;
203853654Seric 
203958050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
204053654Seric 		continue;
204158050Seric 	if (!(isascii(*p) && isalnum(*p)))
204253654Seric 	{
204353654Seric 		syserr("readcf: config K line: no map name");
204453654Seric 		return;
204553654Seric 	}
204653654Seric 
204753654Seric 	mapname = p;
204868481Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
204953654Seric 		continue;
205053654Seric 	if (*p != '\0')
205153654Seric 		*p++ = '\0';
205258050Seric 	while (isascii(*p) && isspace(*p))
205353654Seric 		p++;
205458050Seric 	if (!(isascii(*p) && isalnum(*p)))
205553654Seric 	{
205653654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
205753654Seric 		return;
205853654Seric 	}
205953654Seric 	classname = p;
206058050Seric 	while (isascii(*++p) && isalnum(*p))
206153654Seric 		continue;
206253654Seric 	if (*p != '\0')
206353654Seric 		*p++ = '\0';
206458050Seric 	while (isascii(*p) && isspace(*p))
206553654Seric 		p++;
206653654Seric 
206753654Seric 	/* look up the class */
206853654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
206953654Seric 	if (class == NULL)
207053654Seric 	{
207153654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
207253654Seric 		return;
207353654Seric 	}
207453654Seric 
207553654Seric 	/* enter the map */
207664078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
207764078Seric 	s->s_map.map_class = &class->s_mapclass;
207864078Seric 	s->s_map.map_mname = newstr(mapname);
207953654Seric 
208064078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
208164078Seric 		s->s_map.map_mflags |= MF_VALID;
208264078Seric 
208364078Seric 	if (tTd(37, 5))
208464078Seric 	{
208564078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
208664078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
208764078Seric 			s->s_map.map_mflags,
208864078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
208964078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
209064078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
209164078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
209264078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
209364078Seric 	}
209453654Seric }
209558112Seric /*
209668481Seric **  INITTIMEOUTS -- parse and set timeout values
209758112Seric **
209858112Seric **	Parameters:
209958112Seric **		val -- a pointer to the values.  If NULL, do initial
210058112Seric **			settings.
210158112Seric **
210258112Seric **	Returns:
210358112Seric **		none.
210458112Seric **
210558112Seric **	Side Effects:
210658112Seric **		Initializes the TimeOuts structure
210758112Seric */
210858112Seric 
210964255Seric #define SECONDS
211058112Seric #define MINUTES	* 60
211158112Seric #define HOUR	* 3600
211258112Seric 
211368481Seric inittimeouts(val)
211458112Seric 	register char *val;
211558112Seric {
211658112Seric 	register char *p;
211758671Seric 	extern time_t convtime();
211858112Seric 
211958112Seric 	if (val == NULL)
212058112Seric 	{
212158112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
212258112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
212358112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
212458112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
212558112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
212658112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
212758112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
212858112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
212958112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
213058112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
213158112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
213268481Seric #if IDENTPROTO
213364255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
213468481Seric #else
213568481Seric 		TimeOuts.to_ident = (time_t) 0 SECONDS;
213668481Seric #endif
213768481Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
213858112Seric 		return;
213958112Seric 	}
214058112Seric 
214158112Seric 	for (;; val = p)
214258112Seric 	{
214358112Seric 		while (isascii(*val) && isspace(*val))
214458112Seric 			val++;
214558112Seric 		if (*val == '\0')
214658112Seric 			break;
214758112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
214858112Seric 			continue;
214958112Seric 		if (*p != '\0')
215058112Seric 			*p++ = '\0';
215158112Seric 
215258112Seric 		if (isascii(*val) && isdigit(*val))
215358112Seric 		{
215458112Seric 			/* old syntax -- set everything */
215558796Seric 			TimeOuts.to_mail = convtime(val, 'm');
215658112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
215758112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
215858112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
215958112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
216058112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
216158112Seric 			continue;
216258112Seric 		}
216358112Seric 		else
216458112Seric 		{
216568481Seric 			register char *q = strchr(val, ':');
216658112Seric 
216768481Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
216858112Seric 			{
216958112Seric 				/* syntax error */
217058112Seric 				continue;
217158112Seric 			}
217258112Seric 			*q++ = '\0';
217368481Seric 			settimeout(val, q);
217468481Seric 		}
217568481Seric 	}
217668481Seric }
217768481Seric /*
217868481Seric **  SETTIMEOUT -- set an individual timeout
217968481Seric **
218068481Seric **	Parameters:
218168481Seric **		name -- the name of the timeout.
218268481Seric **		val -- the value of the timeout.
218368481Seric **
218468481Seric **	Returns:
218568481Seric **		none.
218668481Seric */
218758112Seric 
218868481Seric settimeout(name, val)
218968481Seric 	char *name;
219068481Seric 	char *val;
219168481Seric {
219268481Seric 	register char *p;
219368481Seric 	time_t to;
219468481Seric 	extern time_t convtime();
219568481Seric 
219668481Seric 	to = convtime(val, 'm');
219768481Seric 	p = strchr(name, '.');
219868481Seric 	if (p != NULL)
219968481Seric 		*p++ = '\0';
220068481Seric 
220168481Seric 	if (strcasecmp(name, "initial") == 0)
220268481Seric 		TimeOuts.to_initial = to;
220368481Seric 	else if (strcasecmp(name, "mail") == 0)
220468481Seric 		TimeOuts.to_mail = to;
220568481Seric 	else if (strcasecmp(name, "rcpt") == 0)
220668481Seric 		TimeOuts.to_rcpt = to;
220768481Seric 	else if (strcasecmp(name, "datainit") == 0)
220868481Seric 		TimeOuts.to_datainit = to;
220968481Seric 	else if (strcasecmp(name, "datablock") == 0)
221068481Seric 		TimeOuts.to_datablock = to;
221168481Seric 	else if (strcasecmp(name, "datafinal") == 0)
221268481Seric 		TimeOuts.to_datafinal = to;
221368481Seric 	else if (strcasecmp(name, "command") == 0)
221468481Seric 		TimeOuts.to_nextcommand = to;
221568481Seric 	else if (strcasecmp(name, "rset") == 0)
221668481Seric 		TimeOuts.to_rset = to;
221768481Seric 	else if (strcasecmp(name, "helo") == 0)
221868481Seric 		TimeOuts.to_helo = to;
221968481Seric 	else if (strcasecmp(name, "quit") == 0)
222068481Seric 		TimeOuts.to_quit = to;
222168481Seric 	else if (strcasecmp(name, "misc") == 0)
222268481Seric 		TimeOuts.to_miscshort = to;
222368481Seric 	else if (strcasecmp(name, "ident") == 0)
222468481Seric 		TimeOuts.to_ident = to;
222568481Seric 	else if (strcasecmp(name, "fileopen") == 0)
222668481Seric 		TimeOuts.to_fileopen = to;
222768481Seric 	else if (strcasecmp(name, "queuewarn") == 0)
222868481Seric 	{
222968481Seric 		to = convtime(val, 'h');
223068481Seric 		if (p == NULL || strcmp(p, "*") == 0)
223168481Seric 		{
223268481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
223368481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
223468481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
223558112Seric 		}
223668481Seric 		else if (strcasecmp(p, "normal") == 0)
223768481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
223868481Seric 		else if (strcasecmp(p, "urgent") == 0)
223968481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
224068481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
224168481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
224268481Seric 		else
224368481Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
224458112Seric 	}
224568481Seric 	else if (strcasecmp(name, "queuereturn") == 0)
224668481Seric 	{
224768481Seric 		to = convtime(val, 'd');
224868481Seric 		if (p == NULL || strcmp(p, "*") == 0)
224968481Seric 		{
225068481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
225168481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
225268481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
225368481Seric 		}
225468481Seric 		else if (strcasecmp(p, "normal") == 0)
225568481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
225668481Seric 		else if (strcasecmp(p, "urgent") == 0)
225768481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
225868481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
225968481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
226068481Seric 		else
226168481Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
226268481Seric 	}
226368481Seric 	else
226468481Seric 		syserr("settimeout: invalid timeout %s", name);
226558112Seric }
2266