xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 68529)
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*68529Seric static char sccsid[] = "@(#)readcf.c	8.74 (Berkeley) 03/14/95";
1133731Sbostic #endif /* not lint */
1222709Sdist 
133313Seric # include "sendmail.h"
1464133Seric # include <pwd.h>
1564133Seric # include <grp.h>
1666334Seric #if NAMED_BIND
1757207Seric # include <resolv.h>
1857207Seric #endif
193308Seric 
203308Seric /*
213308Seric **  READCF -- read control file.
223308Seric **
233308Seric **	This routine reads the control file and builds the internal
243308Seric **	form.
253308Seric **
264432Seric **	The file is formatted as a sequence of lines, each taken
274432Seric **	atomically.  The first character of each line describes how
284432Seric **	the line is to be interpreted.  The lines are:
294432Seric **		Dxval		Define macro x to have value val.
304432Seric **		Cxword		Put word into class x.
314432Seric **		Fxfile [fmt]	Read file for lines to put into
324432Seric **				class x.  Use scanf string 'fmt'
334432Seric **				or "%s" if not present.  Fmt should
344432Seric **				only produce one string-valued result.
354432Seric **		Hname: value	Define header with field-name 'name'
364432Seric **				and value as specified; this will be
374432Seric **				macro expanded immediately before
384432Seric **				use.
394432Seric **		Sn		Use rewriting set n.
404432Seric **		Rlhs rhs	Rewrite addresses that match lhs to
414432Seric **				be rhs.
4224944Seric **		Mn arg=val...	Define mailer.  n is the internal name.
4324944Seric **				Args specify mailer parameters.
448252Seric **		Oxvalue		Set option x to value.
458252Seric **		Pname=value	Set precedence name to value.
4664718Seric **		Vversioncode[/vendorcode]
4764718Seric **				Version level/vendor name of
4864718Seric **				configuration syntax.
4953654Seric **		Kmapname mapclass arguments....
5053654Seric **				Define keyed lookup of a given class.
5153654Seric **				Arguments are class dependent.
524432Seric **
533308Seric **	Parameters:
543308Seric **		cfname -- control file name.
5554973Seric **		safe -- TRUE if this is the system config file;
5654973Seric **			FALSE otherwise.
5755012Seric **		e -- the main envelope.
583308Seric **
593308Seric **	Returns:
603308Seric **		none.
613308Seric **
623308Seric **	Side Effects:
633308Seric **		Builds several internal tables.
643308Seric */
653308Seric 
6655012Seric readcf(cfname, safe, e)
673308Seric 	char *cfname;
6854973Seric 	bool safe;
6955012Seric 	register ENVELOPE *e;
703308Seric {
713308Seric 	FILE *cf;
728547Seric 	int ruleset = 0;
7368481Seric 	int nextruleset = MAXRWSETS;
748547Seric 	char *q;
759350Seric 	struct rewrite *rwp = NULL;
7657135Seric 	char *bp;
7764718Seric 	auto char *ep;
7857589Seric 	int nfuzzy;
7964133Seric 	char *file;
8064133Seric 	bool optional;
8168481Seric 	int mid;
823308Seric 	char buf[MAXLINE];
833308Seric 	register char *p;
843308Seric 	extern char **copyplist();
8552647Seric 	struct stat statb;
865909Seric 	char exbuf[MAXLINE];
8765066Seric 	char pvpbuf[MAXLINE + MAXATOM];
8868481Seric 	static char *null_list[1] = { NULL };
8910709Seric 	extern char *munchstring();
9053654Seric 	extern void makemapentry();
913308Seric 
9252647Seric 	FileName = cfname;
9352647Seric 	LineNumber = 0;
9452647Seric 
953308Seric 	cf = fopen(cfname, "r");
963308Seric 	if (cf == NULL)
973308Seric 	{
9852647Seric 		syserr("cannot open");
993308Seric 		exit(EX_OSFILE);
1003308Seric 	}
1013308Seric 
10252647Seric 	if (fstat(fileno(cf), &statb) < 0)
10352647Seric 	{
10452647Seric 		syserr("cannot fstat");
10552647Seric 		exit(EX_OSFILE);
10652647Seric 	}
10752647Seric 
10852647Seric 	if (!S_ISREG(statb.st_mode))
10952647Seric 	{
11052647Seric 		syserr("not a plain file");
11152647Seric 		exit(EX_OSFILE);
11252647Seric 	}
11352647Seric 
11452647Seric 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
11552647Seric 	{
11653037Seric 		if (OpMode == MD_DAEMON || OpMode == MD_FREEZE)
11753037Seric 			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
11853037Seric 				FileName);
11953037Seric #ifdef LOG
12053037Seric 		if (LogLevel > 0)
12153037Seric 			syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
12253037Seric 				FileName);
12353037Seric #endif
12452647Seric 	}
12552647Seric 
12659254Seric #ifdef XLA
12759254Seric 	xla_zero();
12859254Seric #endif
12959254Seric 
13057135Seric 	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
1313308Seric 	{
13257135Seric 		if (bp[0] == '#')
13357135Seric 		{
13457135Seric 			if (bp != buf)
13557135Seric 				free(bp);
13652637Seric 			continue;
13757135Seric 		}
13852637Seric 
13968481Seric 		/* do macro expansion mappings */
14057135Seric 		for (p = bp; *p != '\0'; p++)
14116157Seric 		{
14257135Seric 			if (*p == '#' && p > bp && ConfigLevel >= 3)
14352647Seric 			{
14452647Seric 				/* this is an on-line comment */
14552647Seric 				register char *e;
14652647Seric 
14758050Seric 				switch (*--p & 0377)
14852647Seric 				{
14958050Seric 				  case MACROEXPAND:
15052647Seric 					/* it's from $# -- let it go through */
15152647Seric 					p++;
15252647Seric 					break;
15352647Seric 
15452647Seric 				  case '\\':
15552647Seric 					/* it's backslash escaped */
15652647Seric 					(void) strcpy(p, p + 1);
15752647Seric 					break;
15852647Seric 
15952647Seric 				  default:
16052647Seric 					/* delete preceeding white space */
16158050Seric 					while (isascii(*p) && isspace(*p) && p > bp)
16252647Seric 						p--;
16356795Seric 					if ((e = strchr(++p, '\n')) != NULL)
16452647Seric 						(void) strcpy(p, e);
16552647Seric 					else
16652647Seric 						p[0] = p[1] = '\0';
16752647Seric 					break;
16852647Seric 				}
16952647Seric 				continue;
17052647Seric 			}
17152647Seric 
17268481Seric 			if (*p != '$' || p[1] == '\0')
17316157Seric 				continue;
17416157Seric 
17516157Seric 			if (p[1] == '$')
17616157Seric 			{
17716157Seric 				/* actual dollar sign.... */
17823111Seric 				(void) strcpy(p, p + 1);
17916157Seric 				continue;
18016157Seric 			}
18116157Seric 
18216157Seric 			/* convert to macro expansion character */
18368481Seric 			*p++ = MACROEXPAND;
18468481Seric 
18568481Seric 			/* convert macro name to code */
18668481Seric 			*p = macid(p, &ep);
18768481Seric 			if (ep != p)
18868481Seric 				strcpy(p + 1, ep);
18916157Seric 		}
19016157Seric 
19116157Seric 		/* interpret this line */
19264718Seric 		errno = 0;
19357135Seric 		switch (bp[0])
1943308Seric 		{
1953308Seric 		  case '\0':
1963308Seric 		  case '#':		/* comment */
1973308Seric 			break;
1983308Seric 
1993308Seric 		  case 'R':		/* rewriting rule */
20057135Seric 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
2013308Seric 				continue;
2023308Seric 
2033308Seric 			if (*p == '\0')
2045909Seric 			{
20565821Seric 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
2065909Seric 				break;
2075909Seric 			}
2085909Seric 
2095909Seric 			/* allocate space for the rule header */
2105909Seric 			if (rwp == NULL)
2115909Seric 			{
2125909Seric 				RewriteRules[ruleset] = rwp =
2135909Seric 					(struct rewrite *) xalloc(sizeof *rwp);
2145909Seric 			}
2153308Seric 			else
2163308Seric 			{
2175909Seric 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
2185909Seric 				rwp = rwp->r_next;
2195909Seric 			}
2205909Seric 			rwp->r_next = NULL;
2213308Seric 
2225909Seric 			/* expand and save the LHS */
2235909Seric 			*p = '\0';
224*68529Seric 			expand(&bp[1], exbuf, sizeof exbuf, e);
22565066Seric 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
22665066Seric 					     sizeof pvpbuf, NULL);
22757589Seric 			nfuzzy = 0;
2285909Seric 			if (rwp->r_lhs != NULL)
22957589Seric 			{
23057589Seric 				register char **ap;
23157589Seric 
2325909Seric 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
23357589Seric 
23457589Seric 				/* count the number of fuzzy matches in LHS */
23557589Seric 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
23657589Seric 				{
23758148Seric 					char *botch;
23858148Seric 
23958148Seric 					botch = NULL;
24058050Seric 					switch (**ap & 0377)
24157589Seric 					{
24257589Seric 					  case MATCHZANY:
24357589Seric 					  case MATCHANY:
24457589Seric 					  case MATCHONE:
24557589Seric 					  case MATCHCLASS:
24657589Seric 					  case MATCHNCLASS:
24757589Seric 						nfuzzy++;
24858148Seric 						break;
24958148Seric 
25058148Seric 					  case MATCHREPL:
25158148Seric 						botch = "$0-$9";
25258148Seric 						break;
25358148Seric 
25458148Seric 					  case CANONNET:
25558148Seric 						botch = "$#";
25658148Seric 						break;
25758148Seric 
25858148Seric 					  case CANONUSER:
25958148Seric 						botch = "$:";
26058148Seric 						break;
26158148Seric 
26258148Seric 					  case CALLSUBR:
26358148Seric 						botch = "$>";
26458148Seric 						break;
26558148Seric 
26658148Seric 					  case CONDIF:
26758148Seric 						botch = "$?";
26858148Seric 						break;
26958148Seric 
27058148Seric 					  case CONDELSE:
27158148Seric 						botch = "$|";
27258148Seric 						break;
27358148Seric 
27458148Seric 					  case CONDFI:
27558148Seric 						botch = "$.";
27658148Seric 						break;
27758148Seric 
27858148Seric 					  case HOSTBEGIN:
27958148Seric 						botch = "$[";
28058148Seric 						break;
28158148Seric 
28258148Seric 					  case HOSTEND:
28358148Seric 						botch = "$]";
28458148Seric 						break;
28558148Seric 
28658148Seric 					  case LOOKUPBEGIN:
28758148Seric 						botch = "$(";
28858148Seric 						break;
28958148Seric 
29058148Seric 					  case LOOKUPEND:
29158148Seric 						botch = "$)";
29258148Seric 						break;
29357589Seric 					}
29458148Seric 					if (botch != NULL)
29558148Seric 						syserr("Inappropriate use of %s on LHS",
29658148Seric 							botch);
29757589Seric 				}
29857589Seric 			}
29956678Seric 			else
30068481Seric 			{
30156678Seric 				syserr("R line: null LHS");
30268481Seric 				rwp->r_lhs = null_list;
30368481Seric 			}
3045909Seric 
3055909Seric 			/* expand and save the RHS */
3065909Seric 			while (*++p == '\t')
3075909Seric 				continue;
3087231Seric 			q = p;
3097231Seric 			while (*p != '\0' && *p != '\t')
3107231Seric 				p++;
3117231Seric 			*p = '\0';
312*68529Seric 			expand(q, exbuf, sizeof exbuf, e);
31365066Seric 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
31465066Seric 					     sizeof pvpbuf, NULL);
3155909Seric 			if (rwp->r_rhs != NULL)
31657589Seric 			{
31757589Seric 				register char **ap;
31857589Seric 
3195909Seric 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
32057589Seric 
32157589Seric 				/* check no out-of-bounds replacements */
32257589Seric 				nfuzzy += '0';
32357589Seric 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
32457589Seric 				{
32558148Seric 					char *botch;
32658148Seric 
32758148Seric 					botch = NULL;
32858148Seric 					switch (**ap & 0377)
32957589Seric 					{
33058148Seric 					  case MATCHREPL:
33158148Seric 						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
33258148Seric 						{
33358148Seric 							syserr("replacement $%c out of bounds",
33458148Seric 								(*ap)[1]);
33558148Seric 						}
33658148Seric 						break;
33758148Seric 
33858148Seric 					  case MATCHZANY:
33958148Seric 						botch = "$*";
34058148Seric 						break;
34158148Seric 
34258148Seric 					  case MATCHANY:
34358148Seric 						botch = "$+";
34458148Seric 						break;
34558148Seric 
34658148Seric 					  case MATCHONE:
34758148Seric 						botch = "$-";
34858148Seric 						break;
34958148Seric 
35058148Seric 					  case MATCHCLASS:
35158148Seric 						botch = "$=";
35258148Seric 						break;
35358148Seric 
35458148Seric 					  case MATCHNCLASS:
35558148Seric 						botch = "$~";
35658148Seric 						break;
35757589Seric 					}
35858148Seric 					if (botch != NULL)
35958148Seric 						syserr("Inappropriate use of %s on RHS",
36058148Seric 							botch);
36157589Seric 				}
36257589Seric 			}
36356678Seric 			else
36468481Seric 			{
36556678Seric 				syserr("R line: null RHS");
36668481Seric 				rwp->r_rhs = null_list;
36768481Seric 			}
3683308Seric 			break;
3693308Seric 
3704072Seric 		  case 'S':		/* select rewriting set */
37164440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
37264440Seric 				continue;
37368481Seric 			if (!isascii(*p))
37464440Seric 			{
37564440Seric 				syserr("invalid argument to S line: \"%.20s\"",
37664440Seric 					&bp[1]);
37764440Seric 				break;
37864440Seric 			}
37968481Seric 			if (isdigit(*p))
3808056Seric 			{
38168481Seric 				ruleset = atoi(p);
38268481Seric 				if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
38368481Seric 				{
38468481Seric 					syserr("bad ruleset %d (%d max)",
38568481Seric 						ruleset, MAXRWSETS / 2);
38668481Seric 					ruleset = 0;
38768481Seric 				}
3888056Seric 			}
38968481Seric 			else
39068481Seric 			{
39168481Seric 				STAB *s;
39268481Seric 				char delim;
39368481Seric 
39468481Seric 				q = p;
39568481Seric 				while (*p != '\0' && isascii(*p) &&
39668481Seric 				       (isalnum(*p) || strchr("-_$", *p) != NULL))
39768481Seric 					p++;
39868481Seric 				while (isascii(*p) && isspace(*p))
39968481Seric 					*p++ = '\0';
40068481Seric 				delim = *p;
40168481Seric 				if (delim != '\0')
40268481Seric 					*p++ = '\0';
40368481Seric 				s = stab(q, ST_RULESET, ST_ENTER);
40468481Seric 				if (s->s_ruleset != 0)
40568481Seric 					ruleset = s->s_ruleset;
40668481Seric 				else if (delim == '=')
40768481Seric 				{
40868481Seric 					ruleset = atoi(p);
40968481Seric 					if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
41068481Seric 					{
41168481Seric 						syserr("bad ruleset %s = %d (%d max)",
41268481Seric 							q, ruleset, MAXRWSETS / 2);
41368481Seric 						ruleset = 0;
41468481Seric 					}
41568481Seric 				}
41668481Seric 				else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
41768481Seric 				{
41868481Seric 					syserr("%s: too many named rulesets (%d max)",
41968481Seric 						q, MAXRWSETS / 2);
42068481Seric 					ruleset = 0;
42168481Seric 				}
42268481Seric 				s->s_ruleset = ruleset;
42368481Seric 			}
4244072Seric 			rwp = NULL;
4254072Seric 			break;
4264072Seric 
4273308Seric 		  case 'D':		/* macro definition */
42868481Seric 			mid = macid(&bp[1], &ep);
42968481Seric 			p = munchstring(ep, NULL);
43068481Seric 			define(mid, newstr(p), e);
4313308Seric 			break;
4323308Seric 
4333387Seric 		  case 'H':		/* required header line */
43457135Seric 			(void) chompheader(&bp[1], TRUE, e);
4353387Seric 			break;
4363387Seric 
4374061Seric 		  case 'C':		/* word class */
43868481Seric 		  case 'T':		/* trusted user (set class `t') */
43968481Seric 			if (bp[0] == 'C')
4404061Seric 			{
44168481Seric 				mid = macid(&bp[1], &ep);
442*68529Seric 				expand(ep, exbuf, sizeof exbuf, e);
44368481Seric 				p = exbuf;
44468481Seric 			}
44568481Seric 			else
44668481Seric 			{
44768481Seric 				mid = 't';
44868481Seric 				p = &bp[1];
44968481Seric 			}
45068481Seric 			while (*p != '\0')
45168481Seric 			{
4524061Seric 				register char *wd;
4534061Seric 				char delim;
4544061Seric 
45558050Seric 				while (*p != '\0' && isascii(*p) && isspace(*p))
4564061Seric 					p++;
4574061Seric 				wd = p;
45858050Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4594061Seric 					p++;
4604061Seric 				delim = *p;
4614061Seric 				*p = '\0';
4624061Seric 				if (wd[0] != '\0')
46368481Seric 					setclass(mid, wd);
4644061Seric 				*p = delim;
4654061Seric 			}
4664061Seric 			break;
4674061Seric 
46859272Seric 		  case 'F':		/* word class from file */
46968481Seric 			mid = macid(&bp[1], &ep);
47068481Seric 			for (p = ep; isascii(*p) && isspace(*p); )
47164133Seric 				p++;
47264133Seric 			if (p[0] == '-' && p[1] == 'o')
47364133Seric 			{
47464133Seric 				optional = TRUE;
47564133Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
47664133Seric 					p++;
47764133Seric 				while (isascii(*p) && isspace(*p))
47868481Seric 					p++;
47964133Seric 			}
48064133Seric 			else
48164133Seric 				optional = FALSE;
48264133Seric 			file = p;
48364133Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
48464133Seric 				p++;
48559272Seric 			if (*p == '\0')
48659272Seric 				p = "%s";
48759272Seric 			else
48859272Seric 			{
48959272Seric 				*p = '\0';
49059272Seric 				while (isascii(*++p) && isspace(*p))
49159272Seric 					continue;
49259272Seric 			}
49364133Seric 			fileclass(bp[1], file, p, safe, optional);
49459272Seric 			break;
49559272Seric 
49659156Seric #ifdef XLA
49759156Seric 		  case 'L':		/* extended load average description */
49859156Seric 			xla_init(&bp[1]);
49959156Seric 			break;
50059156Seric #endif
50159156Seric 
5024096Seric 		  case 'M':		/* define mailer */
50357135Seric 			makemailer(&bp[1]);
5044096Seric 			break;
5054096Seric 
5068252Seric 		  case 'O':		/* set option */
50758734Seric 			setoption(bp[1], &bp[2], safe, FALSE, e);
5088252Seric 			break;
5098252Seric 
5108252Seric 		  case 'P':		/* set precedence */
5118252Seric 			if (NumPriorities >= MAXPRIORITIES)
5128252Seric 			{
5138547Seric 				toomany('P', MAXPRIORITIES);
5148252Seric 				break;
5158252Seric 			}
51657135Seric 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
5178252Seric 				continue;
5188252Seric 			if (*p == '\0')
5198252Seric 				goto badline;
5208252Seric 			*p = '\0';
52157135Seric 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
5228252Seric 			Priorities[NumPriorities].pri_val = atoi(++p);
5238252Seric 			NumPriorities++;
5248252Seric 			break;
5258252Seric 
52652645Seric 		  case 'V':		/* configuration syntax version */
52764440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
52864440Seric 				continue;
52964440Seric 			if (!isascii(*p) || !isdigit(*p))
53064440Seric 			{
53164440Seric 				syserr("invalid argument to V line: \"%.20s\"",
53264440Seric 					&bp[1]);
53364440Seric 				break;
53464440Seric 			}
53564718Seric 			ConfigLevel = strtol(p, &ep, 10);
53664279Seric 			if (ConfigLevel >= 5)
53764279Seric 			{
53864279Seric 				/* level 5 configs have short name in $w */
53964279Seric 				p = macvalue('w', e);
54064279Seric 				if (p != NULL && (p = strchr(p, '.')) != NULL)
54164279Seric 					*p = '\0';
54264279Seric 			}
54364718Seric 			if (*ep++ == '/')
54464718Seric 			{
54564718Seric 				/* extract vendor code */
54664718Seric 				for (p = ep; isascii(*p) && isalpha(*p); )
54764718Seric 					p++;
54864718Seric 				*p = '\0';
54964718Seric 
55064718Seric 				if (!setvendor(ep))
55164718Seric 					syserr("invalid V line vendor code: \"%s\"",
55264718Seric 						ep);
55364718Seric 			}
55452645Seric 			break;
55552645Seric 
55653654Seric 		  case 'K':
55757135Seric 			makemapentry(&bp[1]);
55853654Seric 			break;
55953654Seric 
5603308Seric 		  default:
5614061Seric 		  badline:
56257135Seric 			syserr("unknown control line \"%s\"", bp);
5633308Seric 		}
56457135Seric 		if (bp != buf)
56557135Seric 			free(bp);
5663308Seric 	}
56752637Seric 	if (ferror(cf))
56852637Seric 	{
56952647Seric 		syserr("I/O read error", cfname);
57052637Seric 		exit(EX_OSFILE);
57152637Seric 	}
57252637Seric 	fclose(cf);
5739381Seric 	FileName = NULL;
57456836Seric 
57568481Seric 	/* initialize host maps from local service tables */
57668481Seric 	inithostmaps();
57768481Seric 
57868481Seric 	/* determine if we need to do special name-server frotz */
57967905Seric 	{
58068481Seric 		int nmaps;
58168481Seric 		char *maptype[MAXMAPSTACK];
58268481Seric 		short mapreturn[MAXMAPACTIONS];
58368481Seric 
58468481Seric 		nmaps = switch_map_find("hosts", maptype, mapreturn);
58568481Seric 		UseNameServer = FALSE;
58668481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
58768481Seric 		{
58868481Seric 			register int mapno;
58968481Seric 
59068481Seric 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
59168481Seric 			{
59268481Seric 				if (strcmp(maptype[mapno], "dns") == 0)
59368481Seric 					UseNameServer = TRUE;
59468481Seric 			}
59568481Seric 		}
59668481Seric 
59768481Seric #ifdef HESIOD
59868481Seric 		nmaps = switch_map_find("passwd", maptype, mapreturn);
59968481Seric 		UseHesiod = FALSE;
60068481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
60168481Seric 		{
60268481Seric 			register int mapno;
60368481Seric 
60468481Seric 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
60568481Seric 			{
60668481Seric 				if (strcmp(maptype[mapno], "hesiod") == 0)
60768481Seric 					UseHesiod = TRUE;
60868481Seric 			}
60968481Seric 		}
61068204Seric #endif
61167905Seric 	}
6124096Seric }
6134096Seric /*
6148547Seric **  TOOMANY -- signal too many of some option
6158547Seric **
6168547Seric **	Parameters:
6178547Seric **		id -- the id of the error line
6188547Seric **		maxcnt -- the maximum possible values
6198547Seric **
6208547Seric **	Returns:
6218547Seric **		none.
6228547Seric **
6238547Seric **	Side Effects:
6248547Seric **		gives a syserr.
6258547Seric */
6268547Seric 
6278547Seric toomany(id, maxcnt)
6288547Seric 	char id;
6298547Seric 	int maxcnt;
6308547Seric {
6319381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
6328547Seric }
6338547Seric /*
6344432Seric **  FILECLASS -- read members of a class from a file
6354432Seric **
6364432Seric **	Parameters:
6374432Seric **		class -- class to define.
6384432Seric **		filename -- name of file to read.
6394432Seric **		fmt -- scanf string to use for match.
64064133Seric **		safe -- if set, this is a safe read.
64164133Seric **		optional -- if set, it is not an error for the file to
64264133Seric **			not exist.
6434432Seric **
6444432Seric **	Returns:
6454432Seric **		none
6464432Seric **
6474432Seric **	Side Effects:
6484432Seric **
6494432Seric **		puts all lines in filename that match a scanf into
6504432Seric **			the named class.
6514432Seric */
6524432Seric 
65364133Seric fileclass(class, filename, fmt, safe, optional)
6544432Seric 	int class;
6554432Seric 	char *filename;
6564432Seric 	char *fmt;
65754973Seric 	bool safe;
65864133Seric 	bool optional;
6594432Seric {
66025808Seric 	FILE *f;
66168513Seric 	int sff;
6624432Seric 	char buf[MAXLINE];
6634432Seric 
66466101Seric 	if (tTd(37, 2))
66566101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
66666101Seric 
66766031Seric 	if (filename[0] == '|')
66866031Seric 	{
66966031Seric 		syserr("fileclass: pipes (F%c%s) not supported due to security problems",
67066031Seric 			class, filename);
67166031Seric 		return;
67266031Seric 	}
67368513Seric 	sff = SFF_REGONLY;
67468513Seric 	if (safe)
67568513Seric 		sff |= SFF_OPENASROOT;
67668513Seric 	f = safefopen(filename, O_RDONLY, 0, sff);
67768513Seric 	if (f == NULL && !optional)
67854973Seric 	{
67954973Seric 		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';
90968481Seric 				pw = 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,
126168481Seric 	"NameServerOptions",	'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,
129168481Seric 	"ForkQueueRuns",	'Y',		FALSE,
129268481Seric 	"ClassFactor",		'z',		FALSE,
129368481Seric 	"TimeFactor",		'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,
131468481Seric 
131568481Seric 	NULL,			'\0',		FALSE,
131668481Seric };
131768481Seric 
131868481Seric 
131968481Seric 
132058734Seric setoption(opt, val, safe, sticky, e)
132168481Seric 	u_char opt;
13228256Seric 	char *val;
132321755Seric 	bool safe;
13248269Seric 	bool sticky;
132558734Seric 	register ENVELOPE *e;
13268256Seric {
132757207Seric 	register char *p;
132868481Seric 	register struct optioninfo *o;
132968481Seric 	char *subopt;
13308265Seric 	extern bool atobool();
133112633Seric 	extern time_t convtime();
133214879Seric 	extern int QueueLA;
133314879Seric 	extern int RefuseLA;
133464718Seric 	extern bool Warn_Q_option;
13358256Seric 
133668481Seric 	errno = 0;
133768481Seric 	if (opt == ' ')
133868481Seric 	{
133968481Seric 		/* full word options */
134068481Seric 		struct optioninfo *sel;
134168481Seric 
134268481Seric 		p = strchr(val, '=');
134368481Seric 		if (p == NULL)
134468481Seric 			p = &val[strlen(val)];
134568481Seric 		while (*--p == ' ')
134668481Seric 			continue;
134768481Seric 		while (*++p == ' ')
134868481Seric 			*p = '\0';
134968481Seric 		if (p == val)
135068481Seric 		{
135168481Seric 			syserr("readcf: null option name");
135268481Seric 			return;
135368481Seric 		}
135468481Seric 		if (*p == '=')
135568481Seric 			*p++ = '\0';
135668481Seric 		while (*p == ' ')
135768481Seric 			p++;
135868481Seric 		subopt = strchr(val, '.');
135968481Seric 		if (subopt != NULL)
136068481Seric 			*subopt++ = '\0';
136168481Seric 		sel = NULL;
136268481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
136368481Seric 		{
136468481Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
136568481Seric 				continue;
136668481Seric 			if (strlen(o->o_name) == strlen(val))
136768481Seric 			{
136868481Seric 				/* completely specified -- this must be it */
136968481Seric 				sel = NULL;
137068481Seric 				break;
137168481Seric 			}
137268481Seric 			if (sel != NULL)
137368481Seric 				break;
137468481Seric 			sel = o;
137568481Seric 		}
137668481Seric 		if (sel != NULL && o->o_name == NULL)
137768481Seric 			o = sel;
137868481Seric 		else if (o->o_name == NULL)
137968481Seric 		{
138068481Seric 			syserr("readcf: unknown option name %s", val);
138168481Seric 			return;
138268481Seric 		}
138368481Seric 		else if (sel != NULL)
138468481Seric 		{
138568481Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
138668481Seric 				val, sel->o_name, o->o_name);
138768481Seric 			return;
138868481Seric 		}
138968481Seric 		if (strlen(val) != strlen(o->o_name))
139068481Seric 		{
139168481Seric 			bool oldVerbose = Verbose;
139268481Seric 
139368481Seric 			Verbose = TRUE;
139468481Seric 			message("Option %s used as abbreviation for %s",
139568481Seric 				val, o->o_name);
139668481Seric 			Verbose = oldVerbose;
139768481Seric 		}
139868481Seric 		opt = o->o_code;
139968481Seric 		val = p;
140068481Seric 	}
140168481Seric 	else
140268481Seric 	{
140368481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
140468481Seric 		{
140568481Seric 			if (o->o_code == opt)
140668481Seric 				break;
140768481Seric 		}
140868481Seric 		subopt = NULL;
140968481Seric 	}
141068481Seric 
14118256Seric 	if (tTd(37, 1))
141268481Seric 	{
141368481Seric 		printf(isascii(opt) && isprint(opt) ?
141468481Seric 			    "setoption %s (%c).%s=%s" :
141568481Seric 			    "setoption %s (0x%x).%s=%s",
141668481Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
141768481Seric 			opt,
141868481Seric 			subopt == NULL ? "" : subopt,
141968481Seric 			val);
142068481Seric 	}
14218256Seric 
14228256Seric 	/*
14238269Seric 	**  See if this option is preset for us.
14248256Seric 	*/
14258256Seric 
142659731Seric 	if (!sticky && bitnset(opt, StickyOpt))
14278269Seric 	{
14289341Seric 		if (tTd(37, 1))
14299341Seric 			printf(" (ignored)\n");
14308269Seric 		return;
14318269Seric 	}
14328269Seric 
143321755Seric 	/*
143421755Seric 	**  Check to see if this option can be specified by this user.
143521755Seric 	*/
143621755Seric 
143763787Seric 	if (!safe && RealUid == 0)
143821755Seric 		safe = TRUE;
143968481Seric 	if (!safe && !o->o_safe)
144021755Seric 	{
144139111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
144221755Seric 		{
144336582Sbostic 			if (tTd(37, 1))
144436582Sbostic 				printf(" (unsafe)");
144563787Seric 			if (RealUid != geteuid())
144636582Sbostic 			{
144751210Seric 				if (tTd(37, 1))
144851210Seric 					printf("(Resetting uid)");
144963787Seric 				(void) setgid(RealGid);
145063787Seric 				(void) setuid(RealUid);
145136582Sbostic 			}
145221755Seric 		}
145321755Seric 	}
145451210Seric 	if (tTd(37, 1))
145517985Seric 		printf("\n");
14568269Seric 
145768481Seric 	switch (opt & 0xff)
14588256Seric 	{
145959709Seric 	  case '7':		/* force seven-bit input */
146068481Seric 		SevenBitInput = atobool(val);
146152106Seric 		break;
146252106Seric 
146368481Seric 	  case '8':		/* handling of 8-bit input */
146468481Seric 		switch (*val)
146568481Seric 		{
146668481Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
146768481Seric 			MimeMode = 0;
146868481Seric 			break;
146968481Seric 
147068481Seric 		  case 'm':		/* convert 8-bit, convert MIME */
147168481Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
147268481Seric 			break;
147368481Seric 
147468481Seric 		  case 'j':		/* "just send 8" */
147568481Seric 			MimeMode = MM_PASS8BIT;
147668481Seric 			break;
147768481Seric 
147868481Seric 		  case 'p':		/* pass 8 bit, convert MIME */
147968481Seric 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
148068481Seric 			break;
148168481Seric 
148268481Seric 		  case 's':		/* strict adherence */
148368481Seric 			MimeMode = MM_CVTMIME;
148468481Seric 			break;
148568481Seric 
148668481Seric 		  case 'a':		/* encode 8 bit if available */
148768481Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
148868481Seric 			break;
148968481Seric 
149068481Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
149168481Seric 			MimeMode = MM_MIME8BIT;
149268481Seric 			break;
149368481Seric 
149468481Seric 		  default:
149568481Seric 			syserr("Unknown 8-bit mode %c", *val);
149668481Seric 			exit(EX_USAGE);
149768481Seric 		}
149868481Seric 		break;
149968481Seric 
15008256Seric 	  case 'A':		/* set default alias file */
15019381Seric 		if (val[0] == '\0')
150259672Seric 			setalias("aliases");
15039381Seric 		else
150459672Seric 			setalias(val);
15058256Seric 		break;
15068256Seric 
150717474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
150817474Seric 		if (val[0] == '\0')
150964796Seric 			SafeAlias = 5 * 60;		/* five minutes */
151017474Seric 		else
151164796Seric 			SafeAlias = convtime(val, 'm');
151217474Seric 		break;
151317474Seric 
151416843Seric 	  case 'B':		/* substitution for blank character */
151516843Seric 		SpaceSub = val[0];
151616843Seric 		if (SpaceSub == '\0')
151716843Seric 			SpaceSub = ' ';
151816843Seric 		break;
151916843Seric 
152059283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
152159283Seric 		p = strchr(val, '/');
152259283Seric 		if (p != NULL)
152359283Seric 		{
152459283Seric 			*p++ = '\0';
152559283Seric 			MaxMessageSize = atol(p);
152659283Seric 		}
152758082Seric 		MinBlocksFree = atol(val);
152858082Seric 		break;
152958082Seric 
15309284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
15319381Seric 		NoConnect = atobool(val);
15329284Seric 		break;
15339284Seric 
153451305Seric 	  case 'C':		/* checkpoint every N addresses */
153551305Seric 		CheckpointInterval = atoi(val);
153624944Seric 		break;
153724944Seric 
15389284Seric 	  case 'd':		/* delivery mode */
15399284Seric 		switch (*val)
15408269Seric 		{
15419284Seric 		  case '\0':
154258734Seric 			e->e_sendmode = SM_DELIVER;
15438269Seric 			break;
15448269Seric 
154510755Seric 		  case SM_QUEUE:	/* queue only */
154610755Seric #ifndef QUEUE
154710755Seric 			syserr("need QUEUE to set -odqueue");
154856795Seric #endif /* QUEUE */
154910755Seric 			/* fall through..... */
155010755Seric 
15519284Seric 		  case SM_DELIVER:	/* do everything */
15529284Seric 		  case SM_FORK:		/* fork after verification */
155358734Seric 			e->e_sendmode = *val;
15548269Seric 			break;
15558269Seric 
15568269Seric 		  default:
15579284Seric 			syserr("Unknown delivery mode %c", *val);
15588269Seric 			exit(EX_USAGE);
15598269Seric 		}
15608269Seric 		break;
15618269Seric 
15629146Seric 	  case 'D':		/* rebuild alias database as needed */
15639381Seric 		AutoRebuild = atobool(val);
15649146Seric 		break;
15659146Seric 
156655372Seric 	  case 'E':		/* error message header/header file */
156755379Seric 		if (*val != '\0')
156855379Seric 			ErrMsgFile = newstr(val);
156955372Seric 		break;
157055372Seric 
15718269Seric 	  case 'e':		/* set error processing mode */
15728269Seric 		switch (*val)
15738269Seric 		{
15749381Seric 		  case EM_QUIET:	/* be silent about it */
15759381Seric 		  case EM_MAIL:		/* mail back */
15769381Seric 		  case EM_BERKNET:	/* do berknet error processing */
15779381Seric 		  case EM_WRITE:	/* write back (or mail) */
15789381Seric 		  case EM_PRINT:	/* print errors normally (default) */
157958734Seric 			e->e_errormode = *val;
15808269Seric 			break;
15818269Seric 		}
15828269Seric 		break;
15838269Seric 
15849049Seric 	  case 'F':		/* file mode */
158517975Seric 		FileMode = atooct(val) & 0777;
15869049Seric 		break;
15879049Seric 
15888269Seric 	  case 'f':		/* save Unix-style From lines on front */
15899381Seric 		SaveFrom = atobool(val);
15908269Seric 		break;
15918269Seric 
159253735Seric 	  case 'G':		/* match recipients against GECOS field */
159353735Seric 		MatchGecos = atobool(val);
159453735Seric 		break;
159553735Seric 
15968256Seric 	  case 'g':		/* default gid */
159768481Seric   g_opt:
159864133Seric 		if (isascii(*val) && isdigit(*val))
159964133Seric 			DefGid = atoi(val);
160064133Seric 		else
160164133Seric 		{
160264133Seric 			register struct group *gr;
160364133Seric 
160464133Seric 			DefGid = -1;
160564133Seric 			gr = getgrnam(val);
160664133Seric 			if (gr == NULL)
160768481Seric 				syserr("readcf: option %c: unknown group %s",
160868481Seric 					opt, val);
160964133Seric 			else
161064133Seric 				DefGid = gr->gr_gid;
161164133Seric 		}
16128256Seric 		break;
16138256Seric 
16148256Seric 	  case 'H':		/* help file */
16159381Seric 		if (val[0] == '\0')
16168269Seric 			HelpFile = "sendmail.hf";
16179381Seric 		else
16189381Seric 			HelpFile = newstr(val);
16198256Seric 		break;
16208256Seric 
162151305Seric 	  case 'h':		/* maximum hop count */
162251305Seric 		MaxHopCount = atoi(val);
162351305Seric 		break;
162451305Seric 
162535651Seric 	  case 'I':		/* use internet domain name server */
162666334Seric #if NAMED_BIND
162757207Seric 		for (p = val; *p != 0; )
162857207Seric 		{
162957207Seric 			bool clearmode;
163057207Seric 			char *q;
163157207Seric 			struct resolverflags *rfp;
163257207Seric 
163357207Seric 			while (*p == ' ')
163457207Seric 				p++;
163557207Seric 			if (*p == '\0')
163657207Seric 				break;
163757207Seric 			clearmode = FALSE;
163857207Seric 			if (*p == '-')
163957207Seric 				clearmode = TRUE;
164057207Seric 			else if (*p != '+')
164157207Seric 				p--;
164257207Seric 			p++;
164357207Seric 			q = p;
164458050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
164557207Seric 				p++;
164657207Seric 			if (*p != '\0')
164757207Seric 				*p++ = '\0';
164857207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
164957207Seric 			{
165057207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
165157207Seric 					break;
165257207Seric 			}
165364923Seric 			if (rfp->rf_name == NULL)
165464923Seric 				syserr("readcf: I option value %s unrecognized", q);
165564923Seric 			else if (clearmode)
165657207Seric 				_res.options &= ~rfp->rf_bits;
165757207Seric 			else
165857207Seric 				_res.options |= rfp->rf_bits;
165957207Seric 		}
166057207Seric 		if (tTd(8, 2))
166157207Seric 			printf("_res.options = %x\n", _res.options);
166257207Seric #else
166357207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
166457207Seric #endif
166535651Seric 		break;
166635651Seric 
16678269Seric 	  case 'i':		/* ignore dot lines in message */
16689381Seric 		IgnrDot = atobool(val);
16698269Seric 		break;
16708269Seric 
167159730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
167259730Seric 		SendMIMEErrors = atobool(val);
167359730Seric 		break;
167459730Seric 
167557136Seric 	  case 'J':		/* .forward search path */
167657136Seric 		ForwardPath = newstr(val);
167757136Seric 		break;
167857136Seric 
167954967Seric 	  case 'k':		/* connection cache size */
168054967Seric 		MaxMciCache = atoi(val);
168156215Seric 		if (MaxMciCache < 0)
168256215Seric 			MaxMciCache = 0;
168354967Seric 		break;
168454967Seric 
168554967Seric 	  case 'K':		/* connection cache timeout */
168658796Seric 		MciCacheTimeout = convtime(val, 'm');
168754967Seric 		break;
168854967Seric 
168961104Seric 	  case 'l':		/* use Errors-To: header */
169061104Seric 		UseErrorsTo = atobool(val);
169161104Seric 		break;
169261104Seric 
16938256Seric 	  case 'L':		/* log level */
169464140Seric 		if (safe || LogLevel < atoi(val))
169564140Seric 			LogLevel = atoi(val);
16968256Seric 		break;
16978256Seric 
16988269Seric 	  case 'M':		/* define macro */
169968267Seric 		p = newstr(&val[1]);
170068267Seric 		if (!safe)
170168267Seric 			cleanstrcpy(p, p, MAXNAME);
170268267Seric 		define(val[0], p, CurEnv);
170316878Seric 		sticky = FALSE;
17048269Seric 		break;
17058269Seric 
17068269Seric 	  case 'm':		/* send to me too */
17079381Seric 		MeToo = atobool(val);
17088269Seric 		break;
17098269Seric 
171025820Seric 	  case 'n':		/* validate RHS in newaliases */
171125820Seric 		CheckAliases = atobool(val);
171225820Seric 		break;
171325820Seric 
171461104Seric 	    /* 'N' available -- was "net name" */
171561104Seric 
171658851Seric 	  case 'O':		/* daemon options */
171758851Seric 		setdaemonoptions(val);
171858851Seric 		break;
171958851Seric 
17208269Seric 	  case 'o':		/* assume old style headers */
17219381Seric 		if (atobool(val))
17229341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
17239341Seric 		else
17249341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
17258269Seric 		break;
17268269Seric 
172758082Seric 	  case 'p':		/* select privacy level */
172858082Seric 		p = val;
172958082Seric 		for (;;)
173058082Seric 		{
173158082Seric 			register struct prival *pv;
173258082Seric 			extern struct prival PrivacyValues[];
173358082Seric 
173458082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
173558082Seric 				p++;
173658082Seric 			if (*p == '\0')
173758082Seric 				break;
173858082Seric 			val = p;
173958082Seric 			while (isascii(*p) && isalnum(*p))
174058082Seric 				p++;
174158082Seric 			if (*p != '\0')
174258082Seric 				*p++ = '\0';
174358082Seric 
174458082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
174558082Seric 			{
174658082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
174758082Seric 					break;
174858082Seric 			}
174958886Seric 			if (pv->pv_name == NULL)
175058886Seric 				syserr("readcf: Op line: %s unrecognized", val);
175158082Seric 			PrivacyFlags |= pv->pv_flag;
175258082Seric 		}
175368479Seric 		sticky = FALSE;
175458082Seric 		break;
175558082Seric 
175624944Seric 	  case 'P':		/* postmaster copy address for returned mail */
175724944Seric 		PostMasterCopy = newstr(val);
175824944Seric 		break;
175924944Seric 
176024944Seric 	  case 'q':		/* slope of queue only function */
176124944Seric 		QueueFactor = atoi(val);
176224944Seric 		break;
176324944Seric 
17648256Seric 	  case 'Q':		/* queue directory */
17659381Seric 		if (val[0] == '\0')
17668269Seric 			QueueDir = "mqueue";
17679381Seric 		else
17689381Seric 			QueueDir = newstr(val);
176958789Seric 		if (RealUid != 0 && !safe)
177064718Seric 			Warn_Q_option = TRUE;
17718256Seric 		break;
17728256Seric 
177358148Seric 	  case 'R':		/* don't prune routes */
177458148Seric 		DontPruneRoutes = atobool(val);
177558148Seric 		break;
177658148Seric 
17778256Seric 	  case 'r':		/* read timeout */
177868481Seric 		if (subopt == NULL)
177968481Seric 			inittimeouts(val);
178068481Seric 		else
178168481Seric 			settimeout(subopt, val);
17828256Seric 		break;
17838256Seric 
17848256Seric 	  case 'S':		/* status file */
17859381Seric 		if (val[0] == '\0')
17868269Seric 			StatFile = "sendmail.st";
17879381Seric 		else
17889381Seric 			StatFile = newstr(val);
17898256Seric 		break;
17908256Seric 
17918265Seric 	  case 's':		/* be super safe, even if expensive */
17929381Seric 		SuperSafe = atobool(val);
17938256Seric 		break;
17948256Seric 
17958256Seric 	  case 'T':		/* queue timeout */
179658737Seric 		p = strchr(val, '/');
179758737Seric 		if (p != NULL)
179858737Seric 		{
179958737Seric 			*p++ = '\0';
180068481Seric 			settimeout("queuewarn", p);
180158737Seric 		}
180268481Seric 		settimeout("queuereturn", val);
180354967Seric 		break;
18048256Seric 
18058265Seric 	  case 't':		/* time zone name */
180652106Seric 		TimeZoneSpec = newstr(val);
18078265Seric 		break;
18088265Seric 
180950556Seric 	  case 'U':		/* location of user database */
181051360Seric 		UdbSpec = newstr(val);
181150556Seric 		break;
181250556Seric 
18138256Seric 	  case 'u':		/* set default uid */
181468481Seric 		for (p = val; *p != '\0'; p++)
181568481Seric 		{
181668481Seric 			if (*p == '.' || *p == '/' || *p == ':')
181768481Seric 			{
181868481Seric 				*p++ = '\0';
181968481Seric 				break;
182068481Seric 			}
182168481Seric 		}
182264133Seric 		if (isascii(*val) && isdigit(*val))
182364133Seric 			DefUid = atoi(val);
182464133Seric 		else
182564133Seric 		{
182664133Seric 			register struct passwd *pw;
182764133Seric 
182864133Seric 			DefUid = -1;
182964133Seric 			pw = getpwnam(val);
183064133Seric 			if (pw == NULL)
183164133Seric 				syserr("readcf: option u: unknown user %s", val);
183264133Seric 			else
183368481Seric 			{
183464133Seric 				DefUid = pw->pw_uid;
183568481Seric 				DefGid = pw->pw_gid;
183668481Seric 			}
183764133Seric 		}
183840973Sbostic 		setdefuser();
18398256Seric 
184068481Seric 		/* handle the group if it is there */
184168481Seric 		if (*p == '\0')
184268481Seric 			break;
184368481Seric 		val = p;
184468481Seric 		goto g_opt;
184568481Seric 
184658851Seric 	  case 'V':		/* fallback MX host */
184758851Seric 		FallBackMX = newstr(val);
184858851Seric 		break;
184958851Seric 
18508269Seric 	  case 'v':		/* run in verbose mode */
18519381Seric 		Verbose = atobool(val);
18528256Seric 		break;
18538256Seric 
185463837Seric 	  case 'w':		/* if we are best MX, try host directly */
185563837Seric 		TryNullMXList = atobool(val);
185663837Seric 		break;
185761104Seric 
185861104Seric 	    /* 'W' available -- was wizard password */
185961104Seric 
186014879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
186114879Seric 		QueueLA = atoi(val);
186214879Seric 		break;
186314879Seric 
186414879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
186514879Seric 		RefuseLA = atoi(val);
186614879Seric 		break;
186714879Seric 
186824981Seric 	  case 'y':		/* work recipient factor */
186924981Seric 		WkRecipFact = atoi(val);
187024981Seric 		break;
187124981Seric 
187224981Seric 	  case 'Y':		/* fork jobs during queue runs */
187324952Seric 		ForkQueueRuns = atobool(val);
187424952Seric 		break;
187524952Seric 
187624981Seric 	  case 'z':		/* work message class factor */
187724981Seric 		WkClassFact = atoi(val);
187824981Seric 		break;
187924981Seric 
188024981Seric 	  case 'Z':		/* work time factor */
188124981Seric 		WkTimeFact = atoi(val);
188224981Seric 		break;
188324981Seric 
188468481Seric 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
188568481Seric 		BrokenSmtpPeers = atobool(val);
188668481Seric 		break;
188768481Seric 
188868481Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
188968481Seric 		switch (*val)
189068481Seric 		{
189168481Seric 		  case 'h':	/* Host first */
189268481Seric 		  case 'H':
189368481Seric 			QueueSortOrder = QS_BYHOST;
189468481Seric 			break;
189568481Seric 
189668481Seric 		  case 'p':	/* Priority order */
189768481Seric 		  case 'P':
189868481Seric 			QueueSortOrder = QS_BYPRIORITY;
189968481Seric 			break;
190068481Seric 
190168481Seric 		  default:
190268481Seric 			syserr("Invalid queue sort order \"%s\"", val);
190368481Seric 		}
190468481Seric 		break;
190568481Seric 
190668481Seric 	  case O_MQA:		/* minimum queue age between deliveries */
190768481Seric 		MinQueueAge = convtime(val, 'm');
190868481Seric 		break;
190968481Seric 
191068481Seric 	  case O_MHSA:		/* maximum age of cached host status */
191168481Seric 		MaxHostStatAge = convtime(val, 'm');
191268481Seric 		break;
191368481Seric 
191468481Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
191568481Seric 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
191668481Seric 		break;
191768481Seric 
191868481Seric 	  case O_SSFILE:	/* service switch file */
191968481Seric 		ServiceSwitchFile = newstr(val);
192068481Seric 		break;
192168481Seric 
192268481Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
192368481Seric 		DialDelay = convtime(val, 's');
192468481Seric 		break;
192568481Seric 
192668481Seric 	  case O_NORCPTACTION:	/* what to do if no recipient */
192768481Seric 		if (strcasecmp(val, "none") == 0)
192868481Seric 			NoRecipientAction = NRA_NO_ACTION;
192968481Seric 		else if (strcasecmp(val, "add-to") == 0)
193068481Seric 			NoRecipientAction = NRA_ADD_TO;
193168481Seric 		else if (strcasecmp(val, "add-apparently-to") == 0)
193268481Seric 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
193368481Seric 		else if (strcasecmp(val, "add-bcc") == 0)
193468481Seric 			NoRecipientAction = NRA_ADD_BCC;
193568481Seric 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
193668481Seric 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
193768481Seric 		else
193868481Seric 			syserr("Invalid NoRecipientAction: %s", val);
193968481Seric 
194068490Seric 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
194168490Seric 		SafeFileEnv = newstr(val);
194268490Seric 		break;
194368490Seric 
19448256Seric 	  default:
194568481Seric 		if (tTd(37, 1))
194668481Seric 		{
194768481Seric 			if (isascii(opt) && isprint(opt))
194868481Seric 				printf("Warning: option %c unknown\n", opt);
194968481Seric 			else
195068481Seric 				printf("Warning: option 0x%x unknown\n", opt);
195168481Seric 		}
19528256Seric 		break;
19538256Seric 	}
195416878Seric 	if (sticky)
195516878Seric 		setbitn(opt, StickyOpt);
19569188Seric 	return;
19578256Seric }
195810687Seric /*
195968481Seric **  SETCLASS -- set a string into a class
196010687Seric **
196110687Seric **	Parameters:
196268481Seric **		class -- the class to put the string in.
196368481Seric **		str -- the string to enter
196410687Seric **
196510687Seric **	Returns:
196610687Seric **		none.
196710687Seric **
196810687Seric **	Side Effects:
196910687Seric **		puts the word into the symbol table.
197010687Seric */
197110687Seric 
197268481Seric setclass(class, str)
197310687Seric 	int class;
197468481Seric 	char *str;
197510687Seric {
197610687Seric 	register STAB *s;
197710687Seric 
197857943Seric 	if (tTd(37, 8))
197968481Seric 		printf("setclass(%c, %s)\n", class, str);
198068481Seric 	s = stab(str, ST_CLASS, ST_ENTER);
198110687Seric 	setbitn(class, s->s_class);
198210687Seric }
198353654Seric /*
198453654Seric **  MAKEMAPENTRY -- create a map entry
198553654Seric **
198653654Seric **	Parameters:
198753654Seric **		line -- the config file line
198853654Seric **
198953654Seric **	Returns:
199053654Seric **		TRUE if it successfully entered the map entry.
199153654Seric **		FALSE otherwise (usually syntax error).
199253654Seric **
199353654Seric **	Side Effects:
199453654Seric **		Enters the map into the dictionary.
199553654Seric */
199653654Seric 
199753654Seric void
199853654Seric makemapentry(line)
199953654Seric 	char *line;
200053654Seric {
200153654Seric 	register char *p;
200253654Seric 	char *mapname;
200353654Seric 	char *classname;
200464078Seric 	register STAB *s;
200553654Seric 	STAB *class;
200653654Seric 
200758050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
200853654Seric 		continue;
200958050Seric 	if (!(isascii(*p) && isalnum(*p)))
201053654Seric 	{
201153654Seric 		syserr("readcf: config K line: no map name");
201253654Seric 		return;
201353654Seric 	}
201453654Seric 
201553654Seric 	mapname = p;
201668481Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
201753654Seric 		continue;
201853654Seric 	if (*p != '\0')
201953654Seric 		*p++ = '\0';
202058050Seric 	while (isascii(*p) && isspace(*p))
202153654Seric 		p++;
202258050Seric 	if (!(isascii(*p) && isalnum(*p)))
202353654Seric 	{
202453654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
202553654Seric 		return;
202653654Seric 	}
202753654Seric 	classname = p;
202858050Seric 	while (isascii(*++p) && isalnum(*p))
202953654Seric 		continue;
203053654Seric 	if (*p != '\0')
203153654Seric 		*p++ = '\0';
203258050Seric 	while (isascii(*p) && isspace(*p))
203353654Seric 		p++;
203453654Seric 
203553654Seric 	/* look up the class */
203653654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
203753654Seric 	if (class == NULL)
203853654Seric 	{
203953654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
204053654Seric 		return;
204153654Seric 	}
204253654Seric 
204353654Seric 	/* enter the map */
204464078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
204564078Seric 	s->s_map.map_class = &class->s_mapclass;
204664078Seric 	s->s_map.map_mname = newstr(mapname);
204753654Seric 
204864078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
204964078Seric 		s->s_map.map_mflags |= MF_VALID;
205064078Seric 
205164078Seric 	if (tTd(37, 5))
205264078Seric 	{
205364078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
205464078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
205564078Seric 			s->s_map.map_mflags,
205664078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
205764078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
205864078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
205964078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
206064078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
206164078Seric 	}
206253654Seric }
206358112Seric /*
206468481Seric **  INITTIMEOUTS -- parse and set timeout values
206558112Seric **
206658112Seric **	Parameters:
206758112Seric **		val -- a pointer to the values.  If NULL, do initial
206858112Seric **			settings.
206958112Seric **
207058112Seric **	Returns:
207158112Seric **		none.
207258112Seric **
207358112Seric **	Side Effects:
207458112Seric **		Initializes the TimeOuts structure
207558112Seric */
207658112Seric 
207764255Seric #define SECONDS
207858112Seric #define MINUTES	* 60
207958112Seric #define HOUR	* 3600
208058112Seric 
208168481Seric inittimeouts(val)
208258112Seric 	register char *val;
208358112Seric {
208458112Seric 	register char *p;
208558671Seric 	extern time_t convtime();
208658112Seric 
208758112Seric 	if (val == NULL)
208858112Seric 	{
208958112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
209058112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
209158112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
209258112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
209358112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
209458112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
209558112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
209658112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
209758112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
209858112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
209958112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
210068481Seric #if IDENTPROTO
210164255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
210268481Seric #else
210368481Seric 		TimeOuts.to_ident = (time_t) 0 SECONDS;
210468481Seric #endif
210568481Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
210658112Seric 		return;
210758112Seric 	}
210858112Seric 
210958112Seric 	for (;; val = p)
211058112Seric 	{
211158112Seric 		while (isascii(*val) && isspace(*val))
211258112Seric 			val++;
211358112Seric 		if (*val == '\0')
211458112Seric 			break;
211558112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
211658112Seric 			continue;
211758112Seric 		if (*p != '\0')
211858112Seric 			*p++ = '\0';
211958112Seric 
212058112Seric 		if (isascii(*val) && isdigit(*val))
212158112Seric 		{
212258112Seric 			/* old syntax -- set everything */
212358796Seric 			TimeOuts.to_mail = convtime(val, 'm');
212458112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
212558112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
212658112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
212758112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
212858112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
212958112Seric 			continue;
213058112Seric 		}
213158112Seric 		else
213258112Seric 		{
213368481Seric 			register char *q = strchr(val, ':');
213458112Seric 
213568481Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
213658112Seric 			{
213758112Seric 				/* syntax error */
213858112Seric 				continue;
213958112Seric 			}
214058112Seric 			*q++ = '\0';
214168481Seric 			settimeout(val, q);
214268481Seric 		}
214368481Seric 	}
214468481Seric }
214568481Seric /*
214668481Seric **  SETTIMEOUT -- set an individual timeout
214768481Seric **
214868481Seric **	Parameters:
214968481Seric **		name -- the name of the timeout.
215068481Seric **		val -- the value of the timeout.
215168481Seric **
215268481Seric **	Returns:
215368481Seric **		none.
215468481Seric */
215558112Seric 
215668481Seric settimeout(name, val)
215768481Seric 	char *name;
215868481Seric 	char *val;
215968481Seric {
216068481Seric 	register char *p;
216168481Seric 	time_t to;
216268481Seric 	extern time_t convtime();
216368481Seric 
216468481Seric 	to = convtime(val, 'm');
216568481Seric 	p = strchr(name, '.');
216668481Seric 	if (p != NULL)
216768481Seric 		*p++ = '\0';
216868481Seric 
216968481Seric 	if (strcasecmp(name, "initial") == 0)
217068481Seric 		TimeOuts.to_initial = to;
217168481Seric 	else if (strcasecmp(name, "mail") == 0)
217268481Seric 		TimeOuts.to_mail = to;
217368481Seric 	else if (strcasecmp(name, "rcpt") == 0)
217468481Seric 		TimeOuts.to_rcpt = to;
217568481Seric 	else if (strcasecmp(name, "datainit") == 0)
217668481Seric 		TimeOuts.to_datainit = to;
217768481Seric 	else if (strcasecmp(name, "datablock") == 0)
217868481Seric 		TimeOuts.to_datablock = to;
217968481Seric 	else if (strcasecmp(name, "datafinal") == 0)
218068481Seric 		TimeOuts.to_datafinal = to;
218168481Seric 	else if (strcasecmp(name, "command") == 0)
218268481Seric 		TimeOuts.to_nextcommand = to;
218368481Seric 	else if (strcasecmp(name, "rset") == 0)
218468481Seric 		TimeOuts.to_rset = to;
218568481Seric 	else if (strcasecmp(name, "helo") == 0)
218668481Seric 		TimeOuts.to_helo = to;
218768481Seric 	else if (strcasecmp(name, "quit") == 0)
218868481Seric 		TimeOuts.to_quit = to;
218968481Seric 	else if (strcasecmp(name, "misc") == 0)
219068481Seric 		TimeOuts.to_miscshort = to;
219168481Seric 	else if (strcasecmp(name, "ident") == 0)
219268481Seric 		TimeOuts.to_ident = to;
219368481Seric 	else if (strcasecmp(name, "fileopen") == 0)
219468481Seric 		TimeOuts.to_fileopen = to;
219568481Seric 	else if (strcasecmp(name, "queuewarn") == 0)
219668481Seric 	{
219768481Seric 		to = convtime(val, 'h');
219868481Seric 		if (p == NULL || strcmp(p, "*") == 0)
219968481Seric 		{
220068481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
220168481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
220268481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
220358112Seric 		}
220468481Seric 		else if (strcasecmp(p, "normal") == 0)
220568481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
220668481Seric 		else if (strcasecmp(p, "urgent") == 0)
220768481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
220868481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
220968481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
221068481Seric 		else
221168481Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
221258112Seric 	}
221368481Seric 	else if (strcasecmp(name, "queuereturn") == 0)
221468481Seric 	{
221568481Seric 		to = convtime(val, 'd');
221668481Seric 		if (p == NULL || strcmp(p, "*") == 0)
221768481Seric 		{
221868481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
221968481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
222068481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
222168481Seric 		}
222268481Seric 		else if (strcasecmp(p, "normal") == 0)
222368481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
222468481Seric 		else if (strcasecmp(p, "urgent") == 0)
222568481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
222668481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
222768481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
222868481Seric 		else
222968481Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
223068481Seric 	}
223168481Seric 	else
223268481Seric 		syserr("settimeout: invalid timeout %s", name);
223358112Seric }
2234