xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 67826)
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*67826Seric static char sccsid[] = "@(#)readcf.c	8.43 (Berkeley) 10/17/94";
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];
87*67826Seric 	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
299*67826Seric 			{
30056678Seric 				syserr("R line: null LHS");
301*67826Seric 				rwp->r_lhs = null_list;
302*67826Seric 			}
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*67826Seric 			{
36456678Seric 				syserr("R line: null RHS");
365*67826Seric 				rwp->r_rhs = null_list;
366*67826Seric 			}
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();
53267730Seric 
53357076Seric 	if (stab("host", ST_MAP, ST_FIND) == NULL)
53457076Seric 	{
53557076Seric 		/* user didn't initialize: set up host map */
53657076Seric 		strcpy(buf, "host host");
53766334Seric #if NAMED_BIND
53857076Seric 		if (ConfigLevel >= 2)
53957076Seric 			strcat(buf, " -a.");
54064947Seric #endif
54157076Seric 		makemapentry(buf);
54257076Seric 	}
5434096Seric }
5444096Seric /*
5458547Seric **  TOOMANY -- signal too many of some option
5468547Seric **
5478547Seric **	Parameters:
5488547Seric **		id -- the id of the error line
5498547Seric **		maxcnt -- the maximum possible values
5508547Seric **
5518547Seric **	Returns:
5528547Seric **		none.
5538547Seric **
5548547Seric **	Side Effects:
5558547Seric **		gives a syserr.
5568547Seric */
5578547Seric 
5588547Seric toomany(id, maxcnt)
5598547Seric 	char id;
5608547Seric 	int maxcnt;
5618547Seric {
5629381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
5638547Seric }
5648547Seric /*
5654432Seric **  FILECLASS -- read members of a class from a file
5664432Seric **
5674432Seric **	Parameters:
5684432Seric **		class -- class to define.
5694432Seric **		filename -- name of file to read.
5704432Seric **		fmt -- scanf string to use for match.
57164133Seric **		safe -- if set, this is a safe read.
57264133Seric **		optional -- if set, it is not an error for the file to
57364133Seric **			not exist.
5744432Seric **
5754432Seric **	Returns:
5764432Seric **		none
5774432Seric **
5784432Seric **	Side Effects:
5794432Seric **
5804432Seric **		puts all lines in filename that match a scanf into
5814432Seric **			the named class.
5824432Seric */
5834432Seric 
58464133Seric fileclass(class, filename, fmt, safe, optional)
5854432Seric 	int class;
5864432Seric 	char *filename;
5874432Seric 	char *fmt;
58854973Seric 	bool safe;
58964133Seric 	bool optional;
5904432Seric {
59125808Seric 	FILE *f;
59254973Seric 	struct stat stbuf;
5934432Seric 	char buf[MAXLINE];
5944432Seric 
59566101Seric 	if (tTd(37, 2))
59666101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
59766101Seric 
59866031Seric 	if (filename[0] == '|')
59966031Seric 	{
60066031Seric 		syserr("fileclass: pipes (F%c%s) not supported due to security problems",
60166031Seric 			class, filename);
60266031Seric 		return;
60366031Seric 	}
60454973Seric 	if (stat(filename, &stbuf) < 0)
60554973Seric 	{
60666101Seric 		if (tTd(37, 2))
60766101Seric 			printf("  cannot stat (%s)\n", errstring(errno));
60864133Seric 		if (!optional)
60964133Seric 			syserr("fileclass: cannot stat %s", filename);
61054973Seric 		return;
61154973Seric 	}
61254973Seric 	if (!S_ISREG(stbuf.st_mode))
61354973Seric 	{
61454973Seric 		syserr("fileclass: %s not a regular file", filename);
61554973Seric 		return;
61654973Seric 	}
61754973Seric 	if (!safe && access(filename, R_OK) < 0)
61854973Seric 	{
61954973Seric 		syserr("fileclass: access denied on %s", filename);
62054973Seric 		return;
62154973Seric 	}
62254973Seric 	f = fopen(filename, "r");
6234432Seric 	if (f == NULL)
6244432Seric 	{
62554973Seric 		syserr("fileclass: cannot open %s", filename);
6264432Seric 		return;
6274432Seric 	}
6284432Seric 
6294432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
6304432Seric 	{
6314432Seric 		register STAB *s;
63225808Seric 		register char *p;
63325808Seric # ifdef SCANF
6344432Seric 		char wordbuf[MAXNAME+1];
6354432Seric 
6364432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
6374432Seric 			continue;
63825808Seric 		p = wordbuf;
63956795Seric # else /* SCANF */
64025808Seric 		p = buf;
64156795Seric # endif /* SCANF */
64225808Seric 
64325808Seric 		/*
64425808Seric 		**  Break up the match into words.
64525808Seric 		*/
64625808Seric 
64725808Seric 		while (*p != '\0')
64825808Seric 		{
64925808Seric 			register char *q;
65025808Seric 
65125808Seric 			/* strip leading spaces */
65258050Seric 			while (isascii(*p) && isspace(*p))
65325808Seric 				p++;
65425808Seric 			if (*p == '\0')
65525808Seric 				break;
65625808Seric 
65725808Seric 			/* find the end of the word */
65825808Seric 			q = p;
65958050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
66025808Seric 				p++;
66125808Seric 			if (*p != '\0')
66225808Seric 				*p++ = '\0';
66325808Seric 
66425808Seric 			/* enter the word in the symbol table */
66566101Seric 			setclass(class, q);
66625808Seric 		}
6674432Seric 	}
6684432Seric 
66954973Seric 	(void) fclose(f);
6704432Seric }
6714432Seric /*
6724096Seric **  MAKEMAILER -- define a new mailer.
6734096Seric **
6744096Seric **	Parameters:
67510327Seric **		line -- description of mailer.  This is in labeled
67610327Seric **			fields.  The fields are:
67710327Seric **			   P -- the path to the mailer
67810327Seric **			   F -- the flags associated with the mailer
67910327Seric **			   A -- the argv for this mailer
68010327Seric **			   S -- the sender rewriting set
68110327Seric **			   R -- the recipient rewriting set
68210327Seric **			   E -- the eol string
68310327Seric **			The first word is the canonical name of the mailer.
6844096Seric **
6854096Seric **	Returns:
6864096Seric **		none.
6874096Seric **
6884096Seric **	Side Effects:
6894096Seric **		enters the mailer into the mailer table.
6904096Seric */
6913308Seric 
69221066Seric makemailer(line)
6934096Seric 	char *line;
6944096Seric {
6954096Seric 	register char *p;
6968067Seric 	register struct mailer *m;
6978067Seric 	register STAB *s;
6988067Seric 	int i;
69910327Seric 	char fcode;
70058020Seric 	auto char *endp;
7014096Seric 	extern int NextMailer;
70210327Seric 	extern char **makeargv();
70310327Seric 	extern char *munchstring();
70410701Seric 	extern long atol();
7054096Seric 
70610327Seric 	/* allocate a mailer and set up defaults */
70710327Seric 	m = (struct mailer *) xalloc(sizeof *m);
70810327Seric 	bzero((char *) m, sizeof *m);
70910327Seric 	m->m_eol = "\n";
71067604Seric 	m->m_uid = m->m_gid = 0;
71110327Seric 
71210327Seric 	/* collect the mailer name */
71358050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
71410327Seric 		continue;
71510327Seric 	if (*p != '\0')
71610327Seric 		*p++ = '\0';
71710327Seric 	m->m_name = newstr(line);
71810327Seric 
71910327Seric 	/* now scan through and assign info from the fields */
72010327Seric 	while (*p != '\0')
72110327Seric 	{
72258333Seric 		auto char *delimptr;
72358333Seric 
72458050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
72510327Seric 			p++;
72610327Seric 
72710327Seric 		/* p now points to field code */
72810327Seric 		fcode = *p;
72910327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
73010327Seric 			p++;
73110327Seric 		if (*p++ != '=')
73210327Seric 		{
73352637Seric 			syserr("mailer %s: `=' expected", m->m_name);
73410327Seric 			return;
73510327Seric 		}
73658050Seric 		while (isascii(*p) && isspace(*p))
73710327Seric 			p++;
73810327Seric 
73910327Seric 		/* p now points to the field body */
74058333Seric 		p = munchstring(p, &delimptr);
74110327Seric 
74210327Seric 		/* install the field into the mailer struct */
74310327Seric 		switch (fcode)
74410327Seric 		{
74510327Seric 		  case 'P':		/* pathname */
74610327Seric 			m->m_mailer = newstr(p);
74710327Seric 			break;
74810327Seric 
74910327Seric 		  case 'F':		/* flags */
75010687Seric 			for (; *p != '\0'; p++)
75158050Seric 				if (!(isascii(*p) && isspace(*p)))
75252637Seric 					setbitn(*p, m->m_flags);
75310327Seric 			break;
75410327Seric 
75510327Seric 		  case 'S':		/* sender rewriting ruleset */
75610327Seric 		  case 'R':		/* recipient rewriting ruleset */
75758020Seric 			i = strtol(p, &endp, 10);
75810327Seric 			if (i < 0 || i >= MAXRWSETS)
75910327Seric 			{
76010327Seric 				syserr("invalid rewrite set, %d max", MAXRWSETS);
76110327Seric 				return;
76210327Seric 			}
76310327Seric 			if (fcode == 'S')
76458020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
76510327Seric 			else
76658020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
76758020Seric 
76858020Seric 			p = endp;
76959985Seric 			if (*p++ == '/')
77058020Seric 			{
77158020Seric 				i = strtol(p, NULL, 10);
77258020Seric 				if (i < 0 || i >= MAXRWSETS)
77358020Seric 				{
77458020Seric 					syserr("invalid rewrite set, %d max",
77558020Seric 						MAXRWSETS);
77658020Seric 					return;
77758020Seric 				}
77858020Seric 				if (fcode == 'S')
77958020Seric 					m->m_sh_rwset = i;
78058020Seric 				else
78158020Seric 					m->m_rh_rwset = i;
78258020Seric 			}
78310327Seric 			break;
78410327Seric 
78510327Seric 		  case 'E':		/* end of line string */
78610327Seric 			m->m_eol = newstr(p);
78710327Seric 			break;
78810327Seric 
78910327Seric 		  case 'A':		/* argument vector */
79010327Seric 			m->m_argv = makeargv(p);
79110327Seric 			break;
79210701Seric 
79310701Seric 		  case 'M':		/* maximum message size */
79410701Seric 			m->m_maxsize = atol(p);
79510701Seric 			break;
79652106Seric 
79752106Seric 		  case 'L':		/* maximum line length */
79852106Seric 			m->m_linelimit = atoi(p);
79952106Seric 			break;
80058935Seric 
80158935Seric 		  case 'D':		/* working directory */
80258935Seric 			m->m_execdir = newstr(p);
80358935Seric 			break;
80467604Seric 
80567604Seric 		  case 'U':		/* user id */
80667604Seric 			if (isascii(*p) && !isdigit(*p))
80767604Seric 			{
80867604Seric 				char *q = p;
80967604Seric 				struct passwd *pw;
81067604Seric 
81167604Seric 				while (isascii(*p) && isalnum(*p))
81267604Seric 					p++;
81367604Seric 				while (isascii(*p) && isspace(*p))
81467604Seric 					*p++ = '\0';
81567604Seric 				if (*p != '\0')
81667604Seric 					*p++ = '\0';
81767604Seric 				pw = getpwnam(q);
81867604Seric 				if (pw == NULL)
81967604Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
82067604Seric 				else
82167604Seric 				{
82267604Seric 					m->m_uid = pw->pw_uid;
82367604Seric 					m->m_gid = pw->pw_gid;
82467604Seric 				}
82567604Seric 			}
82667604Seric 			else
82767604Seric 			{
82867604Seric 				auto char *q;
82967604Seric 
83067604Seric 				m->m_uid = strtol(p, &q, 0);
83167604Seric 				p = q;
83267604Seric 			}
83367604Seric 			while (isascii(*p) && isspace(*p))
83467604Seric 				p++;
83567604Seric 			if (*p == '\0')
83667604Seric 				break;
83767604Seric 			if (isascii(*p) && !isdigit(*p))
83867604Seric 			{
83967604Seric 				char *q = p;
84067604Seric 				struct group *gr;
84167604Seric 
84267604Seric 				while (isascii(*p) && isalnum(*p))
84367604Seric 					p++;
84467604Seric 				*p++ = '\0';
84567604Seric 				gr = getgrnam(q);
84667604Seric 				if (gr == NULL)
84767604Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
84867604Seric 				else
84967604Seric 					m->m_gid = gr->gr_gid;
85067604Seric 			}
85167604Seric 			else
85267604Seric 			{
85367604Seric 				m->m_gid = strtol(p, NULL, 0);
85467604Seric 			}
85567604Seric 			break;
85610327Seric 		}
85710327Seric 
85858333Seric 		p = delimptr;
85910327Seric 	}
86010327Seric 
86152106Seric 	/* do some heuristic cleanup for back compatibility */
86252106Seric 	if (bitnset(M_LIMITS, m->m_flags))
86352106Seric 	{
86452106Seric 		if (m->m_linelimit == 0)
86552106Seric 			m->m_linelimit = SMTPLINELIM;
86655418Seric 		if (ConfigLevel < 2)
86752106Seric 			setbitn(M_7BITS, m->m_flags);
86852106Seric 	}
86952106Seric 
87058321Seric 	/* do some rationality checking */
87158321Seric 	if (m->m_argv == NULL)
87258321Seric 	{
87358321Seric 		syserr("M%s: A= argument required", m->m_name);
87458321Seric 		return;
87558321Seric 	}
87658321Seric 	if (m->m_mailer == NULL)
87758321Seric 	{
87858321Seric 		syserr("M%s: P= argument required", m->m_name);
87958321Seric 		return;
88058321Seric 	}
88158321Seric 
8824096Seric 	if (NextMailer >= MAXMAILERS)
8834096Seric 	{
8849381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
8854096Seric 		return;
8864096Seric 	}
88757402Seric 
88810327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
88957402Seric 	if (s->s_mailer != NULL)
89057402Seric 	{
89157402Seric 		i = s->s_mailer->m_mno;
89257402Seric 		free(s->s_mailer);
89357402Seric 	}
89457402Seric 	else
89557402Seric 	{
89657402Seric 		i = NextMailer++;
89757402Seric 	}
89857402Seric 	Mailer[i] = s->s_mailer = m;
89957454Seric 	m->m_mno = i;
90010327Seric }
90110327Seric /*
90210327Seric **  MUNCHSTRING -- translate a string into internal form.
90310327Seric **
90410327Seric **	Parameters:
90510327Seric **		p -- the string to munch.
90658333Seric **		delimptr -- if non-NULL, set to the pointer of the
90758333Seric **			field delimiter character.
90810327Seric **
90910327Seric **	Returns:
91010327Seric **		the munched string.
91110327Seric */
9124096Seric 
91310327Seric char *
91458333Seric munchstring(p, delimptr)
91510327Seric 	register char *p;
91658333Seric 	char **delimptr;
91710327Seric {
91810327Seric 	register char *q;
91910327Seric 	bool backslash = FALSE;
92010327Seric 	bool quotemode = FALSE;
92110327Seric 	static char buf[MAXLINE];
9224096Seric 
92310327Seric 	for (q = buf; *p != '\0'; p++)
9244096Seric 	{
92510327Seric 		if (backslash)
92610327Seric 		{
92710327Seric 			/* everything is roughly literal */
92810357Seric 			backslash = FALSE;
92910327Seric 			switch (*p)
93010327Seric 			{
93110327Seric 			  case 'r':		/* carriage return */
93210327Seric 				*q++ = '\r';
93310327Seric 				continue;
93410327Seric 
93510327Seric 			  case 'n':		/* newline */
93610327Seric 				*q++ = '\n';
93710327Seric 				continue;
93810327Seric 
93910327Seric 			  case 'f':		/* form feed */
94010327Seric 				*q++ = '\f';
94110327Seric 				continue;
94210327Seric 
94310327Seric 			  case 'b':		/* backspace */
94410327Seric 				*q++ = '\b';
94510327Seric 				continue;
94610327Seric 			}
94710327Seric 			*q++ = *p;
94810327Seric 		}
94910327Seric 		else
95010327Seric 		{
95110327Seric 			if (*p == '\\')
95210327Seric 				backslash = TRUE;
95310327Seric 			else if (*p == '"')
95410327Seric 				quotemode = !quotemode;
95510327Seric 			else if (quotemode || *p != ',')
95610327Seric 				*q++ = *p;
95710327Seric 			else
95810327Seric 				break;
95910327Seric 		}
9604096Seric 	}
9614096Seric 
96258333Seric 	if (delimptr != NULL)
96358333Seric 		*delimptr = p;
96410327Seric 	*q++ = '\0';
96510327Seric 	return (buf);
96610327Seric }
96710327Seric /*
96810327Seric **  MAKEARGV -- break up a string into words
96910327Seric **
97010327Seric **	Parameters:
97110327Seric **		p -- the string to break up.
97210327Seric **
97310327Seric **	Returns:
97410327Seric **		a char **argv (dynamically allocated)
97510327Seric **
97610327Seric **	Side Effects:
97710327Seric **		munges p.
97810327Seric */
9794096Seric 
98010327Seric char **
98110327Seric makeargv(p)
98210327Seric 	register char *p;
98310327Seric {
98410327Seric 	char *q;
98510327Seric 	int i;
98610327Seric 	char **avp;
98710327Seric 	char *argv[MAXPV + 1];
98810327Seric 
98910327Seric 	/* take apart the words */
99010327Seric 	i = 0;
99110327Seric 	while (*p != '\0' && i < MAXPV)
9924096Seric 	{
99310327Seric 		q = p;
99458050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
99510327Seric 			p++;
99658050Seric 		while (isascii(*p) && isspace(*p))
99710327Seric 			*p++ = '\0';
99810327Seric 		argv[i++] = newstr(q);
9994096Seric 	}
100010327Seric 	argv[i++] = NULL;
10014096Seric 
100210327Seric 	/* now make a copy of the argv */
100310327Seric 	avp = (char **) xalloc(sizeof *avp * i);
100416893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
100510327Seric 
100610327Seric 	return (avp);
10073308Seric }
10083308Seric /*
10093308Seric **  PRINTRULES -- print rewrite rules (for debugging)
10103308Seric **
10113308Seric **	Parameters:
10123308Seric **		none.
10133308Seric **
10143308Seric **	Returns:
10153308Seric **		none.
10163308Seric **
10173308Seric **	Side Effects:
10183308Seric **		prints rewrite rules.
10193308Seric */
10203308Seric 
10213308Seric printrules()
10223308Seric {
10233308Seric 	register struct rewrite *rwp;
10244072Seric 	register int ruleset;
10253308Seric 
10264072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
10273308Seric 	{
10284072Seric 		if (RewriteRules[ruleset] == NULL)
10294072Seric 			continue;
10308067Seric 		printf("\n----Rule Set %d:", ruleset);
10313308Seric 
10324072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
10333308Seric 		{
10348067Seric 			printf("\nLHS:");
10358067Seric 			printav(rwp->r_lhs);
10368067Seric 			printf("RHS:");
10378067Seric 			printav(rwp->r_rhs);
10383308Seric 		}
10393308Seric 	}
10403308Seric }
10414319Seric 
10424096Seric /*
10438256Seric **  SETOPTION -- set global processing option
10448256Seric **
10458256Seric **	Parameters:
10468256Seric **		opt -- option name.
10478256Seric **		val -- option value (as a text string).
104821755Seric **		safe -- set if this came from a configuration file.
104921755Seric **			Some options (if set from the command line) will
105021755Seric **			reset the user id to avoid security problems.
10518269Seric **		sticky -- if set, don't let other setoptions override
10528269Seric **			this value.
105358734Seric **		e -- the main envelope.
10548256Seric **
10558256Seric **	Returns:
10568256Seric **		none.
10578256Seric **
10588256Seric **	Side Effects:
10598256Seric **		Sets options as implied by the arguments.
10608256Seric */
10618256Seric 
106210687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
10638269Seric 
106457207Seric 
106566334Seric #if NAMED_BIND
106657207Seric 
106757207Seric struct resolverflags
106857207Seric {
106957207Seric 	char	*rf_name;	/* name of the flag */
107057207Seric 	long	rf_bits;	/* bits to set/clear */
107157207Seric } ResolverFlags[] =
107257207Seric {
107357207Seric 	"debug",	RES_DEBUG,
107457207Seric 	"aaonly",	RES_AAONLY,
107557207Seric 	"usevc",	RES_USEVC,
107657207Seric 	"primary",	RES_PRIMARY,
107757207Seric 	"igntc",	RES_IGNTC,
107857207Seric 	"recurse",	RES_RECURSE,
107957207Seric 	"defnames",	RES_DEFNAMES,
108057207Seric 	"stayopen",	RES_STAYOPEN,
108157207Seric 	"dnsrch",	RES_DNSRCH,
108265583Seric 	"true",		0,		/* to avoid error on old syntax */
108357207Seric 	NULL,		0
108457207Seric };
108557207Seric 
108657207Seric #endif
108757207Seric 
108867614Seric struct optioninfo
108967614Seric {
109067614Seric 	char	*o_name;	/* long name of option */
109167787Seric 	u_char	o_code;		/* short name of option */
109267614Seric 	bool	o_safe;		/* safe for random people to use */
109367614Seric } OptionTab[] =
109467614Seric {
109567707Seric 	"SevenBitInput",	'7',		TRUE,
109667707Seric 	"EightBitMode",		'8',		TRUE,
109767707Seric 	"AliasFile",		'A',		FALSE,
109867707Seric 	"AliasWait",		'a',		FALSE,
109967707Seric 	"BlankSub",		'B',		FALSE,
110067707Seric 	"MinFreeBlocks",	'b',		TRUE,
110167707Seric 	"CheckpointInterval",	'C',		TRUE,
110267707Seric 	"HoldExpensive",	'c',		FALSE,
110367707Seric 	"AutoRebuildAliases",	'D',		FALSE,
110467707Seric 	"DeliveryMode",		'd',		TRUE,
110567707Seric 	"ErrorHeader",		'E',		FALSE,
110667707Seric 	"ErrorMode",		'e',		TRUE,
110767707Seric 	"TempFileMode",		'F',		FALSE,
110867707Seric 	"SaveFromLine",		'f',		FALSE,
110967707Seric 	"MatchGECOS",		'G',		FALSE,
111067707Seric 	"HelpFile",		'H',		FALSE,
111167707Seric 	"MaxHopCount",		'h',		FALSE,
111267707Seric 	"NameServerOptions",	'I',		FALSE,
111367707Seric 	"IgnoreDots",		'i',		TRUE,
111467707Seric 	"ForwardPath",		'J',		FALSE,
111567707Seric 	"SendMimeErrors",	'j',		TRUE,
111667707Seric 	"ConnectionCacheSize",	'k',		FALSE,
111767707Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
111867707Seric 	"UseErrorsTo",		'l',		FALSE,
111967707Seric 	"LogLevel",		'L',		FALSE,
112067707Seric 	"MeToo",		'm',		TRUE,
112167707Seric 	"CheckAliases",		'n',		FALSE,
112267707Seric 	"OldStyleHeaders",	'o',		TRUE,
112367707Seric 	"DaemonPortOptions",	'O',		FALSE,
112467707Seric 	"PrivacyOptions",	'p',		TRUE,
112567707Seric 	"PostmasterCopy",	'P',		FALSE,
112667707Seric 	"QueueFactor",		'q',		FALSE,
112767707Seric 	"QueueDirectory",	'Q',		FALSE,
112867707Seric 	"DontPruneRoutes",	'R',		FALSE,
112967711Seric 	"Timeouts",		'r',		TRUE,
113067707Seric 	"StatusFile",		'S',		FALSE,
113167707Seric 	"SuperSafe",		's',		TRUE,
113267707Seric 	"QueueTimeout",		'T',		FALSE,
113367707Seric 	"TimeZoneSpec",		't',		FALSE,
113467707Seric 	"UserDatabaseSpec",	'U',		FALSE,
113567707Seric 	"DefaultUser",		'u',		FALSE,
113667707Seric 	"FallbackMXhost",	'V',		FALSE,
113767707Seric 	"Verbose",		'v',		TRUE,
113867707Seric 	"TryNullMXList",	'w',		TRUE,
113967707Seric 	"QueueLA",		'x',		FALSE,
114067707Seric 	"RefuseLA",		'X',		FALSE,
114167707Seric 	"RecipientFactor",	'y',		FALSE,
114267707Seric 	"ForkQueueRuns",	'Y',		FALSE,
114367707Seric 	"ClassFactor",		'z',		FALSE,
114467707Seric 	"TimeFactor",		'Z',		FALSE,
114567707Seric #define O_BSP		0x80
114667707Seric 	"BrokenSmtpPeers",	O_BSP,		TRUE,
114767707Seric #define O_SQBH		0x81
114867707Seric 	"SortQueueByHost",	O_SQBH,		TRUE,
114967707Seric #define O_DNICE		0x82
115067707Seric 	"DeliveryNiceness",	O_DNICE,	TRUE,
115167707Seric #define O_MQA		0x83
115267707Seric 	"MinQueueAge",		O_MQA,		TRUE,
115367707Seric #define O_MHSA		0x84
115467707Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
115567813Seric #define O_DEFCHARSET	0x85
115667813Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
115767707Seric 
115867707Seric 	NULL,			'\0',		FALSE,
115967614Seric };
116067614Seric 
116167614Seric 
116267614Seric 
116358734Seric setoption(opt, val, safe, sticky, e)
116467614Seric 	u_char opt;
11658256Seric 	char *val;
116621755Seric 	bool safe;
11678269Seric 	bool sticky;
116858734Seric 	register ENVELOPE *e;
11698256Seric {
117057207Seric 	register char *p;
117167614Seric 	register struct optioninfo *o;
11728265Seric 	extern bool atobool();
117312633Seric 	extern time_t convtime();
117414879Seric 	extern int QueueLA;
117514879Seric 	extern int RefuseLA;
117664718Seric 	extern bool Warn_Q_option;
11778256Seric 
117867736Seric 	errno = 0;
117967614Seric 	if (opt == ' ')
118067614Seric 	{
118167614Seric 		/* full word options */
118267736Seric 		struct optioninfo *sel;
118367614Seric 
118467614Seric 		p = strchr(val, '=');
118567614Seric 		if (p == NULL)
118667614Seric 			p = &val[strlen(val)];
118767614Seric 		while (*--p == ' ')
118867614Seric 			continue;
118967614Seric 		while (*++p == ' ')
119067614Seric 			*p = '\0';
119167731Seric 		if (p == val)
119267731Seric 		{
119367731Seric 			syserr("readcf: null option name");
119467731Seric 			return;
119567731Seric 		}
119667614Seric 		if (*p == '=')
119767614Seric 			*p++ = '\0';
119867614Seric 		while (*p == ' ')
119967614Seric 			p++;
120067736Seric 		sel = NULL;
120167614Seric 		for (o = OptionTab; o->o_name != NULL; o++)
120267614Seric 		{
120367736Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
120467736Seric 				continue;
120567736Seric 			if (strlen(o->o_name) == strlen(val))
120667736Seric 			{
120767736Seric 				/* completely specified -- this must be it */
120867736Seric 				sel = NULL;
120967614Seric 				break;
121067736Seric 			}
121167736Seric 			if (sel != NULL)
121267736Seric 				break;
121367736Seric 			sel = o;
121467614Seric 		}
121567736Seric 		if (sel != NULL && o->o_name == NULL)
121667736Seric 			o = sel;
121767736Seric 		else if (o->o_name == NULL)
121867787Seric 		{
121967614Seric 			syserr("readcf: unknown option name %s", val);
122067787Seric 			return;
122167787Seric 		}
122267736Seric 		else if (sel != NULL)
122367736Seric 		{
122467736Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
122567736Seric 				val, sel->o_name, o->o_name);
122667736Seric 			return;
122767736Seric 		}
122867736Seric 		if (strlen(val) != strlen(o->o_name))
122967736Seric 		{
123067736Seric 			bool oldVerbose = Verbose;
123167736Seric 
123267736Seric 			Verbose = TRUE;
123367736Seric 			message("Option %s used as abbreviation for %s",
123467736Seric 				val, o->o_name);
123567736Seric 			Verbose = oldVerbose;
123667736Seric 		}
123767614Seric 		opt = o->o_code;
123867614Seric 		val = p;
123967614Seric 	}
124067614Seric 	else
124167614Seric 	{
124267614Seric 		for (o = OptionTab; o->o_name != NULL; o++)
124367614Seric 		{
124467614Seric 			if (o->o_code == opt)
124567614Seric 				break;
124667614Seric 		}
124767614Seric 	}
124867614Seric 
12498256Seric 	if (tTd(37, 1))
125067731Seric 	{
125167731Seric 		printf(isascii(opt) && isprint(opt) ?
125267731Seric 			    "setoption %s (%c)=%s" : "setoption %s (0x%x)=%s",
125367614Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
125467614Seric 			opt, val);
125567731Seric 	}
12568256Seric 
12578256Seric 	/*
12588269Seric 	**  See if this option is preset for us.
12598256Seric 	*/
12608256Seric 
126159731Seric 	if (!sticky && bitnset(opt, StickyOpt))
12628269Seric 	{
12639341Seric 		if (tTd(37, 1))
12649341Seric 			printf(" (ignored)\n");
12658269Seric 		return;
12668269Seric 	}
12678269Seric 
126821755Seric 	/*
126921755Seric 	**  Check to see if this option can be specified by this user.
127021755Seric 	*/
127121755Seric 
127263787Seric 	if (!safe && RealUid == 0)
127321755Seric 		safe = TRUE;
127467614Seric 	if (!safe && !o->o_safe)
127521755Seric 	{
127639111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
127721755Seric 		{
127836582Sbostic 			if (tTd(37, 1))
127936582Sbostic 				printf(" (unsafe)");
128063787Seric 			if (RealUid != geteuid())
128136582Sbostic 			{
128251210Seric 				if (tTd(37, 1))
128351210Seric 					printf("(Resetting uid)");
128463787Seric 				(void) setgid(RealGid);
128563787Seric 				(void) setuid(RealUid);
128636582Sbostic 			}
128721755Seric 		}
128821755Seric 	}
128951210Seric 	if (tTd(37, 1))
129017985Seric 		printf("\n");
12918269Seric 
129267614Seric 	switch (opt & 0xff)
12938256Seric 	{
129459709Seric 	  case '7':		/* force seven-bit input */
129567546Seric 		SevenBitInput = atobool(val);
129652106Seric 		break;
129752106Seric 
129867546Seric 	  case '8':		/* handling of 8-bit input */
129967546Seric 		switch (*val)
130067546Seric 		{
130167547Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
130267546Seric 			MimeMode = 0;
130367546Seric 			break;
130467546Seric 
130567547Seric 		  case 'm':		/* convert 8-bit, convert MIME */
130667546Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
130767546Seric 			break;
130867546Seric 
130967547Seric 		  case 'j':		/* "just send 8" */
131067546Seric 			MimeMode = MM_PASS8BIT;
131167546Seric 			break;
131267546Seric 
131367546Seric 		  case 'p':		/* pass 8 bit, convert MIME */
131467546Seric 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
131567546Seric 			break;
131667546Seric 
131767546Seric 		  case 's':		/* strict adherence */
131867546Seric 			MimeMode = MM_CVTMIME;
131967546Seric 			break;
132067546Seric 
132167547Seric 		  case 'a':		/* encode 8 bit if available */
132267546Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
132367546Seric 			break;
132467546Seric 
132567547Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
132667547Seric 			MimeMode = MM_MIME8BIT;
132767547Seric 			break;
132867547Seric 
132967546Seric 		  default:
133067546Seric 			syserr("Unknown 8-bit mode %c", *val);
133167546Seric 			exit(EX_USAGE);
133267546Seric 		}
133367546Seric 		break;
133467546Seric 
13358256Seric 	  case 'A':		/* set default alias file */
13369381Seric 		if (val[0] == '\0')
133759672Seric 			setalias("aliases");
13389381Seric 		else
133959672Seric 			setalias(val);
13408256Seric 		break;
13418256Seric 
134217474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
134317474Seric 		if (val[0] == '\0')
134464796Seric 			SafeAlias = 5 * 60;		/* five minutes */
134517474Seric 		else
134664796Seric 			SafeAlias = convtime(val, 'm');
134717474Seric 		break;
134817474Seric 
134916843Seric 	  case 'B':		/* substitution for blank character */
135016843Seric 		SpaceSub = val[0];
135116843Seric 		if (SpaceSub == '\0')
135216843Seric 			SpaceSub = ' ';
135316843Seric 		break;
135416843Seric 
135559283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
135659283Seric 		p = strchr(val, '/');
135759283Seric 		if (p != NULL)
135859283Seric 		{
135959283Seric 			*p++ = '\0';
136059283Seric 			MaxMessageSize = atol(p);
136159283Seric 		}
136258082Seric 		MinBlocksFree = atol(val);
136358082Seric 		break;
136458082Seric 
13659284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
13669381Seric 		NoConnect = atobool(val);
13679284Seric 		break;
13689284Seric 
136951305Seric 	  case 'C':		/* checkpoint every N addresses */
137051305Seric 		CheckpointInterval = atoi(val);
137124944Seric 		break;
137224944Seric 
13739284Seric 	  case 'd':		/* delivery mode */
13749284Seric 		switch (*val)
13758269Seric 		{
13769284Seric 		  case '\0':
137758734Seric 			e->e_sendmode = SM_DELIVER;
13788269Seric 			break;
13798269Seric 
138010755Seric 		  case SM_QUEUE:	/* queue only */
138110755Seric #ifndef QUEUE
138210755Seric 			syserr("need QUEUE to set -odqueue");
138356795Seric #endif /* QUEUE */
138410755Seric 			/* fall through..... */
138510755Seric 
13869284Seric 		  case SM_DELIVER:	/* do everything */
13879284Seric 		  case SM_FORK:		/* fork after verification */
138858734Seric 			e->e_sendmode = *val;
13898269Seric 			break;
13908269Seric 
13918269Seric 		  default:
13929284Seric 			syserr("Unknown delivery mode %c", *val);
13938269Seric 			exit(EX_USAGE);
13948269Seric 		}
13958269Seric 		break;
13968269Seric 
13979146Seric 	  case 'D':		/* rebuild alias database as needed */
13989381Seric 		AutoRebuild = atobool(val);
13999146Seric 		break;
14009146Seric 
140155372Seric 	  case 'E':		/* error message header/header file */
140255379Seric 		if (*val != '\0')
140355379Seric 			ErrMsgFile = newstr(val);
140455372Seric 		break;
140555372Seric 
14068269Seric 	  case 'e':		/* set error processing mode */
14078269Seric 		switch (*val)
14088269Seric 		{
14099381Seric 		  case EM_QUIET:	/* be silent about it */
14109381Seric 		  case EM_MAIL:		/* mail back */
14119381Seric 		  case EM_BERKNET:	/* do berknet error processing */
14129381Seric 		  case EM_WRITE:	/* write back (or mail) */
14139381Seric 		  case EM_PRINT:	/* print errors normally (default) */
141458734Seric 			e->e_errormode = *val;
14158269Seric 			break;
14168269Seric 		}
14178269Seric 		break;
14188269Seric 
14199049Seric 	  case 'F':		/* file mode */
142017975Seric 		FileMode = atooct(val) & 0777;
14219049Seric 		break;
14229049Seric 
14238269Seric 	  case 'f':		/* save Unix-style From lines on front */
14249381Seric 		SaveFrom = atobool(val);
14258269Seric 		break;
14268269Seric 
142753735Seric 	  case 'G':		/* match recipients against GECOS field */
142853735Seric 		MatchGecos = atobool(val);
142953735Seric 		break;
143053735Seric 
14318256Seric 	  case 'g':		/* default gid */
143267823Seric   g_opt:
143364133Seric 		if (isascii(*val) && isdigit(*val))
143464133Seric 			DefGid = atoi(val);
143564133Seric 		else
143664133Seric 		{
143764133Seric 			register struct group *gr;
143864133Seric 
143964133Seric 			DefGid = -1;
144064133Seric 			gr = getgrnam(val);
144164133Seric 			if (gr == NULL)
144267823Seric 				syserr("readcf: option %c: unknown group %s",
144367823Seric 					opt, val);
144464133Seric 			else
144564133Seric 				DefGid = gr->gr_gid;
144664133Seric 		}
14478256Seric 		break;
14488256Seric 
14498256Seric 	  case 'H':		/* help file */
14509381Seric 		if (val[0] == '\0')
14518269Seric 			HelpFile = "sendmail.hf";
14529381Seric 		else
14539381Seric 			HelpFile = newstr(val);
14548256Seric 		break;
14558256Seric 
145651305Seric 	  case 'h':		/* maximum hop count */
145751305Seric 		MaxHopCount = atoi(val);
145851305Seric 		break;
145951305Seric 
146035651Seric 	  case 'I':		/* use internet domain name server */
146166334Seric #if NAMED_BIND
146257207Seric 		UseNameServer = TRUE;
146357207Seric 		for (p = val; *p != 0; )
146457207Seric 		{
146557207Seric 			bool clearmode;
146657207Seric 			char *q;
146757207Seric 			struct resolverflags *rfp;
146857207Seric 
146957207Seric 			while (*p == ' ')
147057207Seric 				p++;
147157207Seric 			if (*p == '\0')
147257207Seric 				break;
147357207Seric 			clearmode = FALSE;
147457207Seric 			if (*p == '-')
147557207Seric 				clearmode = TRUE;
147657207Seric 			else if (*p != '+')
147757207Seric 				p--;
147857207Seric 			p++;
147957207Seric 			q = p;
148058050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
148157207Seric 				p++;
148257207Seric 			if (*p != '\0')
148357207Seric 				*p++ = '\0';
148457207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
148557207Seric 			{
148657207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
148757207Seric 					break;
148857207Seric 			}
148964923Seric 			if (rfp->rf_name == NULL)
149064923Seric 				syserr("readcf: I option value %s unrecognized", q);
149164923Seric 			else if (clearmode)
149257207Seric 				_res.options &= ~rfp->rf_bits;
149357207Seric 			else
149457207Seric 				_res.options |= rfp->rf_bits;
149557207Seric 		}
149657207Seric 		if (tTd(8, 2))
149757207Seric 			printf("_res.options = %x\n", _res.options);
149857207Seric #else
149957207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
150057207Seric #endif
150135651Seric 		break;
150235651Seric 
15038269Seric 	  case 'i':		/* ignore dot lines in message */
15049381Seric 		IgnrDot = atobool(val);
15058269Seric 		break;
15068269Seric 
150759730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
150859730Seric 		SendMIMEErrors = atobool(val);
150959730Seric 		break;
151059730Seric 
151157136Seric 	  case 'J':		/* .forward search path */
151257136Seric 		ForwardPath = newstr(val);
151357136Seric 		break;
151457136Seric 
151554967Seric 	  case 'k':		/* connection cache size */
151654967Seric 		MaxMciCache = atoi(val);
151756215Seric 		if (MaxMciCache < 0)
151856215Seric 			MaxMciCache = 0;
151954967Seric 		break;
152054967Seric 
152154967Seric 	  case 'K':		/* connection cache timeout */
152258796Seric 		MciCacheTimeout = convtime(val, 'm');
152354967Seric 		break;
152454967Seric 
152561104Seric 	  case 'l':		/* use Errors-To: header */
152661104Seric 		UseErrorsTo = atobool(val);
152761104Seric 		break;
152861104Seric 
15298256Seric 	  case 'L':		/* log level */
153064140Seric 		if (safe || LogLevel < atoi(val))
153164140Seric 			LogLevel = atoi(val);
15328256Seric 		break;
15338256Seric 
15348269Seric 	  case 'M':		/* define macro */
15359381Seric 		define(val[0], newstr(&val[1]), CurEnv);
153616878Seric 		sticky = FALSE;
15378269Seric 		break;
15388269Seric 
15398269Seric 	  case 'm':		/* send to me too */
15409381Seric 		MeToo = atobool(val);
15418269Seric 		break;
15428269Seric 
154325820Seric 	  case 'n':		/* validate RHS in newaliases */
154425820Seric 		CheckAliases = atobool(val);
154525820Seric 		break;
154625820Seric 
154761104Seric 	    /* 'N' available -- was "net name" */
154861104Seric 
154958851Seric 	  case 'O':		/* daemon options */
155058851Seric 		setdaemonoptions(val);
155158851Seric 		break;
155258851Seric 
15538269Seric 	  case 'o':		/* assume old style headers */
15549381Seric 		if (atobool(val))
15559341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
15569341Seric 		else
15579341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
15588269Seric 		break;
15598269Seric 
156058082Seric 	  case 'p':		/* select privacy level */
156158082Seric 		p = val;
156258082Seric 		for (;;)
156358082Seric 		{
156458082Seric 			register struct prival *pv;
156558082Seric 			extern struct prival PrivacyValues[];
156658082Seric 
156758082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
156858082Seric 				p++;
156958082Seric 			if (*p == '\0')
157058082Seric 				break;
157158082Seric 			val = p;
157258082Seric 			while (isascii(*p) && isalnum(*p))
157358082Seric 				p++;
157458082Seric 			if (*p != '\0')
157558082Seric 				*p++ = '\0';
157658082Seric 
157758082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
157858082Seric 			{
157958082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
158058082Seric 					break;
158158082Seric 			}
158258886Seric 			if (pv->pv_name == NULL)
158358886Seric 				syserr("readcf: Op line: %s unrecognized", val);
158458082Seric 			PrivacyFlags |= pv->pv_flag;
158558082Seric 		}
158658082Seric 		break;
158758082Seric 
158824944Seric 	  case 'P':		/* postmaster copy address for returned mail */
158924944Seric 		PostMasterCopy = newstr(val);
159024944Seric 		break;
159124944Seric 
159224944Seric 	  case 'q':		/* slope of queue only function */
159324944Seric 		QueueFactor = atoi(val);
159424944Seric 		break;
159524944Seric 
15968256Seric 	  case 'Q':		/* queue directory */
15979381Seric 		if (val[0] == '\0')
15988269Seric 			QueueDir = "mqueue";
15999381Seric 		else
16009381Seric 			QueueDir = newstr(val);
160158789Seric 		if (RealUid != 0 && !safe)
160264718Seric 			Warn_Q_option = TRUE;
16038256Seric 		break;
16048256Seric 
160558148Seric 	  case 'R':		/* don't prune routes */
160658148Seric 		DontPruneRoutes = atobool(val);
160758148Seric 		break;
160858148Seric 
16098256Seric 	  case 'r':		/* read timeout */
161058112Seric 		settimeouts(val);
16118256Seric 		break;
16128256Seric 
16138256Seric 	  case 'S':		/* status file */
16149381Seric 		if (val[0] == '\0')
16158269Seric 			StatFile = "sendmail.st";
16169381Seric 		else
16179381Seric 			StatFile = newstr(val);
16188256Seric 		break;
16198256Seric 
16208265Seric 	  case 's':		/* be super safe, even if expensive */
16219381Seric 		SuperSafe = atobool(val);
16228256Seric 		break;
16238256Seric 
16248256Seric 	  case 'T':		/* queue timeout */
162558737Seric 		p = strchr(val, '/');
162658737Seric 		if (p != NULL)
162758737Seric 		{
162858737Seric 			*p++ = '\0';
162967730Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = convtime(p, 'd');
163058737Seric 		}
163167730Seric 		TimeOuts.to_q_return[TOC_NORMAL] = convtime(val, 'h');
163254967Seric 		break;
16338256Seric 
16348265Seric 	  case 't':		/* time zone name */
163552106Seric 		TimeZoneSpec = newstr(val);
16368265Seric 		break;
16378265Seric 
163850556Seric 	  case 'U':		/* location of user database */
163951360Seric 		UdbSpec = newstr(val);
164050556Seric 		break;
164150556Seric 
16428256Seric 	  case 'u':		/* set default uid */
164367823Seric 		for (p = val; *p != '\0'; p++)
164467823Seric 		{
164567823Seric 			if (*p == '.' || *p == '/' || *p == ':')
164667823Seric 			{
164767823Seric 				*p++ = '\0';
164867823Seric 				break;
164967823Seric 			}
165067823Seric 		}
165164133Seric 		if (isascii(*val) && isdigit(*val))
165264133Seric 			DefUid = atoi(val);
165364133Seric 		else
165464133Seric 		{
165564133Seric 			register struct passwd *pw;
165664133Seric 
165764133Seric 			DefUid = -1;
165864133Seric 			pw = getpwnam(val);
165964133Seric 			if (pw == NULL)
166064133Seric 				syserr("readcf: option u: unknown user %s", val);
166164133Seric 			else
166267823Seric 			{
166364133Seric 				DefUid = pw->pw_uid;
166467823Seric 				DefGid = pw->pw_gid;
166567823Seric 			}
166664133Seric 		}
166740973Sbostic 		setdefuser();
16688256Seric 
166967823Seric 		/* handle the group if it is there */
167067823Seric 		if (*p == '\0')
167167823Seric 			break;
167267823Seric 		val = p;
167367823Seric 		goto g_opt;
167467823Seric 
167558851Seric 	  case 'V':		/* fallback MX host */
167658851Seric 		FallBackMX = newstr(val);
167758851Seric 		break;
167858851Seric 
16798269Seric 	  case 'v':		/* run in verbose mode */
16809381Seric 		Verbose = atobool(val);
16818256Seric 		break;
16828256Seric 
168363837Seric 	  case 'w':		/* if we are best MX, try host directly */
168463837Seric 		TryNullMXList = atobool(val);
168563837Seric 		break;
168661104Seric 
168761104Seric 	    /* 'W' available -- was wizard password */
168861104Seric 
168914879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
169014879Seric 		QueueLA = atoi(val);
169114879Seric 		break;
169214879Seric 
169314879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
169414879Seric 		RefuseLA = atoi(val);
169514879Seric 		break;
169614879Seric 
169724981Seric 	  case 'y':		/* work recipient factor */
169824981Seric 		WkRecipFact = atoi(val);
169924981Seric 		break;
170024981Seric 
170124981Seric 	  case 'Y':		/* fork jobs during queue runs */
170224952Seric 		ForkQueueRuns = atobool(val);
170324952Seric 		break;
170424952Seric 
170524981Seric 	  case 'z':		/* work message class factor */
170624981Seric 		WkClassFact = atoi(val);
170724981Seric 		break;
170824981Seric 
170924981Seric 	  case 'Z':		/* work time factor */
171024981Seric 		WkTimeFact = atoi(val);
171124981Seric 		break;
171224981Seric 
171367614Seric 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
171467614Seric 		BrokenSmtpPeers = atobool(val);
171567614Seric 		break;
171667614Seric 
171767614Seric 	  case O_SQBH:		/* sort work queue by host first */
171867614Seric 		SortQueueByHost = atobool(val);
171967614Seric 		break;
172067614Seric 
172167707Seric 	  case O_DNICE:		/* delivery nice value */
172267707Seric 		DeliveryNiceness = atoi(val);
172367707Seric 		break;
172467707Seric 
172567707Seric 	  case O_MQA:		/* minimum queue age between deliveries */
172667707Seric 		MinQueueAge = convtime(val, 'm');
172767707Seric 		break;
172867707Seric 
172967707Seric 	  case O_MHSA:		/* maximum age of cached host status */
173067707Seric 		MaxHostStatAge = convtime(val, 'm');
173167707Seric 		break;
173267707Seric 
173367813Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
173467814Seric 		DefaultCharSet = newstr(val);
173567813Seric 		break;
173667813Seric 
17378256Seric 	  default:
17388256Seric 		break;
17398256Seric 	}
174016878Seric 	if (sticky)
174116878Seric 		setbitn(opt, StickyOpt);
17429188Seric 	return;
17438256Seric }
174410687Seric /*
174510687Seric **  SETCLASS -- set a word into a class
174610687Seric **
174710687Seric **	Parameters:
174810687Seric **		class -- the class to put the word in.
174910687Seric **		word -- the word to enter
175010687Seric **
175110687Seric **	Returns:
175210687Seric **		none.
175310687Seric **
175410687Seric **	Side Effects:
175510687Seric **		puts the word into the symbol table.
175610687Seric */
175710687Seric 
175810687Seric setclass(class, word)
175910687Seric 	int class;
176010687Seric 	char *word;
176110687Seric {
176210687Seric 	register STAB *s;
176310687Seric 
176457943Seric 	if (tTd(37, 8))
176564326Seric 		printf("setclass(%c, %s)\n", class, word);
176610687Seric 	s = stab(word, ST_CLASS, ST_ENTER);
176710687Seric 	setbitn(class, s->s_class);
176810687Seric }
176953654Seric /*
177053654Seric **  MAKEMAPENTRY -- create a map entry
177153654Seric **
177253654Seric **	Parameters:
177353654Seric **		line -- the config file line
177453654Seric **
177553654Seric **	Returns:
177653654Seric **		TRUE if it successfully entered the map entry.
177753654Seric **		FALSE otherwise (usually syntax error).
177853654Seric **
177953654Seric **	Side Effects:
178053654Seric **		Enters the map into the dictionary.
178153654Seric */
178253654Seric 
178353654Seric void
178453654Seric makemapentry(line)
178553654Seric 	char *line;
178653654Seric {
178753654Seric 	register char *p;
178853654Seric 	char *mapname;
178953654Seric 	char *classname;
179064078Seric 	register STAB *s;
179153654Seric 	STAB *class;
179253654Seric 
179358050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
179453654Seric 		continue;
179558050Seric 	if (!(isascii(*p) && isalnum(*p)))
179653654Seric 	{
179753654Seric 		syserr("readcf: config K line: no map name");
179853654Seric 		return;
179953654Seric 	}
180053654Seric 
180153654Seric 	mapname = p;
180258050Seric 	while (isascii(*++p) && isalnum(*p))
180353654Seric 		continue;
180453654Seric 	if (*p != '\0')
180553654Seric 		*p++ = '\0';
180658050Seric 	while (isascii(*p) && isspace(*p))
180753654Seric 		p++;
180858050Seric 	if (!(isascii(*p) && isalnum(*p)))
180953654Seric 	{
181053654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
181153654Seric 		return;
181253654Seric 	}
181353654Seric 	classname = p;
181458050Seric 	while (isascii(*++p) && isalnum(*p))
181553654Seric 		continue;
181653654Seric 	if (*p != '\0')
181753654Seric 		*p++ = '\0';
181858050Seric 	while (isascii(*p) && isspace(*p))
181953654Seric 		p++;
182053654Seric 
182153654Seric 	/* look up the class */
182253654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
182353654Seric 	if (class == NULL)
182453654Seric 	{
182553654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
182653654Seric 		return;
182753654Seric 	}
182853654Seric 
182953654Seric 	/* enter the map */
183064078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
183164078Seric 	s->s_map.map_class = &class->s_mapclass;
183264078Seric 	s->s_map.map_mname = newstr(mapname);
183353654Seric 
183464078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
183564078Seric 		s->s_map.map_mflags |= MF_VALID;
183664078Seric 
183764078Seric 	if (tTd(37, 5))
183864078Seric 	{
183964078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
184064078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
184164078Seric 			s->s_map.map_mflags,
184264078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
184364078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
184464078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
184564078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
184664078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
184764078Seric 	}
184853654Seric }
184958112Seric /*
185058112Seric **  SETTIMEOUTS -- parse and set timeout values
185158112Seric **
185258112Seric **	Parameters:
185358112Seric **		val -- a pointer to the values.  If NULL, do initial
185458112Seric **			settings.
185558112Seric **
185658112Seric **	Returns:
185758112Seric **		none.
185858112Seric **
185958112Seric **	Side Effects:
186058112Seric **		Initializes the TimeOuts structure
186158112Seric */
186258112Seric 
186364255Seric #define SECONDS
186458112Seric #define MINUTES	* 60
186558112Seric #define HOUR	* 3600
186658112Seric 
186758112Seric settimeouts(val)
186858112Seric 	register char *val;
186958112Seric {
187058112Seric 	register char *p;
187158671Seric 	extern time_t convtime();
187258112Seric 
187358112Seric 	if (val == NULL)
187458112Seric 	{
187558112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
187658112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
187758112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
187858112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
187958112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
188058112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
188158112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
188258112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
188358112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
188458112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
188558112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
188664255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
188767711Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
188858112Seric 		return;
188958112Seric 	}
189058112Seric 
189158112Seric 	for (;; val = p)
189258112Seric 	{
189358112Seric 		while (isascii(*val) && isspace(*val))
189458112Seric 			val++;
189558112Seric 		if (*val == '\0')
189658112Seric 			break;
189758112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
189858112Seric 			continue;
189958112Seric 		if (*p != '\0')
190058112Seric 			*p++ = '\0';
190158112Seric 
190258112Seric 		if (isascii(*val) && isdigit(*val))
190358112Seric 		{
190458112Seric 			/* old syntax -- set everything */
190558796Seric 			TimeOuts.to_mail = convtime(val, 'm');
190658112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
190758112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
190858112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
190958112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
191058112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
191158112Seric 			continue;
191258112Seric 		}
191358112Seric 		else
191458112Seric 		{
191567711Seric 			register char *q = strchr(val, ':');
191658112Seric 			time_t to;
191758112Seric 
191867711Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
191958112Seric 			{
192058112Seric 				/* syntax error */
192158112Seric 				continue;
192258112Seric 			}
192358112Seric 			*q++ = '\0';
192458796Seric 			to = convtime(q, 'm');
192558112Seric 
192658112Seric 			if (strcasecmp(val, "initial") == 0)
192758112Seric 				TimeOuts.to_initial = to;
192858112Seric 			else if (strcasecmp(val, "mail") == 0)
192958112Seric 				TimeOuts.to_mail = to;
193058112Seric 			else if (strcasecmp(val, "rcpt") == 0)
193158112Seric 				TimeOuts.to_rcpt = to;
193258112Seric 			else if (strcasecmp(val, "datainit") == 0)
193358112Seric 				TimeOuts.to_datainit = to;
193458112Seric 			else if (strcasecmp(val, "datablock") == 0)
193558112Seric 				TimeOuts.to_datablock = to;
193658112Seric 			else if (strcasecmp(val, "datafinal") == 0)
193758112Seric 				TimeOuts.to_datafinal = to;
193858112Seric 			else if (strcasecmp(val, "command") == 0)
193958112Seric 				TimeOuts.to_nextcommand = to;
194058112Seric 			else if (strcasecmp(val, "rset") == 0)
194158112Seric 				TimeOuts.to_rset = to;
194258112Seric 			else if (strcasecmp(val, "helo") == 0)
194358112Seric 				TimeOuts.to_helo = to;
194458112Seric 			else if (strcasecmp(val, "quit") == 0)
194558112Seric 				TimeOuts.to_quit = to;
194658112Seric 			else if (strcasecmp(val, "misc") == 0)
194758112Seric 				TimeOuts.to_miscshort = to;
194864255Seric 			else if (strcasecmp(val, "ident") == 0)
194964255Seric 				TimeOuts.to_ident = to;
195067711Seric 			else if (strcasecmp(val, "fileopen") == 0)
195167711Seric 				TimeOuts.to_fileopen = to;
195267711Seric 			else if (strcasecmp(val, "queuewarn") == 0)
195367730Seric 				TimeOuts.to_q_warning[TOC_NORMAL] = to;
195467711Seric 			else if (strcasecmp(val, "queuereturn") == 0)
195567730Seric 				TimeOuts.to_q_return[TOC_NORMAL] = to;
195667730Seric 			else if (strcasecmp(val, "queuewarn.normal") == 0)
195767730Seric 				TimeOuts.to_q_warning[TOC_NORMAL] = to;
195867730Seric 			else if (strcasecmp(val, "queuereturn.normal") == 0)
195967730Seric 				TimeOuts.to_q_return[TOC_NORMAL] = to;
196067730Seric 			else if (strcasecmp(val, "queuewarn.urgent") == 0)
196167730Seric 				TimeOuts.to_q_warning[TOC_URGENT] = to;
196267730Seric 			else if (strcasecmp(val, "queuereturn.urgent") == 0)
196367730Seric 				TimeOuts.to_q_return[TOC_URGENT] = to;
196467730Seric 			else if (strcasecmp(val, "queuewarn.non-urgent") == 0)
196567730Seric 				TimeOuts.to_q_warning[TOC_NONURGENT] = to;
196667730Seric 			else if (strcasecmp(val, "queuereturn.non-urgent") == 0)
196767730Seric 				TimeOuts.to_q_return[TOC_NONURGENT] = to;
196858112Seric 			else
196958112Seric 				syserr("settimeouts: invalid timeout %s", val);
197058112Seric 		}
197158112Seric 	}
197258112Seric }
1973