xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 68481)
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*68481Seric static char sccsid[] = "@(#)readcf.c	8.71 (Berkeley) 03/05/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;
73*68481Seric 	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;
81*68481Seric 	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];
88*68481Seric 	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 
139*68481Seric 		/* 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 
172*68481Seric 			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 */
183*68481Seric 			*p++ = MACROEXPAND;
184*68481Seric 
185*68481Seric 			/* convert macro name to code */
186*68481Seric 			*p = macid(p, &ep);
187*68481Seric 			if (ep != p)
188*68481Seric 				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';
22457135Seric 			expand(&bp[1], exbuf, &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
300*68481Seric 			{
30156678Seric 				syserr("R line: null LHS");
302*68481Seric 				rwp->r_lhs = null_list;
303*68481Seric 			}
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';
31255012Seric 			expand(q, exbuf, &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
364*68481Seric 			{
36556678Seric 				syserr("R line: null RHS");
366*68481Seric 				rwp->r_rhs = null_list;
367*68481Seric 			}
3683308Seric 			break;
3693308Seric 
3704072Seric 		  case 'S':		/* select rewriting set */
37164440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
37264440Seric 				continue;
373*68481Seric 			if (!isascii(*p))
37464440Seric 			{
37564440Seric 				syserr("invalid argument to S line: \"%.20s\"",
37664440Seric 					&bp[1]);
37764440Seric 				break;
37864440Seric 			}
379*68481Seric 			if (isdigit(*p))
3808056Seric 			{
381*68481Seric 				ruleset = atoi(p);
382*68481Seric 				if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
383*68481Seric 				{
384*68481Seric 					syserr("bad ruleset %d (%d max)",
385*68481Seric 						ruleset, MAXRWSETS / 2);
386*68481Seric 					ruleset = 0;
387*68481Seric 				}
3888056Seric 			}
389*68481Seric 			else
390*68481Seric 			{
391*68481Seric 				STAB *s;
392*68481Seric 				char delim;
393*68481Seric 
394*68481Seric 				q = p;
395*68481Seric 				while (*p != '\0' && isascii(*p) &&
396*68481Seric 				       (isalnum(*p) || strchr("-_$", *p) != NULL))
397*68481Seric 					p++;
398*68481Seric 				while (isascii(*p) && isspace(*p))
399*68481Seric 					*p++ = '\0';
400*68481Seric 				delim = *p;
401*68481Seric 				if (delim != '\0')
402*68481Seric 					*p++ = '\0';
403*68481Seric 				s = stab(q, ST_RULESET, ST_ENTER);
404*68481Seric 				if (s->s_ruleset != 0)
405*68481Seric 					ruleset = s->s_ruleset;
406*68481Seric 				else if (delim == '=')
407*68481Seric 				{
408*68481Seric 					ruleset = atoi(p);
409*68481Seric 					if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
410*68481Seric 					{
411*68481Seric 						syserr("bad ruleset %s = %d (%d max)",
412*68481Seric 							q, ruleset, MAXRWSETS / 2);
413*68481Seric 						ruleset = 0;
414*68481Seric 					}
415*68481Seric 				}
416*68481Seric 				else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
417*68481Seric 				{
418*68481Seric 					syserr("%s: too many named rulesets (%d max)",
419*68481Seric 						q, MAXRWSETS / 2);
420*68481Seric 					ruleset = 0;
421*68481Seric 				}
422*68481Seric 				s->s_ruleset = ruleset;
423*68481Seric 			}
4244072Seric 			rwp = NULL;
4254072Seric 			break;
4264072Seric 
4273308Seric 		  case 'D':		/* macro definition */
428*68481Seric 			mid = macid(&bp[1], &ep);
429*68481Seric 			p = munchstring(ep, NULL);
430*68481Seric 			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 */
438*68481Seric 		  case 'T':		/* trusted user (set class `t') */
439*68481Seric 			if (bp[0] == 'C')
4404061Seric 			{
441*68481Seric 				mid = macid(&bp[1], &ep);
442*68481Seric 				expand(ep, exbuf, &exbuf[sizeof exbuf], e);
443*68481Seric 				p = exbuf;
444*68481Seric 			}
445*68481Seric 			else
446*68481Seric 			{
447*68481Seric 				mid = 't';
448*68481Seric 				p = &bp[1];
449*68481Seric 			}
450*68481Seric 			while (*p != '\0')
451*68481Seric 			{
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')
463*68481Seric 					setclass(mid, wd);
4644061Seric 				*p = delim;
4654061Seric 			}
4664061Seric 			break;
4674061Seric 
46859272Seric 		  case 'F':		/* word class from file */
469*68481Seric 			mid = macid(&bp[1], &ep);
470*68481Seric 			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))
478*68481Seric 					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 
575*68481Seric 	/* initialize host maps from local service tables */
576*68481Seric 	inithostmaps();
577*68481Seric 
578*68481Seric 	/* determine if we need to do special name-server frotz */
57967905Seric 	{
580*68481Seric 		int nmaps;
581*68481Seric 		char *maptype[MAXMAPSTACK];
582*68481Seric 		short mapreturn[MAXMAPACTIONS];
583*68481Seric 
584*68481Seric 		nmaps = switch_map_find("hosts", maptype, mapreturn);
585*68481Seric 		UseNameServer = FALSE;
586*68481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
587*68481Seric 		{
588*68481Seric 			register int mapno;
589*68481Seric 
590*68481Seric 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
591*68481Seric 			{
592*68481Seric 				if (strcmp(maptype[mapno], "dns") == 0)
593*68481Seric 					UseNameServer = TRUE;
594*68481Seric 			}
595*68481Seric 		}
596*68481Seric 
597*68481Seric #ifdef HESIOD
598*68481Seric 		nmaps = switch_map_find("passwd", maptype, mapreturn);
599*68481Seric 		UseHesiod = FALSE;
600*68481Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
601*68481Seric 		{
602*68481Seric 			register int mapno;
603*68481Seric 
604*68481Seric 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
605*68481Seric 			{
606*68481Seric 				if (strcmp(maptype[mapno], "hesiod") == 0)
607*68481Seric 					UseHesiod = TRUE;
608*68481Seric 			}
609*68481Seric 		}
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;
66154973Seric 	struct stat stbuf;
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 	}
67354973Seric 	if (stat(filename, &stbuf) < 0)
67454973Seric 	{
67566101Seric 		if (tTd(37, 2))
67666101Seric 			printf("  cannot stat (%s)\n", errstring(errno));
67764133Seric 		if (!optional)
67864133Seric 			syserr("fileclass: cannot stat %s", filename);
67954973Seric 		return;
68054973Seric 	}
68154973Seric 	if (!S_ISREG(stbuf.st_mode))
68254973Seric 	{
68354973Seric 		syserr("fileclass: %s not a regular file", filename);
68454973Seric 		return;
68554973Seric 	}
68654973Seric 	if (!safe && access(filename, R_OK) < 0)
68754973Seric 	{
68854973Seric 		syserr("fileclass: access denied on %s", filename);
68954973Seric 		return;
69054973Seric 	}
69154973Seric 	f = fopen(filename, "r");
6924432Seric 	if (f == NULL)
6934432Seric 	{
69454973Seric 		syserr("fileclass: cannot open %s", filename);
6954432Seric 		return;
6964432Seric 	}
6974432Seric 
6984432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
6994432Seric 	{
7004432Seric 		register STAB *s;
70125808Seric 		register char *p;
70225808Seric # ifdef SCANF
7034432Seric 		char wordbuf[MAXNAME+1];
7044432Seric 
7054432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
7064432Seric 			continue;
70725808Seric 		p = wordbuf;
70856795Seric # else /* SCANF */
70925808Seric 		p = buf;
71056795Seric # endif /* SCANF */
71125808Seric 
71225808Seric 		/*
71325808Seric 		**  Break up the match into words.
71425808Seric 		*/
71525808Seric 
71625808Seric 		while (*p != '\0')
71725808Seric 		{
71825808Seric 			register char *q;
71925808Seric 
72025808Seric 			/* strip leading spaces */
72158050Seric 			while (isascii(*p) && isspace(*p))
72225808Seric 				p++;
72325808Seric 			if (*p == '\0')
72425808Seric 				break;
72525808Seric 
72625808Seric 			/* find the end of the word */
72725808Seric 			q = p;
72858050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
72925808Seric 				p++;
73025808Seric 			if (*p != '\0')
73125808Seric 				*p++ = '\0';
73225808Seric 
73325808Seric 			/* enter the word in the symbol table */
73466101Seric 			setclass(class, q);
73525808Seric 		}
7364432Seric 	}
7374432Seric 
73854973Seric 	(void) fclose(f);
7394432Seric }
7404432Seric /*
7414096Seric **  MAKEMAILER -- define a new mailer.
7424096Seric **
7434096Seric **	Parameters:
74410327Seric **		line -- description of mailer.  This is in labeled
74510327Seric **			fields.  The fields are:
746*68481Seric **			   A -- the argv for this mailer
747*68481Seric **			   C -- the character set for MIME conversions
748*68481Seric **			   D -- the directory to run in
749*68481Seric **			   E -- the eol string
750*68481Seric **			   F -- the flags associated with the mailer
751*68481Seric **			   L -- the maximum line length
752*68481Seric **			   M -- the maximum message size
75368479Seric **			   P -- the path to the mailer
754*68481Seric **			   R -- the recipient rewriting set
75568479Seric **			   S -- the sender rewriting set
756*68481Seric **			   T -- the mailer type (for DSNs)
757*68481Seric **			   U -- the uid to run as
75810327Seric **			The first word is the canonical name of the mailer.
7594096Seric **
7604096Seric **	Returns:
7614096Seric **		none.
7624096Seric **
7634096Seric **	Side Effects:
7644096Seric **		enters the mailer into the mailer table.
7654096Seric */
7663308Seric 
76721066Seric makemailer(line)
7684096Seric 	char *line;
7694096Seric {
7704096Seric 	register char *p;
7718067Seric 	register struct mailer *m;
7728067Seric 	register STAB *s;
7738067Seric 	int i;
77410327Seric 	char fcode;
77558020Seric 	auto char *endp;
7764096Seric 	extern int NextMailer;
77710327Seric 	extern char **makeargv();
77810327Seric 	extern char *munchstring();
77910701Seric 	extern long atol();
7804096Seric 
78110327Seric 	/* allocate a mailer and set up defaults */
78210327Seric 	m = (struct mailer *) xalloc(sizeof *m);
78310327Seric 	bzero((char *) m, sizeof *m);
78410327Seric 	m->m_eol = "\n";
785*68481Seric 	m->m_uid = m->m_gid = 0;
78610327Seric 
78710327Seric 	/* collect the mailer name */
78858050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
78910327Seric 		continue;
79010327Seric 	if (*p != '\0')
79110327Seric 		*p++ = '\0';
79210327Seric 	m->m_name = newstr(line);
79310327Seric 
79410327Seric 	/* now scan through and assign info from the fields */
79510327Seric 	while (*p != '\0')
79610327Seric 	{
79758333Seric 		auto char *delimptr;
79858333Seric 
79958050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
80010327Seric 			p++;
80110327Seric 
80210327Seric 		/* p now points to field code */
80310327Seric 		fcode = *p;
80410327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
80510327Seric 			p++;
80610327Seric 		if (*p++ != '=')
80710327Seric 		{
80852637Seric 			syserr("mailer %s: `=' expected", m->m_name);
80910327Seric 			return;
81010327Seric 		}
81158050Seric 		while (isascii(*p) && isspace(*p))
81210327Seric 			p++;
81310327Seric 
81410327Seric 		/* p now points to the field body */
81558333Seric 		p = munchstring(p, &delimptr);
81610327Seric 
81710327Seric 		/* install the field into the mailer struct */
81810327Seric 		switch (fcode)
81910327Seric 		{
82010327Seric 		  case 'P':		/* pathname */
82110327Seric 			m->m_mailer = newstr(p);
82210327Seric 			break;
82310327Seric 
82410327Seric 		  case 'F':		/* flags */
82510687Seric 			for (; *p != '\0'; p++)
82658050Seric 				if (!(isascii(*p) && isspace(*p)))
82752637Seric 					setbitn(*p, m->m_flags);
82810327Seric 			break;
82910327Seric 
83010327Seric 		  case 'S':		/* sender rewriting ruleset */
83110327Seric 		  case 'R':		/* recipient rewriting ruleset */
83258020Seric 			i = strtol(p, &endp, 10);
83310327Seric 			if (i < 0 || i >= MAXRWSETS)
83410327Seric 			{
83510327Seric 				syserr("invalid rewrite set, %d max", MAXRWSETS);
83610327Seric 				return;
83710327Seric 			}
83810327Seric 			if (fcode == 'S')
83958020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
84010327Seric 			else
84158020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
84258020Seric 
84358020Seric 			p = endp;
84459985Seric 			if (*p++ == '/')
84558020Seric 			{
84658020Seric 				i = strtol(p, NULL, 10);
84758020Seric 				if (i < 0 || i >= MAXRWSETS)
84858020Seric 				{
84958020Seric 					syserr("invalid rewrite set, %d max",
85058020Seric 						MAXRWSETS);
85158020Seric 					return;
85258020Seric 				}
85358020Seric 				if (fcode == 'S')
85458020Seric 					m->m_sh_rwset = i;
85558020Seric 				else
85658020Seric 					m->m_rh_rwset = i;
85758020Seric 			}
85810327Seric 			break;
85910327Seric 
86010327Seric 		  case 'E':		/* end of line string */
86110327Seric 			m->m_eol = newstr(p);
86210327Seric 			break;
86310327Seric 
86410327Seric 		  case 'A':		/* argument vector */
86510327Seric 			m->m_argv = makeargv(p);
86610327Seric 			break;
86710701Seric 
86810701Seric 		  case 'M':		/* maximum message size */
86910701Seric 			m->m_maxsize = atol(p);
87010701Seric 			break;
87152106Seric 
87252106Seric 		  case 'L':		/* maximum line length */
87352106Seric 			m->m_linelimit = atoi(p);
87452106Seric 			break;
87558935Seric 
87658935Seric 		  case 'D':		/* working directory */
87758935Seric 			m->m_execdir = newstr(p);
87858935Seric 			break;
879*68481Seric 
880*68481Seric 		  case 'C':		/* default charset */
881*68481Seric 			m->m_defcharset = newstr(p);
882*68481Seric 			break;
883*68481Seric 
884*68481Seric 		  case 'T':		/* MTA Type */
885*68481Seric 			m->m_mtatype = newstr(p);
886*68481Seric 			p = strchr(m->m_mtatype, '/');
887*68481Seric 			if (p != NULL)
888*68481Seric 			{
889*68481Seric 				*p++ = '\0';
890*68481Seric 				if (*p == '\0')
891*68481Seric 					p = NULL;
892*68481Seric 			}
893*68481Seric 			if (p == NULL)
894*68481Seric 				m->m_addrtype = m->m_mtatype;
895*68481Seric 			else
896*68481Seric 			{
897*68481Seric 				m->m_addrtype = p;
898*68481Seric 				p = strchr(p, '/');
899*68481Seric 			}
900*68481Seric 			if (p != NULL)
901*68481Seric 			{
902*68481Seric 				*p++ = '\0';
903*68481Seric 				if (*p == '\0')
904*68481Seric 					p = NULL;
905*68481Seric 			}
906*68481Seric 			if (p == NULL)
907*68481Seric 				m->m_diagtype = m->m_mtatype;
908*68481Seric 			else
909*68481Seric 				m->m_diagtype = p;
910*68481Seric 			break;
911*68481Seric 
912*68481Seric 		  case 'U':		/* user id */
913*68481Seric 			if (isascii(*p) && !isdigit(*p))
914*68481Seric 			{
915*68481Seric 				char *q = p;
916*68481Seric 				struct passwd *pw;
917*68481Seric 
918*68481Seric 				while (isascii(*p) && isalnum(*p))
919*68481Seric 					p++;
920*68481Seric 				while (isascii(*p) && isspace(*p))
921*68481Seric 					*p++ = '\0';
922*68481Seric 				if (*p != '\0')
923*68481Seric 					*p++ = '\0';
924*68481Seric 				pw = getpwnam(q);
925*68481Seric 				if (pw == NULL)
926*68481Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
927*68481Seric 				else
928*68481Seric 				{
929*68481Seric 					m->m_uid = pw->pw_uid;
930*68481Seric 					m->m_gid = pw->pw_gid;
931*68481Seric 				}
932*68481Seric 			}
933*68481Seric 			else
934*68481Seric 			{
935*68481Seric 				auto char *q;
936*68481Seric 
937*68481Seric 				m->m_uid = strtol(p, &q, 0);
938*68481Seric 				p = q;
939*68481Seric 			}
940*68481Seric 			while (isascii(*p) && isspace(*p))
941*68481Seric 				p++;
942*68481Seric 			if (*p == '\0')
943*68481Seric 				break;
944*68481Seric 			if (isascii(*p) && !isdigit(*p))
945*68481Seric 			{
946*68481Seric 				char *q = p;
947*68481Seric 				struct group *gr;
948*68481Seric 
949*68481Seric 				while (isascii(*p) && isalnum(*p))
950*68481Seric 					p++;
951*68481Seric 				*p++ = '\0';
952*68481Seric 				gr = getgrnam(q);
953*68481Seric 				if (gr == NULL)
954*68481Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
955*68481Seric 				else
956*68481Seric 					m->m_gid = gr->gr_gid;
957*68481Seric 			}
958*68481Seric 			else
959*68481Seric 			{
960*68481Seric 				m->m_gid = strtol(p, NULL, 0);
961*68481Seric 			}
962*68481Seric 			break;
96310327Seric 		}
96410327Seric 
96558333Seric 		p = delimptr;
96610327Seric 	}
96710327Seric 
96858321Seric 	/* do some rationality checking */
96958321Seric 	if (m->m_argv == NULL)
97058321Seric 	{
97158321Seric 		syserr("M%s: A= argument required", m->m_name);
97258321Seric 		return;
97358321Seric 	}
97458321Seric 	if (m->m_mailer == NULL)
97558321Seric 	{
97658321Seric 		syserr("M%s: P= argument required", m->m_name);
97758321Seric 		return;
97858321Seric 	}
97958321Seric 
9804096Seric 	if (NextMailer >= MAXMAILERS)
9814096Seric 	{
9829381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
9834096Seric 		return;
9844096Seric 	}
98557402Seric 
986*68481Seric 	/* do some heuristic cleanup for back compatibility */
987*68481Seric 	if (bitnset(M_LIMITS, m->m_flags))
988*68481Seric 	{
989*68481Seric 		if (m->m_linelimit == 0)
990*68481Seric 			m->m_linelimit = SMTPLINELIM;
991*68481Seric 		if (ConfigLevel < 2)
992*68481Seric 			setbitn(M_7BITS, m->m_flags);
993*68481Seric 	}
994*68481Seric 
995*68481Seric 	if (ConfigLevel < 6 &&
996*68481Seric 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
997*68481Seric 	     strcmp(m->m_mailer, "[TCP]") == 0))
998*68481Seric 	{
999*68481Seric 		if (m->m_mtatype == NULL)
1000*68481Seric 			m->m_mtatype = "dns";
1001*68481Seric 		if (m->m_addrtype == NULL)
1002*68481Seric 			m->m_addrtype = "rfc822";
1003*68481Seric 		if (m->m_diagtype == NULL)
1004*68481Seric 			m->m_diagtype = "smtp";
1005*68481Seric 	}
1006*68481Seric 
1007*68481Seric 	/* enter the mailer into the symbol table */
100810327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
100957402Seric 	if (s->s_mailer != NULL)
101057402Seric 	{
101157402Seric 		i = s->s_mailer->m_mno;
101257402Seric 		free(s->s_mailer);
101357402Seric 	}
101457402Seric 	else
101557402Seric 	{
101657402Seric 		i = NextMailer++;
101757402Seric 	}
101857402Seric 	Mailer[i] = s->s_mailer = m;
101957454Seric 	m->m_mno = i;
102010327Seric }
102110327Seric /*
102210327Seric **  MUNCHSTRING -- translate a string into internal form.
102310327Seric **
102410327Seric **	Parameters:
102510327Seric **		p -- the string to munch.
102658333Seric **		delimptr -- if non-NULL, set to the pointer of the
102758333Seric **			field delimiter character.
102810327Seric **
102910327Seric **	Returns:
103010327Seric **		the munched string.
103110327Seric */
10324096Seric 
103310327Seric char *
103458333Seric munchstring(p, delimptr)
103510327Seric 	register char *p;
103658333Seric 	char **delimptr;
103710327Seric {
103810327Seric 	register char *q;
103910327Seric 	bool backslash = FALSE;
104010327Seric 	bool quotemode = FALSE;
104110327Seric 	static char buf[MAXLINE];
10424096Seric 
104310327Seric 	for (q = buf; *p != '\0'; p++)
10444096Seric 	{
104510327Seric 		if (backslash)
104610327Seric 		{
104710327Seric 			/* everything is roughly literal */
104810357Seric 			backslash = FALSE;
104910327Seric 			switch (*p)
105010327Seric 			{
105110327Seric 			  case 'r':		/* carriage return */
105210327Seric 				*q++ = '\r';
105310327Seric 				continue;
105410327Seric 
105510327Seric 			  case 'n':		/* newline */
105610327Seric 				*q++ = '\n';
105710327Seric 				continue;
105810327Seric 
105910327Seric 			  case 'f':		/* form feed */
106010327Seric 				*q++ = '\f';
106110327Seric 				continue;
106210327Seric 
106310327Seric 			  case 'b':		/* backspace */
106410327Seric 				*q++ = '\b';
106510327Seric 				continue;
106610327Seric 			}
106710327Seric 			*q++ = *p;
106810327Seric 		}
106910327Seric 		else
107010327Seric 		{
107110327Seric 			if (*p == '\\')
107210327Seric 				backslash = TRUE;
107310327Seric 			else if (*p == '"')
107410327Seric 				quotemode = !quotemode;
107510327Seric 			else if (quotemode || *p != ',')
107610327Seric 				*q++ = *p;
107710327Seric 			else
107810327Seric 				break;
107910327Seric 		}
10804096Seric 	}
10814096Seric 
108258333Seric 	if (delimptr != NULL)
108358333Seric 		*delimptr = p;
108410327Seric 	*q++ = '\0';
108510327Seric 	return (buf);
108610327Seric }
108710327Seric /*
108810327Seric **  MAKEARGV -- break up a string into words
108910327Seric **
109010327Seric **	Parameters:
109110327Seric **		p -- the string to break up.
109210327Seric **
109310327Seric **	Returns:
109410327Seric **		a char **argv (dynamically allocated)
109510327Seric **
109610327Seric **	Side Effects:
109710327Seric **		munges p.
109810327Seric */
10994096Seric 
110010327Seric char **
110110327Seric makeargv(p)
110210327Seric 	register char *p;
110310327Seric {
110410327Seric 	char *q;
110510327Seric 	int i;
110610327Seric 	char **avp;
110710327Seric 	char *argv[MAXPV + 1];
110810327Seric 
110910327Seric 	/* take apart the words */
111010327Seric 	i = 0;
111110327Seric 	while (*p != '\0' && i < MAXPV)
11124096Seric 	{
111310327Seric 		q = p;
111458050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
111510327Seric 			p++;
111658050Seric 		while (isascii(*p) && isspace(*p))
111710327Seric 			*p++ = '\0';
111810327Seric 		argv[i++] = newstr(q);
11194096Seric 	}
112010327Seric 	argv[i++] = NULL;
11214096Seric 
112210327Seric 	/* now make a copy of the argv */
112310327Seric 	avp = (char **) xalloc(sizeof *avp * i);
112416893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
112510327Seric 
112610327Seric 	return (avp);
11273308Seric }
11283308Seric /*
11293308Seric **  PRINTRULES -- print rewrite rules (for debugging)
11303308Seric **
11313308Seric **	Parameters:
11323308Seric **		none.
11333308Seric **
11343308Seric **	Returns:
11353308Seric **		none.
11363308Seric **
11373308Seric **	Side Effects:
11383308Seric **		prints rewrite rules.
11393308Seric */
11403308Seric 
11413308Seric printrules()
11423308Seric {
11433308Seric 	register struct rewrite *rwp;
11444072Seric 	register int ruleset;
11453308Seric 
11464072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
11473308Seric 	{
11484072Seric 		if (RewriteRules[ruleset] == NULL)
11494072Seric 			continue;
11508067Seric 		printf("\n----Rule Set %d:", ruleset);
11513308Seric 
11524072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
11533308Seric 		{
11548067Seric 			printf("\nLHS:");
11558067Seric 			printav(rwp->r_lhs);
11568067Seric 			printf("RHS:");
11578067Seric 			printav(rwp->r_rhs);
11583308Seric 		}
11593308Seric 	}
11603308Seric }
1161*68481Seric /*
1162*68481Seric **  PRINTMAILER -- print mailer structure (for debugging)
1163*68481Seric **
1164*68481Seric **	Parameters:
1165*68481Seric **		m -- the mailer to print
1166*68481Seric **
1167*68481Seric **	Returns:
1168*68481Seric **		none.
1169*68481Seric */
11704319Seric 
1171*68481Seric printmailer(m)
1172*68481Seric 	register MAILER *m;
1173*68481Seric {
1174*68481Seric 	int j;
1175*68481Seric 
1176*68481Seric 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
1177*68481Seric 		m->m_mno, m->m_name,
1178*68481Seric 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
1179*68481Seric 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
1180*68481Seric 		m->m_uid, m->m_gid);
1181*68481Seric 	for (j = '\0'; j <= '\177'; j++)
1182*68481Seric 		if (bitnset(j, m->m_flags))
1183*68481Seric 			(void) putchar(j);
1184*68481Seric 	printf(" L=%d E=", m->m_linelimit);
1185*68481Seric 	xputs(m->m_eol);
1186*68481Seric 	if (m->m_defcharset != NULL)
1187*68481Seric 		printf(" C=%s", m->m_defcharset);
1188*68481Seric 	printf(" T=%s/%s/%s",
1189*68481Seric 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
1190*68481Seric 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
1191*68481Seric 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
1192*68481Seric 	if (m->m_argv != NULL)
1193*68481Seric 	{
1194*68481Seric 		char **a = m->m_argv;
1195*68481Seric 
1196*68481Seric 		printf(" A=");
1197*68481Seric 		while (*a != NULL)
1198*68481Seric 		{
1199*68481Seric 			if (a != m->m_argv)
1200*68481Seric 				printf(" ");
1201*68481Seric 			xputs(*a++);
1202*68481Seric 		}
1203*68481Seric 	}
1204*68481Seric 	printf("\n");
1205*68481Seric }
12064096Seric /*
12078256Seric **  SETOPTION -- set global processing option
12088256Seric **
12098256Seric **	Parameters:
12108256Seric **		opt -- option name.
12118256Seric **		val -- option value (as a text string).
121221755Seric **		safe -- set if this came from a configuration file.
121321755Seric **			Some options (if set from the command line) will
121421755Seric **			reset the user id to avoid security problems.
12158269Seric **		sticky -- if set, don't let other setoptions override
12168269Seric **			this value.
121758734Seric **		e -- the main envelope.
12188256Seric **
12198256Seric **	Returns:
12208256Seric **		none.
12218256Seric **
12228256Seric **	Side Effects:
12238256Seric **		Sets options as implied by the arguments.
12248256Seric */
12258256Seric 
122610687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
12278269Seric 
122857207Seric 
122966334Seric #if NAMED_BIND
123057207Seric 
123157207Seric struct resolverflags
123257207Seric {
123357207Seric 	char	*rf_name;	/* name of the flag */
123457207Seric 	long	rf_bits;	/* bits to set/clear */
123557207Seric } ResolverFlags[] =
123657207Seric {
123757207Seric 	"debug",	RES_DEBUG,
123857207Seric 	"aaonly",	RES_AAONLY,
123957207Seric 	"usevc",	RES_USEVC,
124057207Seric 	"primary",	RES_PRIMARY,
124157207Seric 	"igntc",	RES_IGNTC,
124257207Seric 	"recurse",	RES_RECURSE,
124357207Seric 	"defnames",	RES_DEFNAMES,
124457207Seric 	"stayopen",	RES_STAYOPEN,
124557207Seric 	"dnsrch",	RES_DNSRCH,
124665583Seric 	"true",		0,		/* to avoid error on old syntax */
124757207Seric 	NULL,		0
124857207Seric };
124957207Seric 
125057207Seric #endif
125157207Seric 
1252*68481Seric struct optioninfo
1253*68481Seric {
1254*68481Seric 	char	*o_name;	/* long name of option */
1255*68481Seric 	u_char	o_code;		/* short name of option */
1256*68481Seric 	bool	o_safe;		/* safe for random people to use */
1257*68481Seric } OptionTab[] =
1258*68481Seric {
1259*68481Seric 	"SevenBitInput",	'7',		TRUE,
1260*68481Seric 	"EightBitMode",		'8',		TRUE,
1261*68481Seric 	"AliasFile",		'A',		FALSE,
1262*68481Seric 	"AliasWait",		'a',		FALSE,
1263*68481Seric 	"BlankSub",		'B',		FALSE,
1264*68481Seric 	"MinFreeBlocks",	'b',		TRUE,
1265*68481Seric 	"CheckpointInterval",	'C',		TRUE,
1266*68481Seric 	"HoldExpensive",	'c',		FALSE,
1267*68481Seric 	"AutoRebuildAliases",	'D',		FALSE,
1268*68481Seric 	"DeliveryMode",		'd',		TRUE,
1269*68481Seric 	"ErrorHeader",		'E',		FALSE,
1270*68481Seric 	"ErrorMode",		'e',		TRUE,
1271*68481Seric 	"TempFileMode",		'F',		FALSE,
1272*68481Seric 	"SaveFromLine",		'f',		FALSE,
1273*68481Seric 	"MatchGECOS",		'G',		FALSE,
1274*68481Seric 	"HelpFile",		'H',		FALSE,
1275*68481Seric 	"MaxHopCount",		'h',		FALSE,
1276*68481Seric 	"NameServerOptions",	'I',		FALSE,
1277*68481Seric 	"IgnoreDots",		'i',		TRUE,
1278*68481Seric 	"ForwardPath",		'J',		FALSE,
1279*68481Seric 	"SendMimeErrors",	'j',		TRUE,
1280*68481Seric 	"ConnectionCacheSize",	'k',		FALSE,
1281*68481Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
1282*68481Seric 	"UseErrorsTo",		'l',		FALSE,
1283*68481Seric 	"LogLevel",		'L',		FALSE,
1284*68481Seric 	"MeToo",		'm',		TRUE,
1285*68481Seric 	"CheckAliases",		'n',		FALSE,
1286*68481Seric 	"OldStyleHeaders",	'o',		TRUE,
1287*68481Seric 	"DaemonPortOptions",	'O',		FALSE,
1288*68481Seric 	"PrivacyOptions",	'p',		TRUE,
1289*68481Seric 	"PostmasterCopy",	'P',		FALSE,
1290*68481Seric 	"QueueFactor",		'q',		FALSE,
1291*68481Seric 	"QueueDirectory",	'Q',		FALSE,
1292*68481Seric 	"DontPruneRoutes",	'R',		FALSE,
1293*68481Seric 	"Timeout",		'r',		TRUE,
1294*68481Seric 	"StatusFile",		'S',		FALSE,
1295*68481Seric 	"SuperSafe",		's',		TRUE,
1296*68481Seric 	"QueueTimeout",		'T',		FALSE,
1297*68481Seric 	"TimeZoneSpec",		't',		FALSE,
1298*68481Seric 	"UserDatabaseSpec",	'U',		FALSE,
1299*68481Seric 	"DefaultUser",		'u',		FALSE,
1300*68481Seric 	"FallbackMXhost",	'V',		FALSE,
1301*68481Seric 	"Verbose",		'v',		TRUE,
1302*68481Seric 	"TryNullMXList",	'w',		TRUE,
1303*68481Seric 	"QueueLA",		'x',		FALSE,
1304*68481Seric 	"RefuseLA",		'X',		FALSE,
1305*68481Seric 	"RecipientFactor",	'y',		FALSE,
1306*68481Seric 	"ForkQueueRuns",	'Y',		FALSE,
1307*68481Seric 	"ClassFactor",		'z',		FALSE,
1308*68481Seric 	"TimeFactor",		'Z',		FALSE,
1309*68481Seric #define O_BSP		0x80
1310*68481Seric 	"BrokenSmtpPeers",	O_BSP,		TRUE,
1311*68481Seric #define O_QUEUESORTORD	0x81
1312*68481Seric 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
1313*68481Seric #define O_MQA		0x83
1314*68481Seric 	"MinQueueAge",		O_MQA,		TRUE,
1315*68481Seric #define O_MHSA		0x84
1316*68481Seric /*
1317*68481Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
1318*68481Seric */
1319*68481Seric #define O_DEFCHARSET	0x85
1320*68481Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
1321*68481Seric #define O_SSFILE	0x86
1322*68481Seric 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
1323*68481Seric #define O_DIALDELAY	0x87
1324*68481Seric 	"DialDelay",		O_DIALDELAY,	TRUE,
1325*68481Seric #define O_NORCPTACTION	0x88
1326*68481Seric 	"NoRecipientAction",	O_NORCPTACTION,	TRUE,
1327*68481Seric 
1328*68481Seric 	NULL,			'\0',		FALSE,
1329*68481Seric };
1330*68481Seric 
1331*68481Seric 
1332*68481Seric 
133358734Seric setoption(opt, val, safe, sticky, e)
1334*68481Seric 	u_char opt;
13358256Seric 	char *val;
133621755Seric 	bool safe;
13378269Seric 	bool sticky;
133858734Seric 	register ENVELOPE *e;
13398256Seric {
134057207Seric 	register char *p;
1341*68481Seric 	register struct optioninfo *o;
1342*68481Seric 	char *subopt;
13438265Seric 	extern bool atobool();
134412633Seric 	extern time_t convtime();
134514879Seric 	extern int QueueLA;
134614879Seric 	extern int RefuseLA;
134764718Seric 	extern bool Warn_Q_option;
13488256Seric 
1349*68481Seric 	errno = 0;
1350*68481Seric 	if (opt == ' ')
1351*68481Seric 	{
1352*68481Seric 		/* full word options */
1353*68481Seric 		struct optioninfo *sel;
1354*68481Seric 
1355*68481Seric 		p = strchr(val, '=');
1356*68481Seric 		if (p == NULL)
1357*68481Seric 			p = &val[strlen(val)];
1358*68481Seric 		while (*--p == ' ')
1359*68481Seric 			continue;
1360*68481Seric 		while (*++p == ' ')
1361*68481Seric 			*p = '\0';
1362*68481Seric 		if (p == val)
1363*68481Seric 		{
1364*68481Seric 			syserr("readcf: null option name");
1365*68481Seric 			return;
1366*68481Seric 		}
1367*68481Seric 		if (*p == '=')
1368*68481Seric 			*p++ = '\0';
1369*68481Seric 		while (*p == ' ')
1370*68481Seric 			p++;
1371*68481Seric 		subopt = strchr(val, '.');
1372*68481Seric 		if (subopt != NULL)
1373*68481Seric 			*subopt++ = '\0';
1374*68481Seric 		sel = NULL;
1375*68481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
1376*68481Seric 		{
1377*68481Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
1378*68481Seric 				continue;
1379*68481Seric 			if (strlen(o->o_name) == strlen(val))
1380*68481Seric 			{
1381*68481Seric 				/* completely specified -- this must be it */
1382*68481Seric 				sel = NULL;
1383*68481Seric 				break;
1384*68481Seric 			}
1385*68481Seric 			if (sel != NULL)
1386*68481Seric 				break;
1387*68481Seric 			sel = o;
1388*68481Seric 		}
1389*68481Seric 		if (sel != NULL && o->o_name == NULL)
1390*68481Seric 			o = sel;
1391*68481Seric 		else if (o->o_name == NULL)
1392*68481Seric 		{
1393*68481Seric 			syserr("readcf: unknown option name %s", val);
1394*68481Seric 			return;
1395*68481Seric 		}
1396*68481Seric 		else if (sel != NULL)
1397*68481Seric 		{
1398*68481Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
1399*68481Seric 				val, sel->o_name, o->o_name);
1400*68481Seric 			return;
1401*68481Seric 		}
1402*68481Seric 		if (strlen(val) != strlen(o->o_name))
1403*68481Seric 		{
1404*68481Seric 			bool oldVerbose = Verbose;
1405*68481Seric 
1406*68481Seric 			Verbose = TRUE;
1407*68481Seric 			message("Option %s used as abbreviation for %s",
1408*68481Seric 				val, o->o_name);
1409*68481Seric 			Verbose = oldVerbose;
1410*68481Seric 		}
1411*68481Seric 		opt = o->o_code;
1412*68481Seric 		val = p;
1413*68481Seric 	}
1414*68481Seric 	else
1415*68481Seric 	{
1416*68481Seric 		for (o = OptionTab; o->o_name != NULL; o++)
1417*68481Seric 		{
1418*68481Seric 			if (o->o_code == opt)
1419*68481Seric 				break;
1420*68481Seric 		}
1421*68481Seric 		subopt = NULL;
1422*68481Seric 	}
1423*68481Seric 
14248256Seric 	if (tTd(37, 1))
1425*68481Seric 	{
1426*68481Seric 		printf(isascii(opt) && isprint(opt) ?
1427*68481Seric 			    "setoption %s (%c).%s=%s" :
1428*68481Seric 			    "setoption %s (0x%x).%s=%s",
1429*68481Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
1430*68481Seric 			opt,
1431*68481Seric 			subopt == NULL ? "" : subopt,
1432*68481Seric 			val);
1433*68481Seric 	}
14348256Seric 
14358256Seric 	/*
14368269Seric 	**  See if this option is preset for us.
14378256Seric 	*/
14388256Seric 
143959731Seric 	if (!sticky && bitnset(opt, StickyOpt))
14408269Seric 	{
14419341Seric 		if (tTd(37, 1))
14429341Seric 			printf(" (ignored)\n");
14438269Seric 		return;
14448269Seric 	}
14458269Seric 
144621755Seric 	/*
144721755Seric 	**  Check to see if this option can be specified by this user.
144821755Seric 	*/
144921755Seric 
145063787Seric 	if (!safe && RealUid == 0)
145121755Seric 		safe = TRUE;
1452*68481Seric 	if (!safe && !o->o_safe)
145321755Seric 	{
145439111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
145521755Seric 		{
145636582Sbostic 			if (tTd(37, 1))
145736582Sbostic 				printf(" (unsafe)");
145863787Seric 			if (RealUid != geteuid())
145936582Sbostic 			{
146051210Seric 				if (tTd(37, 1))
146151210Seric 					printf("(Resetting uid)");
146263787Seric 				(void) setgid(RealGid);
146363787Seric 				(void) setuid(RealUid);
146436582Sbostic 			}
146521755Seric 		}
146621755Seric 	}
146751210Seric 	if (tTd(37, 1))
146817985Seric 		printf("\n");
14698269Seric 
1470*68481Seric 	switch (opt & 0xff)
14718256Seric 	{
147259709Seric 	  case '7':		/* force seven-bit input */
1473*68481Seric 		SevenBitInput = atobool(val);
147452106Seric 		break;
147552106Seric 
1476*68481Seric 	  case '8':		/* handling of 8-bit input */
1477*68481Seric 		switch (*val)
1478*68481Seric 		{
1479*68481Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
1480*68481Seric 			MimeMode = 0;
1481*68481Seric 			break;
1482*68481Seric 
1483*68481Seric 		  case 'm':		/* convert 8-bit, convert MIME */
1484*68481Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
1485*68481Seric 			break;
1486*68481Seric 
1487*68481Seric 		  case 'j':		/* "just send 8" */
1488*68481Seric 			MimeMode = MM_PASS8BIT;
1489*68481Seric 			break;
1490*68481Seric 
1491*68481Seric 		  case 'p':		/* pass 8 bit, convert MIME */
1492*68481Seric 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
1493*68481Seric 			break;
1494*68481Seric 
1495*68481Seric 		  case 's':		/* strict adherence */
1496*68481Seric 			MimeMode = MM_CVTMIME;
1497*68481Seric 			break;
1498*68481Seric 
1499*68481Seric 		  case 'a':		/* encode 8 bit if available */
1500*68481Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
1501*68481Seric 			break;
1502*68481Seric 
1503*68481Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
1504*68481Seric 			MimeMode = MM_MIME8BIT;
1505*68481Seric 			break;
1506*68481Seric 
1507*68481Seric 		  default:
1508*68481Seric 			syserr("Unknown 8-bit mode %c", *val);
1509*68481Seric 			exit(EX_USAGE);
1510*68481Seric 		}
1511*68481Seric 		break;
1512*68481Seric 
15138256Seric 	  case 'A':		/* set default alias file */
15149381Seric 		if (val[0] == '\0')
151559672Seric 			setalias("aliases");
15169381Seric 		else
151759672Seric 			setalias(val);
15188256Seric 		break;
15198256Seric 
152017474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
152117474Seric 		if (val[0] == '\0')
152264796Seric 			SafeAlias = 5 * 60;		/* five minutes */
152317474Seric 		else
152464796Seric 			SafeAlias = convtime(val, 'm');
152517474Seric 		break;
152617474Seric 
152716843Seric 	  case 'B':		/* substitution for blank character */
152816843Seric 		SpaceSub = val[0];
152916843Seric 		if (SpaceSub == '\0')
153016843Seric 			SpaceSub = ' ';
153116843Seric 		break;
153216843Seric 
153359283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
153459283Seric 		p = strchr(val, '/');
153559283Seric 		if (p != NULL)
153659283Seric 		{
153759283Seric 			*p++ = '\0';
153859283Seric 			MaxMessageSize = atol(p);
153959283Seric 		}
154058082Seric 		MinBlocksFree = atol(val);
154158082Seric 		break;
154258082Seric 
15439284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
15449381Seric 		NoConnect = atobool(val);
15459284Seric 		break;
15469284Seric 
154751305Seric 	  case 'C':		/* checkpoint every N addresses */
154851305Seric 		CheckpointInterval = atoi(val);
154924944Seric 		break;
155024944Seric 
15519284Seric 	  case 'd':		/* delivery mode */
15529284Seric 		switch (*val)
15538269Seric 		{
15549284Seric 		  case '\0':
155558734Seric 			e->e_sendmode = SM_DELIVER;
15568269Seric 			break;
15578269Seric 
155810755Seric 		  case SM_QUEUE:	/* queue only */
155910755Seric #ifndef QUEUE
156010755Seric 			syserr("need QUEUE to set -odqueue");
156156795Seric #endif /* QUEUE */
156210755Seric 			/* fall through..... */
156310755Seric 
15649284Seric 		  case SM_DELIVER:	/* do everything */
15659284Seric 		  case SM_FORK:		/* fork after verification */
156658734Seric 			e->e_sendmode = *val;
15678269Seric 			break;
15688269Seric 
15698269Seric 		  default:
15709284Seric 			syserr("Unknown delivery mode %c", *val);
15718269Seric 			exit(EX_USAGE);
15728269Seric 		}
15738269Seric 		break;
15748269Seric 
15759146Seric 	  case 'D':		/* rebuild alias database as needed */
15769381Seric 		AutoRebuild = atobool(val);
15779146Seric 		break;
15789146Seric 
157955372Seric 	  case 'E':		/* error message header/header file */
158055379Seric 		if (*val != '\0')
158155379Seric 			ErrMsgFile = newstr(val);
158255372Seric 		break;
158355372Seric 
15848269Seric 	  case 'e':		/* set error processing mode */
15858269Seric 		switch (*val)
15868269Seric 		{
15879381Seric 		  case EM_QUIET:	/* be silent about it */
15889381Seric 		  case EM_MAIL:		/* mail back */
15899381Seric 		  case EM_BERKNET:	/* do berknet error processing */
15909381Seric 		  case EM_WRITE:	/* write back (or mail) */
15919381Seric 		  case EM_PRINT:	/* print errors normally (default) */
159258734Seric 			e->e_errormode = *val;
15938269Seric 			break;
15948269Seric 		}
15958269Seric 		break;
15968269Seric 
15979049Seric 	  case 'F':		/* file mode */
159817975Seric 		FileMode = atooct(val) & 0777;
15999049Seric 		break;
16009049Seric 
16018269Seric 	  case 'f':		/* save Unix-style From lines on front */
16029381Seric 		SaveFrom = atobool(val);
16038269Seric 		break;
16048269Seric 
160553735Seric 	  case 'G':		/* match recipients against GECOS field */
160653735Seric 		MatchGecos = atobool(val);
160753735Seric 		break;
160853735Seric 
16098256Seric 	  case 'g':		/* default gid */
1610*68481Seric   g_opt:
161164133Seric 		if (isascii(*val) && isdigit(*val))
161264133Seric 			DefGid = atoi(val);
161364133Seric 		else
161464133Seric 		{
161564133Seric 			register struct group *gr;
161664133Seric 
161764133Seric 			DefGid = -1;
161864133Seric 			gr = getgrnam(val);
161964133Seric 			if (gr == NULL)
1620*68481Seric 				syserr("readcf: option %c: unknown group %s",
1621*68481Seric 					opt, val);
162264133Seric 			else
162364133Seric 				DefGid = gr->gr_gid;
162464133Seric 		}
16258256Seric 		break;
16268256Seric 
16278256Seric 	  case 'H':		/* help file */
16289381Seric 		if (val[0] == '\0')
16298269Seric 			HelpFile = "sendmail.hf";
16309381Seric 		else
16319381Seric 			HelpFile = newstr(val);
16328256Seric 		break;
16338256Seric 
163451305Seric 	  case 'h':		/* maximum hop count */
163551305Seric 		MaxHopCount = atoi(val);
163651305Seric 		break;
163751305Seric 
163835651Seric 	  case 'I':		/* use internet domain name server */
163966334Seric #if NAMED_BIND
164057207Seric 		for (p = val; *p != 0; )
164157207Seric 		{
164257207Seric 			bool clearmode;
164357207Seric 			char *q;
164457207Seric 			struct resolverflags *rfp;
164557207Seric 
164657207Seric 			while (*p == ' ')
164757207Seric 				p++;
164857207Seric 			if (*p == '\0')
164957207Seric 				break;
165057207Seric 			clearmode = FALSE;
165157207Seric 			if (*p == '-')
165257207Seric 				clearmode = TRUE;
165357207Seric 			else if (*p != '+')
165457207Seric 				p--;
165557207Seric 			p++;
165657207Seric 			q = p;
165758050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
165857207Seric 				p++;
165957207Seric 			if (*p != '\0')
166057207Seric 				*p++ = '\0';
166157207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
166257207Seric 			{
166357207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
166457207Seric 					break;
166557207Seric 			}
166664923Seric 			if (rfp->rf_name == NULL)
166764923Seric 				syserr("readcf: I option value %s unrecognized", q);
166864923Seric 			else if (clearmode)
166957207Seric 				_res.options &= ~rfp->rf_bits;
167057207Seric 			else
167157207Seric 				_res.options |= rfp->rf_bits;
167257207Seric 		}
167357207Seric 		if (tTd(8, 2))
167457207Seric 			printf("_res.options = %x\n", _res.options);
167557207Seric #else
167657207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
167757207Seric #endif
167835651Seric 		break;
167935651Seric 
16808269Seric 	  case 'i':		/* ignore dot lines in message */
16819381Seric 		IgnrDot = atobool(val);
16828269Seric 		break;
16838269Seric 
168459730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
168559730Seric 		SendMIMEErrors = atobool(val);
168659730Seric 		break;
168759730Seric 
168857136Seric 	  case 'J':		/* .forward search path */
168957136Seric 		ForwardPath = newstr(val);
169057136Seric 		break;
169157136Seric 
169254967Seric 	  case 'k':		/* connection cache size */
169354967Seric 		MaxMciCache = atoi(val);
169456215Seric 		if (MaxMciCache < 0)
169556215Seric 			MaxMciCache = 0;
169654967Seric 		break;
169754967Seric 
169854967Seric 	  case 'K':		/* connection cache timeout */
169958796Seric 		MciCacheTimeout = convtime(val, 'm');
170054967Seric 		break;
170154967Seric 
170261104Seric 	  case 'l':		/* use Errors-To: header */
170361104Seric 		UseErrorsTo = atobool(val);
170461104Seric 		break;
170561104Seric 
17068256Seric 	  case 'L':		/* log level */
170764140Seric 		if (safe || LogLevel < atoi(val))
170864140Seric 			LogLevel = atoi(val);
17098256Seric 		break;
17108256Seric 
17118269Seric 	  case 'M':		/* define macro */
171268267Seric 		p = newstr(&val[1]);
171368267Seric 		if (!safe)
171468267Seric 			cleanstrcpy(p, p, MAXNAME);
171568267Seric 		define(val[0], p, CurEnv);
171616878Seric 		sticky = FALSE;
17178269Seric 		break;
17188269Seric 
17198269Seric 	  case 'm':		/* send to me too */
17209381Seric 		MeToo = atobool(val);
17218269Seric 		break;
17228269Seric 
172325820Seric 	  case 'n':		/* validate RHS in newaliases */
172425820Seric 		CheckAliases = atobool(val);
172525820Seric 		break;
172625820Seric 
172761104Seric 	    /* 'N' available -- was "net name" */
172861104Seric 
172958851Seric 	  case 'O':		/* daemon options */
173058851Seric 		setdaemonoptions(val);
173158851Seric 		break;
173258851Seric 
17338269Seric 	  case 'o':		/* assume old style headers */
17349381Seric 		if (atobool(val))
17359341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
17369341Seric 		else
17379341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
17388269Seric 		break;
17398269Seric 
174058082Seric 	  case 'p':		/* select privacy level */
174158082Seric 		p = val;
174258082Seric 		for (;;)
174358082Seric 		{
174458082Seric 			register struct prival *pv;
174558082Seric 			extern struct prival PrivacyValues[];
174658082Seric 
174758082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
174858082Seric 				p++;
174958082Seric 			if (*p == '\0')
175058082Seric 				break;
175158082Seric 			val = p;
175258082Seric 			while (isascii(*p) && isalnum(*p))
175358082Seric 				p++;
175458082Seric 			if (*p != '\0')
175558082Seric 				*p++ = '\0';
175658082Seric 
175758082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
175858082Seric 			{
175958082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
176058082Seric 					break;
176158082Seric 			}
176258886Seric 			if (pv->pv_name == NULL)
176358886Seric 				syserr("readcf: Op line: %s unrecognized", val);
176458082Seric 			PrivacyFlags |= pv->pv_flag;
176558082Seric 		}
176668479Seric 		sticky = FALSE;
176758082Seric 		break;
176858082Seric 
176924944Seric 	  case 'P':		/* postmaster copy address for returned mail */
177024944Seric 		PostMasterCopy = newstr(val);
177124944Seric 		break;
177224944Seric 
177324944Seric 	  case 'q':		/* slope of queue only function */
177424944Seric 		QueueFactor = atoi(val);
177524944Seric 		break;
177624944Seric 
17778256Seric 	  case 'Q':		/* queue directory */
17789381Seric 		if (val[0] == '\0')
17798269Seric 			QueueDir = "mqueue";
17809381Seric 		else
17819381Seric 			QueueDir = newstr(val);
178258789Seric 		if (RealUid != 0 && !safe)
178364718Seric 			Warn_Q_option = TRUE;
17848256Seric 		break;
17858256Seric 
178658148Seric 	  case 'R':		/* don't prune routes */
178758148Seric 		DontPruneRoutes = atobool(val);
178858148Seric 		break;
178958148Seric 
17908256Seric 	  case 'r':		/* read timeout */
1791*68481Seric 		if (subopt == NULL)
1792*68481Seric 			inittimeouts(val);
1793*68481Seric 		else
1794*68481Seric 			settimeout(subopt, val);
17958256Seric 		break;
17968256Seric 
17978256Seric 	  case 'S':		/* status file */
17989381Seric 		if (val[0] == '\0')
17998269Seric 			StatFile = "sendmail.st";
18009381Seric 		else
18019381Seric 			StatFile = newstr(val);
18028256Seric 		break;
18038256Seric 
18048265Seric 	  case 's':		/* be super safe, even if expensive */
18059381Seric 		SuperSafe = atobool(val);
18068256Seric 		break;
18078256Seric 
18088256Seric 	  case 'T':		/* queue timeout */
180958737Seric 		p = strchr(val, '/');
181058737Seric 		if (p != NULL)
181158737Seric 		{
181258737Seric 			*p++ = '\0';
1813*68481Seric 			settimeout("queuewarn", p);
181458737Seric 		}
1815*68481Seric 		settimeout("queuereturn", val);
181654967Seric 		break;
18178256Seric 
18188265Seric 	  case 't':		/* time zone name */
181952106Seric 		TimeZoneSpec = newstr(val);
18208265Seric 		break;
18218265Seric 
182250556Seric 	  case 'U':		/* location of user database */
182351360Seric 		UdbSpec = newstr(val);
182450556Seric 		break;
182550556Seric 
18268256Seric 	  case 'u':		/* set default uid */
1827*68481Seric 		for (p = val; *p != '\0'; p++)
1828*68481Seric 		{
1829*68481Seric 			if (*p == '.' || *p == '/' || *p == ':')
1830*68481Seric 			{
1831*68481Seric 				*p++ = '\0';
1832*68481Seric 				break;
1833*68481Seric 			}
1834*68481Seric 		}
183564133Seric 		if (isascii(*val) && isdigit(*val))
183664133Seric 			DefUid = atoi(val);
183764133Seric 		else
183864133Seric 		{
183964133Seric 			register struct passwd *pw;
184064133Seric 
184164133Seric 			DefUid = -1;
184264133Seric 			pw = getpwnam(val);
184364133Seric 			if (pw == NULL)
184464133Seric 				syserr("readcf: option u: unknown user %s", val);
184564133Seric 			else
1846*68481Seric 			{
184764133Seric 				DefUid = pw->pw_uid;
1848*68481Seric 				DefGid = pw->pw_gid;
1849*68481Seric 			}
185064133Seric 		}
185140973Sbostic 		setdefuser();
18528256Seric 
1853*68481Seric 		/* handle the group if it is there */
1854*68481Seric 		if (*p == '\0')
1855*68481Seric 			break;
1856*68481Seric 		val = p;
1857*68481Seric 		goto g_opt;
1858*68481Seric 
185958851Seric 	  case 'V':		/* fallback MX host */
186058851Seric 		FallBackMX = newstr(val);
186158851Seric 		break;
186258851Seric 
18638269Seric 	  case 'v':		/* run in verbose mode */
18649381Seric 		Verbose = atobool(val);
18658256Seric 		break;
18668256Seric 
186763837Seric 	  case 'w':		/* if we are best MX, try host directly */
186863837Seric 		TryNullMXList = atobool(val);
186963837Seric 		break;
187061104Seric 
187161104Seric 	    /* 'W' available -- was wizard password */
187261104Seric 
187314879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
187414879Seric 		QueueLA = atoi(val);
187514879Seric 		break;
187614879Seric 
187714879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
187814879Seric 		RefuseLA = atoi(val);
187914879Seric 		break;
188014879Seric 
188124981Seric 	  case 'y':		/* work recipient factor */
188224981Seric 		WkRecipFact = atoi(val);
188324981Seric 		break;
188424981Seric 
188524981Seric 	  case 'Y':		/* fork jobs during queue runs */
188624952Seric 		ForkQueueRuns = atobool(val);
188724952Seric 		break;
188824952Seric 
188924981Seric 	  case 'z':		/* work message class factor */
189024981Seric 		WkClassFact = atoi(val);
189124981Seric 		break;
189224981Seric 
189324981Seric 	  case 'Z':		/* work time factor */
189424981Seric 		WkTimeFact = atoi(val);
189524981Seric 		break;
189624981Seric 
1897*68481Seric 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
1898*68481Seric 		BrokenSmtpPeers = atobool(val);
1899*68481Seric 		break;
1900*68481Seric 
1901*68481Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
1902*68481Seric 		switch (*val)
1903*68481Seric 		{
1904*68481Seric 		  case 'h':	/* Host first */
1905*68481Seric 		  case 'H':
1906*68481Seric 			QueueSortOrder = QS_BYHOST;
1907*68481Seric 			break;
1908*68481Seric 
1909*68481Seric 		  case 'p':	/* Priority order */
1910*68481Seric 		  case 'P':
1911*68481Seric 			QueueSortOrder = QS_BYPRIORITY;
1912*68481Seric 			break;
1913*68481Seric 
1914*68481Seric 		  default:
1915*68481Seric 			syserr("Invalid queue sort order \"%s\"", val);
1916*68481Seric 		}
1917*68481Seric 		break;
1918*68481Seric 
1919*68481Seric 	  case O_MQA:		/* minimum queue age between deliveries */
1920*68481Seric 		MinQueueAge = convtime(val, 'm');
1921*68481Seric 		break;
1922*68481Seric 
1923*68481Seric 	  case O_MHSA:		/* maximum age of cached host status */
1924*68481Seric 		MaxHostStatAge = convtime(val, 'm');
1925*68481Seric 		break;
1926*68481Seric 
1927*68481Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
1928*68481Seric 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
1929*68481Seric 		break;
1930*68481Seric 
1931*68481Seric 	  case O_SSFILE:	/* service switch file */
1932*68481Seric 		ServiceSwitchFile = newstr(val);
1933*68481Seric 		break;
1934*68481Seric 
1935*68481Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
1936*68481Seric 		DialDelay = convtime(val, 's');
1937*68481Seric 		break;
1938*68481Seric 
1939*68481Seric 	  case O_NORCPTACTION:	/* what to do if no recipient */
1940*68481Seric 		if (strcasecmp(val, "none") == 0)
1941*68481Seric 			NoRecipientAction = NRA_NO_ACTION;
1942*68481Seric 		else if (strcasecmp(val, "add-to") == 0)
1943*68481Seric 			NoRecipientAction = NRA_ADD_TO;
1944*68481Seric 		else if (strcasecmp(val, "add-apparently-to") == 0)
1945*68481Seric 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
1946*68481Seric 		else if (strcasecmp(val, "add-bcc") == 0)
1947*68481Seric 			NoRecipientAction = NRA_ADD_BCC;
1948*68481Seric 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
1949*68481Seric 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
1950*68481Seric 		else
1951*68481Seric 			syserr("Invalid NoRecipientAction: %s", val);
1952*68481Seric 
19538256Seric 	  default:
1954*68481Seric 		if (tTd(37, 1))
1955*68481Seric 		{
1956*68481Seric 			if (isascii(opt) && isprint(opt))
1957*68481Seric 				printf("Warning: option %c unknown\n", opt);
1958*68481Seric 			else
1959*68481Seric 				printf("Warning: option 0x%x unknown\n", opt);
1960*68481Seric 		}
19618256Seric 		break;
19628256Seric 	}
196316878Seric 	if (sticky)
196416878Seric 		setbitn(opt, StickyOpt);
19659188Seric 	return;
19668256Seric }
196710687Seric /*
1968*68481Seric **  SETCLASS -- set a string into a class
196910687Seric **
197010687Seric **	Parameters:
1971*68481Seric **		class -- the class to put the string in.
1972*68481Seric **		str -- the string to enter
197310687Seric **
197410687Seric **	Returns:
197510687Seric **		none.
197610687Seric **
197710687Seric **	Side Effects:
197810687Seric **		puts the word into the symbol table.
197910687Seric */
198010687Seric 
1981*68481Seric setclass(class, str)
198210687Seric 	int class;
1983*68481Seric 	char *str;
198410687Seric {
198510687Seric 	register STAB *s;
198610687Seric 
198757943Seric 	if (tTd(37, 8))
1988*68481Seric 		printf("setclass(%c, %s)\n", class, str);
1989*68481Seric 	s = stab(str, ST_CLASS, ST_ENTER);
199010687Seric 	setbitn(class, s->s_class);
199110687Seric }
199253654Seric /*
199353654Seric **  MAKEMAPENTRY -- create a map entry
199453654Seric **
199553654Seric **	Parameters:
199653654Seric **		line -- the config file line
199753654Seric **
199853654Seric **	Returns:
199953654Seric **		TRUE if it successfully entered the map entry.
200053654Seric **		FALSE otherwise (usually syntax error).
200153654Seric **
200253654Seric **	Side Effects:
200353654Seric **		Enters the map into the dictionary.
200453654Seric */
200553654Seric 
200653654Seric void
200753654Seric makemapentry(line)
200853654Seric 	char *line;
200953654Seric {
201053654Seric 	register char *p;
201153654Seric 	char *mapname;
201253654Seric 	char *classname;
201364078Seric 	register STAB *s;
201453654Seric 	STAB *class;
201553654Seric 
201658050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
201753654Seric 		continue;
201858050Seric 	if (!(isascii(*p) && isalnum(*p)))
201953654Seric 	{
202053654Seric 		syserr("readcf: config K line: no map name");
202153654Seric 		return;
202253654Seric 	}
202353654Seric 
202453654Seric 	mapname = p;
2025*68481Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
202653654Seric 		continue;
202753654Seric 	if (*p != '\0')
202853654Seric 		*p++ = '\0';
202958050Seric 	while (isascii(*p) && isspace(*p))
203053654Seric 		p++;
203158050Seric 	if (!(isascii(*p) && isalnum(*p)))
203253654Seric 	{
203353654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
203453654Seric 		return;
203553654Seric 	}
203653654Seric 	classname = p;
203758050Seric 	while (isascii(*++p) && isalnum(*p))
203853654Seric 		continue;
203953654Seric 	if (*p != '\0')
204053654Seric 		*p++ = '\0';
204158050Seric 	while (isascii(*p) && isspace(*p))
204253654Seric 		p++;
204353654Seric 
204453654Seric 	/* look up the class */
204553654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
204653654Seric 	if (class == NULL)
204753654Seric 	{
204853654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
204953654Seric 		return;
205053654Seric 	}
205153654Seric 
205253654Seric 	/* enter the map */
205364078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
205464078Seric 	s->s_map.map_class = &class->s_mapclass;
205564078Seric 	s->s_map.map_mname = newstr(mapname);
205653654Seric 
205764078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
205864078Seric 		s->s_map.map_mflags |= MF_VALID;
205964078Seric 
206064078Seric 	if (tTd(37, 5))
206164078Seric 	{
206264078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
206364078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
206464078Seric 			s->s_map.map_mflags,
206564078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
206664078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
206764078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
206864078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
206964078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
207064078Seric 	}
207153654Seric }
207258112Seric /*
2073*68481Seric **  INITTIMEOUTS -- parse and set timeout values
207458112Seric **
207558112Seric **	Parameters:
207658112Seric **		val -- a pointer to the values.  If NULL, do initial
207758112Seric **			settings.
207858112Seric **
207958112Seric **	Returns:
208058112Seric **		none.
208158112Seric **
208258112Seric **	Side Effects:
208358112Seric **		Initializes the TimeOuts structure
208458112Seric */
208558112Seric 
208664255Seric #define SECONDS
208758112Seric #define MINUTES	* 60
208858112Seric #define HOUR	* 3600
208958112Seric 
2090*68481Seric inittimeouts(val)
209158112Seric 	register char *val;
209258112Seric {
209358112Seric 	register char *p;
209458671Seric 	extern time_t convtime();
209558112Seric 
209658112Seric 	if (val == NULL)
209758112Seric 	{
209858112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
209958112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
210058112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
210158112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
210258112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
210358112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
210458112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
210558112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
210658112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
210758112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
210858112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
2109*68481Seric #if IDENTPROTO
211064255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
2111*68481Seric #else
2112*68481Seric 		TimeOuts.to_ident = (time_t) 0 SECONDS;
2113*68481Seric #endif
2114*68481Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
211558112Seric 		return;
211658112Seric 	}
211758112Seric 
211858112Seric 	for (;; val = p)
211958112Seric 	{
212058112Seric 		while (isascii(*val) && isspace(*val))
212158112Seric 			val++;
212258112Seric 		if (*val == '\0')
212358112Seric 			break;
212458112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
212558112Seric 			continue;
212658112Seric 		if (*p != '\0')
212758112Seric 			*p++ = '\0';
212858112Seric 
212958112Seric 		if (isascii(*val) && isdigit(*val))
213058112Seric 		{
213158112Seric 			/* old syntax -- set everything */
213258796Seric 			TimeOuts.to_mail = convtime(val, 'm');
213358112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
213458112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
213558112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
213658112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
213758112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
213858112Seric 			continue;
213958112Seric 		}
214058112Seric 		else
214158112Seric 		{
2142*68481Seric 			register char *q = strchr(val, ':');
214358112Seric 
2144*68481Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
214558112Seric 			{
214658112Seric 				/* syntax error */
214758112Seric 				continue;
214858112Seric 			}
214958112Seric 			*q++ = '\0';
2150*68481Seric 			settimeout(val, q);
2151*68481Seric 		}
2152*68481Seric 	}
2153*68481Seric }
2154*68481Seric /*
2155*68481Seric **  SETTIMEOUT -- set an individual timeout
2156*68481Seric **
2157*68481Seric **	Parameters:
2158*68481Seric **		name -- the name of the timeout.
2159*68481Seric **		val -- the value of the timeout.
2160*68481Seric **
2161*68481Seric **	Returns:
2162*68481Seric **		none.
2163*68481Seric */
216458112Seric 
2165*68481Seric settimeout(name, val)
2166*68481Seric 	char *name;
2167*68481Seric 	char *val;
2168*68481Seric {
2169*68481Seric 	register char *p;
2170*68481Seric 	time_t to;
2171*68481Seric 	extern time_t convtime();
2172*68481Seric 
2173*68481Seric 	to = convtime(val, 'm');
2174*68481Seric 	p = strchr(name, '.');
2175*68481Seric 	if (p != NULL)
2176*68481Seric 		*p++ = '\0';
2177*68481Seric 
2178*68481Seric 	if (strcasecmp(name, "initial") == 0)
2179*68481Seric 		TimeOuts.to_initial = to;
2180*68481Seric 	else if (strcasecmp(name, "mail") == 0)
2181*68481Seric 		TimeOuts.to_mail = to;
2182*68481Seric 	else if (strcasecmp(name, "rcpt") == 0)
2183*68481Seric 		TimeOuts.to_rcpt = to;
2184*68481Seric 	else if (strcasecmp(name, "datainit") == 0)
2185*68481Seric 		TimeOuts.to_datainit = to;
2186*68481Seric 	else if (strcasecmp(name, "datablock") == 0)
2187*68481Seric 		TimeOuts.to_datablock = to;
2188*68481Seric 	else if (strcasecmp(name, "datafinal") == 0)
2189*68481Seric 		TimeOuts.to_datafinal = to;
2190*68481Seric 	else if (strcasecmp(name, "command") == 0)
2191*68481Seric 		TimeOuts.to_nextcommand = to;
2192*68481Seric 	else if (strcasecmp(name, "rset") == 0)
2193*68481Seric 		TimeOuts.to_rset = to;
2194*68481Seric 	else if (strcasecmp(name, "helo") == 0)
2195*68481Seric 		TimeOuts.to_helo = to;
2196*68481Seric 	else if (strcasecmp(name, "quit") == 0)
2197*68481Seric 		TimeOuts.to_quit = to;
2198*68481Seric 	else if (strcasecmp(name, "misc") == 0)
2199*68481Seric 		TimeOuts.to_miscshort = to;
2200*68481Seric 	else if (strcasecmp(name, "ident") == 0)
2201*68481Seric 		TimeOuts.to_ident = to;
2202*68481Seric 	else if (strcasecmp(name, "fileopen") == 0)
2203*68481Seric 		TimeOuts.to_fileopen = to;
2204*68481Seric 	else if (strcasecmp(name, "queuewarn") == 0)
2205*68481Seric 	{
2206*68481Seric 		to = convtime(val, 'h');
2207*68481Seric 		if (p == NULL || strcmp(p, "*") == 0)
2208*68481Seric 		{
2209*68481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2210*68481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2211*68481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
221258112Seric 		}
2213*68481Seric 		else if (strcasecmp(p, "normal") == 0)
2214*68481Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2215*68481Seric 		else if (strcasecmp(p, "urgent") == 0)
2216*68481Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2217*68481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
2218*68481Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2219*68481Seric 		else
2220*68481Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
222158112Seric 	}
2222*68481Seric 	else if (strcasecmp(name, "queuereturn") == 0)
2223*68481Seric 	{
2224*68481Seric 		to = convtime(val, 'd');
2225*68481Seric 		if (p == NULL || strcmp(p, "*") == 0)
2226*68481Seric 		{
2227*68481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2228*68481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
2229*68481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2230*68481Seric 		}
2231*68481Seric 		else if (strcasecmp(p, "normal") == 0)
2232*68481Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2233*68481Seric 		else if (strcasecmp(p, "urgent") == 0)
2234*68481Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
2235*68481Seric 		else if (strcasecmp(p, "non-urgent") == 0)
2236*68481Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2237*68481Seric 		else
2238*68481Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
2239*68481Seric 	}
2240*68481Seric 	else
2241*68481Seric 		syserr("settimeout: invalid timeout %s", name);
224258112Seric }
2243