xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 68276)
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*68276Seric static char sccsid[] = "@(#)readcf.c	8.65 (Berkeley) 02/10/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;
738547Seric 	char *q;
749350Seric 	struct rewrite *rwp = NULL;
7557135Seric 	char *bp;
7664718Seric 	auto char *ep;
7757589Seric 	int nfuzzy;
7864133Seric 	char *file;
7964133Seric 	bool optional;
80*68276Seric 	int mid;
813308Seric 	char buf[MAXLINE];
823308Seric 	register char *p;
833308Seric 	extern char **copyplist();
8452647Seric 	struct stat statb;
855909Seric 	char exbuf[MAXLINE];
8665066Seric 	char pvpbuf[MAXLINE + MAXATOM];
87*68276Seric 	static char *null_list[1] = { NULL };
8810709Seric 	extern char *munchstring();
8953654Seric 	extern void makemapentry();
903308Seric 
9152647Seric 	FileName = cfname;
9252647Seric 	LineNumber = 0;
9352647Seric 
943308Seric 	cf = fopen(cfname, "r");
953308Seric 	if (cf == NULL)
963308Seric 	{
9752647Seric 		syserr("cannot open");
983308Seric 		exit(EX_OSFILE);
993308Seric 	}
1003308Seric 
10152647Seric 	if (fstat(fileno(cf), &statb) < 0)
10252647Seric 	{
10352647Seric 		syserr("cannot fstat");
10452647Seric 		exit(EX_OSFILE);
10552647Seric 	}
10652647Seric 
10752647Seric 	if (!S_ISREG(statb.st_mode))
10852647Seric 	{
10952647Seric 		syserr("not a plain file");
11052647Seric 		exit(EX_OSFILE);
11152647Seric 	}
11252647Seric 
11352647Seric 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
11452647Seric 	{
11553037Seric 		if (OpMode == MD_DAEMON || OpMode == MD_FREEZE)
11653037Seric 			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
11753037Seric 				FileName);
11853037Seric #ifdef LOG
11953037Seric 		if (LogLevel > 0)
12053037Seric 			syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
12153037Seric 				FileName);
12253037Seric #endif
12352647Seric 	}
12452647Seric 
12559254Seric #ifdef XLA
12659254Seric 	xla_zero();
12759254Seric #endif
12859254Seric 
12957135Seric 	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
1303308Seric 	{
13157135Seric 		if (bp[0] == '#')
13257135Seric 		{
13357135Seric 			if (bp != buf)
13457135Seric 				free(bp);
13552637Seric 			continue;
13657135Seric 		}
13752637Seric 
138*68276Seric 		/* do macro expansion mappings */
13957135Seric 		for (p = bp; *p != '\0'; p++)
14016157Seric 		{
14157135Seric 			if (*p == '#' && p > bp && ConfigLevel >= 3)
14252647Seric 			{
14352647Seric 				/* this is an on-line comment */
14452647Seric 				register char *e;
14552647Seric 
14658050Seric 				switch (*--p & 0377)
14752647Seric 				{
14858050Seric 				  case MACROEXPAND:
14952647Seric 					/* it's from $# -- let it go through */
15052647Seric 					p++;
15152647Seric 					break;
15252647Seric 
15352647Seric 				  case '\\':
15452647Seric 					/* it's backslash escaped */
15552647Seric 					(void) strcpy(p, p + 1);
15652647Seric 					break;
15752647Seric 
15852647Seric 				  default:
15952647Seric 					/* delete preceeding white space */
16058050Seric 					while (isascii(*p) && isspace(*p) && p > bp)
16152647Seric 						p--;
16256795Seric 					if ((e = strchr(++p, '\n')) != NULL)
16352647Seric 						(void) strcpy(p, e);
16452647Seric 					else
16552647Seric 						p[0] = p[1] = '\0';
16652647Seric 					break;
16752647Seric 				}
16852647Seric 				continue;
16952647Seric 			}
17052647Seric 
171*68276Seric 			if (*p != '$' || p[1] == '\0')
17216157Seric 				continue;
17316157Seric 
17416157Seric 			if (p[1] == '$')
17516157Seric 			{
17616157Seric 				/* actual dollar sign.... */
17723111Seric 				(void) strcpy(p, p + 1);
17816157Seric 				continue;
17916157Seric 			}
18016157Seric 
18116157Seric 			/* convert to macro expansion character */
182*68276Seric 			*p++ = MACROEXPAND;
183*68276Seric 
184*68276Seric 			/* convert macro name to code */
185*68276Seric 			*p = macid(p, &ep);
186*68276Seric 			if (ep != p)
187*68276Seric 				strcpy(p + 1, ep);
18816157Seric 		}
18916157Seric 
19016157Seric 		/* interpret this line */
19164718Seric 		errno = 0;
19257135Seric 		switch (bp[0])
1933308Seric 		{
1943308Seric 		  case '\0':
1953308Seric 		  case '#':		/* comment */
1963308Seric 			break;
1973308Seric 
1983308Seric 		  case 'R':		/* rewriting rule */
19957135Seric 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
2003308Seric 				continue;
2013308Seric 
2023308Seric 			if (*p == '\0')
2035909Seric 			{
20465821Seric 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
2055909Seric 				break;
2065909Seric 			}
2075909Seric 
2085909Seric 			/* allocate space for the rule header */
2095909Seric 			if (rwp == NULL)
2105909Seric 			{
2115909Seric 				RewriteRules[ruleset] = rwp =
2125909Seric 					(struct rewrite *) xalloc(sizeof *rwp);
2135909Seric 			}
2143308Seric 			else
2153308Seric 			{
2165909Seric 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
2175909Seric 				rwp = rwp->r_next;
2185909Seric 			}
2195909Seric 			rwp->r_next = NULL;
2203308Seric 
2215909Seric 			/* expand and save the LHS */
2225909Seric 			*p = '\0';
22357135Seric 			expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e);
22465066Seric 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
22565066Seric 					     sizeof pvpbuf, NULL);
22657589Seric 			nfuzzy = 0;
2275909Seric 			if (rwp->r_lhs != NULL)
22857589Seric 			{
22957589Seric 				register char **ap;
23057589Seric 
2315909Seric 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
23257589Seric 
23357589Seric 				/* count the number of fuzzy matches in LHS */
23457589Seric 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
23557589Seric 				{
23658148Seric 					char *botch;
23758148Seric 
23858148Seric 					botch = NULL;
23958050Seric 					switch (**ap & 0377)
24057589Seric 					{
24157589Seric 					  case MATCHZANY:
24257589Seric 					  case MATCHANY:
24357589Seric 					  case MATCHONE:
24457589Seric 					  case MATCHCLASS:
24557589Seric 					  case MATCHNCLASS:
24657589Seric 						nfuzzy++;
24758148Seric 						break;
24858148Seric 
24958148Seric 					  case MATCHREPL:
25058148Seric 						botch = "$0-$9";
25158148Seric 						break;
25258148Seric 
25358148Seric 					  case CANONNET:
25458148Seric 						botch = "$#";
25558148Seric 						break;
25658148Seric 
25758148Seric 					  case CANONUSER:
25858148Seric 						botch = "$:";
25958148Seric 						break;
26058148Seric 
26158148Seric 					  case CALLSUBR:
26258148Seric 						botch = "$>";
26358148Seric 						break;
26458148Seric 
26558148Seric 					  case CONDIF:
26658148Seric 						botch = "$?";
26758148Seric 						break;
26858148Seric 
26958148Seric 					  case CONDELSE:
27058148Seric 						botch = "$|";
27158148Seric 						break;
27258148Seric 
27358148Seric 					  case CONDFI:
27458148Seric 						botch = "$.";
27558148Seric 						break;
27658148Seric 
27758148Seric 					  case HOSTBEGIN:
27858148Seric 						botch = "$[";
27958148Seric 						break;
28058148Seric 
28158148Seric 					  case HOSTEND:
28258148Seric 						botch = "$]";
28358148Seric 						break;
28458148Seric 
28558148Seric 					  case LOOKUPBEGIN:
28658148Seric 						botch = "$(";
28758148Seric 						break;
28858148Seric 
28958148Seric 					  case LOOKUPEND:
29058148Seric 						botch = "$)";
29158148Seric 						break;
29257589Seric 					}
29358148Seric 					if (botch != NULL)
29458148Seric 						syserr("Inappropriate use of %s on LHS",
29558148Seric 							botch);
29657589Seric 				}
29757589Seric 			}
29856678Seric 			else
299*68276Seric 			{
30056678Seric 				syserr("R line: null LHS");
301*68276Seric 				rwp->r_lhs = null_list;
302*68276Seric 			}
3035909Seric 
3045909Seric 			/* expand and save the RHS */
3055909Seric 			while (*++p == '\t')
3065909Seric 				continue;
3077231Seric 			q = p;
3087231Seric 			while (*p != '\0' && *p != '\t')
3097231Seric 				p++;
3107231Seric 			*p = '\0';
31155012Seric 			expand(q, exbuf, &exbuf[sizeof exbuf], e);
31265066Seric 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
31365066Seric 					     sizeof pvpbuf, NULL);
3145909Seric 			if (rwp->r_rhs != NULL)
31557589Seric 			{
31657589Seric 				register char **ap;
31757589Seric 
3185909Seric 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
31957589Seric 
32057589Seric 				/* check no out-of-bounds replacements */
32157589Seric 				nfuzzy += '0';
32257589Seric 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
32357589Seric 				{
32458148Seric 					char *botch;
32558148Seric 
32658148Seric 					botch = NULL;
32758148Seric 					switch (**ap & 0377)
32857589Seric 					{
32958148Seric 					  case MATCHREPL:
33058148Seric 						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
33158148Seric 						{
33258148Seric 							syserr("replacement $%c out of bounds",
33358148Seric 								(*ap)[1]);
33458148Seric 						}
33558148Seric 						break;
33658148Seric 
33758148Seric 					  case MATCHZANY:
33858148Seric 						botch = "$*";
33958148Seric 						break;
34058148Seric 
34158148Seric 					  case MATCHANY:
34258148Seric 						botch = "$+";
34358148Seric 						break;
34458148Seric 
34558148Seric 					  case MATCHONE:
34658148Seric 						botch = "$-";
34758148Seric 						break;
34858148Seric 
34958148Seric 					  case MATCHCLASS:
35058148Seric 						botch = "$=";
35158148Seric 						break;
35258148Seric 
35358148Seric 					  case MATCHNCLASS:
35458148Seric 						botch = "$~";
35558148Seric 						break;
35657589Seric 					}
35758148Seric 					if (botch != NULL)
35858148Seric 						syserr("Inappropriate use of %s on RHS",
35958148Seric 							botch);
36057589Seric 				}
36157589Seric 			}
36256678Seric 			else
363*68276Seric 			{
36456678Seric 				syserr("R line: null RHS");
365*68276Seric 				rwp->r_rhs = null_list;
366*68276Seric 			}
3673308Seric 			break;
3683308Seric 
3694072Seric 		  case 'S':		/* select rewriting set */
37064440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
37164440Seric 				continue;
37264440Seric 			if (!isascii(*p) || !isdigit(*p))
37364440Seric 			{
37464440Seric 				syserr("invalid argument to S line: \"%.20s\"",
37564440Seric 					&bp[1]);
37664440Seric 				break;
37764440Seric 			}
37864440Seric 			ruleset = atoi(p);
3798056Seric 			if (ruleset >= MAXRWSETS || ruleset < 0)
3808056Seric 			{
3819381Seric 				syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
3828056Seric 				ruleset = 0;
3838056Seric 			}
3844072Seric 			rwp = NULL;
3854072Seric 			break;
3864072Seric 
3873308Seric 		  case 'D':		/* macro definition */
388*68276Seric 			mid = macid(&bp[1], &ep);
389*68276Seric 			p = munchstring(ep, NULL);
390*68276Seric 			define(mid, newstr(p), e);
3913308Seric 			break;
3923308Seric 
3933387Seric 		  case 'H':		/* required header line */
39457135Seric 			(void) chompheader(&bp[1], TRUE, e);
3953387Seric 			break;
3963387Seric 
3974061Seric 		  case 'C':		/* word class */
3984432Seric 			/* scan the list of words and set class for all */
399*68276Seric 			mid = macid(&bp[1], &ep);
400*68276Seric 			expand(ep, exbuf, &exbuf[sizeof exbuf], e);
40164121Seric 			for (p = exbuf; *p != '\0'; )
4024061Seric 			{
4034061Seric 				register char *wd;
4044061Seric 				char delim;
4054061Seric 
40658050Seric 				while (*p != '\0' && isascii(*p) && isspace(*p))
4074061Seric 					p++;
4084061Seric 				wd = p;
40958050Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4104061Seric 					p++;
4114061Seric 				delim = *p;
4124061Seric 				*p = '\0';
4134061Seric 				if (wd[0] != '\0')
414*68276Seric 					setclass(mid, wd);
4154061Seric 				*p = delim;
4164061Seric 			}
4174061Seric 			break;
4184061Seric 
41959272Seric 		  case 'F':		/* word class from file */
420*68276Seric 			mid = macid(&bp[1], &ep);
421*68276Seric 			for (p = ep; isascii(*p) && isspace(*p); )
42264133Seric 				p++;
42364133Seric 			if (p[0] == '-' && p[1] == 'o')
42464133Seric 			{
42564133Seric 				optional = TRUE;
42664133Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
42764133Seric 					p++;
42864133Seric 				while (isascii(*p) && isspace(*p))
429*68276Seric 					p++;
43064133Seric 			}
43164133Seric 			else
43264133Seric 				optional = FALSE;
43364133Seric 			file = p;
43464133Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
43564133Seric 				p++;
43659272Seric 			if (*p == '\0')
43759272Seric 				p = "%s";
43859272Seric 			else
43959272Seric 			{
44059272Seric 				*p = '\0';
44159272Seric 				while (isascii(*++p) && isspace(*p))
44259272Seric 					continue;
44359272Seric 			}
44464133Seric 			fileclass(bp[1], file, p, safe, optional);
44559272Seric 			break;
44659272Seric 
44759156Seric #ifdef XLA
44859156Seric 		  case 'L':		/* extended load average description */
44959156Seric 			xla_init(&bp[1]);
45059156Seric 			break;
45159156Seric #endif
45259156Seric 
4534096Seric 		  case 'M':		/* define mailer */
45457135Seric 			makemailer(&bp[1]);
4554096Seric 			break;
4564096Seric 
4578252Seric 		  case 'O':		/* set option */
45858734Seric 			setoption(bp[1], &bp[2], safe, FALSE, e);
4598252Seric 			break;
4608252Seric 
4618252Seric 		  case 'P':		/* set precedence */
4628252Seric 			if (NumPriorities >= MAXPRIORITIES)
4638252Seric 			{
4648547Seric 				toomany('P', MAXPRIORITIES);
4658252Seric 				break;
4668252Seric 			}
46757135Seric 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
4688252Seric 				continue;
4698252Seric 			if (*p == '\0')
4708252Seric 				goto badline;
4718252Seric 			*p = '\0';
47257135Seric 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
4738252Seric 			Priorities[NumPriorities].pri_val = atoi(++p);
4748252Seric 			NumPriorities++;
4758252Seric 			break;
4768252Seric 
4778547Seric 		  case 'T':		/* trusted user(s) */
478*68276Seric 			p = &buf[1];
479*68276Seric 			while (*p != '\0')
480*68276Seric 			{
481*68276Seric 				while (isspace(*p))
482*68276Seric 					p++;
483*68276Seric 				q = p;
484*68276Seric 				while (*p != '\0' && !isspace(*p))
485*68276Seric 					p++;
486*68276Seric 				if (*p != '\0')
487*68276Seric 					*p++ = '\0';
488*68276Seric 				if (*q == '\0')
489*68276Seric 					continue;
490*68276Seric 				(void) stab(q, ST_TRUSTED, ST_ENTER);
491*68276Seric 			}
4928547Seric 			break;
4938547Seric 
49452645Seric 		  case 'V':		/* configuration syntax version */
49564440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
49664440Seric 				continue;
49764440Seric 			if (!isascii(*p) || !isdigit(*p))
49864440Seric 			{
49964440Seric 				syserr("invalid argument to V line: \"%.20s\"",
50064440Seric 					&bp[1]);
50164440Seric 				break;
50264440Seric 			}
50364718Seric 			ConfigLevel = strtol(p, &ep, 10);
50464279Seric 			if (ConfigLevel >= 5)
50564279Seric 			{
50664279Seric 				/* level 5 configs have short name in $w */
50764279Seric 				p = macvalue('w', e);
50864279Seric 				if (p != NULL && (p = strchr(p, '.')) != NULL)
50964279Seric 					*p = '\0';
51064279Seric 			}
51164718Seric 			if (*ep++ == '/')
51264718Seric 			{
51364718Seric 				/* extract vendor code */
51464718Seric 				for (p = ep; isascii(*p) && isalpha(*p); )
51564718Seric 					p++;
51664718Seric 				*p = '\0';
51764718Seric 
51864718Seric 				if (!setvendor(ep))
51964718Seric 					syserr("invalid V line vendor code: \"%s\"",
52064718Seric 						ep);
52164718Seric 			}
52252645Seric 			break;
52352645Seric 
52453654Seric 		  case 'K':
52557135Seric 			makemapentry(&bp[1]);
52653654Seric 			break;
52753654Seric 
5283308Seric 		  default:
5294061Seric 		  badline:
53057135Seric 			syserr("unknown control line \"%s\"", bp);
5313308Seric 		}
53257135Seric 		if (bp != buf)
53357135Seric 			free(bp);
5343308Seric 	}
53552637Seric 	if (ferror(cf))
53652637Seric 	{
53752647Seric 		syserr("I/O read error", cfname);
53852637Seric 		exit(EX_OSFILE);
53952637Seric 	}
54052637Seric 	fclose(cf);
5419381Seric 	FileName = NULL;
54256836Seric 
543*68276Seric 	/* initialize host maps from local service tables */
544*68276Seric 	inithostmaps();
545*68276Seric 
546*68276Seric 	/* determine if we need to do special name-server frotz */
54767905Seric 	{
548*68276Seric 		int nmaps;
549*68276Seric 		char *maptype[MAXMAPSTACK];
550*68276Seric 		short mapreturn[MAXMAPACTIONS];
551*68276Seric 
552*68276Seric 		nmaps = switch_map_find("hosts", maptype, mapreturn);
553*68276Seric 		UseNameServer = FALSE;
554*68276Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
555*68276Seric 		{
556*68276Seric 			register int mapno;
557*68276Seric 
558*68276Seric 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
559*68276Seric 			{
560*68276Seric 				if (strcmp(maptype[mapno], "dns") == 0)
561*68276Seric 					UseNameServer = TRUE;
562*68276Seric 			}
563*68276Seric 		}
564*68276Seric 
565*68276Seric #ifdef HESIOD
566*68276Seric 		nmaps = switch_map_find("passwd", maptype, mapreturn);
567*68276Seric 		UseHesiod = FALSE;
568*68276Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
569*68276Seric 		{
570*68276Seric 			register int mapno;
571*68276Seric 
572*68276Seric 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
573*68276Seric 			{
574*68276Seric 				if (strcmp(maptype[mapno], "hesiod") == 0)
575*68276Seric 					UseHesiod = TRUE;
576*68276Seric 			}
577*68276Seric 		}
57868204Seric #endif
57967905Seric 	}
5804096Seric }
5814096Seric /*
5828547Seric **  TOOMANY -- signal too many of some option
5838547Seric **
5848547Seric **	Parameters:
5858547Seric **		id -- the id of the error line
5868547Seric **		maxcnt -- the maximum possible values
5878547Seric **
5888547Seric **	Returns:
5898547Seric **		none.
5908547Seric **
5918547Seric **	Side Effects:
5928547Seric **		gives a syserr.
5938547Seric */
5948547Seric 
5958547Seric toomany(id, maxcnt)
5968547Seric 	char id;
5978547Seric 	int maxcnt;
5988547Seric {
5999381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
6008547Seric }
6018547Seric /*
6024432Seric **  FILECLASS -- read members of a class from a file
6034432Seric **
6044432Seric **	Parameters:
6054432Seric **		class -- class to define.
6064432Seric **		filename -- name of file to read.
6074432Seric **		fmt -- scanf string to use for match.
60864133Seric **		safe -- if set, this is a safe read.
60964133Seric **		optional -- if set, it is not an error for the file to
61064133Seric **			not exist.
6114432Seric **
6124432Seric **	Returns:
6134432Seric **		none
6144432Seric **
6154432Seric **	Side Effects:
6164432Seric **
6174432Seric **		puts all lines in filename that match a scanf into
6184432Seric **			the named class.
6194432Seric */
6204432Seric 
62164133Seric fileclass(class, filename, fmt, safe, optional)
6224432Seric 	int class;
6234432Seric 	char *filename;
6244432Seric 	char *fmt;
62554973Seric 	bool safe;
62664133Seric 	bool optional;
6274432Seric {
62825808Seric 	FILE *f;
62954973Seric 	struct stat stbuf;
6304432Seric 	char buf[MAXLINE];
6314432Seric 
63266101Seric 	if (tTd(37, 2))
63366101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
63466101Seric 
63566031Seric 	if (filename[0] == '|')
63666031Seric 	{
63766031Seric 		syserr("fileclass: pipes (F%c%s) not supported due to security problems",
63866031Seric 			class, filename);
63966031Seric 		return;
64066031Seric 	}
64154973Seric 	if (stat(filename, &stbuf) < 0)
64254973Seric 	{
64366101Seric 		if (tTd(37, 2))
64466101Seric 			printf("  cannot stat (%s)\n", errstring(errno));
64564133Seric 		if (!optional)
64664133Seric 			syserr("fileclass: cannot stat %s", filename);
64754973Seric 		return;
64854973Seric 	}
64954973Seric 	if (!S_ISREG(stbuf.st_mode))
65054973Seric 	{
65154973Seric 		syserr("fileclass: %s not a regular file", filename);
65254973Seric 		return;
65354973Seric 	}
65454973Seric 	if (!safe && access(filename, R_OK) < 0)
65554973Seric 	{
65654973Seric 		syserr("fileclass: access denied on %s", filename);
65754973Seric 		return;
65854973Seric 	}
65954973Seric 	f = fopen(filename, "r");
6604432Seric 	if (f == NULL)
6614432Seric 	{
66254973Seric 		syserr("fileclass: cannot open %s", filename);
6634432Seric 		return;
6644432Seric 	}
6654432Seric 
6664432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
6674432Seric 	{
6684432Seric 		register STAB *s;
66925808Seric 		register char *p;
67025808Seric # ifdef SCANF
6714432Seric 		char wordbuf[MAXNAME+1];
6724432Seric 
6734432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
6744432Seric 			continue;
67525808Seric 		p = wordbuf;
67656795Seric # else /* SCANF */
67725808Seric 		p = buf;
67856795Seric # endif /* SCANF */
67925808Seric 
68025808Seric 		/*
68125808Seric 		**  Break up the match into words.
68225808Seric 		*/
68325808Seric 
68425808Seric 		while (*p != '\0')
68525808Seric 		{
68625808Seric 			register char *q;
68725808Seric 
68825808Seric 			/* strip leading spaces */
68958050Seric 			while (isascii(*p) && isspace(*p))
69025808Seric 				p++;
69125808Seric 			if (*p == '\0')
69225808Seric 				break;
69325808Seric 
69425808Seric 			/* find the end of the word */
69525808Seric 			q = p;
69658050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
69725808Seric 				p++;
69825808Seric 			if (*p != '\0')
69925808Seric 				*p++ = '\0';
70025808Seric 
70125808Seric 			/* enter the word in the symbol table */
70266101Seric 			setclass(class, q);
70325808Seric 		}
7044432Seric 	}
7054432Seric 
70654973Seric 	(void) fclose(f);
7074432Seric }
7084432Seric /*
7094096Seric **  MAKEMAILER -- define a new mailer.
7104096Seric **
7114096Seric **	Parameters:
71210327Seric **		line -- description of mailer.  This is in labeled
71310327Seric **			fields.  The fields are:
714*68276Seric **			   A -- the argv for this mailer
715*68276Seric **			   C -- the character set for MIME conversions
716*68276Seric **			   D -- the directory to run in
717*68276Seric **			   E -- the eol string
718*68276Seric **			   F -- the flags associated with the mailer
719*68276Seric **			   L -- the maximum line length
720*68276Seric **			   M -- the maximum message size
72168270Seric **			   P -- the path to the mailer
722*68276Seric **			   R -- the recipient rewriting set
72368270Seric **			   S -- the sender rewriting set
724*68276Seric **			   T -- the mailer type (for DSNs)
725*68276Seric **			   U -- the uid to run as
72610327Seric **			The first word is the canonical name of the mailer.
7274096Seric **
7284096Seric **	Returns:
7294096Seric **		none.
7304096Seric **
7314096Seric **	Side Effects:
7324096Seric **		enters the mailer into the mailer table.
7334096Seric */
7343308Seric 
73521066Seric makemailer(line)
7364096Seric 	char *line;
7374096Seric {
7384096Seric 	register char *p;
7398067Seric 	register struct mailer *m;
7408067Seric 	register STAB *s;
7418067Seric 	int i;
74210327Seric 	char fcode;
74358020Seric 	auto char *endp;
7444096Seric 	extern int NextMailer;
74510327Seric 	extern char **makeargv();
74610327Seric 	extern char *munchstring();
74710701Seric 	extern long atol();
7484096Seric 
74910327Seric 	/* allocate a mailer and set up defaults */
75010327Seric 	m = (struct mailer *) xalloc(sizeof *m);
75110327Seric 	bzero((char *) m, sizeof *m);
75210327Seric 	m->m_eol = "\n";
753*68276Seric 	m->m_uid = m->m_gid = 0;
75410327Seric 
75510327Seric 	/* collect the mailer name */
75658050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
75710327Seric 		continue;
75810327Seric 	if (*p != '\0')
75910327Seric 		*p++ = '\0';
76010327Seric 	m->m_name = newstr(line);
76110327Seric 
76210327Seric 	/* now scan through and assign info from the fields */
76310327Seric 	while (*p != '\0')
76410327Seric 	{
76558333Seric 		auto char *delimptr;
76658333Seric 
76758050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
76810327Seric 			p++;
76910327Seric 
77010327Seric 		/* p now points to field code */
77110327Seric 		fcode = *p;
77210327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
77310327Seric 			p++;
77410327Seric 		if (*p++ != '=')
77510327Seric 		{
77652637Seric 			syserr("mailer %s: `=' expected", m->m_name);
77710327Seric 			return;
77810327Seric 		}
77958050Seric 		while (isascii(*p) && isspace(*p))
78010327Seric 			p++;
78110327Seric 
78210327Seric 		/* p now points to the field body */
78358333Seric 		p = munchstring(p, &delimptr);
78410327Seric 
78510327Seric 		/* install the field into the mailer struct */
78610327Seric 		switch (fcode)
78710327Seric 		{
78810327Seric 		  case 'P':		/* pathname */
78910327Seric 			m->m_mailer = newstr(p);
79010327Seric 			break;
79110327Seric 
79210327Seric 		  case 'F':		/* flags */
79310687Seric 			for (; *p != '\0'; p++)
79458050Seric 				if (!(isascii(*p) && isspace(*p)))
79552637Seric 					setbitn(*p, m->m_flags);
79610327Seric 			break;
79710327Seric 
79810327Seric 		  case 'S':		/* sender rewriting ruleset */
79910327Seric 		  case 'R':		/* recipient rewriting ruleset */
80058020Seric 			i = strtol(p, &endp, 10);
80110327Seric 			if (i < 0 || i >= MAXRWSETS)
80210327Seric 			{
80310327Seric 				syserr("invalid rewrite set, %d max", MAXRWSETS);
80410327Seric 				return;
80510327Seric 			}
80610327Seric 			if (fcode == 'S')
80758020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
80810327Seric 			else
80958020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
81058020Seric 
81158020Seric 			p = endp;
81259985Seric 			if (*p++ == '/')
81358020Seric 			{
81458020Seric 				i = strtol(p, NULL, 10);
81558020Seric 				if (i < 0 || i >= MAXRWSETS)
81658020Seric 				{
81758020Seric 					syserr("invalid rewrite set, %d max",
81858020Seric 						MAXRWSETS);
81958020Seric 					return;
82058020Seric 				}
82158020Seric 				if (fcode == 'S')
82258020Seric 					m->m_sh_rwset = i;
82358020Seric 				else
82458020Seric 					m->m_rh_rwset = i;
82558020Seric 			}
82610327Seric 			break;
82710327Seric 
82810327Seric 		  case 'E':		/* end of line string */
82910327Seric 			m->m_eol = newstr(p);
83010327Seric 			break;
83110327Seric 
83210327Seric 		  case 'A':		/* argument vector */
83310327Seric 			m->m_argv = makeargv(p);
83410327Seric 			break;
83510701Seric 
83610701Seric 		  case 'M':		/* maximum message size */
83710701Seric 			m->m_maxsize = atol(p);
83810701Seric 			break;
83952106Seric 
84052106Seric 		  case 'L':		/* maximum line length */
84152106Seric 			m->m_linelimit = atoi(p);
84252106Seric 			break;
84358935Seric 
84458935Seric 		  case 'D':		/* working directory */
84558935Seric 			m->m_execdir = newstr(p);
84658935Seric 			break;
847*68276Seric 
848*68276Seric 		  case 'C':		/* default charset */
849*68276Seric 			m->m_defcharset = newstr(p);
850*68276Seric 			break;
851*68276Seric 
852*68276Seric 		  case 'T':		/* MTA Type */
853*68276Seric 			m->m_mtatype = newstr(p);
854*68276Seric 			p = strchr(m->m_mtatype, '/');
855*68276Seric 			if (p != NULL)
856*68276Seric 			{
857*68276Seric 				*p++ = '\0';
858*68276Seric 				if (*p == '\0')
859*68276Seric 					p = NULL;
860*68276Seric 			}
861*68276Seric 			if (p == NULL)
862*68276Seric 				m->m_addrtype = m->m_mtatype;
863*68276Seric 			else
864*68276Seric 			{
865*68276Seric 				m->m_addrtype = p;
866*68276Seric 				p = strchr(p, '/');
867*68276Seric 			}
868*68276Seric 			if (p != NULL)
869*68276Seric 			{
870*68276Seric 				*p++ = '\0';
871*68276Seric 				if (*p == '\0')
872*68276Seric 					p = NULL;
873*68276Seric 			}
874*68276Seric 			if (p == NULL)
875*68276Seric 				m->m_diagtype = m->m_mtatype;
876*68276Seric 			else
877*68276Seric 				m->m_diagtype = p;
878*68276Seric 			break;
879*68276Seric 
880*68276Seric 		  case 'U':		/* user id */
881*68276Seric 			if (isascii(*p) && !isdigit(*p))
882*68276Seric 			{
883*68276Seric 				char *q = p;
884*68276Seric 				struct passwd *pw;
885*68276Seric 
886*68276Seric 				while (isascii(*p) && isalnum(*p))
887*68276Seric 					p++;
888*68276Seric 				while (isascii(*p) && isspace(*p))
889*68276Seric 					*p++ = '\0';
890*68276Seric 				if (*p != '\0')
891*68276Seric 					*p++ = '\0';
892*68276Seric 				pw = getpwnam(q);
893*68276Seric 				if (pw == NULL)
894*68276Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
895*68276Seric 				else
896*68276Seric 				{
897*68276Seric 					m->m_uid = pw->pw_uid;
898*68276Seric 					m->m_gid = pw->pw_gid;
899*68276Seric 				}
900*68276Seric 			}
901*68276Seric 			else
902*68276Seric 			{
903*68276Seric 				auto char *q;
904*68276Seric 
905*68276Seric 				m->m_uid = strtol(p, &q, 0);
906*68276Seric 				p = q;
907*68276Seric 			}
908*68276Seric 			while (isascii(*p) && isspace(*p))
909*68276Seric 				p++;
910*68276Seric 			if (*p == '\0')
911*68276Seric 				break;
912*68276Seric 			if (isascii(*p) && !isdigit(*p))
913*68276Seric 			{
914*68276Seric 				char *q = p;
915*68276Seric 				struct group *gr;
916*68276Seric 
917*68276Seric 				while (isascii(*p) && isalnum(*p))
918*68276Seric 					p++;
919*68276Seric 				*p++ = '\0';
920*68276Seric 				gr = getgrnam(q);
921*68276Seric 				if (gr == NULL)
922*68276Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
923*68276Seric 				else
924*68276Seric 					m->m_gid = gr->gr_gid;
925*68276Seric 			}
926*68276Seric 			else
927*68276Seric 			{
928*68276Seric 				m->m_gid = strtol(p, NULL, 0);
929*68276Seric 			}
930*68276Seric 			break;
93110327Seric 		}
93210327Seric 
93358333Seric 		p = delimptr;
93410327Seric 	}
93510327Seric 
93658321Seric 	/* do some rationality checking */
93758321Seric 	if (m->m_argv == NULL)
93858321Seric 	{
93958321Seric 		syserr("M%s: A= argument required", m->m_name);
94058321Seric 		return;
94158321Seric 	}
94258321Seric 	if (m->m_mailer == NULL)
94358321Seric 	{
94458321Seric 		syserr("M%s: P= argument required", m->m_name);
94558321Seric 		return;
94658321Seric 	}
94758321Seric 
9484096Seric 	if (NextMailer >= MAXMAILERS)
9494096Seric 	{
9509381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
9514096Seric 		return;
9524096Seric 	}
95357402Seric 
954*68276Seric 	/* do some heuristic cleanup for back compatibility */
955*68276Seric 	if (bitnset(M_LIMITS, m->m_flags))
956*68276Seric 	{
957*68276Seric 		if (m->m_linelimit == 0)
958*68276Seric 			m->m_linelimit = SMTPLINELIM;
959*68276Seric 		if (ConfigLevel < 2)
960*68276Seric 			setbitn(M_7BITS, m->m_flags);
961*68276Seric 	}
962*68276Seric 
963*68276Seric 	if (ConfigLevel < 6 &&
964*68276Seric 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
965*68276Seric 	     strcmp(m->m_mailer, "[TCP]") == 0))
966*68276Seric 	{
967*68276Seric 		if (m->m_mtatype == NULL)
968*68276Seric 			m->m_mtatype = "dns";
969*68276Seric 		if (m->m_addrtype == NULL)
970*68276Seric 			m->m_addrtype = "rfc822";
971*68276Seric 		if (m->m_diagtype == NULL)
972*68276Seric 			m->m_diagtype = "smtp";
973*68276Seric 	}
974*68276Seric 
975*68276Seric 	/* enter the mailer into the symbol table */
97610327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
97757402Seric 	if (s->s_mailer != NULL)
97857402Seric 	{
97957402Seric 		i = s->s_mailer->m_mno;
98057402Seric 		free(s->s_mailer);
98157402Seric 	}
98257402Seric 	else
98357402Seric 	{
98457402Seric 		i = NextMailer++;
98557402Seric 	}
98657402Seric 	Mailer[i] = s->s_mailer = m;
98757454Seric 	m->m_mno = i;
98810327Seric }
98910327Seric /*
99010327Seric **  MUNCHSTRING -- translate a string into internal form.
99110327Seric **
99210327Seric **	Parameters:
99310327Seric **		p -- the string to munch.
99458333Seric **		delimptr -- if non-NULL, set to the pointer of the
99558333Seric **			field delimiter character.
99610327Seric **
99710327Seric **	Returns:
99810327Seric **		the munched string.
99910327Seric */
10004096Seric 
100110327Seric char *
100258333Seric munchstring(p, delimptr)
100310327Seric 	register char *p;
100458333Seric 	char **delimptr;
100510327Seric {
100610327Seric 	register char *q;
100710327Seric 	bool backslash = FALSE;
100810327Seric 	bool quotemode = FALSE;
100910327Seric 	static char buf[MAXLINE];
10104096Seric 
101110327Seric 	for (q = buf; *p != '\0'; p++)
10124096Seric 	{
101310327Seric 		if (backslash)
101410327Seric 		{
101510327Seric 			/* everything is roughly literal */
101610357Seric 			backslash = FALSE;
101710327Seric 			switch (*p)
101810327Seric 			{
101910327Seric 			  case 'r':		/* carriage return */
102010327Seric 				*q++ = '\r';
102110327Seric 				continue;
102210327Seric 
102310327Seric 			  case 'n':		/* newline */
102410327Seric 				*q++ = '\n';
102510327Seric 				continue;
102610327Seric 
102710327Seric 			  case 'f':		/* form feed */
102810327Seric 				*q++ = '\f';
102910327Seric 				continue;
103010327Seric 
103110327Seric 			  case 'b':		/* backspace */
103210327Seric 				*q++ = '\b';
103310327Seric 				continue;
103410327Seric 			}
103510327Seric 			*q++ = *p;
103610327Seric 		}
103710327Seric 		else
103810327Seric 		{
103910327Seric 			if (*p == '\\')
104010327Seric 				backslash = TRUE;
104110327Seric 			else if (*p == '"')
104210327Seric 				quotemode = !quotemode;
104310327Seric 			else if (quotemode || *p != ',')
104410327Seric 				*q++ = *p;
104510327Seric 			else
104610327Seric 				break;
104710327Seric 		}
10484096Seric 	}
10494096Seric 
105058333Seric 	if (delimptr != NULL)
105158333Seric 		*delimptr = p;
105210327Seric 	*q++ = '\0';
105310327Seric 	return (buf);
105410327Seric }
105510327Seric /*
105610327Seric **  MAKEARGV -- break up a string into words
105710327Seric **
105810327Seric **	Parameters:
105910327Seric **		p -- the string to break up.
106010327Seric **
106110327Seric **	Returns:
106210327Seric **		a char **argv (dynamically allocated)
106310327Seric **
106410327Seric **	Side Effects:
106510327Seric **		munges p.
106610327Seric */
10674096Seric 
106810327Seric char **
106910327Seric makeargv(p)
107010327Seric 	register char *p;
107110327Seric {
107210327Seric 	char *q;
107310327Seric 	int i;
107410327Seric 	char **avp;
107510327Seric 	char *argv[MAXPV + 1];
107610327Seric 
107710327Seric 	/* take apart the words */
107810327Seric 	i = 0;
107910327Seric 	while (*p != '\0' && i < MAXPV)
10804096Seric 	{
108110327Seric 		q = p;
108258050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
108310327Seric 			p++;
108458050Seric 		while (isascii(*p) && isspace(*p))
108510327Seric 			*p++ = '\0';
108610327Seric 		argv[i++] = newstr(q);
10874096Seric 	}
108810327Seric 	argv[i++] = NULL;
10894096Seric 
109010327Seric 	/* now make a copy of the argv */
109110327Seric 	avp = (char **) xalloc(sizeof *avp * i);
109216893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
109310327Seric 
109410327Seric 	return (avp);
10953308Seric }
10963308Seric /*
10973308Seric **  PRINTRULES -- print rewrite rules (for debugging)
10983308Seric **
10993308Seric **	Parameters:
11003308Seric **		none.
11013308Seric **
11023308Seric **	Returns:
11033308Seric **		none.
11043308Seric **
11053308Seric **	Side Effects:
11063308Seric **		prints rewrite rules.
11073308Seric */
11083308Seric 
11093308Seric printrules()
11103308Seric {
11113308Seric 	register struct rewrite *rwp;
11124072Seric 	register int ruleset;
11133308Seric 
11144072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
11153308Seric 	{
11164072Seric 		if (RewriteRules[ruleset] == NULL)
11174072Seric 			continue;
11188067Seric 		printf("\n----Rule Set %d:", ruleset);
11193308Seric 
11204072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
11213308Seric 		{
11228067Seric 			printf("\nLHS:");
11238067Seric 			printav(rwp->r_lhs);
11248067Seric 			printf("RHS:");
11258067Seric 			printav(rwp->r_rhs);
11263308Seric 		}
11273308Seric 	}
11283308Seric }
1129*68276Seric /*
1130*68276Seric **  PRINTMAILER -- print mailer structure (for debugging)
1131*68276Seric **
1132*68276Seric **	Parameters:
1133*68276Seric **		m -- the mailer to print
1134*68276Seric **
1135*68276Seric **	Returns:
1136*68276Seric **		none.
1137*68276Seric */
11384319Seric 
1139*68276Seric printmailer(m)
1140*68276Seric 	register MAILER *m;
1141*68276Seric {
1142*68276Seric 	int j;
1143*68276Seric 
1144*68276Seric 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
1145*68276Seric 		m->m_mno, m->m_name,
1146*68276Seric 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
1147*68276Seric 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
1148*68276Seric 		m->m_uid, m->m_gid);
1149*68276Seric 	for (j = '\0'; j <= '\177'; j++)
1150*68276Seric 		if (bitnset(j, m->m_flags))
1151*68276Seric 			(void) putchar(j);
1152*68276Seric 	printf(" L=%d E=", m->m_linelimit);
1153*68276Seric 	xputs(m->m_eol);
1154*68276Seric 	if (m->m_defcharset != NULL)
1155*68276Seric 		printf(" C=%s", m->m_defcharset);
1156*68276Seric 	printf(" T=%s/%s/%s",
1157*68276Seric 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
1158*68276Seric 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
1159*68276Seric 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
1160*68276Seric 	if (m->m_argv != NULL)
1161*68276Seric 	{
1162*68276Seric 		char **a = m->m_argv;
1163*68276Seric 
1164*68276Seric 		printf(" A=");
1165*68276Seric 		while (*a != NULL)
1166*68276Seric 		{
1167*68276Seric 			if (a != m->m_argv)
1168*68276Seric 				printf(" ");
1169*68276Seric 			xputs(*a++);
1170*68276Seric 		}
1171*68276Seric 	}
1172*68276Seric 	printf("\n");
1173*68276Seric }
11744096Seric /*
11758256Seric **  SETOPTION -- set global processing option
11768256Seric **
11778256Seric **	Parameters:
11788256Seric **		opt -- option name.
11798256Seric **		val -- option value (as a text string).
118021755Seric **		safe -- set if this came from a configuration file.
118121755Seric **			Some options (if set from the command line) will
118221755Seric **			reset the user id to avoid security problems.
11838269Seric **		sticky -- if set, don't let other setoptions override
11848269Seric **			this value.
118558734Seric **		e -- the main envelope.
11868256Seric **
11878256Seric **	Returns:
11888256Seric **		none.
11898256Seric **
11908256Seric **	Side Effects:
11918256Seric **		Sets options as implied by the arguments.
11928256Seric */
11938256Seric 
119410687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
11958269Seric 
119657207Seric 
119766334Seric #if NAMED_BIND
119857207Seric 
119957207Seric struct resolverflags
120057207Seric {
120157207Seric 	char	*rf_name;	/* name of the flag */
120257207Seric 	long	rf_bits;	/* bits to set/clear */
120357207Seric } ResolverFlags[] =
120457207Seric {
120557207Seric 	"debug",	RES_DEBUG,
120657207Seric 	"aaonly",	RES_AAONLY,
120757207Seric 	"usevc",	RES_USEVC,
120857207Seric 	"primary",	RES_PRIMARY,
120957207Seric 	"igntc",	RES_IGNTC,
121057207Seric 	"recurse",	RES_RECURSE,
121157207Seric 	"defnames",	RES_DEFNAMES,
121257207Seric 	"stayopen",	RES_STAYOPEN,
121357207Seric 	"dnsrch",	RES_DNSRCH,
121465583Seric 	"true",		0,		/* to avoid error on old syntax */
121557207Seric 	NULL,		0
121657207Seric };
121757207Seric 
121857207Seric #endif
121957207Seric 
1220*68276Seric struct optioninfo
1221*68276Seric {
1222*68276Seric 	char	*o_name;	/* long name of option */
1223*68276Seric 	u_char	o_code;		/* short name of option */
1224*68276Seric 	bool	o_safe;		/* safe for random people to use */
1225*68276Seric } OptionTab[] =
1226*68276Seric {
1227*68276Seric 	"SevenBitInput",	'7',		TRUE,
1228*68276Seric 	"EightBitMode",		'8',		TRUE,
1229*68276Seric 	"AliasFile",		'A',		FALSE,
1230*68276Seric 	"AliasWait",		'a',		FALSE,
1231*68276Seric 	"BlankSub",		'B',		FALSE,
1232*68276Seric 	"MinFreeBlocks",	'b',		TRUE,
1233*68276Seric 	"CheckpointInterval",	'C',		TRUE,
1234*68276Seric 	"HoldExpensive",	'c',		FALSE,
1235*68276Seric 	"AutoRebuildAliases",	'D',		FALSE,
1236*68276Seric 	"DeliveryMode",		'd',		TRUE,
1237*68276Seric 	"ErrorHeader",		'E',		FALSE,
1238*68276Seric 	"ErrorMode",		'e',		TRUE,
1239*68276Seric 	"TempFileMode",		'F',		FALSE,
1240*68276Seric 	"SaveFromLine",		'f',		FALSE,
1241*68276Seric 	"MatchGECOS",		'G',		FALSE,
1242*68276Seric 	"HelpFile",		'H',		FALSE,
1243*68276Seric 	"MaxHopCount",		'h',		FALSE,
1244*68276Seric 	"NameServerOptions",	'I',		FALSE,
1245*68276Seric 	"IgnoreDots",		'i',		TRUE,
1246*68276Seric 	"ForwardPath",		'J',		FALSE,
1247*68276Seric 	"SendMimeErrors",	'j',		TRUE,
1248*68276Seric 	"ConnectionCacheSize",	'k',		FALSE,
1249*68276Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
1250*68276Seric 	"UseErrorsTo",		'l',		FALSE,
1251*68276Seric 	"LogLevel",		'L',		FALSE,
1252*68276Seric 	"MeToo",		'm',		TRUE,
1253*68276Seric 	"CheckAliases",		'n',		FALSE,
1254*68276Seric 	"OldStyleHeaders",	'o',		TRUE,
1255*68276Seric 	"DaemonPortOptions",	'O',		FALSE,
1256*68276Seric 	"PrivacyOptions",	'p',		TRUE,
1257*68276Seric 	"PostmasterCopy",	'P',		FALSE,
1258*68276Seric 	"QueueFactor",		'q',		FALSE,
1259*68276Seric 	"QueueDirectory",	'Q',		FALSE,
1260*68276Seric 	"DontPruneRoutes",	'R',		FALSE,
1261*68276Seric 	"Timeout",		'r',		TRUE,
1262*68276Seric 	"StatusFile",		'S',		FALSE,
1263*68276Seric 	"SuperSafe",		's',		TRUE,
1264*68276Seric 	"QueueTimeout",		'T',		FALSE,
1265*68276Seric 	"TimeZoneSpec",		't',		FALSE,
1266*68276Seric 	"UserDatabaseSpec",	'U',		FALSE,
1267*68276Seric 	"DefaultUser",		'u',		FALSE,
1268*68276Seric 	"FallbackMXhost",	'V',		FALSE,
1269*68276Seric 	"Verbose",		'v',		TRUE,
1270*68276Seric 	"TryNullMXList",	'w',		TRUE,
1271*68276Seric 	"QueueLA",		'x',		FALSE,
1272*68276Seric 	"RefuseLA",		'X',		FALSE,
1273*68276Seric 	"RecipientFactor",	'y',		FALSE,
1274*68276Seric 	"ForkQueueRuns",	'Y',		FALSE,
1275*68276Seric 	"ClassFactor",		'z',		FALSE,
1276*68276Seric 	"TimeFactor",		'Z',		FALSE,
1277*68276Seric #define O_BSP		0x80
1278*68276Seric 	"BrokenSmtpPeers",	O_BSP,		TRUE,
1279*68276Seric #define O_QUEUESORTORD	0x81
1280*68276Seric 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
1281*68276Seric #define O_MQA		0x83
1282*68276Seric 	"MinQueueAge",		O_MQA,		TRUE,
1283*68276Seric #define O_MHSA		0x84
1284*68276Seric /*
1285*68276Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
1286*68276Seric */
1287*68276Seric #define O_DEFCHARSET	0x85
1288*68276Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
1289*68276Seric #define O_SSFILE	0x86
1290*68276Seric 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
1291*68276Seric #define O_DIALDELAY	0x87
1292*68276Seric 	"DialDelay",		O_DIALDELAY,	TRUE,
1293*68276Seric 
1294*68276Seric 	NULL,			'\0',		FALSE,
1295*68276Seric };
1296*68276Seric 
1297*68276Seric 
1298*68276Seric 
129958734Seric setoption(opt, val, safe, sticky, e)
1300*68276Seric 	u_char opt;
13018256Seric 	char *val;
130221755Seric 	bool safe;
13038269Seric 	bool sticky;
130458734Seric 	register ENVELOPE *e;
13058256Seric {
130657207Seric 	register char *p;
1307*68276Seric 	register struct optioninfo *o;
1308*68276Seric 	char *subopt;
13098265Seric 	extern bool atobool();
131012633Seric 	extern time_t convtime();
131114879Seric 	extern int QueueLA;
131214879Seric 	extern int RefuseLA;
131364718Seric 	extern bool Warn_Q_option;
13148256Seric 
1315*68276Seric 	errno = 0;
1316*68276Seric 	if (opt == ' ')
1317*68276Seric 	{
1318*68276Seric 		/* full word options */
1319*68276Seric 		struct optioninfo *sel;
1320*68276Seric 
1321*68276Seric 		p = strchr(val, '=');
1322*68276Seric 		if (p == NULL)
1323*68276Seric 			p = &val[strlen(val)];
1324*68276Seric 		while (*--p == ' ')
1325*68276Seric 			continue;
1326*68276Seric 		while (*++p == ' ')
1327*68276Seric 			*p = '\0';
1328*68276Seric 		if (p == val)
1329*68276Seric 		{
1330*68276Seric 			syserr("readcf: null option name");
1331*68276Seric 			return;
1332*68276Seric 		}
1333*68276Seric 		if (*p == '=')
1334*68276Seric 			*p++ = '\0';
1335*68276Seric 		while (*p == ' ')
1336*68276Seric 			p++;
1337*68276Seric 		subopt = strchr(val, '.');
1338*68276Seric 		if (subopt != NULL)
1339*68276Seric 			*subopt++ = '\0';
1340*68276Seric 		sel = NULL;
1341*68276Seric 		for (o = OptionTab; o->o_name != NULL; o++)
1342*68276Seric 		{
1343*68276Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
1344*68276Seric 				continue;
1345*68276Seric 			if (strlen(o->o_name) == strlen(val))
1346*68276Seric 			{
1347*68276Seric 				/* completely specified -- this must be it */
1348*68276Seric 				sel = NULL;
1349*68276Seric 				break;
1350*68276Seric 			}
1351*68276Seric 			if (sel != NULL)
1352*68276Seric 				break;
1353*68276Seric 			sel = o;
1354*68276Seric 		}
1355*68276Seric 		if (sel != NULL && o->o_name == NULL)
1356*68276Seric 			o = sel;
1357*68276Seric 		else if (o->o_name == NULL)
1358*68276Seric 		{
1359*68276Seric 			syserr("readcf: unknown option name %s", val);
1360*68276Seric 			return;
1361*68276Seric 		}
1362*68276Seric 		else if (sel != NULL)
1363*68276Seric 		{
1364*68276Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
1365*68276Seric 				val, sel->o_name, o->o_name);
1366*68276Seric 			return;
1367*68276Seric 		}
1368*68276Seric 		if (strlen(val) != strlen(o->o_name))
1369*68276Seric 		{
1370*68276Seric 			bool oldVerbose = Verbose;
1371*68276Seric 
1372*68276Seric 			Verbose = TRUE;
1373*68276Seric 			message("Option %s used as abbreviation for %s",
1374*68276Seric 				val, o->o_name);
1375*68276Seric 			Verbose = oldVerbose;
1376*68276Seric 		}
1377*68276Seric 		opt = o->o_code;
1378*68276Seric 		val = p;
1379*68276Seric 	}
1380*68276Seric 	else
1381*68276Seric 	{
1382*68276Seric 		for (o = OptionTab; o->o_name != NULL; o++)
1383*68276Seric 		{
1384*68276Seric 			if (o->o_code == opt)
1385*68276Seric 				break;
1386*68276Seric 		}
1387*68276Seric 		subopt = NULL;
1388*68276Seric 	}
1389*68276Seric 
13908256Seric 	if (tTd(37, 1))
1391*68276Seric 	{
1392*68276Seric 		printf(isascii(opt) && isprint(opt) ?
1393*68276Seric 			    "setoption %s (%c).%s=%s" :
1394*68276Seric 			    "setoption %s (0x%x).%s=%s",
1395*68276Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
1396*68276Seric 			opt,
1397*68276Seric 			subopt == NULL ? "" : subopt,
1398*68276Seric 			val);
1399*68276Seric 	}
14008256Seric 
14018256Seric 	/*
14028269Seric 	**  See if this option is preset for us.
14038256Seric 	*/
14048256Seric 
140559731Seric 	if (!sticky && bitnset(opt, StickyOpt))
14068269Seric 	{
14079341Seric 		if (tTd(37, 1))
14089341Seric 			printf(" (ignored)\n");
14098269Seric 		return;
14108269Seric 	}
14118269Seric 
141221755Seric 	/*
141321755Seric 	**  Check to see if this option can be specified by this user.
141421755Seric 	*/
141521755Seric 
141663787Seric 	if (!safe && RealUid == 0)
141721755Seric 		safe = TRUE;
1418*68276Seric 	if (!safe && !o->o_safe)
141921755Seric 	{
142039111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
142121755Seric 		{
142236582Sbostic 			if (tTd(37, 1))
142336582Sbostic 				printf(" (unsafe)");
142463787Seric 			if (RealUid != geteuid())
142536582Sbostic 			{
142651210Seric 				if (tTd(37, 1))
142751210Seric 					printf("(Resetting uid)");
142863787Seric 				(void) setgid(RealGid);
142963787Seric 				(void) setuid(RealUid);
143036582Sbostic 			}
143121755Seric 		}
143221755Seric 	}
143351210Seric 	if (tTd(37, 1))
143417985Seric 		printf("\n");
14358269Seric 
1436*68276Seric 	switch (opt & 0xff)
14378256Seric 	{
143859709Seric 	  case '7':		/* force seven-bit input */
1439*68276Seric 		SevenBitInput = atobool(val);
144052106Seric 		break;
144152106Seric 
1442*68276Seric 	  case '8':		/* handling of 8-bit input */
1443*68276Seric 		switch (*val)
1444*68276Seric 		{
1445*68276Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
1446*68276Seric 			MimeMode = 0;
1447*68276Seric 			break;
1448*68276Seric 
1449*68276Seric 		  case 'm':		/* convert 8-bit, convert MIME */
1450*68276Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
1451*68276Seric 			break;
1452*68276Seric 
1453*68276Seric 		  case 'j':		/* "just send 8" */
1454*68276Seric 			MimeMode = MM_PASS8BIT;
1455*68276Seric 			break;
1456*68276Seric 
1457*68276Seric 		  case 'p':		/* pass 8 bit, convert MIME */
1458*68276Seric 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
1459*68276Seric 			break;
1460*68276Seric 
1461*68276Seric 		  case 's':		/* strict adherence */
1462*68276Seric 			MimeMode = MM_CVTMIME;
1463*68276Seric 			break;
1464*68276Seric 
1465*68276Seric 		  case 'a':		/* encode 8 bit if available */
1466*68276Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
1467*68276Seric 			break;
1468*68276Seric 
1469*68276Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
1470*68276Seric 			MimeMode = MM_MIME8BIT;
1471*68276Seric 			break;
1472*68276Seric 
1473*68276Seric 		  default:
1474*68276Seric 			syserr("Unknown 8-bit mode %c", *val);
1475*68276Seric 			exit(EX_USAGE);
1476*68276Seric 		}
1477*68276Seric 		break;
1478*68276Seric 
14798256Seric 	  case 'A':		/* set default alias file */
14809381Seric 		if (val[0] == '\0')
148159672Seric 			setalias("aliases");
14829381Seric 		else
148359672Seric 			setalias(val);
14848256Seric 		break;
14858256Seric 
148617474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
148717474Seric 		if (val[0] == '\0')
148864796Seric 			SafeAlias = 5 * 60;		/* five minutes */
148917474Seric 		else
149064796Seric 			SafeAlias = convtime(val, 'm');
149117474Seric 		break;
149217474Seric 
149316843Seric 	  case 'B':		/* substitution for blank character */
149416843Seric 		SpaceSub = val[0];
149516843Seric 		if (SpaceSub == '\0')
149616843Seric 			SpaceSub = ' ';
149716843Seric 		break;
149816843Seric 
149959283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
150059283Seric 		p = strchr(val, '/');
150159283Seric 		if (p != NULL)
150259283Seric 		{
150359283Seric 			*p++ = '\0';
150459283Seric 			MaxMessageSize = atol(p);
150559283Seric 		}
150658082Seric 		MinBlocksFree = atol(val);
150758082Seric 		break;
150858082Seric 
15099284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
15109381Seric 		NoConnect = atobool(val);
15119284Seric 		break;
15129284Seric 
151351305Seric 	  case 'C':		/* checkpoint every N addresses */
151451305Seric 		CheckpointInterval = atoi(val);
151524944Seric 		break;
151624944Seric 
15179284Seric 	  case 'd':		/* delivery mode */
15189284Seric 		switch (*val)
15198269Seric 		{
15209284Seric 		  case '\0':
152158734Seric 			e->e_sendmode = SM_DELIVER;
15228269Seric 			break;
15238269Seric 
152410755Seric 		  case SM_QUEUE:	/* queue only */
152510755Seric #ifndef QUEUE
152610755Seric 			syserr("need QUEUE to set -odqueue");
152756795Seric #endif /* QUEUE */
152810755Seric 			/* fall through..... */
152910755Seric 
15309284Seric 		  case SM_DELIVER:	/* do everything */
15319284Seric 		  case SM_FORK:		/* fork after verification */
153258734Seric 			e->e_sendmode = *val;
15338269Seric 			break;
15348269Seric 
15358269Seric 		  default:
15369284Seric 			syserr("Unknown delivery mode %c", *val);
15378269Seric 			exit(EX_USAGE);
15388269Seric 		}
15398269Seric 		break;
15408269Seric 
15419146Seric 	  case 'D':		/* rebuild alias database as needed */
15429381Seric 		AutoRebuild = atobool(val);
15439146Seric 		break;
15449146Seric 
154555372Seric 	  case 'E':		/* error message header/header file */
154655379Seric 		if (*val != '\0')
154755379Seric 			ErrMsgFile = newstr(val);
154855372Seric 		break;
154955372Seric 
15508269Seric 	  case 'e':		/* set error processing mode */
15518269Seric 		switch (*val)
15528269Seric 		{
15539381Seric 		  case EM_QUIET:	/* be silent about it */
15549381Seric 		  case EM_MAIL:		/* mail back */
15559381Seric 		  case EM_BERKNET:	/* do berknet error processing */
15569381Seric 		  case EM_WRITE:	/* write back (or mail) */
15579381Seric 		  case EM_PRINT:	/* print errors normally (default) */
155858734Seric 			e->e_errormode = *val;
15598269Seric 			break;
15608269Seric 		}
15618269Seric 		break;
15628269Seric 
15639049Seric 	  case 'F':		/* file mode */
156417975Seric 		FileMode = atooct(val) & 0777;
15659049Seric 		break;
15669049Seric 
15678269Seric 	  case 'f':		/* save Unix-style From lines on front */
15689381Seric 		SaveFrom = atobool(val);
15698269Seric 		break;
15708269Seric 
157153735Seric 	  case 'G':		/* match recipients against GECOS field */
157253735Seric 		MatchGecos = atobool(val);
157353735Seric 		break;
157453735Seric 
15758256Seric 	  case 'g':		/* default gid */
1576*68276Seric   g_opt:
157764133Seric 		if (isascii(*val) && isdigit(*val))
157864133Seric 			DefGid = atoi(val);
157964133Seric 		else
158064133Seric 		{
158164133Seric 			register struct group *gr;
158264133Seric 
158364133Seric 			DefGid = -1;
158464133Seric 			gr = getgrnam(val);
158564133Seric 			if (gr == NULL)
1586*68276Seric 				syserr("readcf: option %c: unknown group %s",
1587*68276Seric 					opt, val);
158864133Seric 			else
158964133Seric 				DefGid = gr->gr_gid;
159064133Seric 		}
15918256Seric 		break;
15928256Seric 
15938256Seric 	  case 'H':		/* help file */
15949381Seric 		if (val[0] == '\0')
15958269Seric 			HelpFile = "sendmail.hf";
15969381Seric 		else
15979381Seric 			HelpFile = newstr(val);
15988256Seric 		break;
15998256Seric 
160051305Seric 	  case 'h':		/* maximum hop count */
160151305Seric 		MaxHopCount = atoi(val);
160251305Seric 		break;
160351305Seric 
160435651Seric 	  case 'I':		/* use internet domain name server */
160566334Seric #if NAMED_BIND
160657207Seric 		for (p = val; *p != 0; )
160757207Seric 		{
160857207Seric 			bool clearmode;
160957207Seric 			char *q;
161057207Seric 			struct resolverflags *rfp;
161157207Seric 
161257207Seric 			while (*p == ' ')
161357207Seric 				p++;
161457207Seric 			if (*p == '\0')
161557207Seric 				break;
161657207Seric 			clearmode = FALSE;
161757207Seric 			if (*p == '-')
161857207Seric 				clearmode = TRUE;
161957207Seric 			else if (*p != '+')
162057207Seric 				p--;
162157207Seric 			p++;
162257207Seric 			q = p;
162358050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
162457207Seric 				p++;
162557207Seric 			if (*p != '\0')
162657207Seric 				*p++ = '\0';
162757207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
162857207Seric 			{
162957207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
163057207Seric 					break;
163157207Seric 			}
163264923Seric 			if (rfp->rf_name == NULL)
163364923Seric 				syserr("readcf: I option value %s unrecognized", q);
163464923Seric 			else if (clearmode)
163557207Seric 				_res.options &= ~rfp->rf_bits;
163657207Seric 			else
163757207Seric 				_res.options |= rfp->rf_bits;
163857207Seric 		}
163957207Seric 		if (tTd(8, 2))
164057207Seric 			printf("_res.options = %x\n", _res.options);
164157207Seric #else
164257207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
164357207Seric #endif
164435651Seric 		break;
164535651Seric 
16468269Seric 	  case 'i':		/* ignore dot lines in message */
16479381Seric 		IgnrDot = atobool(val);
16488269Seric 		break;
16498269Seric 
165059730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
165159730Seric 		SendMIMEErrors = atobool(val);
165259730Seric 		break;
165359730Seric 
165457136Seric 	  case 'J':		/* .forward search path */
165557136Seric 		ForwardPath = newstr(val);
165657136Seric 		break;
165757136Seric 
165854967Seric 	  case 'k':		/* connection cache size */
165954967Seric 		MaxMciCache = atoi(val);
166056215Seric 		if (MaxMciCache < 0)
166156215Seric 			MaxMciCache = 0;
166254967Seric 		break;
166354967Seric 
166454967Seric 	  case 'K':		/* connection cache timeout */
166558796Seric 		MciCacheTimeout = convtime(val, 'm');
166654967Seric 		break;
166754967Seric 
166861104Seric 	  case 'l':		/* use Errors-To: header */
166961104Seric 		UseErrorsTo = atobool(val);
167061104Seric 		break;
167161104Seric 
16728256Seric 	  case 'L':		/* log level */
167364140Seric 		if (safe || LogLevel < atoi(val))
167464140Seric 			LogLevel = atoi(val);
16758256Seric 		break;
16768256Seric 
16778269Seric 	  case 'M':		/* define macro */
167868267Seric 		p = newstr(&val[1]);
167968267Seric 		if (!safe)
168068267Seric 			cleanstrcpy(p, p, MAXNAME);
168168267Seric 		define(val[0], p, CurEnv);
168216878Seric 		sticky = FALSE;
16838269Seric 		break;
16848269Seric 
16858269Seric 	  case 'm':		/* send to me too */
16869381Seric 		MeToo = atobool(val);
16878269Seric 		break;
16888269Seric 
168925820Seric 	  case 'n':		/* validate RHS in newaliases */
169025820Seric 		CheckAliases = atobool(val);
169125820Seric 		break;
169225820Seric 
169361104Seric 	    /* 'N' available -- was "net name" */
169461104Seric 
169558851Seric 	  case 'O':		/* daemon options */
169658851Seric 		setdaemonoptions(val);
169758851Seric 		break;
169858851Seric 
16998269Seric 	  case 'o':		/* assume old style headers */
17009381Seric 		if (atobool(val))
17019341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
17029341Seric 		else
17039341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
17048269Seric 		break;
17058269Seric 
170658082Seric 	  case 'p':		/* select privacy level */
170758082Seric 		p = val;
170858082Seric 		for (;;)
170958082Seric 		{
171058082Seric 			register struct prival *pv;
171158082Seric 			extern struct prival PrivacyValues[];
171258082Seric 
171358082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
171458082Seric 				p++;
171558082Seric 			if (*p == '\0')
171658082Seric 				break;
171758082Seric 			val = p;
171858082Seric 			while (isascii(*p) && isalnum(*p))
171958082Seric 				p++;
172058082Seric 			if (*p != '\0')
172158082Seric 				*p++ = '\0';
172258082Seric 
172358082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
172458082Seric 			{
172558082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
172658082Seric 					break;
172758082Seric 			}
172858886Seric 			if (pv->pv_name == NULL)
172958886Seric 				syserr("readcf: Op line: %s unrecognized", val);
173058082Seric 			PrivacyFlags |= pv->pv_flag;
173158082Seric 		}
173258082Seric 		break;
173358082Seric 
173424944Seric 	  case 'P':		/* postmaster copy address for returned mail */
173524944Seric 		PostMasterCopy = newstr(val);
173624944Seric 		break;
173724944Seric 
173824944Seric 	  case 'q':		/* slope of queue only function */
173924944Seric 		QueueFactor = atoi(val);
174024944Seric 		break;
174124944Seric 
17428256Seric 	  case 'Q':		/* queue directory */
17439381Seric 		if (val[0] == '\0')
17448269Seric 			QueueDir = "mqueue";
17459381Seric 		else
17469381Seric 			QueueDir = newstr(val);
174758789Seric 		if (RealUid != 0 && !safe)
174864718Seric 			Warn_Q_option = TRUE;
17498256Seric 		break;
17508256Seric 
175158148Seric 	  case 'R':		/* don't prune routes */
175258148Seric 		DontPruneRoutes = atobool(val);
175358148Seric 		break;
175458148Seric 
17558256Seric 	  case 'r':		/* read timeout */
1756*68276Seric 		if (subopt == NULL)
1757*68276Seric 			inittimeouts(val);
1758*68276Seric 		else
1759*68276Seric 			settimeout(subopt, val);
17608256Seric 		break;
17618256Seric 
17628256Seric 	  case 'S':		/* status file */
17639381Seric 		if (val[0] == '\0')
17648269Seric 			StatFile = "sendmail.st";
17659381Seric 		else
17669381Seric 			StatFile = newstr(val);
17678256Seric 		break;
17688256Seric 
17698265Seric 	  case 's':		/* be super safe, even if expensive */
17709381Seric 		SuperSafe = atobool(val);
17718256Seric 		break;
17728256Seric 
17738256Seric 	  case 'T':		/* queue timeout */
177458737Seric 		p = strchr(val, '/');
177558737Seric 		if (p != NULL)
177658737Seric 		{
177758737Seric 			*p++ = '\0';
1778*68276Seric 			settimeout("queuewarn", p);
177958737Seric 		}
1780*68276Seric 		settimeout("queuereturn", val);
178154967Seric 		break;
17828256Seric 
17838265Seric 	  case 't':		/* time zone name */
178452106Seric 		TimeZoneSpec = newstr(val);
17858265Seric 		break;
17868265Seric 
178750556Seric 	  case 'U':		/* location of user database */
178851360Seric 		UdbSpec = newstr(val);
178950556Seric 		break;
179050556Seric 
17918256Seric 	  case 'u':		/* set default uid */
1792*68276Seric 		for (p = val; *p != '\0'; p++)
1793*68276Seric 		{
1794*68276Seric 			if (*p == '.' || *p == '/' || *p == ':')
1795*68276Seric 			{
1796*68276Seric 				*p++ = '\0';
1797*68276Seric 				break;
1798*68276Seric 			}
1799*68276Seric 		}
180064133Seric 		if (isascii(*val) && isdigit(*val))
180164133Seric 			DefUid = atoi(val);
180264133Seric 		else
180364133Seric 		{
180464133Seric 			register struct passwd *pw;
180564133Seric 
180664133Seric 			DefUid = -1;
180764133Seric 			pw = getpwnam(val);
180864133Seric 			if (pw == NULL)
180964133Seric 				syserr("readcf: option u: unknown user %s", val);
181064133Seric 			else
1811*68276Seric 			{
181264133Seric 				DefUid = pw->pw_uid;
1813*68276Seric 				DefGid = pw->pw_gid;
1814*68276Seric 			}
181564133Seric 		}
181640973Sbostic 		setdefuser();
18178256Seric 
1818*68276Seric 		/* handle the group if it is there */
1819*68276Seric 		if (*p == '\0')
1820*68276Seric 			break;
1821*68276Seric 		val = p;
1822*68276Seric 		goto g_opt;
1823*68276Seric 
182458851Seric 	  case 'V':		/* fallback MX host */
182558851Seric 		FallBackMX = newstr(val);
182658851Seric 		break;
182758851Seric 
18288269Seric 	  case 'v':		/* run in verbose mode */
18299381Seric 		Verbose = atobool(val);
18308256Seric 		break;
18318256Seric 
183263837Seric 	  case 'w':		/* if we are best MX, try host directly */
183363837Seric 		TryNullMXList = atobool(val);
183463837Seric 		break;
183561104Seric 
183661104Seric 	    /* 'W' available -- was wizard password */
183761104Seric 
183814879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
183914879Seric 		QueueLA = atoi(val);
184014879Seric 		break;
184114879Seric 
184214879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
184314879Seric 		RefuseLA = atoi(val);
184414879Seric 		break;
184514879Seric 
184624981Seric 	  case 'y':		/* work recipient factor */
184724981Seric 		WkRecipFact = atoi(val);
184824981Seric 		break;
184924981Seric 
185024981Seric 	  case 'Y':		/* fork jobs during queue runs */
185124952Seric 		ForkQueueRuns = atobool(val);
185224952Seric 		break;
185324952Seric 
185424981Seric 	  case 'z':		/* work message class factor */
185524981Seric 		WkClassFact = atoi(val);
185624981Seric 		break;
185724981Seric 
185824981Seric 	  case 'Z':		/* work time factor */
185924981Seric 		WkTimeFact = atoi(val);
186024981Seric 		break;
186124981Seric 
1862*68276Seric 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
1863*68276Seric 		BrokenSmtpPeers = atobool(val);
1864*68276Seric 		break;
1865*68276Seric 
1866*68276Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
1867*68276Seric 		switch (*val)
1868*68276Seric 		{
1869*68276Seric 		  case 'h':	/* Host first */
1870*68276Seric 		  case 'H':
1871*68276Seric 			QueueSortOrder = QS_BYHOST;
1872*68276Seric 			break;
1873*68276Seric 
1874*68276Seric 		  case 'p':	/* Priority order */
1875*68276Seric 		  case 'P':
1876*68276Seric 			QueueSortOrder = QS_BYPRIORITY;
1877*68276Seric 			break;
1878*68276Seric 
1879*68276Seric 		  default:
1880*68276Seric 			syserr("Invalid queue sort order \"%s\"", val);
1881*68276Seric 		}
1882*68276Seric 		break;
1883*68276Seric 
1884*68276Seric 	  case O_MQA:		/* minimum queue age between deliveries */
1885*68276Seric 		MinQueueAge = convtime(val, 'm');
1886*68276Seric 		break;
1887*68276Seric 
1888*68276Seric 	  case O_MHSA:		/* maximum age of cached host status */
1889*68276Seric 		MaxHostStatAge = convtime(val, 'm');
1890*68276Seric 		break;
1891*68276Seric 
1892*68276Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
1893*68276Seric 		DefaultCharSet = newstr(denlstring(val));
1894*68276Seric 		break;
1895*68276Seric 
1896*68276Seric 	  case O_SSFILE:	/* service switch file */
1897*68276Seric 		ServiceSwitchFile = newstr(val);
1898*68276Seric 		break;
1899*68276Seric 
1900*68276Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
1901*68276Seric 		DialDelay = convtime(val, 's');
1902*68276Seric 		break;
1903*68276Seric 
19048256Seric 	  default:
1905*68276Seric 		if (tTd(37, 1))
1906*68276Seric 		{
1907*68276Seric 			if (isascii(opt) && isprint(opt))
1908*68276Seric 				printf("Warning: option %c unknown\n", opt);
1909*68276Seric 			else
1910*68276Seric 				printf("Warning: option 0x%x unknown\n", opt);
1911*68276Seric 		}
19128256Seric 		break;
19138256Seric 	}
191416878Seric 	if (sticky)
191516878Seric 		setbitn(opt, StickyOpt);
19169188Seric 	return;
19178256Seric }
191810687Seric /*
191910687Seric **  SETCLASS -- set a word into a class
192010687Seric **
192110687Seric **	Parameters:
192210687Seric **		class -- the class to put the word in.
192310687Seric **		word -- the word to enter
192410687Seric **
192510687Seric **	Returns:
192610687Seric **		none.
192710687Seric **
192810687Seric **	Side Effects:
192910687Seric **		puts the word into the symbol table.
193010687Seric */
193110687Seric 
193210687Seric setclass(class, word)
193310687Seric 	int class;
193410687Seric 	char *word;
193510687Seric {
193610687Seric 	register STAB *s;
193710687Seric 
193857943Seric 	if (tTd(37, 8))
193964326Seric 		printf("setclass(%c, %s)\n", class, word);
194010687Seric 	s = stab(word, ST_CLASS, ST_ENTER);
194110687Seric 	setbitn(class, s->s_class);
194210687Seric }
194353654Seric /*
194453654Seric **  MAKEMAPENTRY -- create a map entry
194553654Seric **
194653654Seric **	Parameters:
194753654Seric **		line -- the config file line
194853654Seric **
194953654Seric **	Returns:
195053654Seric **		TRUE if it successfully entered the map entry.
195153654Seric **		FALSE otherwise (usually syntax error).
195253654Seric **
195353654Seric **	Side Effects:
195453654Seric **		Enters the map into the dictionary.
195553654Seric */
195653654Seric 
195753654Seric void
195853654Seric makemapentry(line)
195953654Seric 	char *line;
196053654Seric {
196153654Seric 	register char *p;
196253654Seric 	char *mapname;
196353654Seric 	char *classname;
196464078Seric 	register STAB *s;
196553654Seric 	STAB *class;
196653654Seric 
196758050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
196853654Seric 		continue;
196958050Seric 	if (!(isascii(*p) && isalnum(*p)))
197053654Seric 	{
197153654Seric 		syserr("readcf: config K line: no map name");
197253654Seric 		return;
197353654Seric 	}
197453654Seric 
197553654Seric 	mapname = p;
1976*68276Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
197753654Seric 		continue;
197853654Seric 	if (*p != '\0')
197953654Seric 		*p++ = '\0';
198058050Seric 	while (isascii(*p) && isspace(*p))
198153654Seric 		p++;
198258050Seric 	if (!(isascii(*p) && isalnum(*p)))
198353654Seric 	{
198453654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
198553654Seric 		return;
198653654Seric 	}
198753654Seric 	classname = p;
198858050Seric 	while (isascii(*++p) && isalnum(*p))
198953654Seric 		continue;
199053654Seric 	if (*p != '\0')
199153654Seric 		*p++ = '\0';
199258050Seric 	while (isascii(*p) && isspace(*p))
199353654Seric 		p++;
199453654Seric 
199553654Seric 	/* look up the class */
199653654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
199753654Seric 	if (class == NULL)
199853654Seric 	{
199953654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
200053654Seric 		return;
200153654Seric 	}
200253654Seric 
200353654Seric 	/* enter the map */
200464078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
200564078Seric 	s->s_map.map_class = &class->s_mapclass;
200664078Seric 	s->s_map.map_mname = newstr(mapname);
200753654Seric 
200864078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
200964078Seric 		s->s_map.map_mflags |= MF_VALID;
201064078Seric 
201164078Seric 	if (tTd(37, 5))
201264078Seric 	{
201364078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
201464078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
201564078Seric 			s->s_map.map_mflags,
201664078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
201764078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
201864078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
201964078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
202064078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
202164078Seric 	}
202253654Seric }
202358112Seric /*
2024*68276Seric **  INITTIMEOUTS -- parse and set timeout values
202558112Seric **
202658112Seric **	Parameters:
202758112Seric **		val -- a pointer to the values.  If NULL, do initial
202858112Seric **			settings.
202958112Seric **
203058112Seric **	Returns:
203158112Seric **		none.
203258112Seric **
203358112Seric **	Side Effects:
203458112Seric **		Initializes the TimeOuts structure
203558112Seric */
203658112Seric 
203764255Seric #define SECONDS
203858112Seric #define MINUTES	* 60
203958112Seric #define HOUR	* 3600
204058112Seric 
2041*68276Seric inittimeouts(val)
204258112Seric 	register char *val;
204358112Seric {
204458112Seric 	register char *p;
204558671Seric 	extern time_t convtime();
204658112Seric 
204758112Seric 	if (val == NULL)
204858112Seric 	{
204958112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
205058112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
205158112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
205258112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
205358112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
205458112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
205558112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
205658112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
205758112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
205858112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
205958112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
2060*68276Seric #if IDENTPROTO
206164255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
2062*68276Seric #else
2063*68276Seric 		TimeOuts.to_ident = (time_t) 0 SECONDS;
2064*68276Seric #endif
2065*68276Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
206658112Seric 		return;
206758112Seric 	}
206858112Seric 
206958112Seric 	for (;; val = p)
207058112Seric 	{
207158112Seric 		while (isascii(*val) && isspace(*val))
207258112Seric 			val++;
207358112Seric 		if (*val == '\0')
207458112Seric 			break;
207558112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
207658112Seric 			continue;
207758112Seric 		if (*p != '\0')
207858112Seric 			*p++ = '\0';
207958112Seric 
208058112Seric 		if (isascii(*val) && isdigit(*val))
208158112Seric 		{
208258112Seric 			/* old syntax -- set everything */
208358796Seric 			TimeOuts.to_mail = convtime(val, 'm');
208458112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
208558112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
208658112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
208758112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
208858112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
208958112Seric 			continue;
209058112Seric 		}
209158112Seric 		else
209258112Seric 		{
2093*68276Seric 			register char *q = strchr(val, ':');
209458112Seric 
2095*68276Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
209658112Seric 			{
209758112Seric 				/* syntax error */
209858112Seric 				continue;
209958112Seric 			}
210058112Seric 			*q++ = '\0';
2101*68276Seric 			settimeout(val, q);
2102*68276Seric 		}
2103*68276Seric 	}
2104*68276Seric }
2105*68276Seric /*
2106*68276Seric **  SETTIMEOUT -- set an individual timeout
2107*68276Seric **
2108*68276Seric **	Parameters:
2109*68276Seric **		name -- the name of the timeout.
2110*68276Seric **		val -- the value of the timeout.
2111*68276Seric **
2112*68276Seric **	Returns:
2113*68276Seric **		none.
2114*68276Seric */
211558112Seric 
2116*68276Seric settimeout(name, val)
2117*68276Seric 	char *name;
2118*68276Seric 	char *val;
2119*68276Seric {
2120*68276Seric 	register char *p;
2121*68276Seric 	time_t to;
2122*68276Seric 	extern time_t convtime();
2123*68276Seric 
2124*68276Seric 	to = convtime(val, 'm');
2125*68276Seric 	p = strchr(name, '.');
2126*68276Seric 	if (p != NULL)
2127*68276Seric 		*p++ = '\0';
2128*68276Seric 
2129*68276Seric 	if (strcasecmp(name, "initial") == 0)
2130*68276Seric 		TimeOuts.to_initial = to;
2131*68276Seric 	else if (strcasecmp(name, "mail") == 0)
2132*68276Seric 		TimeOuts.to_mail = to;
2133*68276Seric 	else if (strcasecmp(name, "rcpt") == 0)
2134*68276Seric 		TimeOuts.to_rcpt = to;
2135*68276Seric 	else if (strcasecmp(name, "datainit") == 0)
2136*68276Seric 		TimeOuts.to_datainit = to;
2137*68276Seric 	else if (strcasecmp(name, "datablock") == 0)
2138*68276Seric 		TimeOuts.to_datablock = to;
2139*68276Seric 	else if (strcasecmp(name, "datafinal") == 0)
2140*68276Seric 		TimeOuts.to_datafinal = to;
2141*68276Seric 	else if (strcasecmp(name, "command") == 0)
2142*68276Seric 		TimeOuts.to_nextcommand = to;
2143*68276Seric 	else if (strcasecmp(name, "rset") == 0)
2144*68276Seric 		TimeOuts.to_rset = to;
2145*68276Seric 	else if (strcasecmp(name, "helo") == 0)
2146*68276Seric 		TimeOuts.to_helo = to;
2147*68276Seric 	else if (strcasecmp(name, "quit") == 0)
2148*68276Seric 		TimeOuts.to_quit = to;
2149*68276Seric 	else if (strcasecmp(name, "misc") == 0)
2150*68276Seric 		TimeOuts.to_miscshort = to;
2151*68276Seric 	else if (strcasecmp(name, "ident") == 0)
2152*68276Seric 		TimeOuts.to_ident = to;
2153*68276Seric 	else if (strcasecmp(name, "fileopen") == 0)
2154*68276Seric 		TimeOuts.to_fileopen = to;
2155*68276Seric 	else if (strcasecmp(name, "queuewarn") == 0)
2156*68276Seric 	{
2157*68276Seric 		to = convtime(val, 'h');
2158*68276Seric 		if (p == NULL || strcmp(p, "*") == 0)
2159*68276Seric 		{
2160*68276Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2161*68276Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2162*68276Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
216358112Seric 		}
2164*68276Seric 		else if (strcasecmp(p, "normal") == 0)
2165*68276Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2166*68276Seric 		else if (strcasecmp(p, "urgent") == 0)
2167*68276Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2168*68276Seric 		else if (strcasecmp(p, "non-urgent") == 0)
2169*68276Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2170*68276Seric 		else
2171*68276Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
217258112Seric 	}
2173*68276Seric 	else if (strcasecmp(name, "queuereturn") == 0)
2174*68276Seric 	{
2175*68276Seric 		to = convtime(val, 'd');
2176*68276Seric 		if (p == NULL || strcmp(p, "*") == 0)
2177*68276Seric 		{
2178*68276Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2179*68276Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
2180*68276Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2181*68276Seric 		}
2182*68276Seric 		else if (strcasecmp(p, "normal") == 0)
2183*68276Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2184*68276Seric 		else if (strcasecmp(p, "urgent") == 0)
2185*68276Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
2186*68276Seric 		else if (strcasecmp(p, "non-urgent") == 0)
2187*68276Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2188*68276Seric 		else
2189*68276Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
2190*68276Seric 	}
2191*68276Seric 	else
2192*68276Seric 		syserr("settimeout: invalid timeout %s", name);
219358112Seric }
2194