xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 67787)
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*67787Seric static char sccsid[] = "@(#)readcf.c	8.39 (Berkeley) 10/08/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];
8710709Seric 	extern char *munchstring();
8853654Seric 	extern void makemapentry();
893308Seric 
9052647Seric 	FileName = cfname;
9152647Seric 	LineNumber = 0;
9252647Seric 
933308Seric 	cf = fopen(cfname, "r");
943308Seric 	if (cf == NULL)
953308Seric 	{
9652647Seric 		syserr("cannot open");
973308Seric 		exit(EX_OSFILE);
983308Seric 	}
993308Seric 
10052647Seric 	if (fstat(fileno(cf), &statb) < 0)
10152647Seric 	{
10252647Seric 		syserr("cannot fstat");
10352647Seric 		exit(EX_OSFILE);
10452647Seric 	}
10552647Seric 
10652647Seric 	if (!S_ISREG(statb.st_mode))
10752647Seric 	{
10852647Seric 		syserr("not a plain file");
10952647Seric 		exit(EX_OSFILE);
11052647Seric 	}
11152647Seric 
11252647Seric 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
11352647Seric 	{
11453037Seric 		if (OpMode == MD_DAEMON || OpMode == MD_FREEZE)
11553037Seric 			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
11653037Seric 				FileName);
11753037Seric #ifdef LOG
11853037Seric 		if (LogLevel > 0)
11953037Seric 			syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
12053037Seric 				FileName);
12153037Seric #endif
12252647Seric 	}
12352647Seric 
12459254Seric #ifdef XLA
12559254Seric 	xla_zero();
12659254Seric #endif
12759254Seric 
12857135Seric 	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
1293308Seric 	{
13057135Seric 		if (bp[0] == '#')
13157135Seric 		{
13257135Seric 			if (bp != buf)
13357135Seric 				free(bp);
13452637Seric 			continue;
13557135Seric 		}
13652637Seric 
13767769Seric 		/* do macro expansion mappings */
13857135Seric 		for (p = bp; *p != '\0'; p++)
13916157Seric 		{
14057135Seric 			if (*p == '#' && p > bp && ConfigLevel >= 3)
14152647Seric 			{
14252647Seric 				/* this is an on-line comment */
14352647Seric 				register char *e;
14452647Seric 
14558050Seric 				switch (*--p & 0377)
14652647Seric 				{
14758050Seric 				  case MACROEXPAND:
14852647Seric 					/* it's from $# -- let it go through */
14952647Seric 					p++;
15052647Seric 					break;
15152647Seric 
15252647Seric 				  case '\\':
15352647Seric 					/* it's backslash escaped */
15452647Seric 					(void) strcpy(p, p + 1);
15552647Seric 					break;
15652647Seric 
15752647Seric 				  default:
15852647Seric 					/* delete preceeding white space */
15958050Seric 					while (isascii(*p) && isspace(*p) && p > bp)
16052647Seric 						p--;
16156795Seric 					if ((e = strchr(++p, '\n')) != NULL)
16252647Seric 						(void) strcpy(p, e);
16352647Seric 					else
16452647Seric 						p[0] = p[1] = '\0';
16552647Seric 					break;
16652647Seric 				}
16752647Seric 				continue;
16852647Seric 			}
16952647Seric 
17067769Seric 			if (*p != '$' || p[1] == '\0')
17116157Seric 				continue;
17216157Seric 
17316157Seric 			if (p[1] == '$')
17416157Seric 			{
17516157Seric 				/* actual dollar sign.... */
17623111Seric 				(void) strcpy(p, p + 1);
17716157Seric 				continue;
17816157Seric 			}
17916157Seric 
18016157Seric 			/* convert to macro expansion character */
18167769Seric 			*p++ = MACROEXPAND;
18267769Seric 
18367769Seric 			/* convert macro name to code */
18467769Seric 			*p = macid(p, &ep);
18567769Seric 			if (ep != p)
18667769Seric 				strcpy(p + 1, ep);
18716157Seric 		}
18816157Seric 
18916157Seric 		/* interpret this line */
19064718Seric 		errno = 0;
19157135Seric 		switch (bp[0])
1923308Seric 		{
1933308Seric 		  case '\0':
1943308Seric 		  case '#':		/* comment */
1953308Seric 			break;
1963308Seric 
1973308Seric 		  case 'R':		/* rewriting rule */
19857135Seric 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
1993308Seric 				continue;
2003308Seric 
2013308Seric 			if (*p == '\0')
2025909Seric 			{
20365821Seric 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
2045909Seric 				break;
2055909Seric 			}
2065909Seric 
2075909Seric 			/* allocate space for the rule header */
2085909Seric 			if (rwp == NULL)
2095909Seric 			{
2105909Seric 				RewriteRules[ruleset] = rwp =
2115909Seric 					(struct rewrite *) xalloc(sizeof *rwp);
2125909Seric 			}
2133308Seric 			else
2143308Seric 			{
2155909Seric 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
2165909Seric 				rwp = rwp->r_next;
2175909Seric 			}
2185909Seric 			rwp->r_next = NULL;
2193308Seric 
2205909Seric 			/* expand and save the LHS */
2215909Seric 			*p = '\0';
22257135Seric 			expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e);
22365066Seric 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
22465066Seric 					     sizeof pvpbuf, NULL);
22557589Seric 			nfuzzy = 0;
2265909Seric 			if (rwp->r_lhs != NULL)
22757589Seric 			{
22857589Seric 				register char **ap;
22957589Seric 
2305909Seric 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
23157589Seric 
23257589Seric 				/* count the number of fuzzy matches in LHS */
23357589Seric 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
23457589Seric 				{
23558148Seric 					char *botch;
23658148Seric 
23758148Seric 					botch = NULL;
23858050Seric 					switch (**ap & 0377)
23957589Seric 					{
24057589Seric 					  case MATCHZANY:
24157589Seric 					  case MATCHANY:
24257589Seric 					  case MATCHONE:
24357589Seric 					  case MATCHCLASS:
24457589Seric 					  case MATCHNCLASS:
24557589Seric 						nfuzzy++;
24658148Seric 						break;
24758148Seric 
24858148Seric 					  case MATCHREPL:
24958148Seric 						botch = "$0-$9";
25058148Seric 						break;
25158148Seric 
25258148Seric 					  case CANONNET:
25358148Seric 						botch = "$#";
25458148Seric 						break;
25558148Seric 
25658148Seric 					  case CANONUSER:
25758148Seric 						botch = "$:";
25858148Seric 						break;
25958148Seric 
26058148Seric 					  case CALLSUBR:
26158148Seric 						botch = "$>";
26258148Seric 						break;
26358148Seric 
26458148Seric 					  case CONDIF:
26558148Seric 						botch = "$?";
26658148Seric 						break;
26758148Seric 
26858148Seric 					  case CONDELSE:
26958148Seric 						botch = "$|";
27058148Seric 						break;
27158148Seric 
27258148Seric 					  case CONDFI:
27358148Seric 						botch = "$.";
27458148Seric 						break;
27558148Seric 
27658148Seric 					  case HOSTBEGIN:
27758148Seric 						botch = "$[";
27858148Seric 						break;
27958148Seric 
28058148Seric 					  case HOSTEND:
28158148Seric 						botch = "$]";
28258148Seric 						break;
28358148Seric 
28458148Seric 					  case LOOKUPBEGIN:
28558148Seric 						botch = "$(";
28658148Seric 						break;
28758148Seric 
28858148Seric 					  case LOOKUPEND:
28958148Seric 						botch = "$)";
29058148Seric 						break;
29157589Seric 					}
29258148Seric 					if (botch != NULL)
29358148Seric 						syserr("Inappropriate use of %s on LHS",
29458148Seric 							botch);
29557589Seric 				}
29657589Seric 			}
29756678Seric 			else
29856678Seric 				syserr("R line: null LHS");
2995909Seric 
3005909Seric 			/* expand and save the RHS */
3015909Seric 			while (*++p == '\t')
3025909Seric 				continue;
3037231Seric 			q = p;
3047231Seric 			while (*p != '\0' && *p != '\t')
3057231Seric 				p++;
3067231Seric 			*p = '\0';
30755012Seric 			expand(q, exbuf, &exbuf[sizeof exbuf], e);
30865066Seric 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
30965066Seric 					     sizeof pvpbuf, NULL);
3105909Seric 			if (rwp->r_rhs != NULL)
31157589Seric 			{
31257589Seric 				register char **ap;
31357589Seric 
3145909Seric 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
31557589Seric 
31657589Seric 				/* check no out-of-bounds replacements */
31757589Seric 				nfuzzy += '0';
31857589Seric 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
31957589Seric 				{
32058148Seric 					char *botch;
32158148Seric 
32258148Seric 					botch = NULL;
32358148Seric 					switch (**ap & 0377)
32457589Seric 					{
32558148Seric 					  case MATCHREPL:
32658148Seric 						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
32758148Seric 						{
32858148Seric 							syserr("replacement $%c out of bounds",
32958148Seric 								(*ap)[1]);
33058148Seric 						}
33158148Seric 						break;
33258148Seric 
33358148Seric 					  case MATCHZANY:
33458148Seric 						botch = "$*";
33558148Seric 						break;
33658148Seric 
33758148Seric 					  case MATCHANY:
33858148Seric 						botch = "$+";
33958148Seric 						break;
34058148Seric 
34158148Seric 					  case MATCHONE:
34258148Seric 						botch = "$-";
34358148Seric 						break;
34458148Seric 
34558148Seric 					  case MATCHCLASS:
34658148Seric 						botch = "$=";
34758148Seric 						break;
34858148Seric 
34958148Seric 					  case MATCHNCLASS:
35058148Seric 						botch = "$~";
35158148Seric 						break;
35257589Seric 					}
35358148Seric 					if (botch != NULL)
35458148Seric 						syserr("Inappropriate use of %s on RHS",
35558148Seric 							botch);
35657589Seric 				}
35757589Seric 			}
35856678Seric 			else
35956678Seric 				syserr("R line: null RHS");
3603308Seric 			break;
3613308Seric 
3624072Seric 		  case 'S':		/* select rewriting set */
36364440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
36464440Seric 				continue;
36564440Seric 			if (!isascii(*p) || !isdigit(*p))
36664440Seric 			{
36764440Seric 				syserr("invalid argument to S line: \"%.20s\"",
36864440Seric 					&bp[1]);
36964440Seric 				break;
37064440Seric 			}
37164440Seric 			ruleset = atoi(p);
3728056Seric 			if (ruleset >= MAXRWSETS || ruleset < 0)
3738056Seric 			{
3749381Seric 				syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
3758056Seric 				ruleset = 0;
3768056Seric 			}
3774072Seric 			rwp = NULL;
3784072Seric 			break;
3794072Seric 
3803308Seric 		  case 'D':		/* macro definition */
38167769Seric 			mid = macid(&bp[1], &ep);
38267769Seric 			p = munchstring(ep, NULL);
38367769Seric 			define(mid, newstr(p), e);
3843308Seric 			break;
3853308Seric 
3863387Seric 		  case 'H':		/* required header line */
38757135Seric 			(void) chompheader(&bp[1], TRUE, e);
3883387Seric 			break;
3893387Seric 
3904061Seric 		  case 'C':		/* word class */
3914432Seric 			/* scan the list of words and set class for all */
39267769Seric 			mid = macid(&bp[1], &ep);
39367769Seric 			expand(ep, exbuf, &exbuf[sizeof exbuf], e);
39464121Seric 			for (p = exbuf; *p != '\0'; )
3954061Seric 			{
3964061Seric 				register char *wd;
3974061Seric 				char delim;
3984061Seric 
39958050Seric 				while (*p != '\0' && isascii(*p) && isspace(*p))
4004061Seric 					p++;
4014061Seric 				wd = p;
40258050Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4034061Seric 					p++;
4044061Seric 				delim = *p;
4054061Seric 				*p = '\0';
4064061Seric 				if (wd[0] != '\0')
40767769Seric 					setclass(mid, wd);
4084061Seric 				*p = delim;
4094061Seric 			}
4104061Seric 			break;
4114061Seric 
41259272Seric 		  case 'F':		/* word class from file */
41367769Seric 			mid = macid(&bp[1], &ep);
41467769Seric 			for (p = ep; isascii(*p) && isspace(*p); )
41564133Seric 				p++;
41664133Seric 			if (p[0] == '-' && p[1] == 'o')
41764133Seric 			{
41864133Seric 				optional = TRUE;
41964133Seric 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
42064133Seric 					p++;
42164133Seric 				while (isascii(*p) && isspace(*p))
42267615Seric 					p++;
42364133Seric 			}
42464133Seric 			else
42564133Seric 				optional = FALSE;
42664133Seric 			file = p;
42764133Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
42864133Seric 				p++;
42959272Seric 			if (*p == '\0')
43059272Seric 				p = "%s";
43159272Seric 			else
43259272Seric 			{
43359272Seric 				*p = '\0';
43459272Seric 				while (isascii(*++p) && isspace(*p))
43559272Seric 					continue;
43659272Seric 			}
43764133Seric 			fileclass(bp[1], file, p, safe, optional);
43859272Seric 			break;
43959272Seric 
44059156Seric #ifdef XLA
44159156Seric 		  case 'L':		/* extended load average description */
44259156Seric 			xla_init(&bp[1]);
44359156Seric 			break;
44459156Seric #endif
44559156Seric 
4464096Seric 		  case 'M':		/* define mailer */
44757135Seric 			makemailer(&bp[1]);
4484096Seric 			break;
4494096Seric 
4508252Seric 		  case 'O':		/* set option */
45158734Seric 			setoption(bp[1], &bp[2], safe, FALSE, e);
4528252Seric 			break;
4538252Seric 
4548252Seric 		  case 'P':		/* set precedence */
4558252Seric 			if (NumPriorities >= MAXPRIORITIES)
4568252Seric 			{
4578547Seric 				toomany('P', MAXPRIORITIES);
4588252Seric 				break;
4598252Seric 			}
46057135Seric 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
4618252Seric 				continue;
4628252Seric 			if (*p == '\0')
4638252Seric 				goto badline;
4648252Seric 			*p = '\0';
46557135Seric 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
4668252Seric 			Priorities[NumPriorities].pri_val = atoi(++p);
4678252Seric 			NumPriorities++;
4688252Seric 			break;
4698252Seric 
4708547Seric 		  case 'T':		/* trusted user(s) */
47158161Seric 			/* this option is obsolete, but will be ignored */
4728547Seric 			break;
4738547Seric 
47452645Seric 		  case 'V':		/* configuration syntax version */
47564440Seric 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
47664440Seric 				continue;
47764440Seric 			if (!isascii(*p) || !isdigit(*p))
47864440Seric 			{
47964440Seric 				syserr("invalid argument to V line: \"%.20s\"",
48064440Seric 					&bp[1]);
48164440Seric 				break;
48264440Seric 			}
48364718Seric 			ConfigLevel = strtol(p, &ep, 10);
48464279Seric 			if (ConfigLevel >= 5)
48564279Seric 			{
48664279Seric 				/* level 5 configs have short name in $w */
48764279Seric 				p = macvalue('w', e);
48864279Seric 				if (p != NULL && (p = strchr(p, '.')) != NULL)
48964279Seric 					*p = '\0';
49064279Seric 			}
49164718Seric 			if (*ep++ == '/')
49264718Seric 			{
49364718Seric 				/* extract vendor code */
49464718Seric 				for (p = ep; isascii(*p) && isalpha(*p); )
49564718Seric 					p++;
49664718Seric 				*p = '\0';
49764718Seric 
49864718Seric 				if (!setvendor(ep))
49964718Seric 					syserr("invalid V line vendor code: \"%s\"",
50064718Seric 						ep);
50164718Seric 			}
50252645Seric 			break;
50352645Seric 
50453654Seric 		  case 'K':
50557135Seric 			makemapentry(&bp[1]);
50653654Seric 			break;
50753654Seric 
5083308Seric 		  default:
5094061Seric 		  badline:
51057135Seric 			syserr("unknown control line \"%s\"", bp);
5113308Seric 		}
51257135Seric 		if (bp != buf)
51357135Seric 			free(bp);
5143308Seric 	}
51552637Seric 	if (ferror(cf))
51652637Seric 	{
51752647Seric 		syserr("I/O read error", cfname);
51852637Seric 		exit(EX_OSFILE);
51952637Seric 	}
52052637Seric 	fclose(cf);
5219381Seric 	FileName = NULL;
52256836Seric 
52367730Seric 	/* initialize host maps from local service tables */
52467730Seric 	inithostmaps();
52567730Seric 
52657076Seric 	if (stab("host", ST_MAP, ST_FIND) == NULL)
52757076Seric 	{
52857076Seric 		/* user didn't initialize: set up host map */
52957076Seric 		strcpy(buf, "host host");
53066334Seric #if NAMED_BIND
53157076Seric 		if (ConfigLevel >= 2)
53257076Seric 			strcat(buf, " -a.");
53364947Seric #endif
53457076Seric 		makemapentry(buf);
53557076Seric 	}
5364096Seric }
5374096Seric /*
5388547Seric **  TOOMANY -- signal too many of some option
5398547Seric **
5408547Seric **	Parameters:
5418547Seric **		id -- the id of the error line
5428547Seric **		maxcnt -- the maximum possible values
5438547Seric **
5448547Seric **	Returns:
5458547Seric **		none.
5468547Seric **
5478547Seric **	Side Effects:
5488547Seric **		gives a syserr.
5498547Seric */
5508547Seric 
5518547Seric toomany(id, maxcnt)
5528547Seric 	char id;
5538547Seric 	int maxcnt;
5548547Seric {
5559381Seric 	syserr("too many %c lines, %d max", id, maxcnt);
5568547Seric }
5578547Seric /*
5584432Seric **  FILECLASS -- read members of a class from a file
5594432Seric **
5604432Seric **	Parameters:
5614432Seric **		class -- class to define.
5624432Seric **		filename -- name of file to read.
5634432Seric **		fmt -- scanf string to use for match.
56464133Seric **		safe -- if set, this is a safe read.
56564133Seric **		optional -- if set, it is not an error for the file to
56664133Seric **			not exist.
5674432Seric **
5684432Seric **	Returns:
5694432Seric **		none
5704432Seric **
5714432Seric **	Side Effects:
5724432Seric **
5734432Seric **		puts all lines in filename that match a scanf into
5744432Seric **			the named class.
5754432Seric */
5764432Seric 
57764133Seric fileclass(class, filename, fmt, safe, optional)
5784432Seric 	int class;
5794432Seric 	char *filename;
5804432Seric 	char *fmt;
58154973Seric 	bool safe;
58264133Seric 	bool optional;
5834432Seric {
58425808Seric 	FILE *f;
58554973Seric 	struct stat stbuf;
5864432Seric 	char buf[MAXLINE];
5874432Seric 
58866101Seric 	if (tTd(37, 2))
58966101Seric 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
59066101Seric 
59166031Seric 	if (filename[0] == '|')
59266031Seric 	{
59366031Seric 		syserr("fileclass: pipes (F%c%s) not supported due to security problems",
59466031Seric 			class, filename);
59566031Seric 		return;
59666031Seric 	}
59754973Seric 	if (stat(filename, &stbuf) < 0)
59854973Seric 	{
59966101Seric 		if (tTd(37, 2))
60066101Seric 			printf("  cannot stat (%s)\n", errstring(errno));
60164133Seric 		if (!optional)
60264133Seric 			syserr("fileclass: cannot stat %s", filename);
60354973Seric 		return;
60454973Seric 	}
60554973Seric 	if (!S_ISREG(stbuf.st_mode))
60654973Seric 	{
60754973Seric 		syserr("fileclass: %s not a regular file", filename);
60854973Seric 		return;
60954973Seric 	}
61054973Seric 	if (!safe && access(filename, R_OK) < 0)
61154973Seric 	{
61254973Seric 		syserr("fileclass: access denied on %s", filename);
61354973Seric 		return;
61454973Seric 	}
61554973Seric 	f = fopen(filename, "r");
6164432Seric 	if (f == NULL)
6174432Seric 	{
61854973Seric 		syserr("fileclass: cannot open %s", filename);
6194432Seric 		return;
6204432Seric 	}
6214432Seric 
6224432Seric 	while (fgets(buf, sizeof buf, f) != NULL)
6234432Seric 	{
6244432Seric 		register STAB *s;
62525808Seric 		register char *p;
62625808Seric # ifdef SCANF
6274432Seric 		char wordbuf[MAXNAME+1];
6284432Seric 
6294432Seric 		if (sscanf(buf, fmt, wordbuf) != 1)
6304432Seric 			continue;
63125808Seric 		p = wordbuf;
63256795Seric # else /* SCANF */
63325808Seric 		p = buf;
63456795Seric # endif /* SCANF */
63525808Seric 
63625808Seric 		/*
63725808Seric 		**  Break up the match into words.
63825808Seric 		*/
63925808Seric 
64025808Seric 		while (*p != '\0')
64125808Seric 		{
64225808Seric 			register char *q;
64325808Seric 
64425808Seric 			/* strip leading spaces */
64558050Seric 			while (isascii(*p) && isspace(*p))
64625808Seric 				p++;
64725808Seric 			if (*p == '\0')
64825808Seric 				break;
64925808Seric 
65025808Seric 			/* find the end of the word */
65125808Seric 			q = p;
65258050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
65325808Seric 				p++;
65425808Seric 			if (*p != '\0')
65525808Seric 				*p++ = '\0';
65625808Seric 
65725808Seric 			/* enter the word in the symbol table */
65866101Seric 			setclass(class, q);
65925808Seric 		}
6604432Seric 	}
6614432Seric 
66254973Seric 	(void) fclose(f);
6634432Seric }
6644432Seric /*
6654096Seric **  MAKEMAILER -- define a new mailer.
6664096Seric **
6674096Seric **	Parameters:
66810327Seric **		line -- description of mailer.  This is in labeled
66910327Seric **			fields.  The fields are:
67010327Seric **			   P -- the path to the mailer
67110327Seric **			   F -- the flags associated with the mailer
67210327Seric **			   A -- the argv for this mailer
67310327Seric **			   S -- the sender rewriting set
67410327Seric **			   R -- the recipient rewriting set
67510327Seric **			   E -- the eol string
67610327Seric **			The first word is the canonical name of the mailer.
6774096Seric **
6784096Seric **	Returns:
6794096Seric **		none.
6804096Seric **
6814096Seric **	Side Effects:
6824096Seric **		enters the mailer into the mailer table.
6834096Seric */
6843308Seric 
68521066Seric makemailer(line)
6864096Seric 	char *line;
6874096Seric {
6884096Seric 	register char *p;
6898067Seric 	register struct mailer *m;
6908067Seric 	register STAB *s;
6918067Seric 	int i;
69210327Seric 	char fcode;
69358020Seric 	auto char *endp;
6944096Seric 	extern int NextMailer;
69510327Seric 	extern char **makeargv();
69610327Seric 	extern char *munchstring();
69710701Seric 	extern long atol();
6984096Seric 
69910327Seric 	/* allocate a mailer and set up defaults */
70010327Seric 	m = (struct mailer *) xalloc(sizeof *m);
70110327Seric 	bzero((char *) m, sizeof *m);
70210327Seric 	m->m_eol = "\n";
70367604Seric 	m->m_uid = m->m_gid = 0;
70410327Seric 
70510327Seric 	/* collect the mailer name */
70658050Seric 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
70710327Seric 		continue;
70810327Seric 	if (*p != '\0')
70910327Seric 		*p++ = '\0';
71010327Seric 	m->m_name = newstr(line);
71110327Seric 
71210327Seric 	/* now scan through and assign info from the fields */
71310327Seric 	while (*p != '\0')
71410327Seric 	{
71558333Seric 		auto char *delimptr;
71658333Seric 
71758050Seric 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
71810327Seric 			p++;
71910327Seric 
72010327Seric 		/* p now points to field code */
72110327Seric 		fcode = *p;
72210327Seric 		while (*p != '\0' && *p != '=' && *p != ',')
72310327Seric 			p++;
72410327Seric 		if (*p++ != '=')
72510327Seric 		{
72652637Seric 			syserr("mailer %s: `=' expected", m->m_name);
72710327Seric 			return;
72810327Seric 		}
72958050Seric 		while (isascii(*p) && isspace(*p))
73010327Seric 			p++;
73110327Seric 
73210327Seric 		/* p now points to the field body */
73358333Seric 		p = munchstring(p, &delimptr);
73410327Seric 
73510327Seric 		/* install the field into the mailer struct */
73610327Seric 		switch (fcode)
73710327Seric 		{
73810327Seric 		  case 'P':		/* pathname */
73910327Seric 			m->m_mailer = newstr(p);
74010327Seric 			break;
74110327Seric 
74210327Seric 		  case 'F':		/* flags */
74310687Seric 			for (; *p != '\0'; p++)
74458050Seric 				if (!(isascii(*p) && isspace(*p)))
74552637Seric 					setbitn(*p, m->m_flags);
74610327Seric 			break;
74710327Seric 
74810327Seric 		  case 'S':		/* sender rewriting ruleset */
74910327Seric 		  case 'R':		/* recipient rewriting ruleset */
75058020Seric 			i = strtol(p, &endp, 10);
75110327Seric 			if (i < 0 || i >= MAXRWSETS)
75210327Seric 			{
75310327Seric 				syserr("invalid rewrite set, %d max", MAXRWSETS);
75410327Seric 				return;
75510327Seric 			}
75610327Seric 			if (fcode == 'S')
75758020Seric 				m->m_sh_rwset = m->m_se_rwset = i;
75810327Seric 			else
75958020Seric 				m->m_rh_rwset = m->m_re_rwset = i;
76058020Seric 
76158020Seric 			p = endp;
76259985Seric 			if (*p++ == '/')
76358020Seric 			{
76458020Seric 				i = strtol(p, NULL, 10);
76558020Seric 				if (i < 0 || i >= MAXRWSETS)
76658020Seric 				{
76758020Seric 					syserr("invalid rewrite set, %d max",
76858020Seric 						MAXRWSETS);
76958020Seric 					return;
77058020Seric 				}
77158020Seric 				if (fcode == 'S')
77258020Seric 					m->m_sh_rwset = i;
77358020Seric 				else
77458020Seric 					m->m_rh_rwset = i;
77558020Seric 			}
77610327Seric 			break;
77710327Seric 
77810327Seric 		  case 'E':		/* end of line string */
77910327Seric 			m->m_eol = newstr(p);
78010327Seric 			break;
78110327Seric 
78210327Seric 		  case 'A':		/* argument vector */
78310327Seric 			m->m_argv = makeargv(p);
78410327Seric 			break;
78510701Seric 
78610701Seric 		  case 'M':		/* maximum message size */
78710701Seric 			m->m_maxsize = atol(p);
78810701Seric 			break;
78952106Seric 
79052106Seric 		  case 'L':		/* maximum line length */
79152106Seric 			m->m_linelimit = atoi(p);
79252106Seric 			break;
79358935Seric 
79458935Seric 		  case 'D':		/* working directory */
79558935Seric 			m->m_execdir = newstr(p);
79658935Seric 			break;
79767604Seric 
79867604Seric 		  case 'U':		/* user id */
79967604Seric 			if (isascii(*p) && !isdigit(*p))
80067604Seric 			{
80167604Seric 				char *q = p;
80267604Seric 				struct passwd *pw;
80367604Seric 
80467604Seric 				while (isascii(*p) && isalnum(*p))
80567604Seric 					p++;
80667604Seric 				while (isascii(*p) && isspace(*p))
80767604Seric 					*p++ = '\0';
80867604Seric 				if (*p != '\0')
80967604Seric 					*p++ = '\0';
81067604Seric 				pw = getpwnam(q);
81167604Seric 				if (pw == NULL)
81267604Seric 					syserr("readcf: mailer U= flag: unknown user %s", q);
81367604Seric 				else
81467604Seric 				{
81567604Seric 					m->m_uid = pw->pw_uid;
81667604Seric 					m->m_gid = pw->pw_gid;
81767604Seric 				}
81867604Seric 			}
81967604Seric 			else
82067604Seric 			{
82167604Seric 				auto char *q;
82267604Seric 
82367604Seric 				m->m_uid = strtol(p, &q, 0);
82467604Seric 				p = q;
82567604Seric 			}
82667604Seric 			while (isascii(*p) && isspace(*p))
82767604Seric 				p++;
82867604Seric 			if (*p == '\0')
82967604Seric 				break;
83067604Seric 			if (isascii(*p) && !isdigit(*p))
83167604Seric 			{
83267604Seric 				char *q = p;
83367604Seric 				struct group *gr;
83467604Seric 
83567604Seric 				while (isascii(*p) && isalnum(*p))
83667604Seric 					p++;
83767604Seric 				*p++ = '\0';
83867604Seric 				gr = getgrnam(q);
83967604Seric 				if (gr == NULL)
84067604Seric 					syserr("readcf: mailer U= flag: unknown group %s", q);
84167604Seric 				else
84267604Seric 					m->m_gid = gr->gr_gid;
84367604Seric 			}
84467604Seric 			else
84567604Seric 			{
84667604Seric 				m->m_gid = strtol(p, NULL, 0);
84767604Seric 			}
84867604Seric 			break;
84910327Seric 		}
85010327Seric 
85158333Seric 		p = delimptr;
85210327Seric 	}
85310327Seric 
85452106Seric 	/* do some heuristic cleanup for back compatibility */
85552106Seric 	if (bitnset(M_LIMITS, m->m_flags))
85652106Seric 	{
85752106Seric 		if (m->m_linelimit == 0)
85852106Seric 			m->m_linelimit = SMTPLINELIM;
85955418Seric 		if (ConfigLevel < 2)
86052106Seric 			setbitn(M_7BITS, m->m_flags);
86152106Seric 	}
86252106Seric 
86358321Seric 	/* do some rationality checking */
86458321Seric 	if (m->m_argv == NULL)
86558321Seric 	{
86658321Seric 		syserr("M%s: A= argument required", m->m_name);
86758321Seric 		return;
86858321Seric 	}
86958321Seric 	if (m->m_mailer == NULL)
87058321Seric 	{
87158321Seric 		syserr("M%s: P= argument required", m->m_name);
87258321Seric 		return;
87358321Seric 	}
87458321Seric 
8754096Seric 	if (NextMailer >= MAXMAILERS)
8764096Seric 	{
8779381Seric 		syserr("too many mailers defined (%d max)", MAXMAILERS);
8784096Seric 		return;
8794096Seric 	}
88057402Seric 
88110327Seric 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
88257402Seric 	if (s->s_mailer != NULL)
88357402Seric 	{
88457402Seric 		i = s->s_mailer->m_mno;
88557402Seric 		free(s->s_mailer);
88657402Seric 	}
88757402Seric 	else
88857402Seric 	{
88957402Seric 		i = NextMailer++;
89057402Seric 	}
89157402Seric 	Mailer[i] = s->s_mailer = m;
89257454Seric 	m->m_mno = i;
89310327Seric }
89410327Seric /*
89510327Seric **  MUNCHSTRING -- translate a string into internal form.
89610327Seric **
89710327Seric **	Parameters:
89810327Seric **		p -- the string to munch.
89958333Seric **		delimptr -- if non-NULL, set to the pointer of the
90058333Seric **			field delimiter character.
90110327Seric **
90210327Seric **	Returns:
90310327Seric **		the munched string.
90410327Seric */
9054096Seric 
90610327Seric char *
90758333Seric munchstring(p, delimptr)
90810327Seric 	register char *p;
90958333Seric 	char **delimptr;
91010327Seric {
91110327Seric 	register char *q;
91210327Seric 	bool backslash = FALSE;
91310327Seric 	bool quotemode = FALSE;
91410327Seric 	static char buf[MAXLINE];
9154096Seric 
91610327Seric 	for (q = buf; *p != '\0'; p++)
9174096Seric 	{
91810327Seric 		if (backslash)
91910327Seric 		{
92010327Seric 			/* everything is roughly literal */
92110357Seric 			backslash = FALSE;
92210327Seric 			switch (*p)
92310327Seric 			{
92410327Seric 			  case 'r':		/* carriage return */
92510327Seric 				*q++ = '\r';
92610327Seric 				continue;
92710327Seric 
92810327Seric 			  case 'n':		/* newline */
92910327Seric 				*q++ = '\n';
93010327Seric 				continue;
93110327Seric 
93210327Seric 			  case 'f':		/* form feed */
93310327Seric 				*q++ = '\f';
93410327Seric 				continue;
93510327Seric 
93610327Seric 			  case 'b':		/* backspace */
93710327Seric 				*q++ = '\b';
93810327Seric 				continue;
93910327Seric 			}
94010327Seric 			*q++ = *p;
94110327Seric 		}
94210327Seric 		else
94310327Seric 		{
94410327Seric 			if (*p == '\\')
94510327Seric 				backslash = TRUE;
94610327Seric 			else if (*p == '"')
94710327Seric 				quotemode = !quotemode;
94810327Seric 			else if (quotemode || *p != ',')
94910327Seric 				*q++ = *p;
95010327Seric 			else
95110327Seric 				break;
95210327Seric 		}
9534096Seric 	}
9544096Seric 
95558333Seric 	if (delimptr != NULL)
95658333Seric 		*delimptr = p;
95710327Seric 	*q++ = '\0';
95810327Seric 	return (buf);
95910327Seric }
96010327Seric /*
96110327Seric **  MAKEARGV -- break up a string into words
96210327Seric **
96310327Seric **	Parameters:
96410327Seric **		p -- the string to break up.
96510327Seric **
96610327Seric **	Returns:
96710327Seric **		a char **argv (dynamically allocated)
96810327Seric **
96910327Seric **	Side Effects:
97010327Seric **		munges p.
97110327Seric */
9724096Seric 
97310327Seric char **
97410327Seric makeargv(p)
97510327Seric 	register char *p;
97610327Seric {
97710327Seric 	char *q;
97810327Seric 	int i;
97910327Seric 	char **avp;
98010327Seric 	char *argv[MAXPV + 1];
98110327Seric 
98210327Seric 	/* take apart the words */
98310327Seric 	i = 0;
98410327Seric 	while (*p != '\0' && i < MAXPV)
9854096Seric 	{
98610327Seric 		q = p;
98758050Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
98810327Seric 			p++;
98958050Seric 		while (isascii(*p) && isspace(*p))
99010327Seric 			*p++ = '\0';
99110327Seric 		argv[i++] = newstr(q);
9924096Seric 	}
99310327Seric 	argv[i++] = NULL;
9944096Seric 
99510327Seric 	/* now make a copy of the argv */
99610327Seric 	avp = (char **) xalloc(sizeof *avp * i);
99716893Seric 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
99810327Seric 
99910327Seric 	return (avp);
10003308Seric }
10013308Seric /*
10023308Seric **  PRINTRULES -- print rewrite rules (for debugging)
10033308Seric **
10043308Seric **	Parameters:
10053308Seric **		none.
10063308Seric **
10073308Seric **	Returns:
10083308Seric **		none.
10093308Seric **
10103308Seric **	Side Effects:
10113308Seric **		prints rewrite rules.
10123308Seric */
10133308Seric 
10143308Seric printrules()
10153308Seric {
10163308Seric 	register struct rewrite *rwp;
10174072Seric 	register int ruleset;
10183308Seric 
10194072Seric 	for (ruleset = 0; ruleset < 10; ruleset++)
10203308Seric 	{
10214072Seric 		if (RewriteRules[ruleset] == NULL)
10224072Seric 			continue;
10238067Seric 		printf("\n----Rule Set %d:", ruleset);
10243308Seric 
10254072Seric 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
10263308Seric 		{
10278067Seric 			printf("\nLHS:");
10288067Seric 			printav(rwp->r_lhs);
10298067Seric 			printf("RHS:");
10308067Seric 			printav(rwp->r_rhs);
10313308Seric 		}
10323308Seric 	}
10333308Seric }
10344319Seric 
10354096Seric /*
10368256Seric **  SETOPTION -- set global processing option
10378256Seric **
10388256Seric **	Parameters:
10398256Seric **		opt -- option name.
10408256Seric **		val -- option value (as a text string).
104121755Seric **		safe -- set if this came from a configuration file.
104221755Seric **			Some options (if set from the command line) will
104321755Seric **			reset the user id to avoid security problems.
10448269Seric **		sticky -- if set, don't let other setoptions override
10458269Seric **			this value.
104658734Seric **		e -- the main envelope.
10478256Seric **
10488256Seric **	Returns:
10498256Seric **		none.
10508256Seric **
10518256Seric **	Side Effects:
10528256Seric **		Sets options as implied by the arguments.
10538256Seric */
10548256Seric 
105510687Seric static BITMAP	StickyOpt;		/* set if option is stuck */
10568269Seric 
105757207Seric 
105866334Seric #if NAMED_BIND
105957207Seric 
106057207Seric struct resolverflags
106157207Seric {
106257207Seric 	char	*rf_name;	/* name of the flag */
106357207Seric 	long	rf_bits;	/* bits to set/clear */
106457207Seric } ResolverFlags[] =
106557207Seric {
106657207Seric 	"debug",	RES_DEBUG,
106757207Seric 	"aaonly",	RES_AAONLY,
106857207Seric 	"usevc",	RES_USEVC,
106957207Seric 	"primary",	RES_PRIMARY,
107057207Seric 	"igntc",	RES_IGNTC,
107157207Seric 	"recurse",	RES_RECURSE,
107257207Seric 	"defnames",	RES_DEFNAMES,
107357207Seric 	"stayopen",	RES_STAYOPEN,
107457207Seric 	"dnsrch",	RES_DNSRCH,
107565583Seric 	"true",		0,		/* to avoid error on old syntax */
107657207Seric 	NULL,		0
107757207Seric };
107857207Seric 
107957207Seric #endif
108057207Seric 
108167614Seric struct optioninfo
108267614Seric {
108367614Seric 	char	*o_name;	/* long name of option */
1084*67787Seric 	u_char	o_code;		/* short name of option */
108567614Seric 	bool	o_safe;		/* safe for random people to use */
108667614Seric } OptionTab[] =
108767614Seric {
108867707Seric 	"SevenBitInput",	'7',		TRUE,
108967707Seric 	"EightBitMode",		'8',		TRUE,
109067707Seric 	"AliasFile",		'A',		FALSE,
109167707Seric 	"AliasWait",		'a',		FALSE,
109267707Seric 	"BlankSub",		'B',		FALSE,
109367707Seric 	"MinFreeBlocks",	'b',		TRUE,
109467707Seric 	"CheckpointInterval",	'C',		TRUE,
109567707Seric 	"HoldExpensive",	'c',		FALSE,
109667707Seric 	"AutoRebuildAliases",	'D',		FALSE,
109767707Seric 	"DeliveryMode",		'd',		TRUE,
109867707Seric 	"ErrorHeader",		'E',		FALSE,
109967707Seric 	"ErrorMode",		'e',		TRUE,
110067707Seric 	"TempFileMode",		'F',		FALSE,
110167707Seric 	"SaveFromLine",		'f',		FALSE,
110267707Seric 	"MatchGECOS",		'G',		FALSE,
110367707Seric 	"HelpFile",		'H',		FALSE,
110467707Seric 	"MaxHopCount",		'h',		FALSE,
110567707Seric 	"NameServerOptions",	'I',		FALSE,
110667707Seric 	"IgnoreDots",		'i',		TRUE,
110767707Seric 	"ForwardPath",		'J',		FALSE,
110867707Seric 	"SendMimeErrors",	'j',		TRUE,
110967707Seric 	"ConnectionCacheSize",	'k',		FALSE,
111067707Seric 	"ConnectionCacheTimeout", 'K',		FALSE,
111167707Seric 	"UseErrorsTo",		'l',		FALSE,
111267707Seric 	"LogLevel",		'L',		FALSE,
111367707Seric 	"MeToo",		'm',		TRUE,
111467707Seric 	"CheckAliases",		'n',		FALSE,
111567707Seric 	"OldStyleHeaders",	'o',		TRUE,
111667707Seric 	"DaemonPortOptions",	'O',		FALSE,
111767707Seric 	"PrivacyOptions",	'p',		TRUE,
111867707Seric 	"PostmasterCopy",	'P',		FALSE,
111967707Seric 	"QueueFactor",		'q',		FALSE,
112067707Seric 	"QueueDirectory",	'Q',		FALSE,
112167707Seric 	"DontPruneRoutes",	'R',		FALSE,
112267711Seric 	"Timeouts",		'r',		TRUE,
112367707Seric 	"StatusFile",		'S',		FALSE,
112467707Seric 	"SuperSafe",		's',		TRUE,
112567707Seric 	"QueueTimeout",		'T',		FALSE,
112667707Seric 	"TimeZoneSpec",		't',		FALSE,
112767707Seric 	"UserDatabaseSpec",	'U',		FALSE,
112867707Seric 	"DefaultUser",		'u',		FALSE,
112967707Seric 	"FallbackMXhost",	'V',		FALSE,
113067707Seric 	"Verbose",		'v',		TRUE,
113167707Seric 	"TryNullMXList",	'w',		TRUE,
113267707Seric 	"QueueLA",		'x',		FALSE,
113367707Seric 	"RefuseLA",		'X',		FALSE,
113467707Seric 	"RecipientFactor",	'y',		FALSE,
113567707Seric 	"ForkQueueRuns",	'Y',		FALSE,
113667707Seric 	"ClassFactor",		'z',		FALSE,
113767707Seric 	"TimeFactor",		'Z',		FALSE,
113867707Seric #define O_BSP		0x80
113967707Seric 	"BrokenSmtpPeers",	O_BSP,		TRUE,
114067707Seric #define O_SQBH		0x81
114167707Seric 	"SortQueueByHost",	O_SQBH,		TRUE,
114267707Seric #define O_DNICE		0x82
114367707Seric 	"DeliveryNiceness",	O_DNICE,	TRUE,
114467707Seric #define O_MQA		0x83
114567707Seric 	"MinQueueAge",		O_MQA,		TRUE,
114667707Seric #define O_MHSA		0x84
114767707Seric 	"MaxHostStatAge",	O_MHSA,		TRUE,
114867707Seric 
114967707Seric 	NULL,			'\0',		FALSE,
115067614Seric };
115167614Seric 
115267614Seric 
115367614Seric 
115458734Seric setoption(opt, val, safe, sticky, e)
115567614Seric 	u_char opt;
11568256Seric 	char *val;
115721755Seric 	bool safe;
11588269Seric 	bool sticky;
115958734Seric 	register ENVELOPE *e;
11608256Seric {
116157207Seric 	register char *p;
116267614Seric 	register struct optioninfo *o;
11638265Seric 	extern bool atobool();
116412633Seric 	extern time_t convtime();
116514879Seric 	extern int QueueLA;
116614879Seric 	extern int RefuseLA;
116764718Seric 	extern bool Warn_Q_option;
11688256Seric 
116967736Seric 	errno = 0;
117067614Seric 	if (opt == ' ')
117167614Seric 	{
117267614Seric 		/* full word options */
117367736Seric 		struct optioninfo *sel;
117467614Seric 
117567614Seric 		p = strchr(val, '=');
117667614Seric 		if (p == NULL)
117767614Seric 			p = &val[strlen(val)];
117867614Seric 		while (*--p == ' ')
117967614Seric 			continue;
118067614Seric 		while (*++p == ' ')
118167614Seric 			*p = '\0';
118267731Seric 		if (p == val)
118367731Seric 		{
118467731Seric 			syserr("readcf: null option name");
118567731Seric 			return;
118667731Seric 		}
118767614Seric 		if (*p == '=')
118867614Seric 			*p++ = '\0';
118967614Seric 		while (*p == ' ')
119067614Seric 			p++;
119167736Seric 		sel = NULL;
119267614Seric 		for (o = OptionTab; o->o_name != NULL; o++)
119367614Seric 		{
119467736Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
119567736Seric 				continue;
119667736Seric 			if (strlen(o->o_name) == strlen(val))
119767736Seric 			{
119867736Seric 				/* completely specified -- this must be it */
119967736Seric 				sel = NULL;
120067614Seric 				break;
120167736Seric 			}
120267736Seric 			if (sel != NULL)
120367736Seric 				break;
120467736Seric 			sel = o;
120567614Seric 		}
120667736Seric 		if (sel != NULL && o->o_name == NULL)
120767736Seric 			o = sel;
120867736Seric 		else if (o->o_name == NULL)
1209*67787Seric 		{
121067614Seric 			syserr("readcf: unknown option name %s", val);
1211*67787Seric 			return;
1212*67787Seric 		}
121367736Seric 		else if (sel != NULL)
121467736Seric 		{
121567736Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
121667736Seric 				val, sel->o_name, o->o_name);
121767736Seric 			return;
121867736Seric 		}
121967736Seric 		if (strlen(val) != strlen(o->o_name))
122067736Seric 		{
122167736Seric 			bool oldVerbose = Verbose;
122267736Seric 
122367736Seric 			Verbose = TRUE;
122467736Seric 			message("Option %s used as abbreviation for %s",
122567736Seric 				val, o->o_name);
122667736Seric 			Verbose = oldVerbose;
122767736Seric 		}
122867614Seric 		opt = o->o_code;
122967614Seric 		val = p;
123067614Seric 	}
123167614Seric 	else
123267614Seric 	{
123367614Seric 		for (o = OptionTab; o->o_name != NULL; o++)
123467614Seric 		{
123567614Seric 			if (o->o_code == opt)
123667614Seric 				break;
123767614Seric 		}
123867614Seric 	}
123967614Seric 
12408256Seric 	if (tTd(37, 1))
124167731Seric 	{
124267731Seric 		printf(isascii(opt) && isprint(opt) ?
124367731Seric 			    "setoption %s (%c)=%s" : "setoption %s (0x%x)=%s",
124467614Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
124567614Seric 			opt, val);
124667731Seric 	}
12478256Seric 
12488256Seric 	/*
12498269Seric 	**  See if this option is preset for us.
12508256Seric 	*/
12518256Seric 
125259731Seric 	if (!sticky && bitnset(opt, StickyOpt))
12538269Seric 	{
12549341Seric 		if (tTd(37, 1))
12559341Seric 			printf(" (ignored)\n");
12568269Seric 		return;
12578269Seric 	}
12588269Seric 
125921755Seric 	/*
126021755Seric 	**  Check to see if this option can be specified by this user.
126121755Seric 	*/
126221755Seric 
126363787Seric 	if (!safe && RealUid == 0)
126421755Seric 		safe = TRUE;
126567614Seric 	if (!safe && !o->o_safe)
126621755Seric 	{
126739111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
126821755Seric 		{
126936582Sbostic 			if (tTd(37, 1))
127036582Sbostic 				printf(" (unsafe)");
127163787Seric 			if (RealUid != geteuid())
127236582Sbostic 			{
127351210Seric 				if (tTd(37, 1))
127451210Seric 					printf("(Resetting uid)");
127563787Seric 				(void) setgid(RealGid);
127663787Seric 				(void) setuid(RealUid);
127736582Sbostic 			}
127821755Seric 		}
127921755Seric 	}
128051210Seric 	if (tTd(37, 1))
128117985Seric 		printf("\n");
12828269Seric 
128367614Seric 	switch (opt & 0xff)
12848256Seric 	{
128559709Seric 	  case '7':		/* force seven-bit input */
128667546Seric 		SevenBitInput = atobool(val);
128752106Seric 		break;
128852106Seric 
128967546Seric 	  case '8':		/* handling of 8-bit input */
129067546Seric 		switch (*val)
129167546Seric 		{
129267547Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
129367546Seric 			MimeMode = 0;
129467546Seric 			break;
129567546Seric 
129667547Seric 		  case 'm':		/* convert 8-bit, convert MIME */
129767546Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
129867546Seric 			break;
129967546Seric 
130067547Seric 		  case 'j':		/* "just send 8" */
130167546Seric 			MimeMode = MM_PASS8BIT;
130267546Seric 			break;
130367546Seric 
130467546Seric 		  case 'p':		/* pass 8 bit, convert MIME */
130567546Seric 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
130667546Seric 			break;
130767546Seric 
130867546Seric 		  case 's':		/* strict adherence */
130967546Seric 			MimeMode = MM_CVTMIME;
131067546Seric 			break;
131167546Seric 
131267547Seric 		  case 'a':		/* encode 8 bit if available */
131367546Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
131467546Seric 			break;
131567546Seric 
131667547Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
131767547Seric 			MimeMode = MM_MIME8BIT;
131867547Seric 			break;
131967547Seric 
132067546Seric 		  default:
132167546Seric 			syserr("Unknown 8-bit mode %c", *val);
132267546Seric 			exit(EX_USAGE);
132367546Seric 		}
132467546Seric 		break;
132567546Seric 
13268256Seric 	  case 'A':		/* set default alias file */
13279381Seric 		if (val[0] == '\0')
132859672Seric 			setalias("aliases");
13299381Seric 		else
133059672Seric 			setalias(val);
13318256Seric 		break;
13328256Seric 
133317474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
133417474Seric 		if (val[0] == '\0')
133564796Seric 			SafeAlias = 5 * 60;		/* five minutes */
133617474Seric 		else
133764796Seric 			SafeAlias = convtime(val, 'm');
133817474Seric 		break;
133917474Seric 
134016843Seric 	  case 'B':		/* substitution for blank character */
134116843Seric 		SpaceSub = val[0];
134216843Seric 		if (SpaceSub == '\0')
134316843Seric 			SpaceSub = ' ';
134416843Seric 		break;
134516843Seric 
134659283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
134759283Seric 		p = strchr(val, '/');
134859283Seric 		if (p != NULL)
134959283Seric 		{
135059283Seric 			*p++ = '\0';
135159283Seric 			MaxMessageSize = atol(p);
135259283Seric 		}
135358082Seric 		MinBlocksFree = atol(val);
135458082Seric 		break;
135558082Seric 
13569284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
13579381Seric 		NoConnect = atobool(val);
13589284Seric 		break;
13599284Seric 
136051305Seric 	  case 'C':		/* checkpoint every N addresses */
136151305Seric 		CheckpointInterval = atoi(val);
136224944Seric 		break;
136324944Seric 
13649284Seric 	  case 'd':		/* delivery mode */
13659284Seric 		switch (*val)
13668269Seric 		{
13679284Seric 		  case '\0':
136858734Seric 			e->e_sendmode = SM_DELIVER;
13698269Seric 			break;
13708269Seric 
137110755Seric 		  case SM_QUEUE:	/* queue only */
137210755Seric #ifndef QUEUE
137310755Seric 			syserr("need QUEUE to set -odqueue");
137456795Seric #endif /* QUEUE */
137510755Seric 			/* fall through..... */
137610755Seric 
13779284Seric 		  case SM_DELIVER:	/* do everything */
13789284Seric 		  case SM_FORK:		/* fork after verification */
137958734Seric 			e->e_sendmode = *val;
13808269Seric 			break;
13818269Seric 
13828269Seric 		  default:
13839284Seric 			syserr("Unknown delivery mode %c", *val);
13848269Seric 			exit(EX_USAGE);
13858269Seric 		}
13868269Seric 		break;
13878269Seric 
13889146Seric 	  case 'D':		/* rebuild alias database as needed */
13899381Seric 		AutoRebuild = atobool(val);
13909146Seric 		break;
13919146Seric 
139255372Seric 	  case 'E':		/* error message header/header file */
139355379Seric 		if (*val != '\0')
139455379Seric 			ErrMsgFile = newstr(val);
139555372Seric 		break;
139655372Seric 
13978269Seric 	  case 'e':		/* set error processing mode */
13988269Seric 		switch (*val)
13998269Seric 		{
14009381Seric 		  case EM_QUIET:	/* be silent about it */
14019381Seric 		  case EM_MAIL:		/* mail back */
14029381Seric 		  case EM_BERKNET:	/* do berknet error processing */
14039381Seric 		  case EM_WRITE:	/* write back (or mail) */
14049381Seric 		  case EM_PRINT:	/* print errors normally (default) */
140558734Seric 			e->e_errormode = *val;
14068269Seric 			break;
14078269Seric 		}
14088269Seric 		break;
14098269Seric 
14109049Seric 	  case 'F':		/* file mode */
141117975Seric 		FileMode = atooct(val) & 0777;
14129049Seric 		break;
14139049Seric 
14148269Seric 	  case 'f':		/* save Unix-style From lines on front */
14159381Seric 		SaveFrom = atobool(val);
14168269Seric 		break;
14178269Seric 
141853735Seric 	  case 'G':		/* match recipients against GECOS field */
141953735Seric 		MatchGecos = atobool(val);
142053735Seric 		break;
142153735Seric 
14228256Seric 	  case 'g':		/* default gid */
142364133Seric 		if (isascii(*val) && isdigit(*val))
142464133Seric 			DefGid = atoi(val);
142564133Seric 		else
142664133Seric 		{
142764133Seric 			register struct group *gr;
142864133Seric 
142964133Seric 			DefGid = -1;
143064133Seric 			gr = getgrnam(val);
143164133Seric 			if (gr == NULL)
143264133Seric 				syserr("readcf: option g: unknown group %s", val);
143364133Seric 			else
143464133Seric 				DefGid = gr->gr_gid;
143564133Seric 		}
14368256Seric 		break;
14378256Seric 
14388256Seric 	  case 'H':		/* help file */
14399381Seric 		if (val[0] == '\0')
14408269Seric 			HelpFile = "sendmail.hf";
14419381Seric 		else
14429381Seric 			HelpFile = newstr(val);
14438256Seric 		break;
14448256Seric 
144551305Seric 	  case 'h':		/* maximum hop count */
144651305Seric 		MaxHopCount = atoi(val);
144751305Seric 		break;
144851305Seric 
144935651Seric 	  case 'I':		/* use internet domain name server */
145066334Seric #if NAMED_BIND
145157207Seric 		UseNameServer = TRUE;
145257207Seric 		for (p = val; *p != 0; )
145357207Seric 		{
145457207Seric 			bool clearmode;
145557207Seric 			char *q;
145657207Seric 			struct resolverflags *rfp;
145757207Seric 
145857207Seric 			while (*p == ' ')
145957207Seric 				p++;
146057207Seric 			if (*p == '\0')
146157207Seric 				break;
146257207Seric 			clearmode = FALSE;
146357207Seric 			if (*p == '-')
146457207Seric 				clearmode = TRUE;
146557207Seric 			else if (*p != '+')
146657207Seric 				p--;
146757207Seric 			p++;
146857207Seric 			q = p;
146958050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
147057207Seric 				p++;
147157207Seric 			if (*p != '\0')
147257207Seric 				*p++ = '\0';
147357207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
147457207Seric 			{
147557207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
147657207Seric 					break;
147757207Seric 			}
147864923Seric 			if (rfp->rf_name == NULL)
147964923Seric 				syserr("readcf: I option value %s unrecognized", q);
148064923Seric 			else if (clearmode)
148157207Seric 				_res.options &= ~rfp->rf_bits;
148257207Seric 			else
148357207Seric 				_res.options |= rfp->rf_bits;
148457207Seric 		}
148557207Seric 		if (tTd(8, 2))
148657207Seric 			printf("_res.options = %x\n", _res.options);
148757207Seric #else
148857207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
148957207Seric #endif
149035651Seric 		break;
149135651Seric 
14928269Seric 	  case 'i':		/* ignore dot lines in message */
14939381Seric 		IgnrDot = atobool(val);
14948269Seric 		break;
14958269Seric 
149659730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
149759730Seric 		SendMIMEErrors = atobool(val);
149859730Seric 		break;
149959730Seric 
150057136Seric 	  case 'J':		/* .forward search path */
150157136Seric 		ForwardPath = newstr(val);
150257136Seric 		break;
150357136Seric 
150454967Seric 	  case 'k':		/* connection cache size */
150554967Seric 		MaxMciCache = atoi(val);
150656215Seric 		if (MaxMciCache < 0)
150756215Seric 			MaxMciCache = 0;
150854967Seric 		break;
150954967Seric 
151054967Seric 	  case 'K':		/* connection cache timeout */
151158796Seric 		MciCacheTimeout = convtime(val, 'm');
151254967Seric 		break;
151354967Seric 
151461104Seric 	  case 'l':		/* use Errors-To: header */
151561104Seric 		UseErrorsTo = atobool(val);
151661104Seric 		break;
151761104Seric 
15188256Seric 	  case 'L':		/* log level */
151964140Seric 		if (safe || LogLevel < atoi(val))
152064140Seric 			LogLevel = atoi(val);
15218256Seric 		break;
15228256Seric 
15238269Seric 	  case 'M':		/* define macro */
15249381Seric 		define(val[0], newstr(&val[1]), CurEnv);
152516878Seric 		sticky = FALSE;
15268269Seric 		break;
15278269Seric 
15288269Seric 	  case 'm':		/* send to me too */
15299381Seric 		MeToo = atobool(val);
15308269Seric 		break;
15318269Seric 
153225820Seric 	  case 'n':		/* validate RHS in newaliases */
153325820Seric 		CheckAliases = atobool(val);
153425820Seric 		break;
153525820Seric 
153661104Seric 	    /* 'N' available -- was "net name" */
153761104Seric 
153858851Seric 	  case 'O':		/* daemon options */
153958851Seric 		setdaemonoptions(val);
154058851Seric 		break;
154158851Seric 
15428269Seric 	  case 'o':		/* assume old style headers */
15439381Seric 		if (atobool(val))
15449341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
15459341Seric 		else
15469341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
15478269Seric 		break;
15488269Seric 
154958082Seric 	  case 'p':		/* select privacy level */
155058082Seric 		p = val;
155158082Seric 		for (;;)
155258082Seric 		{
155358082Seric 			register struct prival *pv;
155458082Seric 			extern struct prival PrivacyValues[];
155558082Seric 
155658082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
155758082Seric 				p++;
155858082Seric 			if (*p == '\0')
155958082Seric 				break;
156058082Seric 			val = p;
156158082Seric 			while (isascii(*p) && isalnum(*p))
156258082Seric 				p++;
156358082Seric 			if (*p != '\0')
156458082Seric 				*p++ = '\0';
156558082Seric 
156658082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
156758082Seric 			{
156858082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
156958082Seric 					break;
157058082Seric 			}
157158886Seric 			if (pv->pv_name == NULL)
157258886Seric 				syserr("readcf: Op line: %s unrecognized", val);
157358082Seric 			PrivacyFlags |= pv->pv_flag;
157458082Seric 		}
157558082Seric 		break;
157658082Seric 
157724944Seric 	  case 'P':		/* postmaster copy address for returned mail */
157824944Seric 		PostMasterCopy = newstr(val);
157924944Seric 		break;
158024944Seric 
158124944Seric 	  case 'q':		/* slope of queue only function */
158224944Seric 		QueueFactor = atoi(val);
158324944Seric 		break;
158424944Seric 
15858256Seric 	  case 'Q':		/* queue directory */
15869381Seric 		if (val[0] == '\0')
15878269Seric 			QueueDir = "mqueue";
15889381Seric 		else
15899381Seric 			QueueDir = newstr(val);
159058789Seric 		if (RealUid != 0 && !safe)
159164718Seric 			Warn_Q_option = TRUE;
15928256Seric 		break;
15938256Seric 
159458148Seric 	  case 'R':		/* don't prune routes */
159558148Seric 		DontPruneRoutes = atobool(val);
159658148Seric 		break;
159758148Seric 
15988256Seric 	  case 'r':		/* read timeout */
159958112Seric 		settimeouts(val);
16008256Seric 		break;
16018256Seric 
16028256Seric 	  case 'S':		/* status file */
16039381Seric 		if (val[0] == '\0')
16048269Seric 			StatFile = "sendmail.st";
16059381Seric 		else
16069381Seric 			StatFile = newstr(val);
16078256Seric 		break;
16088256Seric 
16098265Seric 	  case 's':		/* be super safe, even if expensive */
16109381Seric 		SuperSafe = atobool(val);
16118256Seric 		break;
16128256Seric 
16138256Seric 	  case 'T':		/* queue timeout */
161458737Seric 		p = strchr(val, '/');
161558737Seric 		if (p != NULL)
161658737Seric 		{
161758737Seric 			*p++ = '\0';
161867730Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = convtime(p, 'd');
161958737Seric 		}
162067730Seric 		TimeOuts.to_q_return[TOC_NORMAL] = convtime(val, 'h');
162154967Seric 		break;
16228256Seric 
16238265Seric 	  case 't':		/* time zone name */
162452106Seric 		TimeZoneSpec = newstr(val);
16258265Seric 		break;
16268265Seric 
162750556Seric 	  case 'U':		/* location of user database */
162851360Seric 		UdbSpec = newstr(val);
162950556Seric 		break;
163050556Seric 
16318256Seric 	  case 'u':		/* set default uid */
163264133Seric 		if (isascii(*val) && isdigit(*val))
163364133Seric 			DefUid = atoi(val);
163464133Seric 		else
163564133Seric 		{
163664133Seric 			register struct passwd *pw;
163764133Seric 
163864133Seric 			DefUid = -1;
163964133Seric 			pw = getpwnam(val);
164064133Seric 			if (pw == NULL)
164164133Seric 				syserr("readcf: option u: unknown user %s", val);
164264133Seric 			else
164364133Seric 				DefUid = pw->pw_uid;
164464133Seric 		}
164540973Sbostic 		setdefuser();
16468256Seric 		break;
16478256Seric 
164858851Seric 	  case 'V':		/* fallback MX host */
164958851Seric 		FallBackMX = newstr(val);
165058851Seric 		break;
165158851Seric 
16528269Seric 	  case 'v':		/* run in verbose mode */
16539381Seric 		Verbose = atobool(val);
16548256Seric 		break;
16558256Seric 
165663837Seric 	  case 'w':		/* if we are best MX, try host directly */
165763837Seric 		TryNullMXList = atobool(val);
165863837Seric 		break;
165961104Seric 
166061104Seric 	    /* 'W' available -- was wizard password */
166161104Seric 
166214879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
166314879Seric 		QueueLA = atoi(val);
166414879Seric 		break;
166514879Seric 
166614879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
166714879Seric 		RefuseLA = atoi(val);
166814879Seric 		break;
166914879Seric 
167024981Seric 	  case 'y':		/* work recipient factor */
167124981Seric 		WkRecipFact = atoi(val);
167224981Seric 		break;
167324981Seric 
167424981Seric 	  case 'Y':		/* fork jobs during queue runs */
167524952Seric 		ForkQueueRuns = atobool(val);
167624952Seric 		break;
167724952Seric 
167824981Seric 	  case 'z':		/* work message class factor */
167924981Seric 		WkClassFact = atoi(val);
168024981Seric 		break;
168124981Seric 
168224981Seric 	  case 'Z':		/* work time factor */
168324981Seric 		WkTimeFact = atoi(val);
168424981Seric 		break;
168524981Seric 
168667614Seric 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
168767614Seric 		BrokenSmtpPeers = atobool(val);
168867614Seric 		break;
168967614Seric 
169067614Seric 	  case O_SQBH:		/* sort work queue by host first */
169167614Seric 		SortQueueByHost = atobool(val);
169267614Seric 		break;
169367614Seric 
169467707Seric 	  case O_DNICE:		/* delivery nice value */
169567707Seric 		DeliveryNiceness = atoi(val);
169667707Seric 		break;
169767707Seric 
169867707Seric 	  case O_MQA:		/* minimum queue age between deliveries */
169967707Seric 		MinQueueAge = convtime(val, 'm');
170067707Seric 		break;
170167707Seric 
170267707Seric 	  case O_MHSA:		/* maximum age of cached host status */
170367707Seric 		MaxHostStatAge = convtime(val, 'm');
170467707Seric 		break;
170567707Seric 
17068256Seric 	  default:
17078256Seric 		break;
17088256Seric 	}
170916878Seric 	if (sticky)
171016878Seric 		setbitn(opt, StickyOpt);
17119188Seric 	return;
17128256Seric }
171310687Seric /*
171410687Seric **  SETCLASS -- set a word into a class
171510687Seric **
171610687Seric **	Parameters:
171710687Seric **		class -- the class to put the word in.
171810687Seric **		word -- the word to enter
171910687Seric **
172010687Seric **	Returns:
172110687Seric **		none.
172210687Seric **
172310687Seric **	Side Effects:
172410687Seric **		puts the word into the symbol table.
172510687Seric */
172610687Seric 
172710687Seric setclass(class, word)
172810687Seric 	int class;
172910687Seric 	char *word;
173010687Seric {
173110687Seric 	register STAB *s;
173210687Seric 
173357943Seric 	if (tTd(37, 8))
173464326Seric 		printf("setclass(%c, %s)\n", class, word);
173510687Seric 	s = stab(word, ST_CLASS, ST_ENTER);
173610687Seric 	setbitn(class, s->s_class);
173710687Seric }
173853654Seric /*
173953654Seric **  MAKEMAPENTRY -- create a map entry
174053654Seric **
174153654Seric **	Parameters:
174253654Seric **		line -- the config file line
174353654Seric **
174453654Seric **	Returns:
174553654Seric **		TRUE if it successfully entered the map entry.
174653654Seric **		FALSE otherwise (usually syntax error).
174753654Seric **
174853654Seric **	Side Effects:
174953654Seric **		Enters the map into the dictionary.
175053654Seric */
175153654Seric 
175253654Seric void
175353654Seric makemapentry(line)
175453654Seric 	char *line;
175553654Seric {
175653654Seric 	register char *p;
175753654Seric 	char *mapname;
175853654Seric 	char *classname;
175964078Seric 	register STAB *s;
176053654Seric 	STAB *class;
176153654Seric 
176258050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
176353654Seric 		continue;
176458050Seric 	if (!(isascii(*p) && isalnum(*p)))
176553654Seric 	{
176653654Seric 		syserr("readcf: config K line: no map name");
176753654Seric 		return;
176853654Seric 	}
176953654Seric 
177053654Seric 	mapname = p;
177158050Seric 	while (isascii(*++p) && isalnum(*p))
177253654Seric 		continue;
177353654Seric 	if (*p != '\0')
177453654Seric 		*p++ = '\0';
177558050Seric 	while (isascii(*p) && isspace(*p))
177653654Seric 		p++;
177758050Seric 	if (!(isascii(*p) && isalnum(*p)))
177853654Seric 	{
177953654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
178053654Seric 		return;
178153654Seric 	}
178253654Seric 	classname = p;
178358050Seric 	while (isascii(*++p) && isalnum(*p))
178453654Seric 		continue;
178553654Seric 	if (*p != '\0')
178653654Seric 		*p++ = '\0';
178758050Seric 	while (isascii(*p) && isspace(*p))
178853654Seric 		p++;
178953654Seric 
179053654Seric 	/* look up the class */
179153654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
179253654Seric 	if (class == NULL)
179353654Seric 	{
179453654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
179553654Seric 		return;
179653654Seric 	}
179753654Seric 
179853654Seric 	/* enter the map */
179964078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
180064078Seric 	s->s_map.map_class = &class->s_mapclass;
180164078Seric 	s->s_map.map_mname = newstr(mapname);
180253654Seric 
180364078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
180464078Seric 		s->s_map.map_mflags |= MF_VALID;
180564078Seric 
180664078Seric 	if (tTd(37, 5))
180764078Seric 	{
180864078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
180964078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
181064078Seric 			s->s_map.map_mflags,
181164078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
181264078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
181364078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
181464078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
181564078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
181664078Seric 	}
181753654Seric }
181858112Seric /*
181958112Seric **  SETTIMEOUTS -- parse and set timeout values
182058112Seric **
182158112Seric **	Parameters:
182258112Seric **		val -- a pointer to the values.  If NULL, do initial
182358112Seric **			settings.
182458112Seric **
182558112Seric **	Returns:
182658112Seric **		none.
182758112Seric **
182858112Seric **	Side Effects:
182958112Seric **		Initializes the TimeOuts structure
183058112Seric */
183158112Seric 
183264255Seric #define SECONDS
183358112Seric #define MINUTES	* 60
183458112Seric #define HOUR	* 3600
183558112Seric 
183658112Seric settimeouts(val)
183758112Seric 	register char *val;
183858112Seric {
183958112Seric 	register char *p;
184058671Seric 	extern time_t convtime();
184158112Seric 
184258112Seric 	if (val == NULL)
184358112Seric 	{
184458112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
184558112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
184658112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
184758112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
184858112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
184958112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
185058112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
185158112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
185258112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
185358112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
185458112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
185564255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
185667711Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
185758112Seric 		return;
185858112Seric 	}
185958112Seric 
186058112Seric 	for (;; val = p)
186158112Seric 	{
186258112Seric 		while (isascii(*val) && isspace(*val))
186358112Seric 			val++;
186458112Seric 		if (*val == '\0')
186558112Seric 			break;
186658112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
186758112Seric 			continue;
186858112Seric 		if (*p != '\0')
186958112Seric 			*p++ = '\0';
187058112Seric 
187158112Seric 		if (isascii(*val) && isdigit(*val))
187258112Seric 		{
187358112Seric 			/* old syntax -- set everything */
187458796Seric 			TimeOuts.to_mail = convtime(val, 'm');
187558112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
187658112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
187758112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
187858112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
187958112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
188058112Seric 			continue;
188158112Seric 		}
188258112Seric 		else
188358112Seric 		{
188467711Seric 			register char *q = strchr(val, ':');
188558112Seric 			time_t to;
188658112Seric 
188767711Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
188858112Seric 			{
188958112Seric 				/* syntax error */
189058112Seric 				continue;
189158112Seric 			}
189258112Seric 			*q++ = '\0';
189358796Seric 			to = convtime(q, 'm');
189458112Seric 
189558112Seric 			if (strcasecmp(val, "initial") == 0)
189658112Seric 				TimeOuts.to_initial = to;
189758112Seric 			else if (strcasecmp(val, "mail") == 0)
189858112Seric 				TimeOuts.to_mail = to;
189958112Seric 			else if (strcasecmp(val, "rcpt") == 0)
190058112Seric 				TimeOuts.to_rcpt = to;
190158112Seric 			else if (strcasecmp(val, "datainit") == 0)
190258112Seric 				TimeOuts.to_datainit = to;
190358112Seric 			else if (strcasecmp(val, "datablock") == 0)
190458112Seric 				TimeOuts.to_datablock = to;
190558112Seric 			else if (strcasecmp(val, "datafinal") == 0)
190658112Seric 				TimeOuts.to_datafinal = to;
190758112Seric 			else if (strcasecmp(val, "command") == 0)
190858112Seric 				TimeOuts.to_nextcommand = to;
190958112Seric 			else if (strcasecmp(val, "rset") == 0)
191058112Seric 				TimeOuts.to_rset = to;
191158112Seric 			else if (strcasecmp(val, "helo") == 0)
191258112Seric 				TimeOuts.to_helo = to;
191358112Seric 			else if (strcasecmp(val, "quit") == 0)
191458112Seric 				TimeOuts.to_quit = to;
191558112Seric 			else if (strcasecmp(val, "misc") == 0)
191658112Seric 				TimeOuts.to_miscshort = to;
191764255Seric 			else if (strcasecmp(val, "ident") == 0)
191864255Seric 				TimeOuts.to_ident = to;
191967711Seric 			else if (strcasecmp(val, "fileopen") == 0)
192067711Seric 				TimeOuts.to_fileopen = to;
192167711Seric 			else if (strcasecmp(val, "queuewarn") == 0)
192267730Seric 				TimeOuts.to_q_warning[TOC_NORMAL] = to;
192367711Seric 			else if (strcasecmp(val, "queuereturn") == 0)
192467730Seric 				TimeOuts.to_q_return[TOC_NORMAL] = to;
192567730Seric 			else if (strcasecmp(val, "queuewarn.normal") == 0)
192667730Seric 				TimeOuts.to_q_warning[TOC_NORMAL] = to;
192767730Seric 			else if (strcasecmp(val, "queuereturn.normal") == 0)
192867730Seric 				TimeOuts.to_q_return[TOC_NORMAL] = to;
192967730Seric 			else if (strcasecmp(val, "queuewarn.urgent") == 0)
193067730Seric 				TimeOuts.to_q_warning[TOC_URGENT] = to;
193167730Seric 			else if (strcasecmp(val, "queuereturn.urgent") == 0)
193267730Seric 				TimeOuts.to_q_return[TOC_URGENT] = to;
193367730Seric 			else if (strcasecmp(val, "queuewarn.non-urgent") == 0)
193467730Seric 				TimeOuts.to_q_warning[TOC_NONURGENT] = to;
193567730Seric 			else if (strcasecmp(val, "queuereturn.non-urgent") == 0)
193667730Seric 				TimeOuts.to_q_return[TOC_NONURGENT] = to;
193758112Seric 			else
193858112Seric 				syserr("settimeouts: invalid timeout %s", val);
193958112Seric 		}
194058112Seric 	}
194158112Seric }
1942