xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 67814)
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*67814Seric static char sccsid[] = "@(#)readcf.c	8.41 (Berkeley) 10/15/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 */
108467787Seric 	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,
114867813Seric #define O_DEFCHARSET	0x85
114967813Seric 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
115067707Seric 
115167707Seric 	NULL,			'\0',		FALSE,
115267614Seric };
115367614Seric 
115467614Seric 
115567614Seric 
115658734Seric setoption(opt, val, safe, sticky, e)
115767614Seric 	u_char opt;
11588256Seric 	char *val;
115921755Seric 	bool safe;
11608269Seric 	bool sticky;
116158734Seric 	register ENVELOPE *e;
11628256Seric {
116357207Seric 	register char *p;
116467614Seric 	register struct optioninfo *o;
11658265Seric 	extern bool atobool();
116612633Seric 	extern time_t convtime();
116714879Seric 	extern int QueueLA;
116814879Seric 	extern int RefuseLA;
116964718Seric 	extern bool Warn_Q_option;
11708256Seric 
117167736Seric 	errno = 0;
117267614Seric 	if (opt == ' ')
117367614Seric 	{
117467614Seric 		/* full word options */
117567736Seric 		struct optioninfo *sel;
117667614Seric 
117767614Seric 		p = strchr(val, '=');
117867614Seric 		if (p == NULL)
117967614Seric 			p = &val[strlen(val)];
118067614Seric 		while (*--p == ' ')
118167614Seric 			continue;
118267614Seric 		while (*++p == ' ')
118367614Seric 			*p = '\0';
118467731Seric 		if (p == val)
118567731Seric 		{
118667731Seric 			syserr("readcf: null option name");
118767731Seric 			return;
118867731Seric 		}
118967614Seric 		if (*p == '=')
119067614Seric 			*p++ = '\0';
119167614Seric 		while (*p == ' ')
119267614Seric 			p++;
119367736Seric 		sel = NULL;
119467614Seric 		for (o = OptionTab; o->o_name != NULL; o++)
119567614Seric 		{
119667736Seric 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
119767736Seric 				continue;
119867736Seric 			if (strlen(o->o_name) == strlen(val))
119967736Seric 			{
120067736Seric 				/* completely specified -- this must be it */
120167736Seric 				sel = NULL;
120267614Seric 				break;
120367736Seric 			}
120467736Seric 			if (sel != NULL)
120567736Seric 				break;
120667736Seric 			sel = o;
120767614Seric 		}
120867736Seric 		if (sel != NULL && o->o_name == NULL)
120967736Seric 			o = sel;
121067736Seric 		else if (o->o_name == NULL)
121167787Seric 		{
121267614Seric 			syserr("readcf: unknown option name %s", val);
121367787Seric 			return;
121467787Seric 		}
121567736Seric 		else if (sel != NULL)
121667736Seric 		{
121767736Seric 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
121867736Seric 				val, sel->o_name, o->o_name);
121967736Seric 			return;
122067736Seric 		}
122167736Seric 		if (strlen(val) != strlen(o->o_name))
122267736Seric 		{
122367736Seric 			bool oldVerbose = Verbose;
122467736Seric 
122567736Seric 			Verbose = TRUE;
122667736Seric 			message("Option %s used as abbreviation for %s",
122767736Seric 				val, o->o_name);
122867736Seric 			Verbose = oldVerbose;
122967736Seric 		}
123067614Seric 		opt = o->o_code;
123167614Seric 		val = p;
123267614Seric 	}
123367614Seric 	else
123467614Seric 	{
123567614Seric 		for (o = OptionTab; o->o_name != NULL; o++)
123667614Seric 		{
123767614Seric 			if (o->o_code == opt)
123867614Seric 				break;
123967614Seric 		}
124067614Seric 	}
124167614Seric 
12428256Seric 	if (tTd(37, 1))
124367731Seric 	{
124467731Seric 		printf(isascii(opt) && isprint(opt) ?
124567731Seric 			    "setoption %s (%c)=%s" : "setoption %s (0x%x)=%s",
124667614Seric 			o->o_name == NULL ? "<unknown>" : o->o_name,
124767614Seric 			opt, val);
124867731Seric 	}
12498256Seric 
12508256Seric 	/*
12518269Seric 	**  See if this option is preset for us.
12528256Seric 	*/
12538256Seric 
125459731Seric 	if (!sticky && bitnset(opt, StickyOpt))
12558269Seric 	{
12569341Seric 		if (tTd(37, 1))
12579341Seric 			printf(" (ignored)\n");
12588269Seric 		return;
12598269Seric 	}
12608269Seric 
126121755Seric 	/*
126221755Seric 	**  Check to see if this option can be specified by this user.
126321755Seric 	*/
126421755Seric 
126563787Seric 	if (!safe && RealUid == 0)
126621755Seric 		safe = TRUE;
126767614Seric 	if (!safe && !o->o_safe)
126821755Seric 	{
126939111Srick 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
127021755Seric 		{
127136582Sbostic 			if (tTd(37, 1))
127236582Sbostic 				printf(" (unsafe)");
127363787Seric 			if (RealUid != geteuid())
127436582Sbostic 			{
127551210Seric 				if (tTd(37, 1))
127651210Seric 					printf("(Resetting uid)");
127763787Seric 				(void) setgid(RealGid);
127863787Seric 				(void) setuid(RealUid);
127936582Sbostic 			}
128021755Seric 		}
128121755Seric 	}
128251210Seric 	if (tTd(37, 1))
128317985Seric 		printf("\n");
12848269Seric 
128567614Seric 	switch (opt & 0xff)
12868256Seric 	{
128759709Seric 	  case '7':		/* force seven-bit input */
128867546Seric 		SevenBitInput = atobool(val);
128952106Seric 		break;
129052106Seric 
129167546Seric 	  case '8':		/* handling of 8-bit input */
129267546Seric 		switch (*val)
129367546Seric 		{
129467547Seric 		  case 'r':		/* reject 8-bit, don't convert MIME */
129567546Seric 			MimeMode = 0;
129667546Seric 			break;
129767546Seric 
129867547Seric 		  case 'm':		/* convert 8-bit, convert MIME */
129967546Seric 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
130067546Seric 			break;
130167546Seric 
130267547Seric 		  case 'j':		/* "just send 8" */
130367546Seric 			MimeMode = MM_PASS8BIT;
130467546Seric 			break;
130567546Seric 
130667546Seric 		  case 'p':		/* pass 8 bit, convert MIME */
130767546Seric 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
130867546Seric 			break;
130967546Seric 
131067546Seric 		  case 's':		/* strict adherence */
131167546Seric 			MimeMode = MM_CVTMIME;
131267546Seric 			break;
131367546Seric 
131467547Seric 		  case 'a':		/* encode 8 bit if available */
131567546Seric 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
131667546Seric 			break;
131767546Seric 
131867547Seric 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
131967547Seric 			MimeMode = MM_MIME8BIT;
132067547Seric 			break;
132167547Seric 
132267546Seric 		  default:
132367546Seric 			syserr("Unknown 8-bit mode %c", *val);
132467546Seric 			exit(EX_USAGE);
132567546Seric 		}
132667546Seric 		break;
132767546Seric 
13288256Seric 	  case 'A':		/* set default alias file */
13299381Seric 		if (val[0] == '\0')
133059672Seric 			setalias("aliases");
13319381Seric 		else
133259672Seric 			setalias(val);
13338256Seric 		break;
13348256Seric 
133517474Seric 	  case 'a':		/* look N minutes for "@:@" in alias file */
133617474Seric 		if (val[0] == '\0')
133764796Seric 			SafeAlias = 5 * 60;		/* five minutes */
133817474Seric 		else
133964796Seric 			SafeAlias = convtime(val, 'm');
134017474Seric 		break;
134117474Seric 
134216843Seric 	  case 'B':		/* substitution for blank character */
134316843Seric 		SpaceSub = val[0];
134416843Seric 		if (SpaceSub == '\0')
134516843Seric 			SpaceSub = ' ';
134616843Seric 		break;
134716843Seric 
134859283Seric 	  case 'b':		/* min blocks free on queue fs/max msg size */
134959283Seric 		p = strchr(val, '/');
135059283Seric 		if (p != NULL)
135159283Seric 		{
135259283Seric 			*p++ = '\0';
135359283Seric 			MaxMessageSize = atol(p);
135459283Seric 		}
135558082Seric 		MinBlocksFree = atol(val);
135658082Seric 		break;
135758082Seric 
13589284Seric 	  case 'c':		/* don't connect to "expensive" mailers */
13599381Seric 		NoConnect = atobool(val);
13609284Seric 		break;
13619284Seric 
136251305Seric 	  case 'C':		/* checkpoint every N addresses */
136351305Seric 		CheckpointInterval = atoi(val);
136424944Seric 		break;
136524944Seric 
13669284Seric 	  case 'd':		/* delivery mode */
13679284Seric 		switch (*val)
13688269Seric 		{
13699284Seric 		  case '\0':
137058734Seric 			e->e_sendmode = SM_DELIVER;
13718269Seric 			break;
13728269Seric 
137310755Seric 		  case SM_QUEUE:	/* queue only */
137410755Seric #ifndef QUEUE
137510755Seric 			syserr("need QUEUE to set -odqueue");
137656795Seric #endif /* QUEUE */
137710755Seric 			/* fall through..... */
137810755Seric 
13799284Seric 		  case SM_DELIVER:	/* do everything */
13809284Seric 		  case SM_FORK:		/* fork after verification */
138158734Seric 			e->e_sendmode = *val;
13828269Seric 			break;
13838269Seric 
13848269Seric 		  default:
13859284Seric 			syserr("Unknown delivery mode %c", *val);
13868269Seric 			exit(EX_USAGE);
13878269Seric 		}
13888269Seric 		break;
13898269Seric 
13909146Seric 	  case 'D':		/* rebuild alias database as needed */
13919381Seric 		AutoRebuild = atobool(val);
13929146Seric 		break;
13939146Seric 
139455372Seric 	  case 'E':		/* error message header/header file */
139555379Seric 		if (*val != '\0')
139655379Seric 			ErrMsgFile = newstr(val);
139755372Seric 		break;
139855372Seric 
13998269Seric 	  case 'e':		/* set error processing mode */
14008269Seric 		switch (*val)
14018269Seric 		{
14029381Seric 		  case EM_QUIET:	/* be silent about it */
14039381Seric 		  case EM_MAIL:		/* mail back */
14049381Seric 		  case EM_BERKNET:	/* do berknet error processing */
14059381Seric 		  case EM_WRITE:	/* write back (or mail) */
14069381Seric 		  case EM_PRINT:	/* print errors normally (default) */
140758734Seric 			e->e_errormode = *val;
14088269Seric 			break;
14098269Seric 		}
14108269Seric 		break;
14118269Seric 
14129049Seric 	  case 'F':		/* file mode */
141317975Seric 		FileMode = atooct(val) & 0777;
14149049Seric 		break;
14159049Seric 
14168269Seric 	  case 'f':		/* save Unix-style From lines on front */
14179381Seric 		SaveFrom = atobool(val);
14188269Seric 		break;
14198269Seric 
142053735Seric 	  case 'G':		/* match recipients against GECOS field */
142153735Seric 		MatchGecos = atobool(val);
142253735Seric 		break;
142353735Seric 
14248256Seric 	  case 'g':		/* default gid */
142564133Seric 		if (isascii(*val) && isdigit(*val))
142664133Seric 			DefGid = atoi(val);
142764133Seric 		else
142864133Seric 		{
142964133Seric 			register struct group *gr;
143064133Seric 
143164133Seric 			DefGid = -1;
143264133Seric 			gr = getgrnam(val);
143364133Seric 			if (gr == NULL)
143464133Seric 				syserr("readcf: option g: unknown group %s", val);
143564133Seric 			else
143664133Seric 				DefGid = gr->gr_gid;
143764133Seric 		}
14388256Seric 		break;
14398256Seric 
14408256Seric 	  case 'H':		/* help file */
14419381Seric 		if (val[0] == '\0')
14428269Seric 			HelpFile = "sendmail.hf";
14439381Seric 		else
14449381Seric 			HelpFile = newstr(val);
14458256Seric 		break;
14468256Seric 
144751305Seric 	  case 'h':		/* maximum hop count */
144851305Seric 		MaxHopCount = atoi(val);
144951305Seric 		break;
145051305Seric 
145135651Seric 	  case 'I':		/* use internet domain name server */
145266334Seric #if NAMED_BIND
145357207Seric 		UseNameServer = TRUE;
145457207Seric 		for (p = val; *p != 0; )
145557207Seric 		{
145657207Seric 			bool clearmode;
145757207Seric 			char *q;
145857207Seric 			struct resolverflags *rfp;
145957207Seric 
146057207Seric 			while (*p == ' ')
146157207Seric 				p++;
146257207Seric 			if (*p == '\0')
146357207Seric 				break;
146457207Seric 			clearmode = FALSE;
146557207Seric 			if (*p == '-')
146657207Seric 				clearmode = TRUE;
146757207Seric 			else if (*p != '+')
146857207Seric 				p--;
146957207Seric 			p++;
147057207Seric 			q = p;
147158050Seric 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
147257207Seric 				p++;
147357207Seric 			if (*p != '\0')
147457207Seric 				*p++ = '\0';
147557207Seric 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
147657207Seric 			{
147757207Seric 				if (strcasecmp(q, rfp->rf_name) == 0)
147857207Seric 					break;
147957207Seric 			}
148064923Seric 			if (rfp->rf_name == NULL)
148164923Seric 				syserr("readcf: I option value %s unrecognized", q);
148264923Seric 			else if (clearmode)
148357207Seric 				_res.options &= ~rfp->rf_bits;
148457207Seric 			else
148557207Seric 				_res.options |= rfp->rf_bits;
148657207Seric 		}
148757207Seric 		if (tTd(8, 2))
148857207Seric 			printf("_res.options = %x\n", _res.options);
148957207Seric #else
149057207Seric 		usrerr("name server (I option) specified but BIND not compiled in");
149157207Seric #endif
149235651Seric 		break;
149335651Seric 
14948269Seric 	  case 'i':		/* ignore dot lines in message */
14959381Seric 		IgnrDot = atobool(val);
14968269Seric 		break;
14978269Seric 
149859730Seric 	  case 'j':		/* send errors in MIME (RFC 1341) format */
149959730Seric 		SendMIMEErrors = atobool(val);
150059730Seric 		break;
150159730Seric 
150257136Seric 	  case 'J':		/* .forward search path */
150357136Seric 		ForwardPath = newstr(val);
150457136Seric 		break;
150557136Seric 
150654967Seric 	  case 'k':		/* connection cache size */
150754967Seric 		MaxMciCache = atoi(val);
150856215Seric 		if (MaxMciCache < 0)
150956215Seric 			MaxMciCache = 0;
151054967Seric 		break;
151154967Seric 
151254967Seric 	  case 'K':		/* connection cache timeout */
151358796Seric 		MciCacheTimeout = convtime(val, 'm');
151454967Seric 		break;
151554967Seric 
151661104Seric 	  case 'l':		/* use Errors-To: header */
151761104Seric 		UseErrorsTo = atobool(val);
151861104Seric 		break;
151961104Seric 
15208256Seric 	  case 'L':		/* log level */
152164140Seric 		if (safe || LogLevel < atoi(val))
152264140Seric 			LogLevel = atoi(val);
15238256Seric 		break;
15248256Seric 
15258269Seric 	  case 'M':		/* define macro */
15269381Seric 		define(val[0], newstr(&val[1]), CurEnv);
152716878Seric 		sticky = FALSE;
15288269Seric 		break;
15298269Seric 
15308269Seric 	  case 'm':		/* send to me too */
15319381Seric 		MeToo = atobool(val);
15328269Seric 		break;
15338269Seric 
153425820Seric 	  case 'n':		/* validate RHS in newaliases */
153525820Seric 		CheckAliases = atobool(val);
153625820Seric 		break;
153725820Seric 
153861104Seric 	    /* 'N' available -- was "net name" */
153961104Seric 
154058851Seric 	  case 'O':		/* daemon options */
154158851Seric 		setdaemonoptions(val);
154258851Seric 		break;
154358851Seric 
15448269Seric 	  case 'o':		/* assume old style headers */
15459381Seric 		if (atobool(val))
15469341Seric 			CurEnv->e_flags |= EF_OLDSTYLE;
15479341Seric 		else
15489341Seric 			CurEnv->e_flags &= ~EF_OLDSTYLE;
15498269Seric 		break;
15508269Seric 
155158082Seric 	  case 'p':		/* select privacy level */
155258082Seric 		p = val;
155358082Seric 		for (;;)
155458082Seric 		{
155558082Seric 			register struct prival *pv;
155658082Seric 			extern struct prival PrivacyValues[];
155758082Seric 
155858082Seric 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
155958082Seric 				p++;
156058082Seric 			if (*p == '\0')
156158082Seric 				break;
156258082Seric 			val = p;
156358082Seric 			while (isascii(*p) && isalnum(*p))
156458082Seric 				p++;
156558082Seric 			if (*p != '\0')
156658082Seric 				*p++ = '\0';
156758082Seric 
156858082Seric 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
156958082Seric 			{
157058082Seric 				if (strcasecmp(val, pv->pv_name) == 0)
157158082Seric 					break;
157258082Seric 			}
157358886Seric 			if (pv->pv_name == NULL)
157458886Seric 				syserr("readcf: Op line: %s unrecognized", val);
157558082Seric 			PrivacyFlags |= pv->pv_flag;
157658082Seric 		}
157758082Seric 		break;
157858082Seric 
157924944Seric 	  case 'P':		/* postmaster copy address for returned mail */
158024944Seric 		PostMasterCopy = newstr(val);
158124944Seric 		break;
158224944Seric 
158324944Seric 	  case 'q':		/* slope of queue only function */
158424944Seric 		QueueFactor = atoi(val);
158524944Seric 		break;
158624944Seric 
15878256Seric 	  case 'Q':		/* queue directory */
15889381Seric 		if (val[0] == '\0')
15898269Seric 			QueueDir = "mqueue";
15909381Seric 		else
15919381Seric 			QueueDir = newstr(val);
159258789Seric 		if (RealUid != 0 && !safe)
159364718Seric 			Warn_Q_option = TRUE;
15948256Seric 		break;
15958256Seric 
159658148Seric 	  case 'R':		/* don't prune routes */
159758148Seric 		DontPruneRoutes = atobool(val);
159858148Seric 		break;
159958148Seric 
16008256Seric 	  case 'r':		/* read timeout */
160158112Seric 		settimeouts(val);
16028256Seric 		break;
16038256Seric 
16048256Seric 	  case 'S':		/* status file */
16059381Seric 		if (val[0] == '\0')
16068269Seric 			StatFile = "sendmail.st";
16079381Seric 		else
16089381Seric 			StatFile = newstr(val);
16098256Seric 		break;
16108256Seric 
16118265Seric 	  case 's':		/* be super safe, even if expensive */
16129381Seric 		SuperSafe = atobool(val);
16138256Seric 		break;
16148256Seric 
16158256Seric 	  case 'T':		/* queue timeout */
161658737Seric 		p = strchr(val, '/');
161758737Seric 		if (p != NULL)
161858737Seric 		{
161958737Seric 			*p++ = '\0';
162067730Seric 			TimeOuts.to_q_warning[TOC_NORMAL] = convtime(p, 'd');
162158737Seric 		}
162267730Seric 		TimeOuts.to_q_return[TOC_NORMAL] = convtime(val, 'h');
162354967Seric 		break;
16248256Seric 
16258265Seric 	  case 't':		/* time zone name */
162652106Seric 		TimeZoneSpec = newstr(val);
16278265Seric 		break;
16288265Seric 
162950556Seric 	  case 'U':		/* location of user database */
163051360Seric 		UdbSpec = newstr(val);
163150556Seric 		break;
163250556Seric 
16338256Seric 	  case 'u':		/* set default uid */
163464133Seric 		if (isascii(*val) && isdigit(*val))
163564133Seric 			DefUid = atoi(val);
163664133Seric 		else
163764133Seric 		{
163864133Seric 			register struct passwd *pw;
163964133Seric 
164064133Seric 			DefUid = -1;
164164133Seric 			pw = getpwnam(val);
164264133Seric 			if (pw == NULL)
164364133Seric 				syserr("readcf: option u: unknown user %s", val);
164464133Seric 			else
164564133Seric 				DefUid = pw->pw_uid;
164664133Seric 		}
164740973Sbostic 		setdefuser();
16488256Seric 		break;
16498256Seric 
165058851Seric 	  case 'V':		/* fallback MX host */
165158851Seric 		FallBackMX = newstr(val);
165258851Seric 		break;
165358851Seric 
16548269Seric 	  case 'v':		/* run in verbose mode */
16559381Seric 		Verbose = atobool(val);
16568256Seric 		break;
16578256Seric 
165863837Seric 	  case 'w':		/* if we are best MX, try host directly */
165963837Seric 		TryNullMXList = atobool(val);
166063837Seric 		break;
166161104Seric 
166261104Seric 	    /* 'W' available -- was wizard password */
166361104Seric 
166414879Seric 	  case 'x':		/* load avg at which to auto-queue msgs */
166514879Seric 		QueueLA = atoi(val);
166614879Seric 		break;
166714879Seric 
166814879Seric 	  case 'X':		/* load avg at which to auto-reject connections */
166914879Seric 		RefuseLA = atoi(val);
167014879Seric 		break;
167114879Seric 
167224981Seric 	  case 'y':		/* work recipient factor */
167324981Seric 		WkRecipFact = atoi(val);
167424981Seric 		break;
167524981Seric 
167624981Seric 	  case 'Y':		/* fork jobs during queue runs */
167724952Seric 		ForkQueueRuns = atobool(val);
167824952Seric 		break;
167924952Seric 
168024981Seric 	  case 'z':		/* work message class factor */
168124981Seric 		WkClassFact = atoi(val);
168224981Seric 		break;
168324981Seric 
168424981Seric 	  case 'Z':		/* work time factor */
168524981Seric 		WkTimeFact = atoi(val);
168624981Seric 		break;
168724981Seric 
168867614Seric 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
168967614Seric 		BrokenSmtpPeers = atobool(val);
169067614Seric 		break;
169167614Seric 
169267614Seric 	  case O_SQBH:		/* sort work queue by host first */
169367614Seric 		SortQueueByHost = atobool(val);
169467614Seric 		break;
169567614Seric 
169667707Seric 	  case O_DNICE:		/* delivery nice value */
169767707Seric 		DeliveryNiceness = atoi(val);
169867707Seric 		break;
169967707Seric 
170067707Seric 	  case O_MQA:		/* minimum queue age between deliveries */
170167707Seric 		MinQueueAge = convtime(val, 'm');
170267707Seric 		break;
170367707Seric 
170467707Seric 	  case O_MHSA:		/* maximum age of cached host status */
170567707Seric 		MaxHostStatAge = convtime(val, 'm');
170667707Seric 		break;
170767707Seric 
170867813Seric 	  case O_DEFCHARSET:	/* default character set for mimefying */
1709*67814Seric 		DefaultCharSet = newstr(val);
171067813Seric 		break;
171167813Seric 
17128256Seric 	  default:
17138256Seric 		break;
17148256Seric 	}
171516878Seric 	if (sticky)
171616878Seric 		setbitn(opt, StickyOpt);
17179188Seric 	return;
17188256Seric }
171910687Seric /*
172010687Seric **  SETCLASS -- set a word into a class
172110687Seric **
172210687Seric **	Parameters:
172310687Seric **		class -- the class to put the word in.
172410687Seric **		word -- the word to enter
172510687Seric **
172610687Seric **	Returns:
172710687Seric **		none.
172810687Seric **
172910687Seric **	Side Effects:
173010687Seric **		puts the word into the symbol table.
173110687Seric */
173210687Seric 
173310687Seric setclass(class, word)
173410687Seric 	int class;
173510687Seric 	char *word;
173610687Seric {
173710687Seric 	register STAB *s;
173810687Seric 
173957943Seric 	if (tTd(37, 8))
174064326Seric 		printf("setclass(%c, %s)\n", class, word);
174110687Seric 	s = stab(word, ST_CLASS, ST_ENTER);
174210687Seric 	setbitn(class, s->s_class);
174310687Seric }
174453654Seric /*
174553654Seric **  MAKEMAPENTRY -- create a map entry
174653654Seric **
174753654Seric **	Parameters:
174853654Seric **		line -- the config file line
174953654Seric **
175053654Seric **	Returns:
175153654Seric **		TRUE if it successfully entered the map entry.
175253654Seric **		FALSE otherwise (usually syntax error).
175353654Seric **
175453654Seric **	Side Effects:
175553654Seric **		Enters the map into the dictionary.
175653654Seric */
175753654Seric 
175853654Seric void
175953654Seric makemapentry(line)
176053654Seric 	char *line;
176153654Seric {
176253654Seric 	register char *p;
176353654Seric 	char *mapname;
176453654Seric 	char *classname;
176564078Seric 	register STAB *s;
176653654Seric 	STAB *class;
176753654Seric 
176858050Seric 	for (p = line; isascii(*p) && isspace(*p); p++)
176953654Seric 		continue;
177058050Seric 	if (!(isascii(*p) && isalnum(*p)))
177153654Seric 	{
177253654Seric 		syserr("readcf: config K line: no map name");
177353654Seric 		return;
177453654Seric 	}
177553654Seric 
177653654Seric 	mapname = p;
177758050Seric 	while (isascii(*++p) && isalnum(*p))
177853654Seric 		continue;
177953654Seric 	if (*p != '\0')
178053654Seric 		*p++ = '\0';
178158050Seric 	while (isascii(*p) && isspace(*p))
178253654Seric 		p++;
178358050Seric 	if (!(isascii(*p) && isalnum(*p)))
178453654Seric 	{
178553654Seric 		syserr("readcf: config K line, map %s: no map class", mapname);
178653654Seric 		return;
178753654Seric 	}
178853654Seric 	classname = p;
178958050Seric 	while (isascii(*++p) && isalnum(*p))
179053654Seric 		continue;
179153654Seric 	if (*p != '\0')
179253654Seric 		*p++ = '\0';
179358050Seric 	while (isascii(*p) && isspace(*p))
179453654Seric 		p++;
179553654Seric 
179653654Seric 	/* look up the class */
179753654Seric 	class = stab(classname, ST_MAPCLASS, ST_FIND);
179853654Seric 	if (class == NULL)
179953654Seric 	{
180053654Seric 		syserr("readcf: map %s: class %s not available", mapname, classname);
180153654Seric 		return;
180253654Seric 	}
180353654Seric 
180453654Seric 	/* enter the map */
180564078Seric 	s = stab(mapname, ST_MAP, ST_ENTER);
180664078Seric 	s->s_map.map_class = &class->s_mapclass;
180764078Seric 	s->s_map.map_mname = newstr(mapname);
180853654Seric 
180964078Seric 	if (class->s_mapclass.map_parse(&s->s_map, p))
181064078Seric 		s->s_map.map_mflags |= MF_VALID;
181164078Seric 
181264078Seric 	if (tTd(37, 5))
181364078Seric 	{
181464078Seric 		printf("map %s, class %s, flags %x, file %s,\n",
181564078Seric 			s->s_map.map_mname, s->s_map.map_class->map_cname,
181664078Seric 			s->s_map.map_mflags,
181764078Seric 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
181864078Seric 		printf("\tapp %s, domain %s, rebuild %s\n",
181964078Seric 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
182064078Seric 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
182164078Seric 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
182264078Seric 	}
182353654Seric }
182458112Seric /*
182558112Seric **  SETTIMEOUTS -- parse and set timeout values
182658112Seric **
182758112Seric **	Parameters:
182858112Seric **		val -- a pointer to the values.  If NULL, do initial
182958112Seric **			settings.
183058112Seric **
183158112Seric **	Returns:
183258112Seric **		none.
183358112Seric **
183458112Seric **	Side Effects:
183558112Seric **		Initializes the TimeOuts structure
183658112Seric */
183758112Seric 
183864255Seric #define SECONDS
183958112Seric #define MINUTES	* 60
184058112Seric #define HOUR	* 3600
184158112Seric 
184258112Seric settimeouts(val)
184358112Seric 	register char *val;
184458112Seric {
184558112Seric 	register char *p;
184658671Seric 	extern time_t convtime();
184758112Seric 
184858112Seric 	if (val == NULL)
184958112Seric 	{
185058112Seric 		TimeOuts.to_initial = (time_t) 5 MINUTES;
185158112Seric 		TimeOuts.to_helo = (time_t) 5 MINUTES;
185258112Seric 		TimeOuts.to_mail = (time_t) 10 MINUTES;
185358112Seric 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
185458112Seric 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
185558112Seric 		TimeOuts.to_datablock = (time_t) 1 HOUR;
185658112Seric 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
185758112Seric 		TimeOuts.to_rset = (time_t) 5 MINUTES;
185858112Seric 		TimeOuts.to_quit = (time_t) 2 MINUTES;
185958112Seric 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
186058112Seric 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
186164255Seric 		TimeOuts.to_ident = (time_t) 30 SECONDS;
186267711Seric 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
186358112Seric 		return;
186458112Seric 	}
186558112Seric 
186658112Seric 	for (;; val = p)
186758112Seric 	{
186858112Seric 		while (isascii(*val) && isspace(*val))
186958112Seric 			val++;
187058112Seric 		if (*val == '\0')
187158112Seric 			break;
187258112Seric 		for (p = val; *p != '\0' && *p != ','; p++)
187358112Seric 			continue;
187458112Seric 		if (*p != '\0')
187558112Seric 			*p++ = '\0';
187658112Seric 
187758112Seric 		if (isascii(*val) && isdigit(*val))
187858112Seric 		{
187958112Seric 			/* old syntax -- set everything */
188058796Seric 			TimeOuts.to_mail = convtime(val, 'm');
188158112Seric 			TimeOuts.to_rcpt = TimeOuts.to_mail;
188258112Seric 			TimeOuts.to_datainit = TimeOuts.to_mail;
188358112Seric 			TimeOuts.to_datablock = TimeOuts.to_mail;
188458112Seric 			TimeOuts.to_datafinal = TimeOuts.to_mail;
188558112Seric 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
188658112Seric 			continue;
188758112Seric 		}
188858112Seric 		else
188958112Seric 		{
189067711Seric 			register char *q = strchr(val, ':');
189158112Seric 			time_t to;
189258112Seric 
189367711Seric 			if (q == NULL && (q = strchr(val, '=')) == NULL)
189458112Seric 			{
189558112Seric 				/* syntax error */
189658112Seric 				continue;
189758112Seric 			}
189858112Seric 			*q++ = '\0';
189958796Seric 			to = convtime(q, 'm');
190058112Seric 
190158112Seric 			if (strcasecmp(val, "initial") == 0)
190258112Seric 				TimeOuts.to_initial = to;
190358112Seric 			else if (strcasecmp(val, "mail") == 0)
190458112Seric 				TimeOuts.to_mail = to;
190558112Seric 			else if (strcasecmp(val, "rcpt") == 0)
190658112Seric 				TimeOuts.to_rcpt = to;
190758112Seric 			else if (strcasecmp(val, "datainit") == 0)
190858112Seric 				TimeOuts.to_datainit = to;
190958112Seric 			else if (strcasecmp(val, "datablock") == 0)
191058112Seric 				TimeOuts.to_datablock = to;
191158112Seric 			else if (strcasecmp(val, "datafinal") == 0)
191258112Seric 				TimeOuts.to_datafinal = to;
191358112Seric 			else if (strcasecmp(val, "command") == 0)
191458112Seric 				TimeOuts.to_nextcommand = to;
191558112Seric 			else if (strcasecmp(val, "rset") == 0)
191658112Seric 				TimeOuts.to_rset = to;
191758112Seric 			else if (strcasecmp(val, "helo") == 0)
191858112Seric 				TimeOuts.to_helo = to;
191958112Seric 			else if (strcasecmp(val, "quit") == 0)
192058112Seric 				TimeOuts.to_quit = to;
192158112Seric 			else if (strcasecmp(val, "misc") == 0)
192258112Seric 				TimeOuts.to_miscshort = to;
192364255Seric 			else if (strcasecmp(val, "ident") == 0)
192464255Seric 				TimeOuts.to_ident = to;
192567711Seric 			else if (strcasecmp(val, "fileopen") == 0)
192667711Seric 				TimeOuts.to_fileopen = to;
192767711Seric 			else if (strcasecmp(val, "queuewarn") == 0)
192867730Seric 				TimeOuts.to_q_warning[TOC_NORMAL] = to;
192967711Seric 			else if (strcasecmp(val, "queuereturn") == 0)
193067730Seric 				TimeOuts.to_q_return[TOC_NORMAL] = to;
193167730Seric 			else if (strcasecmp(val, "queuewarn.normal") == 0)
193267730Seric 				TimeOuts.to_q_warning[TOC_NORMAL] = to;
193367730Seric 			else if (strcasecmp(val, "queuereturn.normal") == 0)
193467730Seric 				TimeOuts.to_q_return[TOC_NORMAL] = to;
193567730Seric 			else if (strcasecmp(val, "queuewarn.urgent") == 0)
193667730Seric 				TimeOuts.to_q_warning[TOC_URGENT] = to;
193767730Seric 			else if (strcasecmp(val, "queuereturn.urgent") == 0)
193867730Seric 				TimeOuts.to_q_return[TOC_URGENT] = to;
193967730Seric 			else if (strcasecmp(val, "queuewarn.non-urgent") == 0)
194067730Seric 				TimeOuts.to_q_warning[TOC_NONURGENT] = to;
194167730Seric 			else if (strcasecmp(val, "queuereturn.non-urgent") == 0)
194267730Seric 				TimeOuts.to_q_return[TOC_NONURGENT] = to;
194358112Seric 			else
194458112Seric 				syserr("settimeouts: invalid timeout %s", val);
194558112Seric 		}
194658112Seric 	}
194758112Seric }
1948