xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 68756)
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*68756Seric static char sccsid[] = "@(#)readcf.c	8.80 (Berkeley) 04/09/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);
53564279Seric 			if (ConfigLevel >= 5)
53664279Seric 			{
53764279Seric 				/* level 5 configs have short name in $w */
53864279Seric 				p = macvalue('w', e);
53964279Seric 				if (p != NULL && (p = strchr(p, '.')) != NULL)
54064279Seric 					*p = '\0';
54164279Seric 			}
54264718Seric 			if (*ep++ == '/')
54364718Seric 			{
54464718Seric 				/* extract vendor code */
54564718Seric 				for (p = ep; isascii(*p) && isalpha(*p); )
54664718Seric 					p++;
54764718Seric 				*p = '\0';
54864718Seric 
54964718Seric 				if (!setvendor(ep))
55064718Seric 					syserr("invalid V line vendor code: \"%s\"",
55164718Seric 						ep);
55264718Seric 			}
55352645Seric 			break;
55452645Seric 
55553654Seric 		  case 'K':
55657135Seric 			makemapentry(&bp[1]);
55753654Seric 			break;
55853654Seric 
5593308Seric 		  default:
5604061Seric 		  badline:
56157135Seric 			syserr("unknown control line \"%s\"", bp);
5623308Seric 		}
56357135Seric 		if (bp != buf)
56457135Seric 			free(bp);
5653308Seric 	}
56652637Seric 	if (ferror(cf))
56752637Seric 	{
56852647Seric 		syserr("I/O read error", cfname);
56952637Seric 		exit(EX_OSFILE);
57052637Seric 	}
57152637Seric 	fclose(cf);
5729381Seric 	FileName = NULL;
57356836Seric 
57468481Seric 	/* initialize host maps from local service tables */
57568481Seric 	inithostmaps();
57668481Seric 
57768481Seric 	/* determine if we need to do special name-server frotz */
57867905Seric 	{
57968481Seric 		int nmaps;
58068481Seric 		char *maptype[MAXMAPSTACK];
58168481Seric 		short mapreturn[MAXMAPACTIONS];
58268481Seric 
58368481Seric 		nmaps = switch_map_find("hosts", maptype, mapreturn);
58468481Seric 		UseNameServer = FALSE;
58568481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
58668481Seric 		{
58768481Seric 			register int mapno;
58868481Seric 
58968481Seric 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
59068481Seric 			{
59168481Seric 				if (strcmp(maptype[mapno], "dns") == 0)
59268481Seric 					UseNameServer = TRUE;
59368481Seric 			}
59468481Seric 		}
59568481Seric 
59668481Seric #ifdef HESIOD
59768481Seric 		nmaps = switch_map_find("passwd", maptype, mapreturn);
59868481Seric 		UseHesiod = FALSE;
59968481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
60068481Seric 		{
60168481Seric 			register int mapno;
60268481Seric 
60368481Seric 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
60468481Seric 			{
60568481Seric 				if (strcmp(maptype[mapno], "hesiod") == 0)
60668481Seric 					UseHesiod = TRUE;
60768481Seric 			}
60868481Seric 		}
60968204Seric #endif
61067905Seric 	}
6114096Seric }
6124096Seric /*
6138547Seric **  TOOMANY -- signal too many of some option
6148547Seric **
6158547Seric **	Parameters:
6168547Seric **		id -- the id of the error line
6178547Seric **		maxcnt -- the maximum possible values
6188547Seric **
6198547Seric **	Returns:
6208547Seric **		none.
6218547Seric **
6228547Seric **	Side Effects:
6238547Seric **		gives a syserr.
6248547Seric */
6258547Seric 
6268547Seric toomany(id, maxcnt)
6278547Seric 	char id;
6288547Seric 	int maxcnt;
6298547Seric {
6309381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
6318547Seric }
6328547Seric /*
6334432Seric **  FILECLASS -- read members of a class from a file
6344432Seric **
6354432Seric **	Parameters:
6364432Seric **		class -- class to define.
6374432Seric **		filename -- name of file to read.
6384432Seric **		fmt -- scanf string to use for match.
63964133Seric **		safe -- if set, this is a safe read.
64064133Seric **		optional -- if set, it is not an error for the file to
64164133Seric **			not exist.
6424432Seric **
6434432Seric **	Returns:
6444432Seric **		none
6454432Seric **
6464432Seric **	Side Effects:
6474432Seric **
6484432Seric **		puts all lines in filename that match a scanf into
6494432Seric **			the named class.
6504432Seric */
6514432Seric 
65264133Seric fileclass(class, filename, fmt, safe, optional)
6534432Seric 	int class;
6544432Seric 	char *filename;
6554432Seric 	char *fmt;
65654973Seric 	bool safe;
65764133Seric 	bool optional;
6584432Seric {
65925808Seric 	FILE *f;
66068513Seric 	int sff;
6614432Seric 	char buf[MAXLINE];
6624432Seric 
66366101Seric 	if (tTd(37, 2))
66466101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
66566101Seric 
66666031Seric 	if (filename[0] == '|')
66766031Seric 	{
66866031Seric 		syserr("fileclass: pipes (F%c%s) not supported due to security problems",
66966031Seric 			class, filename);
67066031Seric 		return;
67166031Seric 	}
67268513Seric 	sff = SFF_REGONLY;
67368513Seric 	if (safe)
67468513Seric 		sff |= SFF_OPENASROOT;
67568513Seric 	f = safefopen(filename, O_RDONLY, 0, sff);
67668602Seric 	if (f == NULL)
67754973Seric 	{
67868602Seric 		if (!optional)
67968602Seric 			syserr("fileclass: cannot open %s", filename);
6804432Seric 		return;
6814432Seric 	}
6824432Seric 
6834432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
6844432Seric 	{
6854432Seric 		register STAB *s;
68625808Seric 		register char *p;
68725808Seric # ifdef SCANF
6884432Seric 		char wordbuf[MAXNAME+1];
6894432Seric 
6904432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
6914432Seric 			continue;
69225808Seric 		p = wordbuf;
69356795Seric # else /* SCANF */
69425808Seric 		p = buf;
69556795Seric # endif /* SCANF */
69625808Seric 
69725808Seric 		/*
69825808Seric 		**  Break up the match into words.
69925808Seric 		*/
70025808Seric 
70125808Seric 		while (*p != '\0')
70225808Seric 		{
70325808Seric 			register char *q;
70425808Seric 
70525808Seric 			/* strip leading spaces */
70658050Seric 			while (isascii(*p) && isspace(*p))
70725808Seric 				p++;
70825808Seric 			if (*p == '\0')
70925808Seric 				break;
71025808Seric 
71125808Seric 			/* find the end of the word */
71225808Seric 			q = p;
71358050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
71425808Seric 				p++;
71525808Seric 			if (*p != '\0')
71625808Seric 				*p++ = '\0';
71725808Seric 
71825808Seric 			/* enter the word in the symbol table */
71966101Seric 			setclass(class, q);
72025808Seric 		}
7214432Seric 	}
7224432Seric 
72354973Seric 	(void) fclose(f);
7244432Seric }
7254432Seric /*
7264096Seric **  MAKEMAILER -- define a new mailer.
7274096Seric **
7284096Seric **	Parameters:
72910327Seric **		line -- description of mailer.  This is in labeled
73010327Seric **			fields.  The fields are:
73168481Seric **			   A -- the argv for this mailer
73268481Seric **			   C -- the character set for MIME conversions
73368481Seric **			   D -- the directory to run in
73468481Seric **			   E -- the eol string
73568481Seric **			   F -- the flags associated with the mailer
73668481Seric **			   L -- the maximum line length
73768481Seric **			   M -- the maximum message size
73868479Seric **			   P -- the path to the mailer
73968481Seric **			   R -- the recipient rewriting set
74068479Seric **			   S -- the sender rewriting set
74168481Seric **			   T -- the mailer type (for DSNs)
74268481Seric **			   U -- the uid to run as
74310327Seric **			The first word is the canonical name of the mailer.
7444096Seric **
7454096Seric **	Returns:
7464096Seric **		none.
7474096Seric **
7484096Seric **	Side Effects:
7494096Seric **		enters the mailer into the mailer table.
7504096Seric */
7513308Seric 
75221066Seric makemailer(line)
7534096Seric 	char *line;
7544096Seric {
7554096Seric 	register char *p;
7568067Seric 	register struct mailer *m;
7578067Seric 	register STAB *s;
7588067Seric 	int i;
75910327Seric 	char fcode;
76058020Seric 	auto char *endp;
7614096Seric 	extern int NextMailer;
76210327Seric 	extern char **makeargv();
76310327Seric 	extern char *munchstring();
76410701Seric 	extern long atol();
7654096Seric 
76610327Seric 	/* allocate a mailer and set up defaults */
76710327Seric 	m = (struct mailer *) xalloc(sizeof *m);
76810327Seric 	bzero((char *) m, sizeof *m);
76910327Seric 	m->m_eol = "\n";
77068481Seric 	m->m_uid = m->m_gid = 0;
77110327Seric 
77210327Seric 	/* collect the mailer name */
77358050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
77410327Seric 		continue;
77510327Seric 	if (*p != '\0')
77610327Seric 		*p++ = '\0';
77710327Seric 	m->m_name = newstr(line);
77810327Seric 
77910327Seric 	/* now scan through and assign info from the fields */
78010327Seric 	while (*p != '\0')
78110327Seric 	{
78258333Seric 		auto char *delimptr;
78358333Seric 
78458050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
78510327Seric 			p++;
78610327Seric 
78710327Seric 		/* p now points to field code */
78810327Seric 		fcode = *p;
78910327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
79010327Seric 			p++;
79110327Seric 		if (*p++ != '=')
79210327Seric 		{
79352637Seric 			syserr("mailer %s: `=' expected", m->m_name);
79410327Seric 			return;
79510327Seric 		}
79658050Seric 		while (isascii(*p) && isspace(*p))
79710327Seric 			p++;
79810327Seric 
79910327Seric 		/* p now points to the field body */
80058333Seric 		p = munchstring(p, &delimptr);
80110327Seric 
80210327Seric 		/* install the field into the mailer struct */
80310327Seric 		switch (fcode)
80410327Seric 		{
80510327Seric 		  case 'P':		/* pathname */
80610327Seric 			m->m_mailer = newstr(p);
80710327Seric 			break;
80810327Seric 
80910327Seric 		  case 'F':		/* flags */
81010687Seric 			for (; *p != '\0'; p++)
81158050Seric 				if (!(isascii(*p) && isspace(*p)))
81252637Seric 					setbitn(*p, m->m_flags);
81310327Seric 			break;
81410327Seric 
81510327Seric 		  case 'S':		/* sender rewriting ruleset */
81610327Seric 		  case 'R':		/* recipient rewriting ruleset */
81758020Seric 			i = strtol(p, &endp, 10);
81810327Seric 			if (i < 0 || i >= MAXRWSETS)
81910327Seric 			{
82010327Seric 				syserr("invalid rewrite set, %d max", MAXRWSETS);
82110327Seric 				return;
82210327Seric 			}
82310327Seric 			if (fcode == 'S')
82458020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
82510327Seric 			else
82658020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
82758020Seric 
82858020Seric 			p = endp;
82959985Seric 			if (*p++ == '/')
83058020Seric 			{
83158020Seric 				i = strtol(p, NULL, 10);
83258020Seric 				if (i < 0 || i >= MAXRWSETS)
83358020Seric 				{
83458020Seric 					syserr("invalid rewrite set, %d max",
83558020Seric 						MAXRWSETS);
83658020Seric 					return;
83758020Seric 				}
83858020Seric 				if (fcode == 'S')
83958020Seric 					m->m_sh_rwset = i;
84058020Seric 				else
84158020Seric 					m->m_rh_rwset = i;
84258020Seric 			}
84310327Seric 			break;
84410327Seric 
84510327Seric 		  case 'E':		/* end of line string */
84610327Seric 			m->m_eol = newstr(p);
84710327Seric 			break;
84810327Seric 
84910327Seric 		  case 'A':		/* argument vector */
85010327Seric 			m->m_argv = makeargv(p);
85110327Seric 			break;
85210701Seric 
85310701Seric 		  case 'M':		/* maximum message size */
85410701Seric 			m->m_maxsize = atol(p);
85510701Seric 			break;
85652106Seric 
85752106Seric 		  case 'L':		/* maximum line length */
85852106Seric 			m->m_linelimit = atoi(p);
85952106Seric 			break;
86058935Seric 
86158935Seric 		  case 'D':		/* working directory */
86258935Seric 			m->m_execdir = newstr(p);
86358935Seric 			break;
86468481Seric 
86568481Seric 		  case 'C':		/* default charset */
86668481Seric 			m->m_defcharset = newstr(p);
86768481Seric 			break;
86868481Seric 
86968481Seric 		  case 'T':		/* MTA Type */
87068481Seric 			m->m_mtatype = newstr(p);
87168481Seric 			p = strchr(m->m_mtatype, '/');
87268481Seric 			if (p != NULL)
87368481Seric 			{
87468481Seric 				*p++ = '\0';
87568481Seric 				if (*p == '\0')
87668481Seric 					p = NULL;
87768481Seric 			}
87868481Seric 			if (p == NULL)
87968481Seric 				m->m_addrtype = m->m_mtatype;
88068481Seric 			else
88168481Seric 			{
88268481Seric 				m->m_addrtype = p;
88368481Seric 				p = strchr(p, '/');
88468481Seric 			}
88568481Seric 			if (p != NULL)
88668481Seric 			{
88768481Seric 				*p++ = '\0';
88868481Seric 				if (*p == '\0')
88968481Seric 					p = NULL;
89068481Seric 			}
89168481Seric 			if (p == NULL)
89268481Seric 				m->m_diagtype = m->m_mtatype;
89368481Seric 			else
89468481Seric 				m->m_diagtype = p;
89568481Seric 			break;
89668481Seric 
89768481Seric 		  case 'U':		/* user id */
89868481Seric 			if (isascii(*p) && !isdigit(*p))
89968481Seric 			{
90068481Seric 				char *q = p;
90168481Seric 				struct passwd *pw;
90268481Seric 
90368481Seric 				while (isascii(*p) && isalnum(*p))
90468481Seric 					p++;
90568481Seric 				while (isascii(*p) && isspace(*p))
90668481Seric 					*p++ = '\0';
90768481Seric 				if (*p != '\0')
90868481Seric 					*p++ = '\0';
90968693Seric 				pw = sm_getpwnam(q);
91068481Seric 				if (pw == NULL)
91168481Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
91268481Seric 				else
91368481Seric 				{
91468481Seric 					m->m_uid = pw->pw_uid;
91568481Seric 					m->m_gid = pw->pw_gid;
91668481Seric 				}
91768481Seric 			}
91868481Seric 			else
91968481Seric 			{
92068481Seric 				auto char *q;
92168481Seric 
92268481Seric 				m->m_uid = strtol(p, &q, 0);
92368481Seric 				p = q;
92468481Seric 			}
92568481Seric 			while (isascii(*p) && isspace(*p))
92668481Seric 				p++;
92768481Seric 			if (*p == '\0')
92868481Seric 				break;
92968481Seric 			if (isascii(*p) && !isdigit(*p))
93068481Seric 			{
93168481Seric 				char *q = p;
93268481Seric 				struct group *gr;
93368481Seric 
93468481Seric 				while (isascii(*p) && isalnum(*p))
93568481Seric 					p++;
93668481Seric 				*p++ = '\0';
93768481Seric 				gr = getgrnam(q);
93868481Seric 				if (gr == NULL)
93968481Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
94068481Seric 				else
94168481Seric 					m->m_gid = gr->gr_gid;
94268481Seric 			}
94368481Seric 			else
94468481Seric 			{
94568481Seric 				m->m_gid = strtol(p, NULL, 0);
94668481Seric 			}
94768481Seric 			break;
94810327Seric 		}
94910327Seric 
95058333Seric 		p = delimptr;
95110327Seric 	}
95210327Seric 
95358321Seric 	/* do some rationality checking */
95458321Seric 	if (m->m_argv == NULL)
95558321Seric 	{
95658321Seric 		syserr("M%s: A= argument required", m->m_name);
95758321Seric 		return;
95858321Seric 	}
95958321Seric 	if (m->m_mailer == NULL)
96058321Seric 	{
96158321Seric 		syserr("M%s: P= argument required", m->m_name);
96258321Seric 		return;
96358321Seric 	}
96458321Seric 
9654096Seric 	if (NextMailer >= MAXMAILERS)
9664096Seric 	{
9679381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
9684096Seric 		return;
9694096Seric 	}
97057402Seric 
97168481Seric 	/* do some heuristic cleanup for back compatibility */
97268481Seric 	if (bitnset(M_LIMITS, m->m_flags))
97368481Seric 	{
97468481Seric 		if (m->m_linelimit == 0)
97568481Seric 			m->m_linelimit = SMTPLINELIM;
97668481Seric 		if (ConfigLevel < 2)
97768481Seric 			setbitn(M_7BITS, m->m_flags);
97868481Seric 	}
97968481Seric 
98068481Seric 	if (ConfigLevel < 6 &&
98168481Seric 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
98268481Seric 	     strcmp(m->m_mailer, "[TCP]") == 0))
98368481Seric 	{
98468481Seric 		if (m->m_mtatype == NULL)
98568481Seric 			m->m_mtatype = "dns";
98668481Seric 		if (m->m_addrtype == NULL)
98768481Seric 			m->m_addrtype = "rfc822";
98868481Seric 		if (m->m_diagtype == NULL)
98968481Seric 			m->m_diagtype = "smtp";
99068481Seric 	}
99168481Seric 
99268481Seric 	/* enter the mailer into the symbol table */
99310327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
99457402Seric 	if (s->s_mailer != NULL)
99557402Seric 	{
99657402Seric 		i = s->s_mailer->m_mno;
99757402Seric 		free(s->s_mailer);
99857402Seric 	}
99957402Seric 	else
100057402Seric 	{
100157402Seric 		i = NextMailer++;
100257402Seric 	}
100357402Seric 	Mailer[i] = s->s_mailer = m;
100457454Seric 	m->m_mno = i;
100510327Seric }
100610327Seric /*
100710327Seric **  MUNCHSTRING -- translate a string into internal form.
100810327Seric **
100910327Seric **	Parameters:
101010327Seric **		p -- the string to munch.
101158333Seric **		delimptr -- if non-NULL, set to the pointer of the
101258333Seric **			field delimiter character.
101310327Seric **
101410327Seric **	Returns:
101510327Seric **		the munched string.
101610327Seric */
10174096Seric 
101810327Seric char *
101958333Seric munchstring(p, delimptr)
102010327Seric 	register char *p;
102158333Seric 	char **delimptr;
102210327Seric {
102310327Seric 	register char *q;
102410327Seric 	bool backslash = FALSE;
102510327Seric 	bool quotemode = FALSE;
102610327Seric 	static char buf[MAXLINE];
10274096Seric 
102810327Seric 	for (q = buf; *p != '\0'; p++)
10294096Seric 	{
103010327Seric 		if (backslash)
103110327Seric 		{
103210327Seric 			/* everything is roughly literal */
103310357Seric 			backslash = FALSE;
103410327Seric 			switch (*p)
103510327Seric 			{
103610327Seric 			  case 'r':		/* carriage return */
103710327Seric 				*q++ = '\r';
103810327Seric 				continue;
103910327Seric 
104010327Seric 			  case 'n':		/* newline */
104110327Seric 				*q++ = '\n';
104210327Seric 				continue;
104310327Seric 
104410327Seric 			  case 'f':		/* form feed */
104510327Seric 				*q++ = '\f';
104610327Seric 				continue;
104710327Seric 
104810327Seric 			  case 'b':		/* backspace */
104910327Seric 				*q++ = '\b';
105010327Seric 				continue;
105110327Seric 			}
105210327Seric 			*q++ = *p;
105310327Seric 		}
105410327Seric 		else
105510327Seric 		{
105610327Seric 			if (*p == '\\')
105710327Seric 				backslash = TRUE;
105810327Seric 			else if (*p == '"')
105910327Seric 				quotemode = !quotemode;
106010327Seric 			else if (quotemode || *p != ',')
106110327Seric 				*q++ = *p;
106210327Seric 			else
106310327Seric 				break;
106410327Seric 		}
10654096Seric 	}
10664096Seric 
106758333Seric 	if (delimptr != NULL)
106858333Seric 		*delimptr = p;
106910327Seric 	*q++ = '\0';
107010327Seric 	return (buf);
107110327Seric }
107210327Seric /*
107310327Seric **  MAKEARGV -- break up a string into words
107410327Seric **
107510327Seric **	Parameters:
107610327Seric **		p -- the string to break up.
107710327Seric **
107810327Seric **	Returns:
107910327Seric **		a char **argv (dynamically allocated)
108010327Seric **
108110327Seric **	Side Effects:
108210327Seric **		munges p.
108310327Seric */
10844096Seric 
108510327Seric char **
108610327Seric makeargv(p)
108710327Seric 	register char *p;
108810327Seric {
108910327Seric 	char *q;
109010327Seric 	int i;
109110327Seric 	char **avp;
109210327Seric 	char *argv[MAXPV + 1];
109310327Seric 
109410327Seric 	/* take apart the words */
109510327Seric 	i = 0;
109610327Seric 	while (*p != '\0' && i < MAXPV)
10974096Seric 	{
109810327Seric 		q = p;
109958050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
110010327Seric 			p++;
110158050Seric 		while (isascii(*p) && isspace(*p))
110210327Seric 			*p++ = '\0';
110310327Seric 		argv[i++] = newstr(q);
11044096Seric 	}
110510327Seric 	argv[i++] = NULL;
11064096Seric 
110710327Seric 	/* now make a copy of the argv */
110810327Seric 	avp = (char **) xalloc(sizeof *avp * i);
110916893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
111010327Seric 
111110327Seric 	return (avp);
11123308Seric }
11133308Seric /*
11143308Seric **  PRINTRULES -- print rewrite rules (for debugging)
11153308Seric **
11163308Seric **	Parameters:
11173308Seric **		none.
11183308Seric **
11193308Seric **	Returns:
11203308Seric **		none.
11213308Seric **
11223308Seric **	Side Effects:
11233308Seric **		prints rewrite rules.
11243308Seric */
11253308Seric 
11263308Seric printrules()
11273308Seric {
11283308Seric 	register struct rewrite *rwp;
11294072Seric 	register int ruleset;
11303308Seric 
11314072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
11323308Seric 	{
11334072Seric 		if (RewriteRules[ruleset] == NULL)
11344072Seric 			continue;
11358067Seric 		printf("\n----Rule Set %d:", ruleset);
11363308Seric 
11374072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
11383308Seric 		{
11398067Seric 			printf("\nLHS:");
11408067Seric 			printav(rwp->r_lhs);
11418067Seric 			printf("RHS:");
11428067Seric 			printav(rwp->r_rhs);
11433308Seric 		}
11443308Seric 	}
11453308Seric }
114668481Seric /*
114768481Seric **  PRINTMAILER -- print mailer structure (for debugging)
114868481Seric **
114968481Seric **	Parameters:
115068481Seric **		m -- the mailer to print
115168481Seric **
115268481Seric **	Returns:
115368481Seric **		none.
115468481Seric */
11554319Seric 
115668481Seric printmailer(m)
115768481Seric 	register MAILER *m;
115868481Seric {
115968481Seric 	int j;
116068481Seric 
116168481Seric 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
116268481Seric 		m->m_mno, m->m_name,
116368481Seric 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
116468481Seric 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
116568481Seric 		m->m_uid, m->m_gid);
116668481Seric 	for (j = '\0'; j <= '\177'; j++)
116768481Seric 		if (bitnset(j, m->m_flags))
116868481Seric 			(void) putchar(j);
116968481Seric 	printf(" L=%d E=", m->m_linelimit);
117068481Seric 	xputs(m->m_eol);
117168481Seric 	if (m->m_defcharset != NULL)
117268481Seric 		printf(" C=%s", m->m_defcharset);
117368481Seric 	printf(" T=%s/%s/%s",
117468481Seric 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
117568481Seric 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
117668481Seric 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
117768481Seric 	if (m->m_argv != NULL)
117868481Seric 	{
117968481Seric 		char **a = m->m_argv;
118068481Seric 
118168481Seric 		printf(" A=");
118268481Seric 		while (*a != NULL)
118368481Seric 		{
118468481Seric 			if (a != m->m_argv)
118568481Seric 				printf(" ");
118668481Seric 			xputs(*a++);
118768481Seric 		}
118868481Seric 	}
118968481Seric 	printf("\n");
119068481Seric }
11914096Seric /*
11928256Seric **  SETOPTION -- set global processing option
11938256Seric **
11948256Seric **	Parameters:
11958256Seric **		opt -- option name.
11968256Seric **		val -- option value (as a text string).
119721755Seric **		safe -- set if this came from a configuration file.
119821755Seric **			Some options (if set from the command line) will
119921755Seric **			reset the user id to avoid security problems.
12008269Seric **		sticky -- if set, don't let other setoptions override
12018269Seric **			this value.
120258734Seric **		e -- the main envelope.
12038256Seric **
12048256Seric **	Returns:
12058256Seric **		none.
12068256Seric **
12078256Seric **	Side Effects:
12088256Seric **		Sets options as implied by the arguments.
12098256Seric */
12108256Seric 
121110687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
12128269Seric 
121357207Seric 
121466334Seric #if NAMED_BIND
121557207Seric 
121657207Seric struct resolverflags
121757207Seric {
121857207Seric 	char	*rf_name;	/* name of the flag */
121957207Seric 	long	rf_bits;	/* bits to set/clear */
122057207Seric } ResolverFlags[] =
122157207Seric {
122257207Seric 	"debug",	RES_DEBUG,
122357207Seric 	"aaonly",	RES_AAONLY,
122457207Seric 	"usevc",	RES_USEVC,
122557207Seric 	"primary",	RES_PRIMARY,
122657207Seric 	"igntc",	RES_IGNTC,
122757207Seric 	"recurse",	RES_RECURSE,
122857207Seric 	"defnames",	RES_DEFNAMES,
122957207Seric 	"stayopen",	RES_STAYOPEN,
123057207Seric 	"dnsrch",	RES_DNSRCH,
123165583Seric 	"true",		0,		/* to avoid error on old syntax */
123257207Seric 	NULL,		0
123357207Seric };
123457207Seric 
123557207Seric #endif
123657207Seric 
123768481Seric struct optioninfo
123868481Seric {
123968481Seric 	char	*o_name;	/* long name of option */
124068481Seric 	u_char	o_code;		/* short name of option */
124168481Seric 	bool	o_safe;		/* safe for random people to use */
124268481Seric } OptionTab[] =
124368481Seric {
124468481Seric 	"SevenBitInput",	'7',		TRUE,
124568481Seric 	"EightBitMode",		'8',		TRUE,
124668481Seric 	"AliasFile",		'A',		FALSE,
124768481Seric 	"AliasWait",		'a',		FALSE,
124868481Seric 	"BlankSub",		'B',		FALSE,
124968481Seric 	"MinFreeBlocks",	'b',		TRUE,
125068481Seric 	"CheckpointInterval",	'C',		TRUE,
125168481Seric 	"HoldExpensive",	'c',		FALSE,
125268481Seric 	"AutoRebuildAliases",	'D',		FALSE,
125368481Seric 	"DeliveryMode",		'd',		TRUE,
125468481Seric 	"ErrorHeader",		'E',		FALSE,
125568481Seric 	"ErrorMode",		'e',		TRUE,
125668481Seric 	"TempFileMode",		'F',		FALSE,
125768481Seric 	"SaveFromLine",		'f',		FALSE,
125868481Seric 	"MatchGECOS",		'G',		FALSE,
125968481Seric 	"HelpFile",		'H',		FALSE,
126068481Seric 	"MaxHopCount",		'h',		FALSE,
126168569Seric 	"ResolverOptions",	'I',		FALSE,
126268481Seric 	"IgnoreDots",		'i',		TRUE,
126368481Seric 	"ForwardPath",		'J',		FALSE,
126468481Seric 	"SendMimeErrors",	'j',		TRUE,
126568481Seric 	"ConnectionCacheSize",	'k',		FALSE,
126668481Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
126768481Seric 	"UseErrorsTo",		'l',		FALSE,
126868481Seric 	"LogLevel",		'L',		FALSE,
126968481Seric 	"MeToo",		'm',		TRUE,
127068481Seric 	"CheckAliases",		'n',		FALSE,
127168481Seric 	"OldStyleHeaders",	'o',		TRUE,
127268481Seric 	"DaemonPortOptions",	'O',		FALSE,
127368481Seric 	"PrivacyOptions",	'p',		TRUE,
127468481Seric 	"PostmasterCopy",	'P',		FALSE,
127568481Seric 	"QueueFactor",		'q',		FALSE,
127668481Seric 	"QueueDirectory",	'Q',		FALSE,
127768481Seric 	"DontPruneRoutes",	'R',		FALSE,
127868481Seric 	"Timeout",		'r',		TRUE,
127968481Seric 	"StatusFile",		'S',		FALSE,
128068481Seric 	"SuperSafe",		's',		TRUE,
128168481Seric 	"QueueTimeout",		'T',		FALSE,
128268481Seric 	"TimeZoneSpec",		't',		FALSE,
128368481Seric 	"UserDatabaseSpec",	'U',		FALSE,
128468481Seric 	"DefaultUser",		'u',		FALSE,
128568481Seric 	"FallbackMXhost",	'V',		FALSE,
128668481Seric 	"Verbose",		'v',		TRUE,
128768481Seric 	"TryNullMXList",	'w',		TRUE,
128868481Seric 	"QueueLA",		'x',		FALSE,
128968481Seric 	"RefuseLA",		'X',		FALSE,
129068481Seric 	"RecipientFactor",	'y',		FALSE,
129168569Seric 	"ForkEachJob",		'Y',		FALSE,
129268481Seric 	"ClassFactor",		'z',		FALSE,
129368569Seric 	"RetryFactor",		'Z',		FALSE,
129468481Seric #define O_BSP		0x80
129568481Seric 	"BrokenSmtpPeers",	O_BSP,		TRUE,
129668481Seric #define O_QUEUESORTORD	0x81
129768481Seric 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
129868481Seric #define O_MQA		0x83
129968481Seric 	"MinQueueAge",		O_MQA,		TRUE,
130068481Seric #define O_MHSA		0x84
130168481Seric /*
130268481Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
130368481Seric */
130468481Seric #define O_DEFCHARSET	0x85
130568481Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
130668481Seric #define O_SSFILE	0x86
130768481Seric 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
130868481Seric #define O_DIALDELAY	0x87
130968481Seric 	"DialDelay",		O_DIALDELAY,	TRUE,
131068481Seric #define O_NORCPTACTION	0x88
131168481Seric 	"NoRecipientAction",	O_NORCPTACTION,	TRUE,
131268490Seric #define O_SAFEFILEENV	0x89
131368490Seric 	"SafeFileEnvironment",	O_SAFEFILEENV,	FALSE,
131468569Seric #define O_MAXMSGSIZE	0x8a
131568569Seric 	"MaxMessageSize",	O_MAXMSGSIZE,	FALSE,
1316*68756Seric #define O_COLONOKINADDR	0x8b
1317*68756Seric 	"ColonOkInAddr",	O_COLONOKINADDR, TRUE,
131868481Seric 
131968481Seric 	NULL,			'\0',		FALSE,
132068481Seric };
132168481Seric 
132268481Seric 
132368481Seric 
132458734Seric setoption(opt, val, safe, sticky, e)
132568481Seric 	u_char opt;
13268256Seric 	char *val;
132721755Seric 	bool safe;
13288269Seric 	bool sticky;
132958734Seric 	register ENVELOPE *e;
13308256Seric {
133157207Seric 	register char *p;
133268481Seric 	register struct optioninfo *o;
133368481Seric 	char *subopt;
13348265Seric 	extern bool atobool();
133512633Seric 	extern time_t convtime();
133614879Seric 	extern int QueueLA;
133714879Seric 	extern int RefuseLA;
133864718Seric 	extern bool Warn_Q_option;
13398256Seric 
134068481Seric 	errno = 0;
134168481Seric 	if (opt == ' ')
134268481Seric 	{
134368481Seric 		/* full word options */
134468481Seric 		struct optioninfo *sel;
134568481Seric 
134668481Seric 		p = strchr(val, '=');
134768481Seric 		if (p == NULL)
134868481Seric 			p = &val[strlen(val)];
134968481Seric 		while (*--p == ' ')
135068481Seric 			continue;
135168481Seric 		while (*++p == ' ')
135268481Seric 			*p = '\0';
135368481Seric 		if (p == val)
135468481Seric 		{
135568481Seric 			syserr("readcf: null option name");
135668481Seric 			return;
135768481Seric 		}
135868481Seric 		if (*p == '=')
135968481Seric 			*p++ = '\0';
136068481Seric 		while (*p == ' ')
136168481Seric 			p++;
136268481Seric 		subopt = strchr(val, '.');
136368481Seric 		if (subopt != NULL)
136468481Seric 			*subopt++ = '\0';
136568481Seric 		sel = NULL;
136668481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
136768481Seric 		{
136868481Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
136968481Seric 				continue;
137068481Seric 			if (strlen(o->o_name) == strlen(val))
137168481Seric 			{
137268481Seric 				/* completely specified -- this must be it */
137368481Seric 				sel = NULL;
137468481Seric 				break;
137568481Seric 			}
137668481Seric 			if (sel != NULL)
137768481Seric 				break;
137868481Seric 			sel = o;
137968481Seric 		}
138068481Seric 		if (sel != NULL && o->o_name == NULL)
138168481Seric 			o = sel;
138268481Seric 		else if (o->o_name == NULL)
138368481Seric 		{
138468481Seric 			syserr("readcf: unknown option name %s", val);
138568481Seric 			return;
138668481Seric 		}
138768481Seric 		else if (sel != NULL)
138868481Seric 		{
138968481Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
139068481Seric 				val, sel->o_name, o->o_name);
139168481Seric 			return;
139268481Seric 		}
139368481Seric 		if (strlen(val) != strlen(o->o_name))
139468481Seric 		{
139568481Seric 			bool oldVerbose = Verbose;
139668481Seric 
139768481Seric 			Verbose = TRUE;
139868481Seric 			message("Option %s used as abbreviation for %s",
139968481Seric 				val, o->o_name);
140068481Seric 			Verbose = oldVerbose;
140168481Seric 		}
140268481Seric 		opt = o->o_code;
140368481Seric 		val = p;
140468481Seric 	}
140568481Seric 	else
140668481Seric 	{
140768481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
140868481Seric 		{
140968481Seric 			if (o->o_code == opt)
141068481Seric 				break;
141168481Seric 		}
141268481Seric 		subopt = NULL;
141368481Seric 	}
141468481Seric 
14158256Seric 	if (tTd(37, 1))
141668481Seric 	{
141768481Seric 		printf(isascii(opt) && isprint(opt) ?
141868481Seric 			    "setoption %s (%c).%s=%s" :
141968481Seric 			    "setoption %s (0x%x).%s=%s",
142068481Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
142168481Seric 			opt,
142268481Seric 			subopt == NULL ? "" : subopt,
142368481Seric 			val);
142468481Seric 	}
14258256Seric 
14268256Seric 	/*
14278269Seric 	**  See if this option is preset for us.
14288256Seric 	*/
14298256Seric 
143059731Seric 	if (!sticky && bitnset(opt, StickyOpt))
14318269Seric 	{
14329341Seric 		if (tTd(37, 1))
14339341Seric 			printf(" (ignored)\n");
14348269Seric 		return;
14358269Seric 	}
14368269Seric 
143721755Seric 	/*
143821755Seric 	**  Check to see if this option can be specified by this user.
143921755Seric 	*/
144021755Seric 
144163787Seric 	if (!safe && RealUid == 0)
144221755Seric 		safe = TRUE;
144368481Seric 	if (!safe && !o->o_safe)
144421755Seric 	{
144539111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
144621755Seric 		{
144736582Sbostic 			if (tTd(37, 1))
144836582Sbostic 				printf(" (unsafe)");
144963787Seric 			if (RealUid != geteuid())
145036582Sbostic 			{
145151210Seric 				if (tTd(37, 1))
145251210Seric 					printf("(Resetting uid)");
145363787Seric 				(void) setgid(RealGid);
145463787Seric 				(void) setuid(RealUid);
145536582Sbostic 			}
145621755Seric 		}
145721755Seric 	}
145851210Seric 	if (tTd(37, 1))
145917985Seric 		printf("\n");
14608269Seric 
146168481Seric 	switch (opt & 0xff)
14628256Seric 	{
146359709Seric 	  case '7':		/* force seven-bit input */
146468481Seric 		SevenBitInput = atobool(val);
146552106Seric 		break;
146652106Seric 
146768481Seric 	  case '8':		/* handling of 8-bit input */
146868481Seric 		switch (*val)
146968481Seric 		{
147068481Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
147168481Seric 			MimeMode = 0;
147268481Seric 			break;
147368481Seric 
147468481Seric 		  case 'm':		/* convert 8-bit, convert MIME */
147568481Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
147668481Seric 			break;
147768481Seric 
147868481Seric 		  case 'j':		/* "just send 8" */
147968481Seric 			MimeMode = MM_PASS8BIT;
148068481Seric 			break;
148168481Seric 
148268481Seric 		  case 'p':		/* pass 8 bit, convert MIME */
148368481Seric 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
148468481Seric 			break;
148568481Seric 
148668481Seric 		  case 's':		/* strict adherence */
148768481Seric 			MimeMode = MM_CVTMIME;
148868481Seric 			break;
148968481Seric 
149068481Seric 		  case 'a':		/* encode 8 bit if available */
149168481Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
149268481Seric 			break;
149368481Seric 
149468481Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
149568481Seric 			MimeMode = MM_MIME8BIT;
149668481Seric 			break;
149768481Seric 
149868481Seric 		  default:
149968481Seric 			syserr("Unknown 8-bit mode %c", *val);
150068481Seric 			exit(EX_USAGE);
150168481Seric 		}
150268481Seric 		break;
150368481Seric 
15048256Seric 	  case 'A':		/* set default alias file */
15059381Seric 		if (val[0] == '\0')
150659672Seric 			setalias("aliases");
15079381Seric 		else
150859672Seric 			setalias(val);
15098256Seric 		break;
15108256Seric 
151117474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
151217474Seric 		if (val[0] == '\0')
151364796Seric 			SafeAlias = 5 * 60;		/* five minutes */
151417474Seric 		else
151564796Seric 			SafeAlias = convtime(val, 'm');
151617474Seric 		break;
151717474Seric 
151816843Seric 	  case 'B':		/* substitution for blank character */
151916843Seric 		SpaceSub = val[0];
152016843Seric 		if (SpaceSub == '\0')
152116843Seric 			SpaceSub = ' ';
152216843Seric 		break;
152316843Seric 
152459283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
152559283Seric 		p = strchr(val, '/');
152659283Seric 		if (p != NULL)
152759283Seric 		{
152859283Seric 			*p++ = '\0';
152959283Seric 			MaxMessageSize = atol(p);
153059283Seric 		}
153158082Seric 		MinBlocksFree = atol(val);
153258082Seric 		break;
153358082Seric 
15349284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
15359381Seric 		NoConnect = atobool(val);
15369284Seric 		break;
15379284Seric 
153851305Seric 	  case 'C':		/* checkpoint every N addresses */
153951305Seric 		CheckpointInterval = atoi(val);
154024944Seric 		break;
154124944Seric 
15429284Seric 	  case 'd':		/* delivery mode */
15439284Seric 		switch (*val)
15448269Seric 		{
15459284Seric 		  case '\0':
154658734Seric 			e->e_sendmode = SM_DELIVER;
15478269Seric 			break;
15488269Seric 
154910755Seric 		  case SM_QUEUE:	/* queue only */
155010755Seric #ifndef QUEUE
155110755Seric 			syserr("need QUEUE to set -odqueue");
155256795Seric #endif /* QUEUE */
155310755Seric 			/* fall through..... */
155410755Seric 
15559284Seric 		  case SM_DELIVER:	/* do everything */
15569284Seric 		  case SM_FORK:		/* fork after verification */
155758734Seric 			e->e_sendmode = *val;
15588269Seric 			break;
15598269Seric 
15608269Seric 		  default:
15619284Seric 			syserr("Unknown delivery mode %c", *val);
15628269Seric 			exit(EX_USAGE);
15638269Seric 		}
15648269Seric 		break;
15658269Seric 
15669146Seric 	  case 'D':		/* rebuild alias database as needed */
15679381Seric 		AutoRebuild = atobool(val);
15689146Seric 		break;
15699146Seric 
157055372Seric 	  case 'E':		/* error message header/header file */
157155379Seric 		if (*val != '\0')
157255379Seric 			ErrMsgFile = newstr(val);
157355372Seric 		break;
157455372Seric 
15758269Seric 	  case 'e':		/* set error processing mode */
15768269Seric 		switch (*val)
15778269Seric 		{
15789381Seric 		  case EM_QUIET:	/* be silent about it */
15799381Seric 		  case EM_MAIL:		/* mail back */
15809381Seric 		  case EM_BERKNET:	/* do berknet error processing */
15819381Seric 		  case EM_WRITE:	/* write back (or mail) */
15829381Seric 		  case EM_PRINT:	/* print errors normally (default) */
158358734Seric 			e->e_errormode = *val;
15848269Seric 			break;
15858269Seric 		}
15868269Seric 		break;
15878269Seric 
15889049Seric 	  case 'F':		/* file mode */
158917975Seric 		FileMode = atooct(val) & 0777;
15909049Seric 		break;
15919049Seric 
15928269Seric 	  case 'f':		/* save Unix-style From lines on front */
15939381Seric 		SaveFrom = atobool(val);
15948269Seric 		break;
15958269Seric 
159653735Seric 	  case 'G':		/* match recipients against GECOS field */
159753735Seric 		MatchGecos = atobool(val);
159853735Seric 		break;
159953735Seric 
16008256Seric 	  case 'g':		/* default gid */
160168481Seric   g_opt:
160264133Seric 		if (isascii(*val) && isdigit(*val))
160364133Seric 			DefGid = atoi(val);
160464133Seric 		else
160564133Seric 		{
160664133Seric 			register struct group *gr;
160764133Seric 
160864133Seric 			DefGid = -1;
160964133Seric 			gr = getgrnam(val);
161064133Seric 			if (gr == NULL)
161168481Seric 				syserr("readcf: option %c: unknown group %s",
161268481Seric 					opt, val);
161364133Seric 			else
161464133Seric 				DefGid = gr->gr_gid;
161564133Seric 		}
16168256Seric 		break;
16178256Seric 
16188256Seric 	  case 'H':		/* help file */
16199381Seric 		if (val[0] == '\0')
16208269Seric 			HelpFile = "sendmail.hf";
16219381Seric 		else
16229381Seric 			HelpFile = newstr(val);
16238256Seric 		break;
16248256Seric 
162551305Seric 	  case 'h':		/* maximum hop count */
162651305Seric 		MaxHopCount = atoi(val);
162751305Seric 		break;
162851305Seric 
162935651Seric 	  case 'I':		/* use internet domain name server */
163066334Seric #if NAMED_BIND
163157207Seric 		for (p = val; *p != 0; )
163257207Seric 		{
163357207Seric 			bool clearmode;
163457207Seric 			char *q;
163557207Seric 			struct resolverflags *rfp;
163657207Seric 
163757207Seric 			while (*p == ' ')
163857207Seric 				p++;
163957207Seric 			if (*p == '\0')
164057207Seric 				break;
164157207Seric 			clearmode = FALSE;
164257207Seric 			if (*p == '-')
164357207Seric 				clearmode = TRUE;
164457207Seric 			else if (*p != '+')
164557207Seric 				p--;
164657207Seric 			p++;
164757207Seric 			q = p;
164858050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
164957207Seric 				p++;
165057207Seric 			if (*p != '\0')
165157207Seric 				*p++ = '\0';
165257207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
165357207Seric 			{
165457207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
165557207Seric 					break;
165657207Seric 			}
165764923Seric 			if (rfp->rf_name == NULL)
165864923Seric 				syserr("readcf: I option value %s unrecognized", q);
165964923Seric 			else if (clearmode)
166057207Seric 				_res.options &= ~rfp->rf_bits;
166157207Seric 			else
166257207Seric 				_res.options |= rfp->rf_bits;
166357207Seric 		}
166457207Seric 		if (tTd(8, 2))
166557207Seric 			printf("_res.options = %x\n", _res.options);
166657207Seric #else
166757207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
166857207Seric #endif
166935651Seric 		break;
167035651Seric 
16718269Seric 	  case 'i':		/* ignore dot lines in message */
16729381Seric 		IgnrDot = atobool(val);
16738269Seric 		break;
16748269Seric 
167559730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
167659730Seric 		SendMIMEErrors = atobool(val);
167759730Seric 		break;
167859730Seric 
167957136Seric 	  case 'J':		/* .forward search path */
168057136Seric 		ForwardPath = newstr(val);
168157136Seric 		break;
168257136Seric 
168354967Seric 	  case 'k':		/* connection cache size */
168454967Seric 		MaxMciCache = atoi(val);
168556215Seric 		if (MaxMciCache < 0)
168656215Seric 			MaxMciCache = 0;
168754967Seric 		break;
168854967Seric 
168954967Seric 	  case 'K':		/* connection cache timeout */
169058796Seric 		MciCacheTimeout = convtime(val, 'm');
169154967Seric 		break;
169254967Seric 
169361104Seric 	  case 'l':		/* use Errors-To: header */
169461104Seric 		UseErrorsTo = atobool(val);
169561104Seric 		break;
169661104Seric 
16978256Seric 	  case 'L':		/* log level */
169864140Seric 		if (safe || LogLevel < atoi(val))
169964140Seric 			LogLevel = atoi(val);
17008256Seric 		break;
17018256Seric 
17028269Seric 	  case 'M':		/* define macro */
170368267Seric 		p = newstr(&val[1]);
170468267Seric 		if (!safe)
170568267Seric 			cleanstrcpy(p, p, MAXNAME);
170668267Seric 		define(val[0], p, CurEnv);
170716878Seric 		sticky = FALSE;
17088269Seric 		break;
17098269Seric 
17108269Seric 	  case 'm':		/* send to me too */
17119381Seric 		MeToo = atobool(val);
17128269Seric 		break;
17138269Seric 
171425820Seric 	  case 'n':		/* validate RHS in newaliases */
171525820Seric 		CheckAliases = atobool(val);
171625820Seric 		break;
171725820Seric 
171861104Seric 	    /* 'N' available -- was "net name" */
171961104Seric 
172058851Seric 	  case 'O':		/* daemon options */
172158851Seric 		setdaemonoptions(val);
172258851Seric 		break;
172358851Seric 
17248269Seric 	  case 'o':		/* assume old style headers */
17259381Seric 		if (atobool(val))
17269341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
17279341Seric 		else
17289341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
17298269Seric 		break;
17308269Seric 
173158082Seric 	  case 'p':		/* select privacy level */
173258082Seric 		p = val;
173358082Seric 		for (;;)
173458082Seric 		{
173558082Seric 			register struct prival *pv;
173658082Seric 			extern struct prival PrivacyValues[];
173758082Seric 
173858082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
173958082Seric 				p++;
174058082Seric 			if (*p == '\0')
174158082Seric 				break;
174258082Seric 			val = p;
174358082Seric 			while (isascii(*p) && isalnum(*p))
174458082Seric 				p++;
174558082Seric 			if (*p != '\0')
174658082Seric 				*p++ = '\0';
174758082Seric 
174858082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
174958082Seric 			{
175058082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
175158082Seric 					break;
175258082Seric 			}
175358886Seric 			if (pv->pv_name == NULL)
175458886Seric 				syserr("readcf: Op line: %s unrecognized", val);
175558082Seric 			PrivacyFlags |= pv->pv_flag;
175658082Seric 		}
175768479Seric 		sticky = FALSE;
175858082Seric 		break;
175958082Seric 
176024944Seric 	  case 'P':		/* postmaster copy address for returned mail */
176124944Seric 		PostMasterCopy = newstr(val);
176224944Seric 		break;
176324944Seric 
176424944Seric 	  case 'q':		/* slope of queue only function */
176524944Seric 		QueueFactor = atoi(val);
176624944Seric 		break;
176724944Seric 
17688256Seric 	  case 'Q':		/* queue directory */
17699381Seric 		if (val[0] == '\0')
17708269Seric 			QueueDir = "mqueue";
17719381Seric 		else
17729381Seric 			QueueDir = newstr(val);
177358789Seric 		if (RealUid != 0 && !safe)
177464718Seric 			Warn_Q_option = TRUE;
17758256Seric 		break;
17768256Seric 
177758148Seric 	  case 'R':		/* don't prune routes */
177858148Seric 		DontPruneRoutes = atobool(val);
177958148Seric 		break;
178058148Seric 
17818256Seric 	  case 'r':		/* read timeout */
178268481Seric 		if (subopt == NULL)
178368481Seric 			inittimeouts(val);
178468481Seric 		else
178568481Seric 			settimeout(subopt, val);
17868256Seric 		break;
17878256Seric 
17888256Seric 	  case 'S':		/* status file */
17899381Seric 		if (val[0] == '\0')
17908269Seric 			StatFile = "sendmail.st";
17919381Seric 		else
17929381Seric 			StatFile = newstr(val);
17938256Seric 		break;
17948256Seric 
17958265Seric 	  case 's':		/* be super safe, even if expensive */
17969381Seric 		SuperSafe = atobool(val);
17978256Seric 		break;
17988256Seric 
17998256Seric 	  case 'T':		/* queue timeout */
180058737Seric 		p = strchr(val, '/');
180158737Seric 		if (p != NULL)
180258737Seric 		{
180358737Seric 			*p++ = '\0';
180468481Seric 			settimeout("queuewarn", p);
180558737Seric 		}
180668481Seric 		settimeout("queuereturn", val);
180754967Seric 		break;
18088256Seric 
18098265Seric 	  case 't':		/* time zone name */
181052106Seric 		TimeZoneSpec = newstr(val);
18118265Seric 		break;
18128265Seric 
181350556Seric 	  case 'U':		/* location of user database */
181451360Seric 		UdbSpec = newstr(val);
181550556Seric 		break;
181650556Seric 
18178256Seric 	  case 'u':		/* set default uid */
181868481Seric 		for (p = val; *p != '\0'; p++)
181968481Seric 		{
182068481Seric 			if (*p == '.' || *p == '/' || *p == ':')
182168481Seric 			{
182268481Seric 				*p++ = '\0';
182368481Seric 				break;
182468481Seric 			}
182568481Seric 		}
182664133Seric 		if (isascii(*val) && isdigit(*val))
182764133Seric 			DefUid = atoi(val);
182864133Seric 		else
182964133Seric 		{
183064133Seric 			register struct passwd *pw;
183164133Seric 
183264133Seric 			DefUid = -1;
183368693Seric 			pw = sm_getpwnam(val);
183464133Seric 			if (pw == NULL)
183564133Seric 				syserr("readcf: option u: unknown user %s", val);
183664133Seric 			else
183768481Seric 			{
183864133Seric 				DefUid = pw->pw_uid;
183968481Seric 				DefGid = pw->pw_gid;
184068481Seric 			}
184164133Seric 		}
184240973Sbostic 		setdefuser();
18438256Seric 
184468481Seric 		/* handle the group if it is there */
184568481Seric 		if (*p == '\0')
184668481Seric 			break;
184768481Seric 		val = p;
184868481Seric 		goto g_opt;
184968481Seric 
185058851Seric 	  case 'V':		/* fallback MX host */
185158851Seric 		FallBackMX = newstr(val);
185258851Seric 		break;
185358851Seric 
18548269Seric 	  case 'v':		/* run in verbose mode */
18559381Seric 		Verbose = atobool(val);
18568256Seric 		break;
18578256Seric 
185863837Seric 	  case 'w':		/* if we are best MX, try host directly */
185963837Seric 		TryNullMXList = atobool(val);
186063837Seric 		break;
186161104Seric 
186261104Seric 	    /* 'W' available -- was wizard password */
186361104Seric 
186414879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
186514879Seric 		QueueLA = atoi(val);
186614879Seric 		break;
186714879Seric 
186814879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
186914879Seric 		RefuseLA = atoi(val);
187014879Seric 		break;
187114879Seric 
187224981Seric 	  case 'y':		/* work recipient factor */
187324981Seric 		WkRecipFact = atoi(val);
187424981Seric 		break;
187524981Seric 
187624981Seric 	  case 'Y':		/* fork jobs during queue runs */
187724952Seric 		ForkQueueRuns = atobool(val);
187824952Seric 		break;
187924952Seric 
188024981Seric 	  case 'z':		/* work message class factor */
188124981Seric 		WkClassFact = atoi(val);
188224981Seric 		break;
188324981Seric 
188424981Seric 	  case 'Z':		/* work time factor */
188524981Seric 		WkTimeFact = atoi(val);
188624981Seric 		break;
188724981Seric 
188868481Seric 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
188968481Seric 		BrokenSmtpPeers = atobool(val);
189068481Seric 		break;
189168481Seric 
189268481Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
189368481Seric 		switch (*val)
189468481Seric 		{
189568481Seric 		  case 'h':	/* Host first */
189668481Seric 		  case 'H':
189768481Seric 			QueueSortOrder = QS_BYHOST;
189868481Seric 			break;
189968481Seric 
190068481Seric 		  case 'p':	/* Priority order */
190168481Seric 		  case 'P':
190268481Seric 			QueueSortOrder = QS_BYPRIORITY;
190368481Seric 			break;
190468481Seric 
190568481Seric 		  default:
190668481Seric 			syserr("Invalid queue sort order \"%s\"", val);
190768481Seric 		}
190868481Seric 		break;
190968481Seric 
191068481Seric 	  case O_MQA:		/* minimum queue age between deliveries */
191168481Seric 		MinQueueAge = convtime(val, 'm');
191268481Seric 		break;
191368481Seric 
191468481Seric 	  case O_MHSA:		/* maximum age of cached host status */
191568481Seric 		MaxHostStatAge = convtime(val, 'm');
191668481Seric 		break;
191768481Seric 
191868481Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
191968481Seric 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
192068481Seric 		break;
192168481Seric 
192268481Seric 	  case O_SSFILE:	/* service switch file */
192368481Seric 		ServiceSwitchFile = newstr(val);
192468481Seric 		break;
192568481Seric 
192668481Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
192768481Seric 		DialDelay = convtime(val, 's');
192868481Seric 		break;
192968481Seric 
193068481Seric 	  case O_NORCPTACTION:	/* what to do if no recipient */
193168481Seric 		if (strcasecmp(val, "none") == 0)
193268481Seric 			NoRecipientAction = NRA_NO_ACTION;
193368481Seric 		else if (strcasecmp(val, "add-to") == 0)
193468481Seric 			NoRecipientAction = NRA_ADD_TO;
193568481Seric 		else if (strcasecmp(val, "add-apparently-to") == 0)
193668481Seric 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
193768481Seric 		else if (strcasecmp(val, "add-bcc") == 0)
193868481Seric 			NoRecipientAction = NRA_ADD_BCC;
193968481Seric 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
194068481Seric 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
194168481Seric 		else
194268481Seric 			syserr("Invalid NoRecipientAction: %s", val);
194368481Seric 
194468490Seric 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
194568490Seric 		SafeFileEnv = newstr(val);
194668490Seric 		break;
194768490Seric 
194868569Seric 	  case O_MAXMSGSIZE:	/* maximum message size */
194968569Seric 		MaxMessageSize = atol(p);
195068569Seric 		break;
195168569Seric 
1952*68756Seric 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
1953*68756Seric 		ColonOkInAddr = atobool(p);
1954*68756Seric 		break;
1955*68756Seric 
19568256Seric 	  default:
195768481Seric 		if (tTd(37, 1))
195868481Seric 		{
195968481Seric 			if (isascii(opt) && isprint(opt))
196068481Seric 				printf("Warning: option %c unknown\n", opt);
196168481Seric 			else
196268481Seric 				printf("Warning: option 0x%x unknown\n", opt);
196368481Seric 		}
19648256Seric 		break;
19658256Seric 	}
196616878Seric 	if (sticky)
196716878Seric 		setbitn(opt, StickyOpt);
19689188Seric 	return;
19698256Seric }
197010687Seric /*
197168481Seric **  SETCLASS -- set a string into a class
197210687Seric **
197310687Seric **	Parameters:
197468481Seric **		class -- the class to put the string in.
197568481Seric **		str -- the string to enter
197610687Seric **
197710687Seric **	Returns:
197810687Seric **		none.
197910687Seric **
198010687Seric **	Side Effects:
198110687Seric **		puts the word into the symbol table.
198210687Seric */
198310687Seric 
198468481Seric setclass(class, str)
198510687Seric 	int class;
198668481Seric 	char *str;
198710687Seric {
198810687Seric 	register STAB *s;
198910687Seric 
199057943Seric 	if (tTd(37, 8))
199168481Seric 		printf("setclass(%c, %s)\n", class, str);
199268481Seric 	s = stab(str, ST_CLASS, ST_ENTER);
199310687Seric 	setbitn(class, s->s_class);
199410687Seric }
199553654Seric /*
199653654Seric **  MAKEMAPENTRY -- create a map entry
199753654Seric **
199853654Seric **	Parameters:
199953654Seric **		line -- the config file line
200053654Seric **
200153654Seric **	Returns:
200253654Seric **		TRUE if it successfully entered the map entry.
200353654Seric **		FALSE otherwise (usually syntax error).
200453654Seric **
200553654Seric **	Side Effects:
200653654Seric **		Enters the map into the dictionary.
200753654Seric */
200853654Seric 
200953654Seric void
201053654Seric makemapentry(line)
201153654Seric 	char *line;
201253654Seric {
201353654Seric 	register char *p;
201453654Seric 	char *mapname;
201553654Seric 	char *classname;
201664078Seric 	register STAB *s;
201753654Seric 	STAB *class;
201853654Seric 
201958050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
202053654Seric 		continue;
202158050Seric 	if (!(isascii(*p) && isalnum(*p)))
202253654Seric 	{
202353654Seric 		syserr("readcf: config K line: no map name");
202453654Seric 		return;
202553654Seric 	}
202653654Seric 
202753654Seric 	mapname = p;
202868481Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
202953654Seric 		continue;
203053654Seric 	if (*p != '\0')
203153654Seric 		*p++ = '\0';
203258050Seric 	while (isascii(*p) && isspace(*p))
203353654Seric 		p++;
203458050Seric 	if (!(isascii(*p) && isalnum(*p)))
203553654Seric 	{
203653654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
203753654Seric 		return;
203853654Seric 	}
203953654Seric 	classname = p;
204058050Seric 	while (isascii(*++p) && isalnum(*p))
204153654Seric 		continue;
204253654Seric 	if (*p != '\0')
204353654Seric 		*p++ = '\0';
204458050Seric 	while (isascii(*p) && isspace(*p))
204553654Seric 		p++;
204653654Seric 
204753654Seric 	/* look up the class */
204853654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
204953654Seric 	if (class == NULL)
205053654Seric 	{
205153654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
205253654Seric 		return;
205353654Seric 	}
205453654Seric 
205553654Seric 	/* enter the map */
205664078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
205764078Seric 	s->s_map.map_class = &class->s_mapclass;
205864078Seric 	s->s_map.map_mname = newstr(mapname);
205953654Seric 
206064078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
206164078Seric 		s->s_map.map_mflags |= MF_VALID;
206264078Seric 
206364078Seric 	if (tTd(37, 5))
206464078Seric 	{
206564078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
206664078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
206764078Seric 			s->s_map.map_mflags,
206864078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
206964078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
207064078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
207164078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
207264078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
207364078Seric 	}
207453654Seric }
207558112Seric /*
207668481Seric **  INITTIMEOUTS -- parse and set timeout values
207758112Seric **
207858112Seric **	Parameters:
207958112Seric **		val -- a pointer to the values.  If NULL, do initial
208058112Seric **			settings.
208158112Seric **
208258112Seric **	Returns:
208358112Seric **		none.
208458112Seric **
208558112Seric **	Side Effects:
208658112Seric **		Initializes the TimeOuts structure
208758112Seric */
208858112Seric 
208964255Seric #define SECONDS
209058112Seric #define MINUTES	* 60
209158112Seric #define HOUR	* 3600
209258112Seric 
209368481Seric inittimeouts(val)
209458112Seric 	register char *val;
209558112Seric {
209658112Seric 	register char *p;
209758671Seric 	extern time_t convtime();
209858112Seric 
209958112Seric 	if (val == NULL)
210058112Seric 	{
210158112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
210258112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
210358112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
210458112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
210558112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
210658112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
210758112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
210858112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
210958112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
211058112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
211158112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
211268481Seric #if IDENTPROTO
211364255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
211468481Seric #else
211568481Seric 		TimeOuts.to_ident = (time_t) 0 SECONDS;
211668481Seric #endif
211768481Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
211858112Seric 		return;
211958112Seric 	}
212058112Seric 
212158112Seric 	for (;; val = p)
212258112Seric 	{
212358112Seric 		while (isascii(*val) && isspace(*val))
212458112Seric 			val++;
212558112Seric 		if (*val == '\0')
212658112Seric 			break;
212758112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
212858112Seric 			continue;
212958112Seric 		if (*p != '\0')
213058112Seric 			*p++ = '\0';
213158112Seric 
213258112Seric 		if (isascii(*val) && isdigit(*val))
213358112Seric 		{
213458112Seric 			/* old syntax -- set everything */
213558796Seric 			TimeOuts.to_mail = convtime(val, 'm');
213658112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
213758112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
213858112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
213958112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
214058112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
214158112Seric 			continue;
214258112Seric 		}
214358112Seric 		else
214458112Seric 		{
214568481Seric 			register char *q = strchr(val, ':');
214658112Seric 
214768481Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
214858112Seric 			{
214958112Seric 				/* syntax error */
215058112Seric 				continue;
215158112Seric 			}
215258112Seric 			*q++ = '\0';
215368481Seric 			settimeout(val, q);
215468481Seric 		}
215568481Seric 	}
215668481Seric }
215768481Seric /*
215868481Seric **  SETTIMEOUT -- set an individual timeout
215968481Seric **
216068481Seric **	Parameters:
216168481Seric **		name -- the name of the timeout.
216268481Seric **		val -- the value of the timeout.
216368481Seric **
216468481Seric **	Returns:
216568481Seric **		none.
216668481Seric */
216758112Seric 
216868481Seric settimeout(name, val)
216968481Seric 	char *name;
217068481Seric 	char *val;
217168481Seric {
217268481Seric 	register char *p;
217368481Seric 	time_t to;
217468481Seric 	extern time_t convtime();
217568481Seric 
217668481Seric 	to = convtime(val, 'm');
217768481Seric 	p = strchr(name, '.');
217868481Seric 	if (p != NULL)
217968481Seric 		*p++ = '\0';
218068481Seric 
218168481Seric 	if (strcasecmp(name, "initial") == 0)
218268481Seric 		TimeOuts.to_initial = to;
218368481Seric 	else if (strcasecmp(name, "mail") == 0)
218468481Seric 		TimeOuts.to_mail = to;
218568481Seric 	else if (strcasecmp(name, "rcpt") == 0)
218668481Seric 		TimeOuts.to_rcpt = to;
218768481Seric 	else if (strcasecmp(name, "datainit") == 0)
218868481Seric 		TimeOuts.to_datainit = to;
218968481Seric 	else if (strcasecmp(name, "datablock") == 0)
219068481Seric 		TimeOuts.to_datablock = to;
219168481Seric 	else if (strcasecmp(name, "datafinal") == 0)
219268481Seric 		TimeOuts.to_datafinal = to;
219368481Seric 	else if (strcasecmp(name, "command") == 0)
219468481Seric 		TimeOuts.to_nextcommand = to;
219568481Seric 	else if (strcasecmp(name, "rset") == 0)
219668481Seric 		TimeOuts.to_rset = to;
219768481Seric 	else if (strcasecmp(name, "helo") == 0)
219868481Seric 		TimeOuts.to_helo = to;
219968481Seric 	else if (strcasecmp(name, "quit") == 0)
220068481Seric 		TimeOuts.to_quit = to;
220168481Seric 	else if (strcasecmp(name, "misc") == 0)
220268481Seric 		TimeOuts.to_miscshort = to;
220368481Seric 	else if (strcasecmp(name, "ident") == 0)
220468481Seric 		TimeOuts.to_ident = to;
220568481Seric 	else if (strcasecmp(name, "fileopen") == 0)
220668481Seric 		TimeOuts.to_fileopen = to;
220768481Seric 	else if (strcasecmp(name, "queuewarn") == 0)
220868481Seric 	{
220968481Seric 		to = convtime(val, 'h');
221068481Seric 		if (p == NULL || strcmp(p, "*") == 0)
221168481Seric 		{
221268481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
221368481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
221468481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
221558112Seric 		}
221668481Seric 		else if (strcasecmp(p, "normal") == 0)
221768481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
221868481Seric 		else if (strcasecmp(p, "urgent") == 0)
221968481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
222068481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
222168481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
222268481Seric 		else
222368481Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
222458112Seric 	}
222568481Seric 	else if (strcasecmp(name, "queuereturn") == 0)
222668481Seric 	{
222768481Seric 		to = convtime(val, 'd');
222868481Seric 		if (p == NULL || strcmp(p, "*") == 0)
222968481Seric 		{
223068481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
223168481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
223268481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
223368481Seric 		}
223468481Seric 		else if (strcasecmp(p, "normal") == 0)
223568481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
223668481Seric 		else if (strcasecmp(p, "urgent") == 0)
223768481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
223868481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
223968481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
224068481Seric 		else
224168481Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
224268481Seric 	}
224368481Seric 	else
224468481Seric 		syserr("settimeout: invalid timeout %s", name);
224558112Seric }
2246