xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 68204)
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*68204Seric static char sccsid[] = "@(#)readcf.c	8.60 (Berkeley) 01/25/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;
8067769Seric 	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];
8767826Seric 	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 
13867769Seric 		/* 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 
17167769Seric 			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 */
18267769Seric 			*p++ = MACROEXPAND;
18367769Seric 
18467769Seric 			/* convert macro name to code */
18567769Seric 			*p = macid(p, &ep);
18667769Seric 			if (ep != p)
18767769Seric 				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
29967826Seric 			{
30056678Seric 				syserr("R line: null LHS");
30167826Seric 				rwp->r_lhs = null_list;
30267826Seric 			}
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
36367826Seric 			{
36456678Seric 				syserr("R line: null RHS");
36567826Seric 				rwp->r_rhs = null_list;
36667826Seric 			}
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 */
38867769Seric 			mid = macid(&bp[1], &ep);
38967769Seric 			p = munchstring(ep, NULL);
39067769Seric 			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 */
39967769Seric 			mid = macid(&bp[1], &ep);
40067769Seric 			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')
41467769Seric 					setclass(mid, wd);
4154061Seric 				*p = delim;
4164061Seric 			}
4174061Seric 			break;
4184061Seric 
41959272Seric 		  case 'F':		/* word class from file */
42067769Seric 			mid = macid(&bp[1], &ep);
42167769Seric 			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))
42967615Seric 					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) */
47858161Seric 			/* this option is obsolete, but will be ignored */
4798547Seric 			break;
4808547Seric 
48152645Seric 		  case 'V':		/* configuration syntax version */
48264440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
48364440Seric 				continue;
48464440Seric 			if (!isascii(*p) || !isdigit(*p))
48564440Seric 			{
48664440Seric 				syserr("invalid argument to V line: \"%.20s\"",
48764440Seric 					&bp[1]);
48864440Seric 				break;
48964440Seric 			}
49064718Seric 			ConfigLevel = strtol(p, &ep, 10);
49164279Seric 			if (ConfigLevel >= 5)
49264279Seric 			{
49364279Seric 				/* level 5 configs have short name in $w */
49464279Seric 				p = macvalue('w', e);
49564279Seric 				if (p != NULL && (p = strchr(p, '.')) != NULL)
49664279Seric 					*p = '\0';
49764279Seric 			}
49864718Seric 			if (*ep++ == '/')
49964718Seric 			{
50064718Seric 				/* extract vendor code */
50164718Seric 				for (p = ep; isascii(*p) && isalpha(*p); )
50264718Seric 					p++;
50364718Seric 				*p = '\0';
50464718Seric 
50564718Seric 				if (!setvendor(ep))
50664718Seric 					syserr("invalid V line vendor code: \"%s\"",
50764718Seric 						ep);
50864718Seric 			}
50952645Seric 			break;
51052645Seric 
51153654Seric 		  case 'K':
51257135Seric 			makemapentry(&bp[1]);
51353654Seric 			break;
51453654Seric 
5153308Seric 		  default:
5164061Seric 		  badline:
51757135Seric 			syserr("unknown control line \"%s\"", bp);
5183308Seric 		}
51957135Seric 		if (bp != buf)
52057135Seric 			free(bp);
5213308Seric 	}
52252637Seric 	if (ferror(cf))
52352637Seric 	{
52452647Seric 		syserr("I/O read error", cfname);
52552637Seric 		exit(EX_OSFILE);
52652637Seric 	}
52752637Seric 	fclose(cf);
5289381Seric 	FileName = NULL;
52956836Seric 
53067730Seric 	/* initialize host maps from local service tables */
53167730Seric 	inithostmaps();
53267905Seric 
53367905Seric 	/* determine if we need to do special name-server frotz */
53467905Seric 	{
53567905Seric 		int nmaps;
53667905Seric 		char *maptype[MAXMAPSTACK];
53767905Seric 		short mapreturn[MAXMAPACTIONS];
53867905Seric 
53967905Seric 		nmaps = switch_map_find("hosts", maptype, mapreturn);
54067905Seric 		UseNameServer = FALSE;
54167905Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
54267905Seric 		{
54367905Seric 			register int mapno;
54467905Seric 
54567905Seric 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
54667905Seric 			{
54767905Seric 				if (strcmp(maptype[mapno], "dns") == 0)
54867905Seric 					UseNameServer = TRUE;
54967905Seric 			}
55067905Seric 		}
551*68204Seric 
552*68204Seric #ifdef HESIOD
553*68204Seric 		nmaps = switch_map_find("passwd", maptype, mapreturn);
554*68204Seric 		UseHesiod = FALSE;
555*68204Seric 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
556*68204Seric 		{
557*68204Seric 			register int mapno;
558*68204Seric 
559*68204Seric 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
560*68204Seric 			{
561*68204Seric 				if (strcmp(maptype[mapno], "hesiod") == 0)
562*68204Seric 					UseHesiod = TRUE;
563*68204Seric 			}
564*68204Seric 		}
565*68204Seric #endif
56667905Seric 	}
5674096Seric }
5684096Seric /*
5698547Seric **  TOOMANY -- signal too many of some option
5708547Seric **
5718547Seric **	Parameters:
5728547Seric **		id -- the id of the error line
5738547Seric **		maxcnt -- the maximum possible values
5748547Seric **
5758547Seric **	Returns:
5768547Seric **		none.
5778547Seric **
5788547Seric **	Side Effects:
5798547Seric **		gives a syserr.
5808547Seric */
5818547Seric 
5828547Seric toomany(id, maxcnt)
5838547Seric 	char id;
5848547Seric 	int maxcnt;
5858547Seric {
5869381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
5878547Seric }
5888547Seric /*
5894432Seric **  FILECLASS -- read members of a class from a file
5904432Seric **
5914432Seric **	Parameters:
5924432Seric **		class -- class to define.
5934432Seric **		filename -- name of file to read.
5944432Seric **		fmt -- scanf string to use for match.
59564133Seric **		safe -- if set, this is a safe read.
59664133Seric **		optional -- if set, it is not an error for the file to
59764133Seric **			not exist.
5984432Seric **
5994432Seric **	Returns:
6004432Seric **		none
6014432Seric **
6024432Seric **	Side Effects:
6034432Seric **
6044432Seric **		puts all lines in filename that match a scanf into
6054432Seric **			the named class.
6064432Seric */
6074432Seric 
60864133Seric fileclass(class, filename, fmt, safe, optional)
6094432Seric 	int class;
6104432Seric 	char *filename;
6114432Seric 	char *fmt;
61254973Seric 	bool safe;
61364133Seric 	bool optional;
6144432Seric {
61525808Seric 	FILE *f;
61654973Seric 	struct stat stbuf;
6174432Seric 	char buf[MAXLINE];
6184432Seric 
61966101Seric 	if (tTd(37, 2))
62066101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
62166101Seric 
62266031Seric 	if (filename[0] == '|')
62366031Seric 	{
62466031Seric 		syserr("fileclass: pipes (F%c%s) not supported due to security problems",
62566031Seric 			class, filename);
62666031Seric 		return;
62766031Seric 	}
62854973Seric 	if (stat(filename, &stbuf) < 0)
62954973Seric 	{
63066101Seric 		if (tTd(37, 2))
63166101Seric 			printf("  cannot stat (%s)\n", errstring(errno));
63264133Seric 		if (!optional)
63364133Seric 			syserr("fileclass: cannot stat %s", filename);
63454973Seric 		return;
63554973Seric 	}
63654973Seric 	if (!S_ISREG(stbuf.st_mode))
63754973Seric 	{
63854973Seric 		syserr("fileclass: %s not a regular file", filename);
63954973Seric 		return;
64054973Seric 	}
64154973Seric 	if (!safe && access(filename, R_OK) < 0)
64254973Seric 	{
64354973Seric 		syserr("fileclass: access denied on %s", filename);
64454973Seric 		return;
64554973Seric 	}
64654973Seric 	f = fopen(filename, "r");
6474432Seric 	if (f == NULL)
6484432Seric 	{
64954973Seric 		syserr("fileclass: cannot open %s", filename);
6504432Seric 		return;
6514432Seric 	}
6524432Seric 
6534432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
6544432Seric 	{
6554432Seric 		register STAB *s;
65625808Seric 		register char *p;
65725808Seric # ifdef SCANF
6584432Seric 		char wordbuf[MAXNAME+1];
6594432Seric 
6604432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
6614432Seric 			continue;
66225808Seric 		p = wordbuf;
66356795Seric # else /* SCANF */
66425808Seric 		p = buf;
66556795Seric # endif /* SCANF */
66625808Seric 
66725808Seric 		/*
66825808Seric 		**  Break up the match into words.
66925808Seric 		*/
67025808Seric 
67125808Seric 		while (*p != '\0')
67225808Seric 		{
67325808Seric 			register char *q;
67425808Seric 
67525808Seric 			/* strip leading spaces */
67658050Seric 			while (isascii(*p) && isspace(*p))
67725808Seric 				p++;
67825808Seric 			if (*p == '\0')
67925808Seric 				break;
68025808Seric 
68125808Seric 			/* find the end of the word */
68225808Seric 			q = p;
68358050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
68425808Seric 				p++;
68525808Seric 			if (*p != '\0')
68625808Seric 				*p++ = '\0';
68725808Seric 
68825808Seric 			/* enter the word in the symbol table */
68966101Seric 			setclass(class, q);
69025808Seric 		}
6914432Seric 	}
6924432Seric 
69354973Seric 	(void) fclose(f);
6944432Seric }
6954432Seric /*
6964096Seric **  MAKEMAILER -- define a new mailer.
6974096Seric **
6984096Seric **	Parameters:
69910327Seric **		line -- description of mailer.  This is in labeled
70010327Seric **			fields.  The fields are:
70167998Seric **			   A -- the argv for this mailer
70267998Seric **			   C -- the character set for MIME conversions
70367998Seric **			   D -- the directory to run in
70467998Seric **			   E -- the eol string
70567998Seric **			   F -- the flags associated with the mailer
70667998Seric **			   L -- the maximum line length
70767998Seric **			   M -- the maximum message size
70810327Seric **			   P -- the path to the mailer
70967998Seric **			   R -- the recipient rewriting set
71010327Seric **			   S -- the sender rewriting set
71167998Seric **			   T -- the mailer type (for DSNs)
71267998Seric **			   U -- the uid to run as
71310327Seric **			The first word is the canonical name of the mailer.
7144096Seric **
7154096Seric **	Returns:
7164096Seric **		none.
7174096Seric **
7184096Seric **	Side Effects:
7194096Seric **		enters the mailer into the mailer table.
7204096Seric */
7213308Seric 
72221066Seric makemailer(line)
7234096Seric 	char *line;
7244096Seric {
7254096Seric 	register char *p;
7268067Seric 	register struct mailer *m;
7278067Seric 	register STAB *s;
7288067Seric 	int i;
72910327Seric 	char fcode;
73058020Seric 	auto char *endp;
7314096Seric 	extern int NextMailer;
73210327Seric 	extern char **makeargv();
73310327Seric 	extern char *munchstring();
73410701Seric 	extern long atol();
7354096Seric 
73610327Seric 	/* allocate a mailer and set up defaults */
73710327Seric 	m = (struct mailer *) xalloc(sizeof *m);
73810327Seric 	bzero((char *) m, sizeof *m);
73910327Seric 	m->m_eol = "\n";
74067604Seric 	m->m_uid = m->m_gid = 0;
74110327Seric 
74210327Seric 	/* collect the mailer name */
74358050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
74410327Seric 		continue;
74510327Seric 	if (*p != '\0')
74610327Seric 		*p++ = '\0';
74710327Seric 	m->m_name = newstr(line);
74810327Seric 
74910327Seric 	/* now scan through and assign info from the fields */
75010327Seric 	while (*p != '\0')
75110327Seric 	{
75258333Seric 		auto char *delimptr;
75358333Seric 
75458050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
75510327Seric 			p++;
75610327Seric 
75710327Seric 		/* p now points to field code */
75810327Seric 		fcode = *p;
75910327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
76010327Seric 			p++;
76110327Seric 		if (*p++ != '=')
76210327Seric 		{
76352637Seric 			syserr("mailer %s: `=' expected", m->m_name);
76410327Seric 			return;
76510327Seric 		}
76658050Seric 		while (isascii(*p) && isspace(*p))
76710327Seric 			p++;
76810327Seric 
76910327Seric 		/* p now points to the field body */
77058333Seric 		p = munchstring(p, &delimptr);
77110327Seric 
77210327Seric 		/* install the field into the mailer struct */
77310327Seric 		switch (fcode)
77410327Seric 		{
77510327Seric 		  case 'P':		/* pathname */
77610327Seric 			m->m_mailer = newstr(p);
77710327Seric 			break;
77810327Seric 
77910327Seric 		  case 'F':		/* flags */
78010687Seric 			for (; *p != '\0'; p++)
78158050Seric 				if (!(isascii(*p) && isspace(*p)))
78252637Seric 					setbitn(*p, m->m_flags);
78310327Seric 			break;
78410327Seric 
78510327Seric 		  case 'S':		/* sender rewriting ruleset */
78610327Seric 		  case 'R':		/* recipient rewriting ruleset */
78758020Seric 			i = strtol(p, &endp, 10);
78810327Seric 			if (i < 0 || i >= MAXRWSETS)
78910327Seric 			{
79010327Seric 				syserr("invalid rewrite set, %d max", MAXRWSETS);
79110327Seric 				return;
79210327Seric 			}
79310327Seric 			if (fcode == 'S')
79458020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
79510327Seric 			else
79658020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
79758020Seric 
79858020Seric 			p = endp;
79959985Seric 			if (*p++ == '/')
80058020Seric 			{
80158020Seric 				i = strtol(p, NULL, 10);
80258020Seric 				if (i < 0 || i >= MAXRWSETS)
80358020Seric 				{
80458020Seric 					syserr("invalid rewrite set, %d max",
80558020Seric 						MAXRWSETS);
80658020Seric 					return;
80758020Seric 				}
80858020Seric 				if (fcode == 'S')
80958020Seric 					m->m_sh_rwset = i;
81058020Seric 				else
81158020Seric 					m->m_rh_rwset = i;
81258020Seric 			}
81310327Seric 			break;
81410327Seric 
81510327Seric 		  case 'E':		/* end of line string */
81610327Seric 			m->m_eol = newstr(p);
81710327Seric 			break;
81810327Seric 
81910327Seric 		  case 'A':		/* argument vector */
82010327Seric 			m->m_argv = makeargv(p);
82110327Seric 			break;
82210701Seric 
82310701Seric 		  case 'M':		/* maximum message size */
82410701Seric 			m->m_maxsize = atol(p);
82510701Seric 			break;
82652106Seric 
82752106Seric 		  case 'L':		/* maximum line length */
82852106Seric 			m->m_linelimit = atoi(p);
82952106Seric 			break;
83058935Seric 
83158935Seric 		  case 'D':		/* working directory */
83258935Seric 			m->m_execdir = newstr(p);
83358935Seric 			break;
83467604Seric 
83567896Seric 		  case 'C':		/* default charset */
83667896Seric 			m->m_defcharset = newstr(p);
83767896Seric 			break;
83867896Seric 
83967990Seric 		  case 'T':		/* MTS Type */
84067990Seric 			m->m_mtstype = newstr(p);
84167990Seric 			break;
84267990Seric 
84367604Seric 		  case 'U':		/* user id */
84467604Seric 			if (isascii(*p) && !isdigit(*p))
84567604Seric 			{
84667604Seric 				char *q = p;
84767604Seric 				struct passwd *pw;
84867604Seric 
84967604Seric 				while (isascii(*p) && isalnum(*p))
85067604Seric 					p++;
85167604Seric 				while (isascii(*p) && isspace(*p))
85267604Seric 					*p++ = '\0';
85367604Seric 				if (*p != '\0')
85467604Seric 					*p++ = '\0';
85567604Seric 				pw = getpwnam(q);
85667604Seric 				if (pw == NULL)
85767604Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
85867604Seric 				else
85967604Seric 				{
86067604Seric 					m->m_uid = pw->pw_uid;
86167604Seric 					m->m_gid = pw->pw_gid;
86267604Seric 				}
86367604Seric 			}
86467604Seric 			else
86567604Seric 			{
86667604Seric 				auto char *q;
86767604Seric 
86867604Seric 				m->m_uid = strtol(p, &q, 0);
86967604Seric 				p = q;
87067604Seric 			}
87167604Seric 			while (isascii(*p) && isspace(*p))
87267604Seric 				p++;
87367604Seric 			if (*p == '\0')
87467604Seric 				break;
87567604Seric 			if (isascii(*p) && !isdigit(*p))
87667604Seric 			{
87767604Seric 				char *q = p;
87867604Seric 				struct group *gr;
87967604Seric 
88067604Seric 				while (isascii(*p) && isalnum(*p))
88167604Seric 					p++;
88267604Seric 				*p++ = '\0';
88367604Seric 				gr = getgrnam(q);
88467604Seric 				if (gr == NULL)
88567604Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
88667604Seric 				else
88767604Seric 					m->m_gid = gr->gr_gid;
88867604Seric 			}
88967604Seric 			else
89067604Seric 			{
89167604Seric 				m->m_gid = strtol(p, NULL, 0);
89267604Seric 			}
89367604Seric 			break;
89410327Seric 		}
89510327Seric 
89658333Seric 		p = delimptr;
89710327Seric 	}
89810327Seric 
89958321Seric 	/* do some rationality checking */
90058321Seric 	if (m->m_argv == NULL)
90158321Seric 	{
90258321Seric 		syserr("M%s: A= argument required", m->m_name);
90358321Seric 		return;
90458321Seric 	}
90558321Seric 	if (m->m_mailer == NULL)
90658321Seric 	{
90758321Seric 		syserr("M%s: P= argument required", m->m_name);
90858321Seric 		return;
90958321Seric 	}
91058321Seric 
9114096Seric 	if (NextMailer >= MAXMAILERS)
9124096Seric 	{
9139381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
9144096Seric 		return;
9154096Seric 	}
91657402Seric 
91767998Seric 	/* do some heuristic cleanup for back compatibility */
91867998Seric 	if (bitnset(M_LIMITS, m->m_flags))
91967998Seric 	{
92067998Seric 		if (m->m_linelimit == 0)
92167998Seric 			m->m_linelimit = SMTPLINELIM;
92267998Seric 		if (ConfigLevel < 2)
92367998Seric 			setbitn(M_7BITS, m->m_flags);
92467998Seric 	}
92567998Seric 
92667998Seric 	if (ConfigLevel < 6 && m->m_mtstype == NULL &&
92767998Seric 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
92868092Seric 	     strcmp(m->m_mailer, "[TCP]") == 0))
92967998Seric 		m->m_mtstype = "Internet";
93067998Seric 
93167998Seric 	/* enter the mailer into the symbol table */
93210327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
93357402Seric 	if (s->s_mailer != NULL)
93457402Seric 	{
93557402Seric 		i = s->s_mailer->m_mno;
93657402Seric 		free(s->s_mailer);
93757402Seric 	}
93857402Seric 	else
93957402Seric 	{
94057402Seric 		i = NextMailer++;
94157402Seric 	}
94257402Seric 	Mailer[i] = s->s_mailer = m;
94357454Seric 	m->m_mno = i;
94410327Seric }
94510327Seric /*
94610327Seric **  MUNCHSTRING -- translate a string into internal form.
94710327Seric **
94810327Seric **	Parameters:
94910327Seric **		p -- the string to munch.
95058333Seric **		delimptr -- if non-NULL, set to the pointer of the
95158333Seric **			field delimiter character.
95210327Seric **
95310327Seric **	Returns:
95410327Seric **		the munched string.
95510327Seric */
9564096Seric 
95710327Seric char *
95858333Seric munchstring(p, delimptr)
95910327Seric 	register char *p;
96058333Seric 	char **delimptr;
96110327Seric {
96210327Seric 	register char *q;
96310327Seric 	bool backslash = FALSE;
96410327Seric 	bool quotemode = FALSE;
96510327Seric 	static char buf[MAXLINE];
9664096Seric 
96710327Seric 	for (q = buf; *p != '\0'; p++)
9684096Seric 	{
96910327Seric 		if (backslash)
97010327Seric 		{
97110327Seric 			/* everything is roughly literal */
97210357Seric 			backslash = FALSE;
97310327Seric 			switch (*p)
97410327Seric 			{
97510327Seric 			  case 'r':		/* carriage return */
97610327Seric 				*q++ = '\r';
97710327Seric 				continue;
97810327Seric 
97910327Seric 			  case 'n':		/* newline */
98010327Seric 				*q++ = '\n';
98110327Seric 				continue;
98210327Seric 
98310327Seric 			  case 'f':		/* form feed */
98410327Seric 				*q++ = '\f';
98510327Seric 				continue;
98610327Seric 
98710327Seric 			  case 'b':		/* backspace */
98810327Seric 				*q++ = '\b';
98910327Seric 				continue;
99010327Seric 			}
99110327Seric 			*q++ = *p;
99210327Seric 		}
99310327Seric 		else
99410327Seric 		{
99510327Seric 			if (*p == '\\')
99610327Seric 				backslash = TRUE;
99710327Seric 			else if (*p == '"')
99810327Seric 				quotemode = !quotemode;
99910327Seric 			else if (quotemode || *p != ',')
100010327Seric 				*q++ = *p;
100110327Seric 			else
100210327Seric 				break;
100310327Seric 		}
10044096Seric 	}
10054096Seric 
100658333Seric 	if (delimptr != NULL)
100758333Seric 		*delimptr = p;
100810327Seric 	*q++ = '\0';
100910327Seric 	return (buf);
101010327Seric }
101110327Seric /*
101210327Seric **  MAKEARGV -- break up a string into words
101310327Seric **
101410327Seric **	Parameters:
101510327Seric **		p -- the string to break up.
101610327Seric **
101710327Seric **	Returns:
101810327Seric **		a char **argv (dynamically allocated)
101910327Seric **
102010327Seric **	Side Effects:
102110327Seric **		munges p.
102210327Seric */
10234096Seric 
102410327Seric char **
102510327Seric makeargv(p)
102610327Seric 	register char *p;
102710327Seric {
102810327Seric 	char *q;
102910327Seric 	int i;
103010327Seric 	char **avp;
103110327Seric 	char *argv[MAXPV + 1];
103210327Seric 
103310327Seric 	/* take apart the words */
103410327Seric 	i = 0;
103510327Seric 	while (*p != '\0' && i < MAXPV)
10364096Seric 	{
103710327Seric 		q = p;
103858050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
103910327Seric 			p++;
104058050Seric 		while (isascii(*p) && isspace(*p))
104110327Seric 			*p++ = '\0';
104210327Seric 		argv[i++] = newstr(q);
10434096Seric 	}
104410327Seric 	argv[i++] = NULL;
10454096Seric 
104610327Seric 	/* now make a copy of the argv */
104710327Seric 	avp = (char **) xalloc(sizeof *avp * i);
104816893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
104910327Seric 
105010327Seric 	return (avp);
10513308Seric }
10523308Seric /*
10533308Seric **  PRINTRULES -- print rewrite rules (for debugging)
10543308Seric **
10553308Seric **	Parameters:
10563308Seric **		none.
10573308Seric **
10583308Seric **	Returns:
10593308Seric **		none.
10603308Seric **
10613308Seric **	Side Effects:
10623308Seric **		prints rewrite rules.
10633308Seric */
10643308Seric 
10653308Seric printrules()
10663308Seric {
10673308Seric 	register struct rewrite *rwp;
10684072Seric 	register int ruleset;
10693308Seric 
10704072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
10713308Seric 	{
10724072Seric 		if (RewriteRules[ruleset] == NULL)
10734072Seric 			continue;
10748067Seric 		printf("\n----Rule Set %d:", ruleset);
10753308Seric 
10764072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
10773308Seric 		{
10788067Seric 			printf("\nLHS:");
10798067Seric 			printav(rwp->r_lhs);
10808067Seric 			printf("RHS:");
10818067Seric 			printav(rwp->r_rhs);
10823308Seric 		}
10833308Seric 	}
10843308Seric }
108567994Seric /*
108667994Seric **  PRINTMAILER -- print mailer structure (for debugging)
108767994Seric **
108867994Seric **	Parameters:
108967994Seric **		m -- the mailer to print
109067994Seric **
109167994Seric **	Returns:
109267994Seric **		none.
109367994Seric */
10944319Seric 
109567994Seric printmailer(m)
109667994Seric 	register MAILER *m;
109767994Seric {
109867994Seric 	int j;
109967994Seric 
110067994Seric 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
110167994Seric 		m->m_mno, m->m_name,
110267994Seric 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
110367994Seric 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
110467994Seric 		m->m_uid, m->m_gid);
110567994Seric 	for (j = '\0'; j <= '\177'; j++)
110667994Seric 		if (bitnset(j, m->m_flags))
110767994Seric 			(void) putchar(j);
110867994Seric 	printf(" L=%d E=", m->m_linelimit);
110967994Seric 	xputs(m->m_eol);
111067994Seric 	if (m->m_defcharset != NULL)
111167994Seric 		printf(" C=%s", m->m_defcharset);
111267994Seric 	if (m->m_mtstype != NULL)
111367994Seric 		printf(" T=%s", m->m_mtstype);
111467994Seric 	if (m->m_argv != NULL)
111567994Seric 	{
111667994Seric 		char **a = m->m_argv;
111767994Seric 
111867994Seric 		printf(" A=");
111967994Seric 		while (*a != NULL)
112067994Seric 		{
112167994Seric 			if (a != m->m_argv)
112267994Seric 				printf(" ");
112367994Seric 			xputs(*a++);
112467994Seric 		}
112567994Seric 	}
112667994Seric 	printf("\n");
112767994Seric }
11284096Seric /*
11298256Seric **  SETOPTION -- set global processing option
11308256Seric **
11318256Seric **	Parameters:
11328256Seric **		opt -- option name.
11338256Seric **		val -- option value (as a text string).
113421755Seric **		safe -- set if this came from a configuration file.
113521755Seric **			Some options (if set from the command line) will
113621755Seric **			reset the user id to avoid security problems.
11378269Seric **		sticky -- if set, don't let other setoptions override
11388269Seric **			this value.
113958734Seric **		e -- the main envelope.
11408256Seric **
11418256Seric **	Returns:
11428256Seric **		none.
11438256Seric **
11448256Seric **	Side Effects:
11458256Seric **		Sets options as implied by the arguments.
11468256Seric */
11478256Seric 
114810687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
11498269Seric 
115057207Seric 
115166334Seric #if NAMED_BIND
115257207Seric 
115357207Seric struct resolverflags
115457207Seric {
115557207Seric 	char	*rf_name;	/* name of the flag */
115657207Seric 	long	rf_bits;	/* bits to set/clear */
115757207Seric } ResolverFlags[] =
115857207Seric {
115957207Seric 	"debug",	RES_DEBUG,
116057207Seric 	"aaonly",	RES_AAONLY,
116157207Seric 	"usevc",	RES_USEVC,
116257207Seric 	"primary",	RES_PRIMARY,
116357207Seric 	"igntc",	RES_IGNTC,
116457207Seric 	"recurse",	RES_RECURSE,
116557207Seric 	"defnames",	RES_DEFNAMES,
116657207Seric 	"stayopen",	RES_STAYOPEN,
116757207Seric 	"dnsrch",	RES_DNSRCH,
116865583Seric 	"true",		0,		/* to avoid error on old syntax */
116957207Seric 	NULL,		0
117057207Seric };
117157207Seric 
117257207Seric #endif
117357207Seric 
117467614Seric struct optioninfo
117567614Seric {
117667614Seric 	char	*o_name;	/* long name of option */
117767787Seric 	u_char	o_code;		/* short name of option */
117867614Seric 	bool	o_safe;		/* safe for random people to use */
117967614Seric } OptionTab[] =
118067614Seric {
118167707Seric 	"SevenBitInput",	'7',		TRUE,
118267707Seric 	"EightBitMode",		'8',		TRUE,
118367707Seric 	"AliasFile",		'A',		FALSE,
118467707Seric 	"AliasWait",		'a',		FALSE,
118567707Seric 	"BlankSub",		'B',		FALSE,
118667707Seric 	"MinFreeBlocks",	'b',		TRUE,
118767707Seric 	"CheckpointInterval",	'C',		TRUE,
118867707Seric 	"HoldExpensive",	'c',		FALSE,
118967707Seric 	"AutoRebuildAliases",	'D',		FALSE,
119067707Seric 	"DeliveryMode",		'd',		TRUE,
119167707Seric 	"ErrorHeader",		'E',		FALSE,
119267707Seric 	"ErrorMode",		'e',		TRUE,
119367707Seric 	"TempFileMode",		'F',		FALSE,
119467707Seric 	"SaveFromLine",		'f',		FALSE,
119567707Seric 	"MatchGECOS",		'G',		FALSE,
119667707Seric 	"HelpFile",		'H',		FALSE,
119767707Seric 	"MaxHopCount",		'h',		FALSE,
119867707Seric 	"NameServerOptions",	'I',		FALSE,
119967707Seric 	"IgnoreDots",		'i',		TRUE,
120067707Seric 	"ForwardPath",		'J',		FALSE,
120167707Seric 	"SendMimeErrors",	'j',		TRUE,
120267707Seric 	"ConnectionCacheSize",	'k',		FALSE,
120367707Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
120467707Seric 	"UseErrorsTo",		'l',		FALSE,
120567707Seric 	"LogLevel",		'L',		FALSE,
120667707Seric 	"MeToo",		'm',		TRUE,
120767707Seric 	"CheckAliases",		'n',		FALSE,
120867707Seric 	"OldStyleHeaders",	'o',		TRUE,
120967707Seric 	"DaemonPortOptions",	'O',		FALSE,
121067707Seric 	"PrivacyOptions",	'p',		TRUE,
121167707Seric 	"PostmasterCopy",	'P',		FALSE,
121267707Seric 	"QueueFactor",		'q',		FALSE,
121367707Seric 	"QueueDirectory",	'Q',		FALSE,
121467707Seric 	"DontPruneRoutes",	'R',		FALSE,
121568109Seric 	"Timeout",		'r',		TRUE,
121667707Seric 	"StatusFile",		'S',		FALSE,
121767707Seric 	"SuperSafe",		's',		TRUE,
121867707Seric 	"QueueTimeout",		'T',		FALSE,
121967707Seric 	"TimeZoneSpec",		't',		FALSE,
122067707Seric 	"UserDatabaseSpec",	'U',		FALSE,
122167707Seric 	"DefaultUser",		'u',		FALSE,
122267707Seric 	"FallbackMXhost",	'V',		FALSE,
122367707Seric 	"Verbose",		'v',		TRUE,
122467707Seric 	"TryNullMXList",	'w',		TRUE,
122567707Seric 	"QueueLA",		'x',		FALSE,
122667707Seric 	"RefuseLA",		'X',		FALSE,
122767707Seric 	"RecipientFactor",	'y',		FALSE,
122867707Seric 	"ForkQueueRuns",	'Y',		FALSE,
122967707Seric 	"ClassFactor",		'z',		FALSE,
123067707Seric 	"TimeFactor",		'Z',		FALSE,
123167707Seric #define O_BSP		0x80
123267707Seric 	"BrokenSmtpPeers",	O_BSP,		TRUE,
123368107Seric #define O_QUEUESORTORD	0x81
123468105Seric 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
123567707Seric #define O_MQA		0x83
123667707Seric 	"MinQueueAge",		O_MQA,		TRUE,
123767707Seric #define O_MHSA		0x84
123868132Seric /*
123967707Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
124068132Seric */
124167813Seric #define O_DEFCHARSET	0x85
124267813Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
124367848Seric #define O_SSFILE	0x86
124467848Seric 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
124568034Seric #define O_DIALDELAY	0x87
124668034Seric 	"DialDelay",		O_DIALDELAY,	TRUE,
124767707Seric 
124867707Seric 	NULL,			'\0',		FALSE,
124967614Seric };
125067614Seric 
125167614Seric 
125267614Seric 
125358734Seric setoption(opt, val, safe, sticky, e)
125467614Seric 	u_char opt;
12558256Seric 	char *val;
125621755Seric 	bool safe;
12578269Seric 	bool sticky;
125858734Seric 	register ENVELOPE *e;
12598256Seric {
126057207Seric 	register char *p;
126167614Seric 	register struct optioninfo *o;
126267903Seric 	char *subopt;
12638265Seric 	extern bool atobool();
126412633Seric 	extern time_t convtime();
126514879Seric 	extern int QueueLA;
126614879Seric 	extern int RefuseLA;
126764718Seric 	extern bool Warn_Q_option;
12688256Seric 
126967736Seric 	errno = 0;
127067614Seric 	if (opt == ' ')
127167614Seric 	{
127267614Seric 		/* full word options */
127367736Seric 		struct optioninfo *sel;
127467614Seric 
127567614Seric 		p = strchr(val, '=');
127667614Seric 		if (p == NULL)
127767614Seric 			p = &val[strlen(val)];
127867614Seric 		while (*--p == ' ')
127967614Seric 			continue;
128067614Seric 		while (*++p == ' ')
128167614Seric 			*p = '\0';
128267731Seric 		if (p == val)
128367731Seric 		{
128467731Seric 			syserr("readcf: null option name");
128567731Seric 			return;
128667731Seric 		}
128767614Seric 		if (*p == '=')
128867614Seric 			*p++ = '\0';
128967614Seric 		while (*p == ' ')
129067614Seric 			p++;
129167903Seric 		subopt = strchr(val, '.');
129267903Seric 		if (subopt != NULL)
129367903Seric 			*subopt++ = '\0';
129467736Seric 		sel = NULL;
129567614Seric 		for (o = OptionTab; o->o_name != NULL; o++)
129667614Seric 		{
129767736Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
129867736Seric 				continue;
129967736Seric 			if (strlen(o->o_name) == strlen(val))
130067736Seric 			{
130167736Seric 				/* completely specified -- this must be it */
130267736Seric 				sel = NULL;
130367614Seric 				break;
130467736Seric 			}
130567736Seric 			if (sel != NULL)
130667736Seric 				break;
130767736Seric 			sel = o;
130867614Seric 		}
130967736Seric 		if (sel != NULL && o->o_name == NULL)
131067736Seric 			o = sel;
131167736Seric 		else if (o->o_name == NULL)
131267787Seric 		{
131367614Seric 			syserr("readcf: unknown option name %s", val);
131467787Seric 			return;
131567787Seric 		}
131667736Seric 		else if (sel != NULL)
131767736Seric 		{
131867736Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
131967736Seric 				val, sel->o_name, o->o_name);
132067736Seric 			return;
132167736Seric 		}
132267736Seric 		if (strlen(val) != strlen(o->o_name))
132367736Seric 		{
132467736Seric 			bool oldVerbose = Verbose;
132567736Seric 
132667736Seric 			Verbose = TRUE;
132767736Seric 			message("Option %s used as abbreviation for %s",
132867736Seric 				val, o->o_name);
132967736Seric 			Verbose = oldVerbose;
133067736Seric 		}
133167614Seric 		opt = o->o_code;
133267614Seric 		val = p;
133367614Seric 	}
133467614Seric 	else
133567614Seric 	{
133667614Seric 		for (o = OptionTab; o->o_name != NULL; o++)
133767614Seric 		{
133867614Seric 			if (o->o_code == opt)
133967614Seric 				break;
134067614Seric 		}
134167903Seric 		subopt = NULL;
134267614Seric 	}
134367614Seric 
13448256Seric 	if (tTd(37, 1))
134567731Seric 	{
134667731Seric 		printf(isascii(opt) && isprint(opt) ?
134767903Seric 			    "setoption %s (%c).%s=%s" :
134867903Seric 			    "setoption %s (0x%x).%s=%s",
134967614Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
135067903Seric 			opt,
135167903Seric 			subopt == NULL ? "" : subopt,
135267903Seric 			val);
135367731Seric 	}
13548256Seric 
13558256Seric 	/*
13568269Seric 	**  See if this option is preset for us.
13578256Seric 	*/
13588256Seric 
135959731Seric 	if (!sticky && bitnset(opt, StickyOpt))
13608269Seric 	{
13619341Seric 		if (tTd(37, 1))
13629341Seric 			printf(" (ignored)\n");
13638269Seric 		return;
13648269Seric 	}
13658269Seric 
136621755Seric 	/*
136721755Seric 	**  Check to see if this option can be specified by this user.
136821755Seric 	*/
136921755Seric 
137063787Seric 	if (!safe && RealUid == 0)
137121755Seric 		safe = TRUE;
137267614Seric 	if (!safe && !o->o_safe)
137321755Seric 	{
137439111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
137521755Seric 		{
137636582Sbostic 			if (tTd(37, 1))
137736582Sbostic 				printf(" (unsafe)");
137863787Seric 			if (RealUid != geteuid())
137936582Sbostic 			{
138051210Seric 				if (tTd(37, 1))
138151210Seric 					printf("(Resetting uid)");
138263787Seric 				(void) setgid(RealGid);
138363787Seric 				(void) setuid(RealUid);
138436582Sbostic 			}
138521755Seric 		}
138621755Seric 	}
138751210Seric 	if (tTd(37, 1))
138817985Seric 		printf("\n");
13898269Seric 
139067614Seric 	switch (opt & 0xff)
13918256Seric 	{
139259709Seric 	  case '7':		/* force seven-bit input */
139367546Seric 		SevenBitInput = atobool(val);
139452106Seric 		break;
139552106Seric 
139667546Seric 	  case '8':		/* handling of 8-bit input */
139767546Seric 		switch (*val)
139867546Seric 		{
139967547Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
140067546Seric 			MimeMode = 0;
140167546Seric 			break;
140267546Seric 
140367547Seric 		  case 'm':		/* convert 8-bit, convert MIME */
140467546Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
140567546Seric 			break;
140667546Seric 
140767547Seric 		  case 'j':		/* "just send 8" */
140867546Seric 			MimeMode = MM_PASS8BIT;
140967546Seric 			break;
141067546Seric 
141167546Seric 		  case 'p':		/* pass 8 bit, convert MIME */
141267546Seric 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
141367546Seric 			break;
141467546Seric 
141567546Seric 		  case 's':		/* strict adherence */
141667546Seric 			MimeMode = MM_CVTMIME;
141767546Seric 			break;
141867546Seric 
141967547Seric 		  case 'a':		/* encode 8 bit if available */
142067546Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
142167546Seric 			break;
142267546Seric 
142367547Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
142467547Seric 			MimeMode = MM_MIME8BIT;
142567547Seric 			break;
142667547Seric 
142767546Seric 		  default:
142867546Seric 			syserr("Unknown 8-bit mode %c", *val);
142967546Seric 			exit(EX_USAGE);
143067546Seric 		}
143167546Seric 		break;
143267546Seric 
14338256Seric 	  case 'A':		/* set default alias file */
14349381Seric 		if (val[0] == '\0')
143559672Seric 			setalias("aliases");
14369381Seric 		else
143759672Seric 			setalias(val);
14388256Seric 		break;
14398256Seric 
144017474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
144117474Seric 		if (val[0] == '\0')
144264796Seric 			SafeAlias = 5 * 60;		/* five minutes */
144317474Seric 		else
144464796Seric 			SafeAlias = convtime(val, 'm');
144517474Seric 		break;
144617474Seric 
144716843Seric 	  case 'B':		/* substitution for blank character */
144816843Seric 		SpaceSub = val[0];
144916843Seric 		if (SpaceSub == '\0')
145016843Seric 			SpaceSub = ' ';
145116843Seric 		break;
145216843Seric 
145359283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
145459283Seric 		p = strchr(val, '/');
145559283Seric 		if (p != NULL)
145659283Seric 		{
145759283Seric 			*p++ = '\0';
145859283Seric 			MaxMessageSize = atol(p);
145959283Seric 		}
146058082Seric 		MinBlocksFree = atol(val);
146158082Seric 		break;
146258082Seric 
14639284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
14649381Seric 		NoConnect = atobool(val);
14659284Seric 		break;
14669284Seric 
146751305Seric 	  case 'C':		/* checkpoint every N addresses */
146851305Seric 		CheckpointInterval = atoi(val);
146924944Seric 		break;
147024944Seric 
14719284Seric 	  case 'd':		/* delivery mode */
14729284Seric 		switch (*val)
14738269Seric 		{
14749284Seric 		  case '\0':
147558734Seric 			e->e_sendmode = SM_DELIVER;
14768269Seric 			break;
14778269Seric 
147810755Seric 		  case SM_QUEUE:	/* queue only */
147910755Seric #ifndef QUEUE
148010755Seric 			syserr("need QUEUE to set -odqueue");
148156795Seric #endif /* QUEUE */
148210755Seric 			/* fall through..... */
148310755Seric 
14849284Seric 		  case SM_DELIVER:	/* do everything */
14859284Seric 		  case SM_FORK:		/* fork after verification */
148658734Seric 			e->e_sendmode = *val;
14878269Seric 			break;
14888269Seric 
14898269Seric 		  default:
14909284Seric 			syserr("Unknown delivery mode %c", *val);
14918269Seric 			exit(EX_USAGE);
14928269Seric 		}
14938269Seric 		break;
14948269Seric 
14959146Seric 	  case 'D':		/* rebuild alias database as needed */
14969381Seric 		AutoRebuild = atobool(val);
14979146Seric 		break;
14989146Seric 
149955372Seric 	  case 'E':		/* error message header/header file */
150055379Seric 		if (*val != '\0')
150155379Seric 			ErrMsgFile = newstr(val);
150255372Seric 		break;
150355372Seric 
15048269Seric 	  case 'e':		/* set error processing mode */
15058269Seric 		switch (*val)
15068269Seric 		{
15079381Seric 		  case EM_QUIET:	/* be silent about it */
15089381Seric 		  case EM_MAIL:		/* mail back */
15099381Seric 		  case EM_BERKNET:	/* do berknet error processing */
15109381Seric 		  case EM_WRITE:	/* write back (or mail) */
15119381Seric 		  case EM_PRINT:	/* print errors normally (default) */
151258734Seric 			e->e_errormode = *val;
15138269Seric 			break;
15148269Seric 		}
15158269Seric 		break;
15168269Seric 
15179049Seric 	  case 'F':		/* file mode */
151817975Seric 		FileMode = atooct(val) & 0777;
15199049Seric 		break;
15209049Seric 
15218269Seric 	  case 'f':		/* save Unix-style From lines on front */
15229381Seric 		SaveFrom = atobool(val);
15238269Seric 		break;
15248269Seric 
152553735Seric 	  case 'G':		/* match recipients against GECOS field */
152653735Seric 		MatchGecos = atobool(val);
152753735Seric 		break;
152853735Seric 
15298256Seric 	  case 'g':		/* default gid */
153067823Seric   g_opt:
153164133Seric 		if (isascii(*val) && isdigit(*val))
153264133Seric 			DefGid = atoi(val);
153364133Seric 		else
153464133Seric 		{
153564133Seric 			register struct group *gr;
153664133Seric 
153764133Seric 			DefGid = -1;
153864133Seric 			gr = getgrnam(val);
153964133Seric 			if (gr == NULL)
154067823Seric 				syserr("readcf: option %c: unknown group %s",
154167823Seric 					opt, val);
154264133Seric 			else
154364133Seric 				DefGid = gr->gr_gid;
154464133Seric 		}
15458256Seric 		break;
15468256Seric 
15478256Seric 	  case 'H':		/* help file */
15489381Seric 		if (val[0] == '\0')
15498269Seric 			HelpFile = "sendmail.hf";
15509381Seric 		else
15519381Seric 			HelpFile = newstr(val);
15528256Seric 		break;
15538256Seric 
155451305Seric 	  case 'h':		/* maximum hop count */
155551305Seric 		MaxHopCount = atoi(val);
155651305Seric 		break;
155751305Seric 
155835651Seric 	  case 'I':		/* use internet domain name server */
155966334Seric #if NAMED_BIND
156057207Seric 		for (p = val; *p != 0; )
156157207Seric 		{
156257207Seric 			bool clearmode;
156357207Seric 			char *q;
156457207Seric 			struct resolverflags *rfp;
156557207Seric 
156657207Seric 			while (*p == ' ')
156757207Seric 				p++;
156857207Seric 			if (*p == '\0')
156957207Seric 				break;
157057207Seric 			clearmode = FALSE;
157157207Seric 			if (*p == '-')
157257207Seric 				clearmode = TRUE;
157357207Seric 			else if (*p != '+')
157457207Seric 				p--;
157557207Seric 			p++;
157657207Seric 			q = p;
157758050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
157857207Seric 				p++;
157957207Seric 			if (*p != '\0')
158057207Seric 				*p++ = '\0';
158157207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
158257207Seric 			{
158357207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
158457207Seric 					break;
158557207Seric 			}
158664923Seric 			if (rfp->rf_name == NULL)
158764923Seric 				syserr("readcf: I option value %s unrecognized", q);
158864923Seric 			else if (clearmode)
158957207Seric 				_res.options &= ~rfp->rf_bits;
159057207Seric 			else
159157207Seric 				_res.options |= rfp->rf_bits;
159257207Seric 		}
159357207Seric 		if (tTd(8, 2))
159457207Seric 			printf("_res.options = %x\n", _res.options);
159557207Seric #else
159657207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
159757207Seric #endif
159835651Seric 		break;
159935651Seric 
16008269Seric 	  case 'i':		/* ignore dot lines in message */
16019381Seric 		IgnrDot = atobool(val);
16028269Seric 		break;
16038269Seric 
160459730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
160559730Seric 		SendMIMEErrors = atobool(val);
160659730Seric 		break;
160759730Seric 
160857136Seric 	  case 'J':		/* .forward search path */
160957136Seric 		ForwardPath = newstr(val);
161057136Seric 		break;
161157136Seric 
161254967Seric 	  case 'k':		/* connection cache size */
161354967Seric 		MaxMciCache = atoi(val);
161456215Seric 		if (MaxMciCache < 0)
161556215Seric 			MaxMciCache = 0;
161654967Seric 		break;
161754967Seric 
161854967Seric 	  case 'K':		/* connection cache timeout */
161958796Seric 		MciCacheTimeout = convtime(val, 'm');
162054967Seric 		break;
162154967Seric 
162261104Seric 	  case 'l':		/* use Errors-To: header */
162361104Seric 		UseErrorsTo = atobool(val);
162461104Seric 		break;
162561104Seric 
16268256Seric 	  case 'L':		/* log level */
162764140Seric 		if (safe || LogLevel < atoi(val))
162864140Seric 			LogLevel = atoi(val);
16298256Seric 		break;
16308256Seric 
16318269Seric 	  case 'M':		/* define macro */
16329381Seric 		define(val[0], newstr(&val[1]), CurEnv);
163316878Seric 		sticky = FALSE;
16348269Seric 		break;
16358269Seric 
16368269Seric 	  case 'm':		/* send to me too */
16379381Seric 		MeToo = atobool(val);
16388269Seric 		break;
16398269Seric 
164025820Seric 	  case 'n':		/* validate RHS in newaliases */
164125820Seric 		CheckAliases = atobool(val);
164225820Seric 		break;
164325820Seric 
164461104Seric 	    /* 'N' available -- was "net name" */
164561104Seric 
164658851Seric 	  case 'O':		/* daemon options */
164758851Seric 		setdaemonoptions(val);
164858851Seric 		break;
164958851Seric 
16508269Seric 	  case 'o':		/* assume old style headers */
16519381Seric 		if (atobool(val))
16529341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
16539341Seric 		else
16549341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
16558269Seric 		break;
16568269Seric 
165758082Seric 	  case 'p':		/* select privacy level */
165858082Seric 		p = val;
165958082Seric 		for (;;)
166058082Seric 		{
166158082Seric 			register struct prival *pv;
166258082Seric 			extern struct prival PrivacyValues[];
166358082Seric 
166458082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
166558082Seric 				p++;
166658082Seric 			if (*p == '\0')
166758082Seric 				break;
166858082Seric 			val = p;
166958082Seric 			while (isascii(*p) && isalnum(*p))
167058082Seric 				p++;
167158082Seric 			if (*p != '\0')
167258082Seric 				*p++ = '\0';
167358082Seric 
167458082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
167558082Seric 			{
167658082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
167758082Seric 					break;
167858082Seric 			}
167958886Seric 			if (pv->pv_name == NULL)
168058886Seric 				syserr("readcf: Op line: %s unrecognized", val);
168158082Seric 			PrivacyFlags |= pv->pv_flag;
168258082Seric 		}
168358082Seric 		break;
168458082Seric 
168524944Seric 	  case 'P':		/* postmaster copy address for returned mail */
168624944Seric 		PostMasterCopy = newstr(val);
168724944Seric 		break;
168824944Seric 
168924944Seric 	  case 'q':		/* slope of queue only function */
169024944Seric 		QueueFactor = atoi(val);
169124944Seric 		break;
169224944Seric 
16938256Seric 	  case 'Q':		/* queue directory */
16949381Seric 		if (val[0] == '\0')
16958269Seric 			QueueDir = "mqueue";
16969381Seric 		else
16979381Seric 			QueueDir = newstr(val);
169858789Seric 		if (RealUid != 0 && !safe)
169964718Seric 			Warn_Q_option = TRUE;
17008256Seric 		break;
17018256Seric 
170258148Seric 	  case 'R':		/* don't prune routes */
170358148Seric 		DontPruneRoutes = atobool(val);
170458148Seric 		break;
170558148Seric 
17068256Seric 	  case 'r':		/* read timeout */
170767903Seric 		if (subopt == NULL)
170867903Seric 			inittimeouts(val);
170967903Seric 		else
171067903Seric 			settimeout(subopt, val);
17118256Seric 		break;
17128256Seric 
17138256Seric 	  case 'S':		/* status file */
17149381Seric 		if (val[0] == '\0')
17158269Seric 			StatFile = "sendmail.st";
17169381Seric 		else
17179381Seric 			StatFile = newstr(val);
17188256Seric 		break;
17198256Seric 
17208265Seric 	  case 's':		/* be super safe, even if expensive */
17219381Seric 		SuperSafe = atobool(val);
17228256Seric 		break;
17238256Seric 
17248256Seric 	  case 'T':		/* queue timeout */
172558737Seric 		p = strchr(val, '/');
172658737Seric 		if (p != NULL)
172758737Seric 		{
172858737Seric 			*p++ = '\0';
172967903Seric 			settimeout("queuewarn", p);
173058737Seric 		}
173167903Seric 		settimeout("queuereturn", val);
173254967Seric 		break;
17338256Seric 
17348265Seric 	  case 't':		/* time zone name */
173552106Seric 		TimeZoneSpec = newstr(val);
17368265Seric 		break;
17378265Seric 
173850556Seric 	  case 'U':		/* location of user database */
173951360Seric 		UdbSpec = newstr(val);
174050556Seric 		break;
174150556Seric 
17428256Seric 	  case 'u':		/* set default uid */
174367823Seric 		for (p = val; *p != '\0'; p++)
174467823Seric 		{
174567823Seric 			if (*p == '.' || *p == '/' || *p == ':')
174667823Seric 			{
174767823Seric 				*p++ = '\0';
174867823Seric 				break;
174967823Seric 			}
175067823Seric 		}
175164133Seric 		if (isascii(*val) && isdigit(*val))
175264133Seric 			DefUid = atoi(val);
175364133Seric 		else
175464133Seric 		{
175564133Seric 			register struct passwd *pw;
175664133Seric 
175764133Seric 			DefUid = -1;
175864133Seric 			pw = getpwnam(val);
175964133Seric 			if (pw == NULL)
176064133Seric 				syserr("readcf: option u: unknown user %s", val);
176164133Seric 			else
176267823Seric 			{
176364133Seric 				DefUid = pw->pw_uid;
176467823Seric 				DefGid = pw->pw_gid;
176567823Seric 			}
176664133Seric 		}
176740973Sbostic 		setdefuser();
17688256Seric 
176967823Seric 		/* handle the group if it is there */
177067823Seric 		if (*p == '\0')
177167823Seric 			break;
177267823Seric 		val = p;
177367823Seric 		goto g_opt;
177467823Seric 
177558851Seric 	  case 'V':		/* fallback MX host */
177658851Seric 		FallBackMX = newstr(val);
177758851Seric 		break;
177858851Seric 
17798269Seric 	  case 'v':		/* run in verbose mode */
17809381Seric 		Verbose = atobool(val);
17818256Seric 		break;
17828256Seric 
178363837Seric 	  case 'w':		/* if we are best MX, try host directly */
178463837Seric 		TryNullMXList = atobool(val);
178563837Seric 		break;
178661104Seric 
178761104Seric 	    /* 'W' available -- was wizard password */
178861104Seric 
178914879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
179014879Seric 		QueueLA = atoi(val);
179114879Seric 		break;
179214879Seric 
179314879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
179414879Seric 		RefuseLA = atoi(val);
179514879Seric 		break;
179614879Seric 
179724981Seric 	  case 'y':		/* work recipient factor */
179824981Seric 		WkRecipFact = atoi(val);
179924981Seric 		break;
180024981Seric 
180124981Seric 	  case 'Y':		/* fork jobs during queue runs */
180224952Seric 		ForkQueueRuns = atobool(val);
180324952Seric 		break;
180424952Seric 
180524981Seric 	  case 'z':		/* work message class factor */
180624981Seric 		WkClassFact = atoi(val);
180724981Seric 		break;
180824981Seric 
180924981Seric 	  case 'Z':		/* work time factor */
181024981Seric 		WkTimeFact = atoi(val);
181124981Seric 		break;
181224981Seric 
181367614Seric 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
181467614Seric 		BrokenSmtpPeers = atobool(val);
181567614Seric 		break;
181667614Seric 
181768105Seric 	  case O_QUEUESORTORD:	/* queue sorting order */
181868105Seric 		switch (*val)
181968105Seric 		{
182068105Seric 		  case 'h':	/* Host first */
182168105Seric 		  case 'H':
182268105Seric 			QueueSortOrder = QS_BYHOST;
182368105Seric 			break;
182468105Seric 
182568105Seric 		  case 'p':	/* Priority order */
182668105Seric 		  case 'P':
182768105Seric 			QueueSortOrder = QS_BYPRIORITY;
182868105Seric 			break;
182968105Seric 
183068105Seric 		  default:
183168105Seric 			syserr("Invalid queue sort order \"%s\"", val);
183268105Seric 		}
183367614Seric 		break;
183467614Seric 
183567707Seric 	  case O_MQA:		/* minimum queue age between deliveries */
183667707Seric 		MinQueueAge = convtime(val, 'm');
183767707Seric 		break;
183867707Seric 
183967707Seric 	  case O_MHSA:		/* maximum age of cached host status */
184067707Seric 		MaxHostStatAge = convtime(val, 'm');
184167707Seric 		break;
184267707Seric 
184367813Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
184467814Seric 		DefaultCharSet = newstr(val);
184567813Seric 		break;
184667813Seric 
184767848Seric 	  case O_SSFILE:	/* service switch file */
184867848Seric 		ServiceSwitchFile = newstr(val);
184967848Seric 		break;
185067848Seric 
185168034Seric 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
185268034Seric 		DialDelay = convtime(val, 's');
185368034Seric 		break;
185468034Seric 
18558256Seric 	  default:
185668132Seric 		if (tTd(37, 1))
185768132Seric 		{
185868132Seric 			if (isascii(opt) && isprint(opt))
185968132Seric 				printf("Warning: option %c unknown\n", opt);
186068132Seric 			else
186168132Seric 				printf("Warning: option 0x%x unknown\n", opt);
186268132Seric 		}
18638256Seric 		break;
18648256Seric 	}
186516878Seric 	if (sticky)
186616878Seric 		setbitn(opt, StickyOpt);
18679188Seric 	return;
18688256Seric }
186910687Seric /*
187010687Seric **  SETCLASS -- set a word into a class
187110687Seric **
187210687Seric **	Parameters:
187310687Seric **		class -- the class to put the word in.
187410687Seric **		word -- the word to enter
187510687Seric **
187610687Seric **	Returns:
187710687Seric **		none.
187810687Seric **
187910687Seric **	Side Effects:
188010687Seric **		puts the word into the symbol table.
188110687Seric */
188210687Seric 
188310687Seric setclass(class, word)
188410687Seric 	int class;
188510687Seric 	char *word;
188610687Seric {
188710687Seric 	register STAB *s;
188810687Seric 
188957943Seric 	if (tTd(37, 8))
189064326Seric 		printf("setclass(%c, %s)\n", class, word);
189110687Seric 	s = stab(word, ST_CLASS, ST_ENTER);
189210687Seric 	setbitn(class, s->s_class);
189310687Seric }
189453654Seric /*
189553654Seric **  MAKEMAPENTRY -- create a map entry
189653654Seric **
189753654Seric **	Parameters:
189853654Seric **		line -- the config file line
189953654Seric **
190053654Seric **	Returns:
190153654Seric **		TRUE if it successfully entered the map entry.
190253654Seric **		FALSE otherwise (usually syntax error).
190353654Seric **
190453654Seric **	Side Effects:
190553654Seric **		Enters the map into the dictionary.
190653654Seric */
190753654Seric 
190853654Seric void
190953654Seric makemapentry(line)
191053654Seric 	char *line;
191153654Seric {
191253654Seric 	register char *p;
191353654Seric 	char *mapname;
191453654Seric 	char *classname;
191564078Seric 	register STAB *s;
191653654Seric 	STAB *class;
191753654Seric 
191858050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
191953654Seric 		continue;
192058050Seric 	if (!(isascii(*p) && isalnum(*p)))
192153654Seric 	{
192253654Seric 		syserr("readcf: config K line: no map name");
192353654Seric 		return;
192453654Seric 	}
192553654Seric 
192653654Seric 	mapname = p;
192767848Seric 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
192853654Seric 		continue;
192953654Seric 	if (*p != '\0')
193053654Seric 		*p++ = '\0';
193158050Seric 	while (isascii(*p) && isspace(*p))
193253654Seric 		p++;
193358050Seric 	if (!(isascii(*p) && isalnum(*p)))
193453654Seric 	{
193553654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
193653654Seric 		return;
193753654Seric 	}
193853654Seric 	classname = p;
193958050Seric 	while (isascii(*++p) && isalnum(*p))
194053654Seric 		continue;
194153654Seric 	if (*p != '\0')
194253654Seric 		*p++ = '\0';
194358050Seric 	while (isascii(*p) && isspace(*p))
194453654Seric 		p++;
194553654Seric 
194653654Seric 	/* look up the class */
194753654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
194853654Seric 	if (class == NULL)
194953654Seric 	{
195053654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
195153654Seric 		return;
195253654Seric 	}
195353654Seric 
195453654Seric 	/* enter the map */
195564078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
195664078Seric 	s->s_map.map_class = &class->s_mapclass;
195764078Seric 	s->s_map.map_mname = newstr(mapname);
195853654Seric 
195964078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
196064078Seric 		s->s_map.map_mflags |= MF_VALID;
196164078Seric 
196264078Seric 	if (tTd(37, 5))
196364078Seric 	{
196464078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
196564078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
196664078Seric 			s->s_map.map_mflags,
196764078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
196864078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
196964078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
197064078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
197164078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
197264078Seric 	}
197353654Seric }
197458112Seric /*
197567903Seric **  INITTIMEOUTS -- parse and set timeout values
197658112Seric **
197758112Seric **	Parameters:
197858112Seric **		val -- a pointer to the values.  If NULL, do initial
197958112Seric **			settings.
198058112Seric **
198158112Seric **	Returns:
198258112Seric **		none.
198358112Seric **
198458112Seric **	Side Effects:
198558112Seric **		Initializes the TimeOuts structure
198658112Seric */
198758112Seric 
198864255Seric #define SECONDS
198958112Seric #define MINUTES	* 60
199058112Seric #define HOUR	* 3600
199158112Seric 
199267903Seric inittimeouts(val)
199358112Seric 	register char *val;
199458112Seric {
199558112Seric 	register char *p;
199658671Seric 	extern time_t convtime();
199758112Seric 
199858112Seric 	if (val == NULL)
199958112Seric 	{
200058112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
200158112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
200258112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
200358112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
200458112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
200558112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
200658112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
200758112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
200858112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
200958112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
201058112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
201168147Seric #if IDENTPROTO
201264255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
201368147Seric #else
201468147Seric 		TimeOuts.to_ident = (time_t) 0 SECONDS;
201568147Seric #endif
201667711Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
201758112Seric 		return;
201858112Seric 	}
201958112Seric 
202058112Seric 	for (;; val = p)
202158112Seric 	{
202258112Seric 		while (isascii(*val) && isspace(*val))
202358112Seric 			val++;
202458112Seric 		if (*val == '\0')
202558112Seric 			break;
202658112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
202758112Seric 			continue;
202858112Seric 		if (*p != '\0')
202958112Seric 			*p++ = '\0';
203058112Seric 
203158112Seric 		if (isascii(*val) && isdigit(*val))
203258112Seric 		{
203358112Seric 			/* old syntax -- set everything */
203458796Seric 			TimeOuts.to_mail = convtime(val, 'm');
203558112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
203658112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
203758112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
203858112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
203958112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
204058112Seric 			continue;
204158112Seric 		}
204258112Seric 		else
204358112Seric 		{
204467711Seric 			register char *q = strchr(val, ':');
204558112Seric 
204667711Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
204758112Seric 			{
204858112Seric 				/* syntax error */
204958112Seric 				continue;
205058112Seric 			}
205158112Seric 			*q++ = '\0';
205267903Seric 			settimeout(val, q);
205367903Seric 		}
205467903Seric 	}
205567903Seric }
205667903Seric /*
205767903Seric **  SETTIMEOUT -- set an individual timeout
205867903Seric **
205967903Seric **	Parameters:
206067903Seric **		name -- the name of the timeout.
206167903Seric **		val -- the value of the timeout.
206267903Seric **
206367903Seric **	Returns:
206467903Seric **		none.
206567903Seric */
206658112Seric 
206767903Seric settimeout(name, val)
206867903Seric 	char *name;
206967903Seric 	char *val;
207067903Seric {
207167903Seric 	register char *p;
207267903Seric 	time_t to;
207367903Seric 	extern time_t convtime();
207467903Seric 
207567903Seric 	to = convtime(val, 'm');
207667903Seric 	p = strchr(name, '.');
207767903Seric 	if (p != NULL)
207867903Seric 		*p++ = '\0';
207967903Seric 
208067903Seric 	if (strcasecmp(name, "initial") == 0)
208167903Seric 		TimeOuts.to_initial = to;
208267903Seric 	else if (strcasecmp(name, "mail") == 0)
208367903Seric 		TimeOuts.to_mail = to;
208467903Seric 	else if (strcasecmp(name, "rcpt") == 0)
208567903Seric 		TimeOuts.to_rcpt = to;
208667903Seric 	else if (strcasecmp(name, "datainit") == 0)
208767903Seric 		TimeOuts.to_datainit = to;
208867903Seric 	else if (strcasecmp(name, "datablock") == 0)
208967903Seric 		TimeOuts.to_datablock = to;
209067903Seric 	else if (strcasecmp(name, "datafinal") == 0)
209167903Seric 		TimeOuts.to_datafinal = to;
209267903Seric 	else if (strcasecmp(name, "command") == 0)
209367903Seric 		TimeOuts.to_nextcommand = to;
209467903Seric 	else if (strcasecmp(name, "rset") == 0)
209567903Seric 		TimeOuts.to_rset = to;
209667903Seric 	else if (strcasecmp(name, "helo") == 0)
209767903Seric 		TimeOuts.to_helo = to;
209867903Seric 	else if (strcasecmp(name, "quit") == 0)
209967903Seric 		TimeOuts.to_quit = to;
210067903Seric 	else if (strcasecmp(name, "misc") == 0)
210167903Seric 		TimeOuts.to_miscshort = to;
210267903Seric 	else if (strcasecmp(name, "ident") == 0)
210367903Seric 		TimeOuts.to_ident = to;
210467903Seric 	else if (strcasecmp(name, "fileopen") == 0)
210567903Seric 		TimeOuts.to_fileopen = to;
210667903Seric 	else if (strcasecmp(name, "queuewarn") == 0)
210767903Seric 	{
210867903Seric 		to = convtime(val, 'h');
210967946Seric 		if (p == NULL || strcmp(p, "*") == 0)
211067903Seric 		{
211167903Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
211267903Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
211367903Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
211458112Seric 		}
211567903Seric 		else if (strcasecmp(p, "normal") == 0)
211667903Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
211767903Seric 		else if (strcasecmp(p, "urgent") == 0)
211867903Seric 			TimeOuts.to_q_warning[TOC_URGENT] = to;
211967903Seric 		else if (strcasecmp(p, "non-urgent") == 0)
212067903Seric 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
212167903Seric 		else
212267903Seric 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
212358112Seric 	}
212467903Seric 	else if (strcasecmp(name, "queuereturn") == 0)
212567903Seric 	{
212667903Seric 		to = convtime(val, 'd');
212767903Seric 		if (p == NULL || strcmp(p, "*") == 0)
212867903Seric 		{
212967903Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
213067903Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
213167903Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
213267903Seric 		}
213367903Seric 		else if (strcasecmp(p, "normal") == 0)
213467903Seric 			TimeOuts.to_q_return[TOC_NORMAL] = to;
213567903Seric 		else if (strcasecmp(p, "urgent") == 0)
213667903Seric 			TimeOuts.to_q_return[TOC_URGENT] = to;
213767903Seric 		else if (strcasecmp(p, "non-urgent") == 0)
213867903Seric 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
213967903Seric 		else
214067903Seric 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
214167903Seric 	}
214267903Seric 	else
214367903Seric 		syserr("settimeout: invalid timeout %s", name);
214458112Seric }
2145