readcf.c revision 90792
1/*
2 * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#include <sendmail.h>
15
16SM_RCSID("@(#)$Id: readcf.c,v 8.594 2001/12/14 00:43:17 gshapiro Exp $")
17
18#if NETINET || NETINET6
19# include <arpa/inet.h>
20#endif /* NETINET || NETINET6 */
21
22#define SECONDS
23#define MINUTES	* 60
24#define HOUR	* 3600
25#define HOURS	HOUR
26
27static void	fileclass __P((int, char *, char *, bool, bool));
28static char	**makeargv __P((char *));
29static void	settimeout __P((char *, char *, bool));
30static void	toomany __P((int, int));
31static char	*extrquotstr __P((char *, char **, char *, bool *));
32
33/*
34**  READCF -- read configuration file.
35**
36**	This routine reads the configuration file and builds the internal
37**	form.
38**
39**	The file is formatted as a sequence of lines, each taken
40**	atomically.  The first character of each line describes how
41**	the line is to be interpreted.  The lines are:
42**		Dxval		Define macro x to have value val.
43**		Cxword		Put word into class x.
44**		Fxfile [fmt]	Read file for lines to put into
45**				class x.  Use scanf string 'fmt'
46**				or "%s" if not present.  Fmt should
47**				only produce one string-valued result.
48**		Hname: value	Define header with field-name 'name'
49**				and value as specified; this will be
50**				macro expanded immediately before
51**				use.
52**		Sn		Use rewriting set n.
53**		Rlhs rhs	Rewrite addresses that match lhs to
54**				be rhs.
55**		Mn arg=val...	Define mailer.  n is the internal name.
56**				Args specify mailer parameters.
57**		Oxvalue		Set option x to value.
58**		O option value	Set option (long name) to value.
59**		Pname=value	Set precedence name to value.
60**		Qn arg=val...	Define queue groups.  n is the internal name.
61**				Args specify queue parameters.
62**		Vversioncode[/vendorcode]
63**				Version level/vendor name of
64**				configuration syntax.
65**		Kmapname mapclass arguments....
66**				Define keyed lookup of a given class.
67**				Arguments are class dependent.
68**		Eenvar=value	Set the environment value to the given value.
69**
70**	Parameters:
71**		cfname -- configuration file name.
72**		safe -- true if this is the system config file;
73**			false otherwise.
74**		e -- the main envelope.
75**
76**	Returns:
77**		none.
78**
79**	Side Effects:
80**		Builds several internal tables.
81*/
82
83void
84readcf(cfname, safe, e)
85	char *cfname;
86	bool safe;
87	register ENVELOPE *e;
88{
89	SM_FILE_T *cf;
90	int ruleset = -1;
91	char *q;
92	struct rewrite *rwp = NULL;
93	char *bp;
94	auto char *ep;
95	int nfuzzy;
96	char *file;
97	bool optional;
98	bool ok;
99	int mid;
100	register char *p;
101	long sff = SFF_OPENASROOT;
102	struct stat statb;
103	char buf[MAXLINE];
104	char exbuf[MAXLINE];
105	char pvpbuf[MAXLINE + MAXATOM];
106	static char *null_list[1] = { NULL };
107	extern unsigned char TokTypeNoC[];
108
109	FileName = cfname;
110	LineNumber = 0;
111
112	if (DontLockReadFiles)
113		sff |= SFF_NOLOCK;
114	cf = safefopen(cfname, O_RDONLY, 0444, sff);
115	if (cf == NULL)
116	{
117		syserr("cannot open");
118		finis(false, true, EX_OSFILE);
119	}
120
121	if (fstat(sm_io_getinfo(cf, SM_IO_WHAT_FD, NULL), &statb) < 0)
122	{
123		syserr("cannot fstat");
124		finis(false, true, EX_OSFILE);
125	}
126
127	if (!S_ISREG(statb.st_mode))
128	{
129		syserr("not a plain file");
130		finis(false, true, EX_OSFILE);
131	}
132
133	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
134	{
135		if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS)
136			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
137					     "%s: WARNING: dangerous write permissions\n",
138					     FileName);
139		if (LogLevel > 0)
140			sm_syslog(LOG_CRIT, NOQID,
141				  "%s: WARNING: dangerous write permissions",
142				  FileName);
143	}
144
145#if XLA
146	xla_zero();
147#endif /* XLA */
148
149	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
150	{
151		if (bp[0] == '#')
152		{
153			if (bp != buf)
154				sm_free(bp); /* XXX */
155			continue;
156		}
157
158		/* do macro expansion mappings */
159		translate_dollars(bp);
160
161		/* interpret this line */
162		errno = 0;
163		switch (bp[0])
164		{
165		  case '\0':
166		  case '#':		/* comment */
167			break;
168
169		  case 'R':		/* rewriting rule */
170			if (ruleset < 0)
171			{
172				syserr("missing valid ruleset for \"%s\"", bp);
173				break;
174			}
175			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
176				continue;
177
178			if (*p == '\0')
179			{
180				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
181				break;
182			}
183
184			/* allocate space for the rule header */
185			if (rwp == NULL)
186			{
187				RewriteRules[ruleset] = rwp =
188					(struct rewrite *) xalloc(sizeof *rwp);
189			}
190			else
191			{
192				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
193				rwp = rwp->r_next;
194			}
195			rwp->r_next = NULL;
196
197			/* expand and save the LHS */
198			*p = '\0';
199			expand(&bp[1], exbuf, sizeof exbuf, e);
200			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
201					     sizeof pvpbuf, NULL,
202					     ConfigLevel >= 9 ? TokTypeNoC : NULL);
203			nfuzzy = 0;
204			if (rwp->r_lhs != NULL)
205			{
206				register char **ap;
207
208				rwp->r_lhs = copyplist(rwp->r_lhs, true, NULL);
209
210				/* count the number of fuzzy matches in LHS */
211				for (ap = rwp->r_lhs; *ap != NULL; ap++)
212				{
213					char *botch;
214
215					botch = NULL;
216					switch (**ap & 0377)
217					{
218					  case MATCHZANY:
219					  case MATCHANY:
220					  case MATCHONE:
221					  case MATCHCLASS:
222					  case MATCHNCLASS:
223						nfuzzy++;
224						break;
225
226					  case MATCHREPL:
227						botch = "$0-$9";
228						break;
229
230					  case CANONUSER:
231						botch = "$:";
232						break;
233
234					  case CALLSUBR:
235						botch = "$>";
236						break;
237
238					  case CONDIF:
239						botch = "$?";
240						break;
241
242					  case CONDFI:
243						botch = "$.";
244						break;
245
246					  case HOSTBEGIN:
247						botch = "$[";
248						break;
249
250					  case HOSTEND:
251						botch = "$]";
252						break;
253
254					  case LOOKUPBEGIN:
255						botch = "$(";
256						break;
257
258					  case LOOKUPEND:
259						botch = "$)";
260						break;
261					}
262					if (botch != NULL)
263						syserr("Inappropriate use of %s on LHS",
264							botch);
265				}
266				rwp->r_line = LineNumber;
267			}
268			else
269			{
270				syserr("R line: null LHS");
271				rwp->r_lhs = null_list;
272			}
273			if (nfuzzy > MAXMATCH)
274			{
275				syserr("R line: too many wildcards");
276				rwp->r_lhs = null_list;
277			}
278
279			/* expand and save the RHS */
280			while (*++p == '\t')
281				continue;
282			q = p;
283			while (*p != '\0' && *p != '\t')
284				p++;
285			*p = '\0';
286			expand(q, exbuf, sizeof exbuf, e);
287			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
288					     sizeof pvpbuf, NULL,
289					     ConfigLevel >= 9 ? TokTypeNoC : NULL);
290			if (rwp->r_rhs != NULL)
291			{
292				register char **ap;
293
294				rwp->r_rhs = copyplist(rwp->r_rhs, true, NULL);
295
296				/* check no out-of-bounds replacements */
297				nfuzzy += '0';
298				for (ap = rwp->r_rhs; *ap != NULL; ap++)
299				{
300					char *botch;
301
302					botch = NULL;
303					switch (**ap & 0377)
304					{
305					  case MATCHREPL:
306						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
307						{
308							syserr("replacement $%c out of bounds",
309								(*ap)[1]);
310						}
311						break;
312
313					  case MATCHZANY:
314						botch = "$*";
315						break;
316
317					  case MATCHANY:
318						botch = "$+";
319						break;
320
321					  case MATCHONE:
322						botch = "$-";
323						break;
324
325					  case MATCHCLASS:
326						botch = "$=";
327						break;
328
329					  case MATCHNCLASS:
330						botch = "$~";
331						break;
332
333#if 0
334/*
335**  This doesn't work yet as there are maps defined *after* the cf
336**  is read such as host, user, and alias.  So for now, it's removed.
337**  When it comes back, the RELEASE_NOTES entry will be:
338**	Emit warnings for unknown maps when reading the .cf file.  Based on
339**		patch from Robert Harker of Harker Systems.
340*/
341
342					  case LOOKUPBEGIN:
343						/*
344						**  Got a database lookup,
345						**  check if map is defined.
346						*/
347
348						ep = *(ap + 1);
349						if ((*ep & 0377) != MACRODEXPAND &&
350						    stab(ep, ST_MAP,
351							 ST_FIND) == NULL)
352						{
353							(void) sm_io_fprintf(smioout,
354									     SM_TIME_DEFAULT,
355									     "Warning: %s: line %d: map %s not found\n",
356									     FileName,
357									     LineNumber,
358									     ep);
359						}
360						break;
361#endif /* 0 */
362					}
363					if (botch != NULL)
364						syserr("Inappropriate use of %s on RHS",
365							botch);
366				}
367			}
368			else
369			{
370				syserr("R line: null RHS");
371				rwp->r_rhs = null_list;
372			}
373			break;
374
375		  case 'S':		/* select rewriting set */
376			expand(&bp[1], exbuf, sizeof exbuf, e);
377			ruleset = strtorwset(exbuf, NULL, ST_ENTER);
378			if (ruleset < 0)
379				break;
380
381			rwp = RewriteRules[ruleset];
382			if (rwp != NULL)
383			{
384				if (OpMode == MD_TEST)
385					(void) sm_io_fprintf(smioout,
386							     SM_TIME_DEFAULT,
387							     "WARNING: Ruleset %s has multiple definitions\n",
388							    &bp[1]);
389				if (tTd(37, 1))
390					sm_dprintf("WARNING: Ruleset %s has multiple definitions\n",
391						   &bp[1]);
392				while (rwp->r_next != NULL)
393					rwp = rwp->r_next;
394			}
395			break;
396
397		  case 'D':		/* macro definition */
398			mid = macid_parse(&bp[1], &ep);
399			if (mid == 0)
400				break;
401			p = munchstring(ep, NULL, '\0');
402			macdefine(&e->e_macro, A_TEMP, mid, p);
403			break;
404
405		  case 'H':		/* required header line */
406			(void) chompheader(&bp[1], CHHDR_DEF, NULL, e);
407			break;
408
409		  case 'C':		/* word class */
410		  case 'T':		/* trusted user (set class `t') */
411			if (bp[0] == 'C')
412			{
413				mid = macid_parse(&bp[1], &ep);
414				if (mid == 0)
415					break;
416				expand(ep, exbuf, sizeof exbuf, e);
417				p = exbuf;
418			}
419			else
420			{
421				mid = 't';
422				p = &bp[1];
423			}
424			while (*p != '\0')
425			{
426				register char *wd;
427				char delim;
428
429				while (*p != '\0' && isascii(*p) && isspace(*p))
430					p++;
431				wd = p;
432				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
433					p++;
434				delim = *p;
435				*p = '\0';
436				if (wd[0] != '\0')
437					setclass(mid, wd);
438				*p = delim;
439			}
440			break;
441
442		  case 'F':		/* word class from file */
443			mid = macid_parse(&bp[1], &ep);
444			if (mid == 0)
445				break;
446			for (p = ep; isascii(*p) && isspace(*p); )
447				p++;
448			if (p[0] == '-' && p[1] == 'o')
449			{
450				optional = true;
451				while (*p != '\0' &&
452				       !(isascii(*p) && isspace(*p)))
453					p++;
454				while (isascii(*p) && isspace(*p))
455					p++;
456				file = p;
457			}
458			else
459				optional = false;
460
461			if (*p == '@')
462			{
463				/* use entire spec */
464				file = p;
465			}
466			else
467			{
468				file = extrquotstr(p, &q, " ", &ok);
469				if (!ok)
470				{
471					syserr("illegal filename '%s'", p);
472					break;
473				}
474			}
475
476			if (*file == '|' || *file == '@')
477				p = "%s";
478			else
479			{
480				p = q;
481				if (*p == '\0')
482					p = "%s";
483				else
484				{
485					*p = '\0';
486					while (isascii(*++p) && isspace(*p))
487						continue;
488				}
489			}
490			fileclass(mid, file, p, safe, optional);
491			break;
492
493#if XLA
494		  case 'L':		/* extended load average description */
495			xla_init(&bp[1]);
496			break;
497#endif /* XLA */
498
499#if defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO)
500		  case 'L':		/* lookup macro */
501		  case 'G':		/* lookup class */
502			/* reserved for Sun -- NIS+ database lookup */
503			if (VendorCode != VENDOR_SUN)
504				goto badline;
505			sun_lg_config_line(bp, e);
506			break;
507#endif /* defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO) */
508
509		  case 'M':		/* define mailer */
510			makemailer(&bp[1]);
511			break;
512
513		  case 'O':		/* set option */
514			setoption(bp[1], &bp[2], safe, false, e);
515			break;
516
517		  case 'P':		/* set precedence */
518			if (NumPriorities >= MAXPRIORITIES)
519			{
520				toomany('P', MAXPRIORITIES);
521				break;
522			}
523			for (p = &bp[1]; *p != '\0' && *p != '='; p++)
524				continue;
525			if (*p == '\0')
526				goto badline;
527			*p = '\0';
528			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
529			Priorities[NumPriorities].pri_val = atoi(++p);
530			NumPriorities++;
531			break;
532
533		  case 'Q':		/* define queue */
534			makequeue(&bp[1], true);
535			break;
536
537		  case 'V':		/* configuration syntax version */
538			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
539				continue;
540			if (!isascii(*p) || !isdigit(*p))
541			{
542				syserr("invalid argument to V line: \"%.20s\"",
543					&bp[1]);
544				break;
545			}
546			ConfigLevel = strtol(p, &ep, 10);
547
548			/*
549			**  Do heuristic tweaking for back compatibility.
550			*/
551
552			if (ConfigLevel >= 5)
553			{
554				/* level 5 configs have short name in $w */
555				p = macvalue('w', e);
556				if (p != NULL && (p = strchr(p, '.')) != NULL)
557				{
558					*p = '\0';
559					macdefine(&e->e_macro, A_TEMP, 'w',
560						  macvalue('w', e));
561				}
562			}
563			if (ConfigLevel >= 6)
564			{
565				ColonOkInAddr = false;
566			}
567
568			/*
569			**  Look for vendor code.
570			*/
571
572			if (*ep++ == '/')
573			{
574				/* extract vendor code */
575				for (p = ep; isascii(*p) && isalpha(*p); )
576					p++;
577				*p = '\0';
578
579				if (!setvendor(ep))
580					syserr("invalid V line vendor code: \"%s\"",
581						ep);
582			}
583			break;
584
585		  case 'K':
586			expand(&bp[1], exbuf, sizeof exbuf, e);
587			(void) makemapentry(exbuf);
588			break;
589
590		  case 'E':
591			p = strchr(bp, '=');
592			if (p != NULL)
593				*p++ = '\0';
594			setuserenv(&bp[1], p);
595			break;
596
597		  case 'X':		/* mail filter */
598#if MILTER
599			milter_setup(&bp[1]);
600#else /* MILTER */
601			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
602					     "Warning: Filter usage ('X') requires Milter support (-DMILTER)\n");
603#endif /* MILTER */
604			break;
605
606		  default:
607		  badline:
608			syserr("unknown configuration line \"%s\"", bp);
609		}
610		if (bp != buf)
611			sm_free(bp); /* XXX */
612	}
613	if (sm_io_error(cf))
614	{
615		syserr("I/O read error");
616		finis(false, true, EX_OSFILE);
617	}
618	(void) sm_io_close(cf, SM_TIME_DEFAULT);
619	FileName = NULL;
620
621	/* initialize host maps from local service tables */
622	inithostmaps();
623
624	/* initialize daemon (if not defined yet) */
625	initdaemon();
626
627	/* determine if we need to do special name-server frotz */
628	{
629		int nmaps;
630		char *maptype[MAXMAPSTACK];
631		short mapreturn[MAXMAPACTIONS];
632
633		nmaps = switch_map_find("hosts", maptype, mapreturn);
634		UseNameServer = false;
635		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
636		{
637			register int mapno;
638
639			for (mapno = 0; mapno < nmaps && !UseNameServer;
640			     mapno++)
641			{
642				if (strcmp(maptype[mapno], "dns") == 0)
643					UseNameServer = true;
644			}
645		}
646	}
647}
648/*
649**  TRANSLATE_DOLLARS -- convert $x into internal form
650**
651**	Actually does all appropriate pre-processing of a config line
652**	to turn it into internal form.
653**
654**	Parameters:
655**		bp -- the buffer to translate.
656**
657**	Returns:
658**		None.  The buffer is translated in place.  Since the
659**		translations always make the buffer shorter, this is
660**		safe without a size parameter.
661*/
662
663void
664translate_dollars(bp)
665	char *bp;
666{
667	register char *p;
668	auto char *ep;
669
670	for (p = bp; *p != '\0'; p++)
671	{
672		if (*p == '#' && p > bp && ConfigLevel >= 3)
673		{
674			register char *e;
675
676			switch (*--p & 0377)
677			{
678			  case MACROEXPAND:
679				/* it's from $# -- let it go through */
680				p++;
681				break;
682
683			  case '\\':
684				/* it's backslash escaped */
685				(void) sm_strlcpy(p, p + 1, strlen(p));
686				break;
687
688			  default:
689				/* delete leading white space */
690				while (isascii(*p) && isspace(*p) &&
691				       *p != '\n' && p > bp)
692					p--;
693				if ((e = strchr(++p, '\n')) != NULL)
694					(void) sm_strlcpy(p, e, strlen(p));
695				else
696					*p-- = '\0';
697				break;
698			}
699			continue;
700		}
701
702		if (*p != '$' || p[1] == '\0')
703			continue;
704
705		if (p[1] == '$')
706		{
707			/* actual dollar sign.... */
708			(void) sm_strlcpy(p, p + 1, strlen(p));
709			continue;
710		}
711
712		/* convert to macro expansion character */
713		*p++ = MACROEXPAND;
714
715		/* special handling for $=, $~, $&, and $? */
716		if (*p == '=' || *p == '~' || *p == '&' || *p == '?')
717			p++;
718
719		/* convert macro name to code */
720		*p = macid_parse(p, &ep);
721		if (ep != p + 1)
722			(void) sm_strlcpy(p + 1, ep, strlen(p + 1));
723	}
724
725	/* strip trailing white space from the line */
726	while (--p > bp && isascii(*p) && isspace(*p))
727		*p = '\0';
728}
729/*
730**  TOOMANY -- signal too many of some option
731**
732**	Parameters:
733**		id -- the id of the error line
734**		maxcnt -- the maximum possible values
735**
736**	Returns:
737**		none.
738**
739**	Side Effects:
740**		gives a syserr.
741*/
742
743static void
744toomany(id, maxcnt)
745	int id;
746	int maxcnt;
747{
748	syserr("too many %c lines, %d max", id, maxcnt);
749}
750/*
751**  FILECLASS -- read members of a class from a file
752**
753**	Parameters:
754**		class -- class to define.
755**		filename -- name of file to read.
756**		fmt -- scanf string to use for match.
757**		safe -- if set, this is a safe read.
758**		optional -- if set, it is not an error for the file to
759**			not exist.
760**
761**	Returns:
762**		none
763**
764**	Side Effects:
765**		puts all lines in filename that match a scanf into
766**			the named class.
767*/
768
769/*
770**  Break up the match into words and add to class.
771*/
772
773static void
774parse_class_words(class, line)
775	int class;
776	char *line;
777{
778	while (line != NULL && *line != '\0')
779	{
780		register char *q;
781
782		/* strip leading spaces */
783		while (isascii(*line) && isspace(*line))
784			line++;
785		if (*line == '\0')
786			break;
787
788		/* find the end of the word */
789		q = line;
790		while (*line != '\0' && !(isascii(*line) && isspace(*line)))
791			line++;
792		if (*line != '\0')
793			*line++ = '\0';
794
795		/* enter the word in the symbol table */
796		setclass(class, q);
797	}
798}
799
800static void
801fileclass(class, filename, fmt, safe, optional)
802	int class;
803	char *filename;
804	char *fmt;
805	bool safe;
806	bool optional;
807{
808	SM_FILE_T *f;
809	long sff;
810	pid_t pid;
811	register char *p;
812	char buf[MAXLINE];
813
814	if (tTd(37, 2))
815		sm_dprintf("fileclass(%s, fmt=%s)\n", filename, fmt);
816
817	if (*filename == '\0')
818	{
819		syserr("fileclass: missing file name");
820		return;
821	}
822	else if (!SM_IS_DIR_DELIM(*filename) && *filename != '|' &&
823		 (p = strchr(filename, '@')) != NULL)
824	{
825		int status = 0;
826		char *key;
827		char *mn;
828		char *cl, *spec;
829		STAB *mapclass;
830		MAP map;
831
832		mn = newstr(macname(class));
833
834		key = filename;
835
836		/* skip past '@' */
837		*p++ = '\0';
838		cl = p;
839
840		if (strcmp(cl, "LDAP") == 0)
841		{
842			int n;
843			char *lc;
844			char jbuf[MAXHOSTNAMELEN];
845			char lcbuf[MAXLINE];
846
847			/* Get $j */
848			expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
849			if (jbuf[0] == '\0')
850			{
851				(void) sm_strlcpy(jbuf, "localhost",
852						  sizeof jbuf);
853			}
854
855			/* impose the default schema */
856			lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
857			if (lc == NULL)
858				lc = "";
859			else
860			{
861				expand(lc, lcbuf, sizeof lcbuf, CurEnv);
862				lc = lcbuf;
863			}
864
865			cl = "ldap";
866			n = sm_snprintf(buf, sizeof buf,
867					"-k (&(objectClass=sendmailMTAClass)(sendmailMTAClassName=%s)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))) -v sendmailMTAClassValue",
868					mn, lc, jbuf);
869			if (n >= sizeof buf)
870			{
871				syserr("fileclass: F{%s}: Default LDAP string too long",
872				       mn);
873				sm_free(mn);
874				return;
875			}
876			spec = buf;
877		}
878		else
879		{
880			if ((spec = strchr(cl, ':')) == NULL)
881			{
882				syserr("fileclass: F{%s}: missing map class",
883				       mn);
884				sm_free(mn);
885				return;
886			}
887			*spec++ ='\0';
888		}
889
890		/* set up map structure */
891		mapclass = stab(cl, ST_MAPCLASS, ST_FIND);
892		if (mapclass == NULL)
893		{
894			syserr("fileclass: F{%s}: class %s not available",
895			       mn, cl);
896			sm_free(mn);
897			return;
898		}
899		memset(&map, '\0', sizeof map);
900		map.map_class = &mapclass->s_mapclass;
901		map.map_mname = mn;
902		map.map_mflags |= MF_FILECLASS;
903
904		/* parse map spec */
905		if (!map.map_class->map_parse(&map, spec))
906		{
907			/* map_parse() showed the error already */
908			sm_free(mn);
909			return;
910		}
911		map.map_mflags |= MF_VALID;
912
913		/* open map */
914		if (map.map_class->map_open(&map, O_RDONLY))
915		{
916			map.map_mflags |= MF_OPEN;
917			map.map_pid = getpid();
918		}
919		else
920		{
921			if (!optional &&
922			    !bitset(MF_OPTIONAL, map.map_mflags))
923				syserr("fileclass: F{%s}: map open failed",
924				       mn);
925			sm_free(mn);
926			return;
927		}
928
929		/* lookup */
930		p = (*map.map_class->map_lookup)(&map, key, NULL, &status);
931		if (status != EX_OK && status != EX_NOTFOUND)
932		{
933			if (!optional)
934				syserr("fileclass: F{%s}: map lookup failed",
935				       mn);
936			p = NULL;
937		}
938
939		/* use the results */
940		if (p != NULL)
941			parse_class_words(class, p);
942
943		/* close map */
944		map.map_mflags |= MF_CLOSING;
945		map.map_class->map_close(&map);
946		map.map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
947		sm_free(mn);
948		return;
949	}
950	else if (filename[0] == '|')
951	{
952		auto int fd;
953		int i;
954		char *argv[MAXPV + 1];
955
956		i = 0;
957		for (p = strtok(&filename[1], " \t");
958		     p != NULL && i < MAXPV;
959		     p = strtok(NULL, " \t"))
960			argv[i++] = p;
961		argv[i] = NULL;
962		pid = prog_open(argv, &fd, CurEnv);
963		if (pid < 0)
964			f = NULL;
965		else
966			f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
967				       (void *) &fd, SM_IO_RDONLY, NULL);
968	}
969	else
970	{
971		pid = -1;
972		sff = SFF_REGONLY;
973		if (!bitnset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail))
974			sff |= SFF_SAFEDIRPATH;
975		if (!bitnset(DBS_LINKEDCLASSFILEINWRITABLEDIR,
976			     DontBlameSendmail))
977			sff |= SFF_NOWLINK;
978		if (safe)
979			sff |= SFF_OPENASROOT;
980		else if (RealUid == 0)
981			sff |= SFF_ROOTOK;
982		if (DontLockReadFiles)
983			sff |= SFF_NOLOCK;
984		f = safefopen(filename, O_RDONLY, 0, sff);
985	}
986	if (f == NULL)
987	{
988		if (!optional)
989			syserr("fileclass: cannot open '%s'", filename);
990		return;
991	}
992
993	while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
994	{
995#if SCANF
996		char wordbuf[MAXLINE + 1];
997#endif /* SCANF */
998
999		if (buf[0] == '#')
1000			continue;
1001#if SCANF
1002		if (sm_io_sscanf(buf, fmt, wordbuf) != 1)
1003			continue;
1004		p = wordbuf;
1005#else /* SCANF */
1006		p = buf;
1007#endif /* SCANF */
1008
1009		parse_class_words(class, p);
1010
1011		/*
1012		**  If anything else is added here,
1013		**  check if the '@' map case above
1014		**  needs the code as well.
1015		*/
1016	}
1017
1018	(void) sm_io_close(f, SM_TIME_DEFAULT);
1019	if (pid > 0)
1020		(void) waitfor(pid);
1021}
1022/*
1023**  MAKEMAILER -- define a new mailer.
1024**
1025**	Parameters:
1026**		line -- description of mailer.  This is in labeled
1027**			fields.  The fields are:
1028**			   A -- the argv for this mailer
1029**			   C -- the character set for MIME conversions
1030**			   D -- the directory to run in
1031**			   E -- the eol string
1032**			   F -- the flags associated with the mailer
1033**			   L -- the maximum line length
1034**			   M -- the maximum message size
1035**			   N -- the niceness at which to run
1036**			   P -- the path to the mailer
1037**			   Q -- the queue group for the mailer
1038**			   R -- the recipient rewriting set
1039**			   S -- the sender rewriting set
1040**			   T -- the mailer type (for DSNs)
1041**			   U -- the uid to run as
1042**			   W -- the time to wait at the end
1043**			   m -- maximum messages per connection
1044**			   r -- maximum number of recipients per message
1045**			   / -- new root directory
1046**			The first word is the canonical name of the mailer.
1047**
1048**	Returns:
1049**		none.
1050**
1051**	Side Effects:
1052**		enters the mailer into the mailer table.
1053*/
1054
1055void
1056makemailer(line)
1057	char *line;
1058{
1059	register char *p;
1060	register struct mailer *m;
1061	register STAB *s;
1062	int i;
1063	char fcode;
1064	auto char *endp;
1065	static int nextmailer = 0;	/* "free" index into Mailer struct */
1066
1067	/* allocate a mailer and set up defaults */
1068	m = (struct mailer *) xalloc(sizeof *m);
1069	memset((char *) m, '\0', sizeof *m);
1070	errno = 0; /* avoid bogus error text */
1071
1072	/* collect the mailer name */
1073	for (p = line;
1074	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
1075	     p++)
1076		continue;
1077	if (*p != '\0')
1078		*p++ = '\0';
1079	if (line[0] == '\0')
1080	{
1081		syserr("name required for mailer");
1082		return;
1083	}
1084	m->m_name = newstr(line);
1085
1086	/* now scan through and assign info from the fields */
1087	while (*p != '\0')
1088	{
1089		auto char *delimptr;
1090
1091		while (*p != '\0' &&
1092		       (*p == ',' || (isascii(*p) && isspace(*p))))
1093			p++;
1094
1095		/* p now points to field code */
1096		fcode = *p;
1097		while (*p != '\0' && *p != '=' && *p != ',')
1098			p++;
1099		if (*p++ != '=')
1100		{
1101			syserr("mailer %s: `=' expected", m->m_name);
1102			return;
1103		}
1104		while (isascii(*p) && isspace(*p))
1105			p++;
1106
1107		/* p now points to the field body */
1108		p = munchstring(p, &delimptr, ',');
1109
1110		/* install the field into the mailer struct */
1111		switch (fcode)
1112		{
1113		  case 'P':		/* pathname */
1114			if (*p != '\0')	/* error is issued below */
1115				m->m_mailer = newstr(p);
1116			break;
1117
1118		  case 'F':		/* flags */
1119			for (; *p != '\0'; p++)
1120			{
1121				if (!(isascii(*p) && isspace(*p)))
1122				{
1123#if _FFR_DEPRECATE_MAILER_FLAG_I
1124					if (*p == M_INTERNAL)
1125						sm_syslog(LOG_WARNING, NOQID,
1126							  "WARNING: mailer=%s, flag=%c deprecated",
1127							  m->m_name, *p);
1128#endif /* _FFR_DEPRECATE_MAILER_FLAG_I */
1129					setbitn(bitidx(*p), m->m_flags);
1130				}
1131			}
1132			break;
1133
1134		  case 'S':		/* sender rewriting ruleset */
1135		  case 'R':		/* recipient rewriting ruleset */
1136			i = strtorwset(p, &endp, ST_ENTER);
1137			if (i < 0)
1138				return;
1139			if (fcode == 'S')
1140				m->m_sh_rwset = m->m_se_rwset = i;
1141			else
1142				m->m_rh_rwset = m->m_re_rwset = i;
1143
1144			p = endp;
1145			if (*p++ == '/')
1146			{
1147				i = strtorwset(p, NULL, ST_ENTER);
1148				if (i < 0)
1149					return;
1150				if (fcode == 'S')
1151					m->m_sh_rwset = i;
1152				else
1153					m->m_rh_rwset = i;
1154			}
1155			break;
1156
1157		  case 'E':		/* end of line string */
1158			if (*p == '\0')
1159				syserr("mailer %s: null end-of-line string",
1160					m->m_name);
1161			else
1162				m->m_eol = newstr(p);
1163			break;
1164
1165		  case 'A':		/* argument vector */
1166			if (*p != '\0')	/* error is issued below */
1167				m->m_argv = makeargv(p);
1168			break;
1169
1170		  case 'M':		/* maximum message size */
1171			m->m_maxsize = atol(p);
1172			break;
1173
1174		  case 'm':		/* maximum messages per connection */
1175			m->m_maxdeliveries = atoi(p);
1176			break;
1177
1178		  case 'r':		/* max recipient per envelope */
1179			m->m_maxrcpt = atoi(p);
1180			break;
1181
1182		  case 'L':		/* maximum line length */
1183			m->m_linelimit = atoi(p);
1184			if (m->m_linelimit < 0)
1185				m->m_linelimit = 0;
1186			break;
1187
1188		  case 'N':		/* run niceness */
1189			m->m_nice = atoi(p);
1190			break;
1191
1192		  case 'D':		/* working directory */
1193			if (*p == '\0')
1194				syserr("mailer %s: null working directory",
1195					m->m_name);
1196			else
1197				m->m_execdir = newstr(p);
1198			break;
1199
1200		  case 'C':		/* default charset */
1201			if (*p == '\0')
1202				syserr("mailer %s: null charset", m->m_name);
1203			else
1204				m->m_defcharset = newstr(p);
1205			break;
1206
1207		  case 'Q':		/* queue for this mailer */
1208			if (*p == '\0')
1209			{
1210				syserr("mailer %s: null queue", m->m_name);
1211				break;
1212			}
1213			s = stab(p, ST_QUEUE, ST_FIND);
1214			if (s == NULL)
1215				syserr("mailer %s: unknown queue %s",
1216					m->m_name, p);
1217			else
1218				m->m_qgrp = s->s_quegrp->qg_index;
1219			break;
1220
1221		  case 'T':		/* MTA-Name/Address/Diagnostic types */
1222			/* extract MTA name type; default to "dns" */
1223			m->m_mtatype = newstr(p);
1224			p = strchr(m->m_mtatype, '/');
1225			if (p != NULL)
1226			{
1227				*p++ = '\0';
1228				if (*p == '\0')
1229					p = NULL;
1230			}
1231			if (*m->m_mtatype == '\0')
1232				m->m_mtatype = "dns";
1233
1234			/* extract address type; default to "rfc822" */
1235			m->m_addrtype = p;
1236			if (p != NULL)
1237				p = strchr(p, '/');
1238			if (p != NULL)
1239			{
1240				*p++ = '\0';
1241				if (*p == '\0')
1242					p = NULL;
1243			}
1244			if (m->m_addrtype == NULL || *m->m_addrtype == '\0')
1245				m->m_addrtype = "rfc822";
1246
1247			/* extract diagnostic type; default to "smtp" */
1248			m->m_diagtype = p;
1249			if (m->m_diagtype == NULL || *m->m_diagtype == '\0')
1250				m->m_diagtype = "smtp";
1251			break;
1252
1253		  case 'U':		/* user id */
1254			if (isascii(*p) && !isdigit(*p))
1255			{
1256				char *q = p;
1257				struct passwd *pw;
1258
1259				while (*p != '\0' && isascii(*p) &&
1260				       (isalnum(*p) || strchr("-_", *p) != NULL))
1261					p++;
1262				while (isascii(*p) && isspace(*p))
1263					*p++ = '\0';
1264				if (*p != '\0')
1265					*p++ = '\0';
1266				if (*q == '\0')
1267				{
1268					syserr("mailer %s: null user name",
1269						m->m_name);
1270					break;
1271				}
1272				pw = sm_getpwnam(q);
1273				if (pw == NULL)
1274				{
1275					syserr("readcf: mailer U= flag: unknown user %s", q);
1276					break;
1277				}
1278				else
1279				{
1280					m->m_uid = pw->pw_uid;
1281					m->m_gid = pw->pw_gid;
1282				}
1283			}
1284			else
1285			{
1286				auto char *q;
1287
1288				m->m_uid = strtol(p, &q, 0);
1289				p = q;
1290				while (isascii(*p) && isspace(*p))
1291					p++;
1292				if (*p != '\0')
1293					p++;
1294			}
1295			while (isascii(*p) && isspace(*p))
1296				p++;
1297			if (*p == '\0')
1298				break;
1299			if (isascii(*p) && !isdigit(*p))
1300			{
1301				char *q = p;
1302				struct group *gr;
1303
1304				while (isascii(*p) && isalnum(*p))
1305					p++;
1306				*p++ = '\0';
1307				if (*q == '\0')
1308				{
1309					syserr("mailer %s: null group name",
1310						m->m_name);
1311					break;
1312				}
1313				gr = getgrnam(q);
1314				if (gr == NULL)
1315				{
1316					syserr("readcf: mailer U= flag: unknown group %s", q);
1317					break;
1318				}
1319				else
1320					m->m_gid = gr->gr_gid;
1321			}
1322			else
1323			{
1324				m->m_gid = strtol(p, NULL, 0);
1325			}
1326			break;
1327
1328		  case 'W':		/* wait timeout */
1329			m->m_wait = convtime(p, 's');
1330			break;
1331
1332		  case '/':		/* new root directory */
1333			if (*p == '\0')
1334				syserr("mailer %s: null root directory",
1335					m->m_name);
1336			else
1337				m->m_rootdir = newstr(p);
1338			break;
1339
1340		  default:
1341			syserr("M%s: unknown mailer equate %c=",
1342			       m->m_name, fcode);
1343			break;
1344		}
1345
1346		p = delimptr;
1347	}
1348
1349#if !HASRRESVPORT
1350	if (bitnset(M_SECURE_PORT, m->m_flags))
1351	{
1352		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1353				     "M%s: Warning: F=%c set on system that doesn't support rresvport()\n",
1354				     m->m_name, M_SECURE_PORT);
1355	}
1356#endif /* !HASRRESVPORT */
1357
1358#if !HASNICE
1359	if (m->m_nice != 0)
1360	{
1361		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1362				     "M%s: Warning: N= set on system that doesn't support nice()\n",
1363				     m->m_name);
1364	}
1365#endif /* !HASNICE */
1366
1367	/* do some rationality checking */
1368	if (m->m_argv == NULL)
1369	{
1370		syserr("M%s: A= argument required", m->m_name);
1371		return;
1372	}
1373	if (m->m_mailer == NULL)
1374	{
1375		syserr("M%s: P= argument required", m->m_name);
1376		return;
1377	}
1378
1379	if (nextmailer >= MAXMAILERS)
1380	{
1381		syserr("too many mailers defined (%d max)", MAXMAILERS);
1382		return;
1383	}
1384
1385	if (m->m_maxrcpt <= 0)
1386		m->m_maxrcpt = DEFAULT_MAX_RCPT;
1387
1388	/* do some heuristic cleanup for back compatibility */
1389	if (bitnset(M_LIMITS, m->m_flags))
1390	{
1391		if (m->m_linelimit == 0)
1392			m->m_linelimit = SMTPLINELIM;
1393		if (ConfigLevel < 2)
1394			setbitn(M_7BITS, m->m_flags);
1395	}
1396
1397	if (strcmp(m->m_mailer, "[TCP]") == 0)
1398	{
1399		syserr("M%s: P=[TCP] must be replaced by P=[IPC]", m->m_name);
1400		return;
1401	}
1402
1403	if (strcmp(m->m_mailer, "[IPC]") == 0)
1404	{
1405		/* Use the second argument for host or path to socket */
1406		if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
1407		    m->m_argv[1][0] == '\0')
1408		{
1409			syserr("M%s: too few parameters for %s mailer",
1410			       m->m_name, m->m_mailer);
1411			return;
1412		}
1413		if (strcmp(m->m_argv[0], "TCP") != 0
1414#if NETUNIX
1415		    && strcmp(m->m_argv[0], "FILE") != 0
1416#endif /* NETUNIX */
1417		    )
1418		{
1419			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1420					     "M%s: Warning: first argument in %s mailer must be %s\n",
1421					     m->m_name, m->m_mailer,
1422#if NETUNIX
1423					     "TCP or FILE"
1424#else /* NETUNIX */
1425					     "TCP"
1426#endif /* NETUNIX */
1427				     );
1428		}
1429		if (m->m_mtatype == NULL)
1430			m->m_mtatype = "dns";
1431		if (m->m_addrtype == NULL)
1432			m->m_addrtype = "rfc822";
1433		if (m->m_diagtype == NULL)
1434		{
1435			if (m->m_argv[0] != NULL &&
1436			    strcmp(m->m_argv[0], "FILE") == 0)
1437				m->m_diagtype = "x-unix";
1438			else
1439				m->m_diagtype = "smtp";
1440		}
1441	}
1442	else if (strcmp(m->m_mailer, "[FILE]") == 0)
1443	{
1444		/* Use the second argument for filename */
1445		if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
1446		    m->m_argv[2] != NULL)
1447		{
1448			syserr("M%s: too %s parameters for [FILE] mailer",
1449			       m->m_name,
1450			       (m->m_argv[0] == NULL ||
1451				m->m_argv[1] == NULL) ? "few" : "many");
1452			return;
1453		}
1454		else if (strcmp(m->m_argv[0], "FILE") != 0)
1455		{
1456			syserr("M%s: first argument in [FILE] mailer must be FILE",
1457			       m->m_name);
1458			return;
1459		}
1460	}
1461
1462	if (m->m_eol == NULL)
1463	{
1464		char **pp;
1465
1466		/* default for SMTP is \r\n; use \n for local delivery */
1467		for (pp = m->m_argv; *pp != NULL; pp++)
1468		{
1469			for (p = *pp; *p != '\0'; )
1470			{
1471				if ((*p++ & 0377) == MACROEXPAND && *p == 'u')
1472					break;
1473			}
1474			if (*p != '\0')
1475				break;
1476		}
1477		if (*pp == NULL)
1478			m->m_eol = "\r\n";
1479		else
1480			m->m_eol = "\n";
1481	}
1482
1483	/* enter the mailer into the symbol table */
1484	s = stab(m->m_name, ST_MAILER, ST_ENTER);
1485	if (s->s_mailer != NULL)
1486	{
1487		i = s->s_mailer->m_mno;
1488		sm_free(s->s_mailer); /* XXX */
1489	}
1490	else
1491	{
1492		i = nextmailer++;
1493	}
1494	Mailer[i] = s->s_mailer = m;
1495	m->m_mno = i;
1496}
1497/*
1498**  MUNCHSTRING -- translate a string into internal form.
1499**
1500**	Parameters:
1501**		p -- the string to munch.
1502**		delimptr -- if non-NULL, set to the pointer of the
1503**			field delimiter character.
1504**		delim -- the delimiter for the field.
1505**
1506**	Returns:
1507**		the munched string.
1508**
1509**	Side Effects:
1510**		the munched string is a local static buffer.
1511**		it must be copied before the function is called again.
1512*/
1513
1514char *
1515munchstring(p, delimptr, delim)
1516	register char *p;
1517	char **delimptr;
1518	int delim;
1519{
1520	register char *q;
1521	bool backslash = false;
1522	bool quotemode = false;
1523	static char buf[MAXLINE];
1524
1525	for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++)
1526	{
1527		if (backslash)
1528		{
1529			/* everything is roughly literal */
1530			backslash = false;
1531			switch (*p)
1532			{
1533			  case 'r':		/* carriage return */
1534				*q++ = '\r';
1535				continue;
1536
1537			  case 'n':		/* newline */
1538				*q++ = '\n';
1539				continue;
1540
1541			  case 'f':		/* form feed */
1542				*q++ = '\f';
1543				continue;
1544
1545			  case 'b':		/* backspace */
1546				*q++ = '\b';
1547				continue;
1548			}
1549			*q++ = *p;
1550		}
1551		else
1552		{
1553			if (*p == '\\')
1554				backslash = true;
1555			else if (*p == '"')
1556				quotemode = !quotemode;
1557			else if (quotemode || *p != delim)
1558				*q++ = *p;
1559			else
1560				break;
1561		}
1562	}
1563
1564	if (delimptr != NULL)
1565		*delimptr = p;
1566	*q++ = '\0';
1567	return buf;
1568}
1569/*
1570**  EXTRQUOTSTR -- extract a (quoted) string.
1571**
1572**	This routine deals with quoted (") strings and escaped
1573**	spaces (\\ ).
1574**
1575**	Parameters:
1576**		p -- source string.
1577**		delimptr -- if non-NULL, set to the pointer of the
1578**			field delimiter character.
1579**		delimbuf -- delimiters for the field.
1580**		st -- if non-NULL, store the return value (whether the
1581**			string was correctly quoted) here.
1582**
1583**	Returns:
1584**		the extracted string.
1585**
1586**	Side Effects:
1587**		the returned string is a local static buffer.
1588**		it must be copied before the function is called again.
1589*/
1590
1591static char *
1592extrquotstr(p, delimptr, delimbuf, st)
1593	register char *p;
1594	char **delimptr;
1595	char *delimbuf;
1596	bool *st;
1597{
1598	register char *q;
1599	bool backslash = false;
1600	bool quotemode = false;
1601	static char buf[MAXLINE];
1602
1603	for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++)
1604	{
1605		if (backslash)
1606		{
1607			backslash = false;
1608			if (*p != ' ')
1609				*q++ = '\\';
1610		}
1611		if (*p == '\\')
1612			backslash = true;
1613		else if (*p == '"')
1614			quotemode = !quotemode;
1615		else if (quotemode ||
1616			 strchr(delimbuf, (int) *p) == NULL)
1617			*q++ = *p;
1618		else
1619			break;
1620	}
1621
1622	if (delimptr != NULL)
1623		*delimptr = p;
1624	*q++ = '\0';
1625	if (st != NULL)
1626		*st = !(quotemode || backslash);
1627	return buf;
1628}
1629/*
1630**  MAKEARGV -- break up a string into words
1631**
1632**	Parameters:
1633**		p -- the string to break up.
1634**
1635**	Returns:
1636**		a char **argv (dynamically allocated)
1637**
1638**	Side Effects:
1639**		munges p.
1640*/
1641
1642static char **
1643makeargv(p)
1644	register char *p;
1645{
1646	char *q;
1647	int i;
1648	char **avp;
1649	char *argv[MAXPV + 1];
1650
1651	/* take apart the words */
1652	i = 0;
1653	while (*p != '\0' && i < MAXPV)
1654	{
1655		q = p;
1656		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1657			p++;
1658		while (isascii(*p) && isspace(*p))
1659			*p++ = '\0';
1660		argv[i++] = newstr(q);
1661	}
1662	argv[i++] = NULL;
1663
1664	/* now make a copy of the argv */
1665	avp = (char **) xalloc(sizeof *avp * i);
1666	memmove((char *) avp, (char *) argv, sizeof *avp * i);
1667
1668	return avp;
1669}
1670/*
1671**  PRINTRULES -- print rewrite rules (for debugging)
1672**
1673**	Parameters:
1674**		none.
1675**
1676**	Returns:
1677**		none.
1678**
1679**	Side Effects:
1680**		prints rewrite rules.
1681*/
1682
1683void
1684printrules()
1685{
1686	register struct rewrite *rwp;
1687	register int ruleset;
1688
1689	for (ruleset = 0; ruleset < 10; ruleset++)
1690	{
1691		if (RewriteRules[ruleset] == NULL)
1692			continue;
1693		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1694				     "\n----Rule Set %d:", ruleset);
1695
1696		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
1697		{
1698			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1699					     "\nLHS:");
1700			printav(rwp->r_lhs);
1701			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1702					     "RHS:");
1703			printav(rwp->r_rhs);
1704		}
1705	}
1706}
1707/*
1708**  PRINTMAILER -- print mailer structure (for debugging)
1709**
1710**	Parameters:
1711**		m -- the mailer to print
1712**
1713**	Returns:
1714**		none.
1715*/
1716
1717void
1718printmailer(m)
1719	register MAILER *m;
1720{
1721	int j;
1722
1723	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1724			     "mailer %d (%s): P=%s S=", m->m_mno, m->m_name,
1725			     m->m_mailer);
1726	if (RuleSetNames[m->m_se_rwset] == NULL)
1727		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%d/",
1728				     m->m_se_rwset);
1729	else
1730		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s/",
1731				     RuleSetNames[m->m_se_rwset]);
1732	if (RuleSetNames[m->m_sh_rwset] == NULL)
1733		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%d R=",
1734				     m->m_sh_rwset);
1735	else
1736		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s R=",
1737				     RuleSetNames[m->m_sh_rwset]);
1738	if (RuleSetNames[m->m_re_rwset] == NULL)
1739		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%d/",
1740				     m->m_re_rwset);
1741	else
1742		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s/",
1743				     RuleSetNames[m->m_re_rwset]);
1744	if (RuleSetNames[m->m_rh_rwset] == NULL)
1745		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%d ",
1746				     m->m_rh_rwset);
1747	else
1748		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s ",
1749				     RuleSetNames[m->m_rh_rwset]);
1750	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "M=%ld U=%d:%d F=",
1751			     m->m_maxsize, (int) m->m_uid, (int) m->m_gid);
1752	for (j = '\0'; j <= '\177'; j++)
1753		if (bitnset(j, m->m_flags))
1754			(void) sm_io_putc(smioout, SM_TIME_DEFAULT, j);
1755	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " L=%d E=",
1756			     m->m_linelimit);
1757	xputs(m->m_eol);
1758	if (m->m_defcharset != NULL)
1759		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " C=%s",
1760				     m->m_defcharset);
1761	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " T=%s/%s/%s",
1762			     m->m_mtatype == NULL
1763				? "<undefined>" : m->m_mtatype,
1764			     m->m_addrtype == NULL
1765				? "<undefined>" : m->m_addrtype,
1766			     m->m_diagtype == NULL
1767				? "<undefined>" : m->m_diagtype);
1768	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " r=%d", m->m_maxrcpt);
1769	if (m->m_argv != NULL)
1770	{
1771		char **a = m->m_argv;
1772
1773		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " A=");
1774		while (*a != NULL)
1775		{
1776			if (a != m->m_argv)
1777				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1778						     " ");
1779			xputs(*a++);
1780		}
1781	}
1782	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n");
1783}
1784/*
1785**  SETOPTION -- set global processing option
1786**
1787**	Parameters:
1788**		opt -- option name.
1789**		val -- option value (as a text string).
1790**		safe -- set if this came from a configuration file.
1791**			Some options (if set from the command line) will
1792**			reset the user id to avoid security problems.
1793**		sticky -- if set, don't let other setoptions override
1794**			this value.
1795**		e -- the main envelope.
1796**
1797**	Returns:
1798**		none.
1799**
1800**	Side Effects:
1801**		Sets options as implied by the arguments.
1802*/
1803
1804static BITMAP256	StickyOpt;		/* set if option is stuck */
1805
1806#if NAMED_BIND
1807
1808static struct resolverflags
1809{
1810	char	*rf_name;	/* name of the flag */
1811	long	rf_bits;	/* bits to set/clear */
1812} ResolverFlags[] =
1813{
1814	{ "debug",	RES_DEBUG	},
1815	{ "aaonly",	RES_AAONLY	},
1816	{ "usevc",	RES_USEVC	},
1817	{ "primary",	RES_PRIMARY	},
1818	{ "igntc",	RES_IGNTC	},
1819	{ "recurse",	RES_RECURSE	},
1820	{ "defnames",	RES_DEFNAMES	},
1821	{ "stayopen",	RES_STAYOPEN	},
1822	{ "dnsrch",	RES_DNSRCH	},
1823# ifdef RES_USE_INET6
1824	{ "use_inet6",	RES_USE_INET6	},
1825# endif /* RES_USE_INET6 */
1826	{ "true",	0		},	/* avoid error on old syntax */
1827	{ NULL,		0		}
1828};
1829
1830#endif /* NAMED_BIND */
1831
1832#define OI_NONE		0	/* no special treatment */
1833#define OI_SAFE		0x0001	/* safe for random people to use */
1834#define OI_SUBOPT	0x0002	/* option has suboptions */
1835
1836static struct optioninfo
1837{
1838	char		*o_name;	/* long name of option */
1839	unsigned char	o_code;		/* short name of option */
1840	unsigned short	o_flags;	/* option flags */
1841} OptionTab[] =
1842{
1843#if defined(SUN_EXTENSIONS) && defined(REMOTE_MODE)
1844	{ "RemoteMode",			'>',		OI_NONE	},
1845#endif /* defined(SUN_EXTENSIONS) && defined(REMOTE_MODE) */
1846	{ "SevenBitInput",		'7',		OI_SAFE	},
1847	{ "EightBitMode",		'8',		OI_SAFE	},
1848	{ "AliasFile",			'A',		OI_NONE	},
1849	{ "AliasWait",			'a',		OI_NONE	},
1850	{ "BlankSub",			'B',		OI_NONE	},
1851	{ "MinFreeBlocks",		'b',		OI_SAFE	},
1852	{ "CheckpointInterval",		'C',		OI_SAFE	},
1853	{ "HoldExpensive",		'c',		OI_NONE	},
1854	{ "DeliveryMode",		'd',		OI_SAFE	},
1855	{ "ErrorHeader",		'E',		OI_NONE	},
1856	{ "ErrorMode",			'e',		OI_SAFE	},
1857	{ "TempFileMode",		'F',		OI_NONE	},
1858	{ "SaveFromLine",		'f',		OI_NONE	},
1859	{ "MatchGECOS",			'G',		OI_NONE	},
1860
1861	/* no long name, just here to avoid problems in setoption */
1862	{ "",				'g',		OI_NONE	},
1863	{ "HelpFile",			'H',		OI_NONE	},
1864	{ "MaxHopCount",		'h',		OI_NONE	},
1865	{ "ResolverOptions",		'I',		OI_NONE	},
1866	{ "IgnoreDots",			'i',		OI_SAFE	},
1867	{ "ForwardPath",		'J',		OI_NONE	},
1868	{ "SendMimeErrors",		'j',		OI_SAFE	},
1869	{ "ConnectionCacheSize",	'k',		OI_NONE	},
1870	{ "ConnectionCacheTimeout",	'K',		OI_NONE	},
1871	{ "UseErrorsTo",		'l',		OI_NONE	},
1872	{ "LogLevel",			'L',		OI_SAFE	},
1873	{ "MeToo",			'm',		OI_SAFE	},
1874
1875	/* no long name, just here to avoid problems in setoption */
1876	{ "",				'M',		OI_NONE	},
1877	{ "CheckAliases",		'n',		OI_NONE	},
1878	{ "OldStyleHeaders",		'o',		OI_SAFE	},
1879	{ "DaemonPortOptions",		'O',		OI_NONE	},
1880	{ "PrivacyOptions",		'p',		OI_SAFE	},
1881	{ "PostmasterCopy",		'P',		OI_NONE	},
1882	{ "QueueFactor",		'q',		OI_NONE	},
1883	{ "QueueDirectory",		'Q',		OI_NONE	},
1884	{ "DontPruneRoutes",		'R',		OI_NONE	},
1885	{ "Timeout",			'r',		OI_SUBOPT },
1886	{ "StatusFile",			'S',		OI_NONE	},
1887	{ "SuperSafe",			's',		OI_SAFE	},
1888	{ "QueueTimeout",		'T',		OI_NONE	},
1889	{ "TimeZoneSpec",		't',		OI_NONE	},
1890	{ "UserDatabaseSpec",		'U',		OI_NONE	},
1891	{ "DefaultUser",		'u',		OI_NONE	},
1892	{ "FallbackMXhost",		'V',		OI_NONE	},
1893	{ "Verbose",			'v',		OI_SAFE	},
1894	{ "TryNullMXList",		'w',		OI_NONE	},
1895	{ "QueueLA",			'x',		OI_NONE	},
1896	{ "RefuseLA",			'X',		OI_NONE	},
1897	{ "RecipientFactor",		'y',		OI_NONE	},
1898	{ "ForkEachJob",		'Y',		OI_NONE	},
1899	{ "ClassFactor",		'z',		OI_NONE	},
1900	{ "RetryFactor",		'Z',		OI_NONE	},
1901#define O_QUEUESORTORD	0x81
1902	{ "QueueSortOrder",		O_QUEUESORTORD,	OI_SAFE	},
1903#define O_HOSTSFILE	0x82
1904	{ "HostsFile",			O_HOSTSFILE,	OI_NONE	},
1905#define O_MQA		0x83
1906	{ "MinQueueAge",		O_MQA,		OI_SAFE	},
1907#define O_DEFCHARSET	0x85
1908	{ "DefaultCharSet",		O_DEFCHARSET,	OI_SAFE	},
1909#define O_SSFILE	0x86
1910	{ "ServiceSwitchFile",		O_SSFILE,	OI_NONE	},
1911#define O_DIALDELAY	0x87
1912	{ "DialDelay",			O_DIALDELAY,	OI_SAFE	},
1913#define O_NORCPTACTION	0x88
1914	{ "NoRecipientAction",		O_NORCPTACTION,	OI_SAFE	},
1915#define O_SAFEFILEENV	0x89
1916	{ "SafeFileEnvironment",	O_SAFEFILEENV,	OI_NONE	},
1917#define O_MAXMSGSIZE	0x8a
1918	{ "MaxMessageSize",		O_MAXMSGSIZE,	OI_NONE	},
1919#define O_COLONOKINADDR	0x8b
1920	{ "ColonOkInAddr",		O_COLONOKINADDR, OI_SAFE },
1921#define O_MAXQUEUERUN	0x8c
1922	{ "MaxQueueRunSize",		O_MAXQUEUERUN,	OI_SAFE	},
1923#define O_MAXCHILDREN	0x8d
1924	{ "MaxDaemonChildren",		O_MAXCHILDREN,	OI_NONE	},
1925#define O_KEEPCNAMES	0x8e
1926	{ "DontExpandCnames",		O_KEEPCNAMES,	OI_NONE	},
1927#define O_MUSTQUOTE	0x8f
1928	{ "MustQuoteChars",		O_MUSTQUOTE,	OI_NONE	},
1929#define O_SMTPGREETING	0x90
1930	{ "SmtpGreetingMessage",	O_SMTPGREETING,	OI_NONE	},
1931#define O_UNIXFROM	0x91
1932	{ "UnixFromLine",		O_UNIXFROM,	OI_NONE	},
1933#define O_OPCHARS	0x92
1934	{ "OperatorChars",		O_OPCHARS,	OI_NONE	},
1935#define O_DONTINITGRPS	0x93
1936	{ "DontInitGroups",		O_DONTINITGRPS,	OI_NONE	},
1937#define O_SLFH		0x94
1938	{ "SingleLineFromHeader",	O_SLFH,		OI_SAFE	},
1939#define O_ABH		0x95
1940	{ "AllowBogusHELO",		O_ABH,		OI_SAFE	},
1941#define O_CONNTHROT	0x97
1942	{ "ConnectionRateThrottle",	O_CONNTHROT,	OI_NONE	},
1943#define O_UGW		0x99
1944	{ "UnsafeGroupWrites",		O_UGW,		OI_NONE	},
1945#define O_DBLBOUNCE	0x9a
1946	{ "DoubleBounceAddress",	O_DBLBOUNCE,	OI_NONE	},
1947#define O_HSDIR		0x9b
1948	{ "HostStatusDirectory",	O_HSDIR,	OI_NONE	},
1949#define O_SINGTHREAD	0x9c
1950	{ "SingleThreadDelivery",	O_SINGTHREAD,	OI_NONE	},
1951#define O_RUNASUSER	0x9d
1952	{ "RunAsUser",			O_RUNASUSER,	OI_NONE	},
1953#define O_DSN_RRT	0x9e
1954	{ "RrtImpliesDsn",		O_DSN_RRT,	OI_NONE	},
1955#define O_PIDFILE	0x9f
1956	{ "PidFile",			O_PIDFILE,	OI_NONE	},
1957#define O_DONTBLAMESENDMAIL	0xa0
1958	{ "DontBlameSendmail",		O_DONTBLAMESENDMAIL,	OI_NONE	},
1959#define O_DPI		0xa1
1960	{ "DontProbeInterfaces",	O_DPI,		OI_NONE	},
1961#define O_MAXRCPT	0xa2
1962	{ "MaxRecipientsPerMessage",	O_MAXRCPT,	OI_SAFE	},
1963#define O_DEADLETTER	0xa3
1964	{ "DeadLetterDrop",		O_DEADLETTER,	OI_NONE	},
1965#if _FFR_DONTLOCKFILESFORREAD_OPTION
1966# define O_DONTLOCK	0xa4
1967	{ "DontLockFilesForRead",	O_DONTLOCK,	OI_NONE	},
1968#endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */
1969#define O_MAXALIASRCSN	0xa5
1970	{ "MaxAliasRecursion",		O_MAXALIASRCSN,	OI_NONE	},
1971#define O_CNCTONLYTO	0xa6
1972	{ "ConnectOnlyTo",		O_CNCTONLYTO,	OI_NONE	},
1973#define O_TRUSTUSER	0xa7
1974	{ "TrustedUser",		O_TRUSTUSER,	OI_NONE	},
1975#define O_MAXMIMEHDRLEN	0xa8
1976	{ "MaxMimeHeaderLength",	O_MAXMIMEHDRLEN,	OI_NONE	},
1977#define O_CONTROLSOCKET	0xa9
1978	{ "ControlSocketName",		O_CONTROLSOCKET,	OI_NONE	},
1979#define O_MAXHDRSLEN	0xaa
1980	{ "MaxHeadersLength",		O_MAXHDRSLEN,	OI_NONE	},
1981#if _FFR_MAX_FORWARD_ENTRIES
1982# define O_MAXFORWARD	0xab
1983	{ "MaxForwardEntries",		O_MAXFORWARD,	OI_NONE	},
1984#endif /* _FFR_MAX_FORWARD_ENTRIES */
1985#define O_PROCTITLEPREFIX	0xac
1986	{ "ProcessTitlePrefix",		O_PROCTITLEPREFIX,	OI_NONE	},
1987#define O_SASLINFO	0xad
1988#if _FFR_ALLOW_SASLINFO
1989	{ "DefaultAuthInfo",		O_SASLINFO,	OI_SAFE	},
1990#else /* _FFR_ALLOW_SASLINFO */
1991	{ "DefaultAuthInfo",		O_SASLINFO,	OI_NONE	},
1992#endif /* _FFR_ALLOW_SASLINFO */
1993#define O_SASLMECH	0xae
1994	{ "AuthMechanisms",		O_SASLMECH,	OI_NONE	},
1995#define O_CLIENTPORT	0xaf
1996	{ "ClientPortOptions",		O_CLIENTPORT,	OI_NONE	},
1997#define O_DF_BUFSIZE	0xb0
1998	{ "DataFileBufferSize",		O_DF_BUFSIZE,	OI_NONE	},
1999#define O_XF_BUFSIZE	0xb1
2000	{ "XscriptFileBufferSize",	O_XF_BUFSIZE,	OI_NONE	},
2001#define O_LDAPDEFAULTSPEC	0xb2
2002	{ "LDAPDefaultSpec",		O_LDAPDEFAULTSPEC,	OI_NONE	},
2003#if _FFR_QUEUEDELAY
2004# define O_QUEUEDELAY	0xb3
2005	{ "QueueDelay",			O_QUEUEDELAY,	OI_NONE	},
2006#endif /* _FFR_QUEUEDELAY */
2007#define O_SRVCERTFILE	0xb4
2008	{ "ServerCertFile",		O_SRVCERTFILE,	OI_NONE	},
2009#define O_SRVKEYFILE	0xb5
2010	{ "Serverkeyfile",		O_SRVKEYFILE,	OI_NONE	},
2011#define O_CLTCERTFILE	0xb6
2012	{ "ClientCertFile",		O_CLTCERTFILE,	OI_NONE	},
2013#define O_CLTKEYFILE	0xb7
2014	{ "Clientkeyfile",		O_CLTKEYFILE,	OI_NONE	},
2015#define O_CACERTFILE	0xb8
2016	{ "CACERTFile",			O_CACERTFILE,	OI_NONE	},
2017#define O_CACERTPATH	0xb9
2018	{ "CACERTPath",			O_CACERTPATH,	OI_NONE	},
2019#define O_DHPARAMS	0xba
2020	{ "DHParameters",		O_DHPARAMS,	OI_NONE	},
2021#define O_INPUTMILTER	0xbb
2022	{ "InputMailFilters",		O_INPUTMILTER,	OI_NONE	},
2023#define O_MILTER	0xbc
2024	{ "Milter",			O_MILTER,	OI_SUBOPT	},
2025#define O_SASLOPTS	0xbd
2026	{ "AuthOptions",		O_SASLOPTS,	OI_NONE	},
2027#define O_QUEUE_FILE_MODE	0xbe
2028	{ "QueueFileMode",		O_QUEUE_FILE_MODE, OI_NONE	},
2029#if _FFR_TLS_1
2030# define O_DHPARAMS5	0xbf
2031	{ "DHParameters512",		O_DHPARAMS5,	OI_NONE	},
2032# define O_CIPHERLIST	0xc0
2033	{ "CipherList",			O_CIPHERLIST,	OI_NONE	},
2034#endif /* _FFR_TLS_1 */
2035#define O_RANDFILE	0xc1
2036	{ "RandFile",			O_RANDFILE,	OI_NONE	},
2037#define O_TLS_SRV_OPTS	0xc2
2038	{ "TLSSrvOptions",		O_TLS_SRV_OPTS,	OI_NONE	},
2039#define O_RCPTTHROT	0xc3
2040	{ "BadRcptThrottle",		O_RCPTTHROT,	OI_SAFE	},
2041#define O_DLVR_MIN	0xc4
2042	{ "DeliverByMin",		O_DLVR_MIN,	OI_NONE	},
2043#define O_MAXQUEUECHILDREN	0xc5
2044	{ "MaxQueueChildren",		O_MAXQUEUECHILDREN,	OI_NONE	},
2045#define O_MAXRUNNERSPERQUEUE	0xc6
2046	{ "MaxRunnersPerQueue",		O_MAXRUNNERSPERQUEUE,	OI_NONE },
2047#define O_DIRECTSUBMODIFIERS	0xc7
2048	{ "DirectSubmissionModifiers",	O_DIRECTSUBMODIFIERS,	OI_NONE },
2049#define O_NICEQUEUERUN	0xc8
2050	{ "NiceQueueRun",		O_NICEQUEUERUN,	OI_NONE	},
2051#define O_SHMKEY	0xc9
2052	{ "SharedMemoryKey",		O_SHMKEY,	OI_NONE	},
2053#define O_SASLBITS	0xca
2054	{ "AuthMaxBits",		O_SASLBITS,	OI_NONE	},
2055#define O_MBDB		0xcb
2056	{ "MailboxDatabase",		O_MBDB,		OI_NONE	},
2057#define O_MSQ		0xcc
2058	{ "UseMSP",	O_MSQ,		OI_NONE	},
2059#define O_DELAY_LA	0xcd
2060	{ "DelayLA",	O_DELAY_LA,	OI_NONE	},
2061#define O_FASTSPLIT	0xce
2062	{ "FastSplit",	O_FASTSPLIT,	OI_NONE	},
2063#if _FFR_SOFT_BOUNCE
2064# define O_SOFTBOUNCE	0xcf
2065	{ "SoftBounce",	O_SOFTBOUNCE,	OI_NONE	},
2066#endif /* _FFR_SOFT_BOUNCE */
2067	{ NULL,				'\0',		OI_NONE	}
2068};
2069
2070# define CANONIFY(val)
2071
2072# define SET_OPT_DEFAULT(opt, val)	opt = val
2073
2074/* set a string option by expanding the value and assigning it */
2075/* WARNING this belongs ONLY into a case statement! */
2076#define SET_STRING_EXP(str)	\
2077		expand(val, exbuf, sizeof exbuf, e);	\
2078		newval = sm_pstrdup_x(exbuf);		\
2079		if (str != NULL)	\
2080			sm_free(str);	\
2081		CANONIFY(newval);	\
2082		str = newval;		\
2083		break
2084
2085#define OPTNAME	o->o_name == NULL ? "<unknown>" : o->o_name
2086
2087void
2088setoption(opt, val, safe, sticky, e)
2089	int opt;
2090	char *val;
2091	bool safe;
2092	bool sticky;
2093	register ENVELOPE *e;
2094{
2095	register char *p;
2096	register struct optioninfo *o;
2097	char *subopt;
2098	int mid;
2099	bool can_setuid = RunAsUid == 0;
2100	auto char *ep;
2101	char buf[50];
2102	extern bool Warn_Q_option;
2103#if _FFR_ALLOW_SASLINFO
2104	extern unsigned int SubmitMode;
2105#endif /* _FFR_ALLOW_SASLINFO */
2106#if STARTTLS
2107	char *newval;
2108	char exbuf[MAXLINE];
2109#endif /* STARTTLS */
2110
2111	errno = 0;
2112	if (opt == ' ')
2113	{
2114		/* full word options */
2115		struct optioninfo *sel;
2116
2117		p = strchr(val, '=');
2118		if (p == NULL)
2119			p = &val[strlen(val)];
2120		while (*--p == ' ')
2121			continue;
2122		while (*++p == ' ')
2123			*p = '\0';
2124		if (p == val)
2125		{
2126			syserr("readcf: null option name");
2127			return;
2128		}
2129		if (*p == '=')
2130			*p++ = '\0';
2131		while (*p == ' ')
2132			p++;
2133		subopt = strchr(val, '.');
2134		if (subopt != NULL)
2135			*subopt++ = '\0';
2136		sel = NULL;
2137		for (o = OptionTab; o->o_name != NULL; o++)
2138		{
2139			if (sm_strncasecmp(o->o_name, val, strlen(val)) != 0)
2140				continue;
2141			if (strlen(o->o_name) == strlen(val))
2142			{
2143				/* completely specified -- this must be it */
2144				sel = NULL;
2145				break;
2146			}
2147			if (sel != NULL)
2148				break;
2149			sel = o;
2150		}
2151		if (sel != NULL && o->o_name == NULL)
2152			o = sel;
2153		else if (o->o_name == NULL)
2154		{
2155			syserr("readcf: unknown option name %s", val);
2156			return;
2157		}
2158		else if (sel != NULL)
2159		{
2160			syserr("readcf: ambiguous option name %s (matches %s and %s)",
2161				val, sel->o_name, o->o_name);
2162			return;
2163		}
2164		if (strlen(val) != strlen(o->o_name))
2165		{
2166			int oldVerbose = Verbose;
2167
2168			Verbose = 1;
2169			message("Option %s used as abbreviation for %s",
2170				val, o->o_name);
2171			Verbose = oldVerbose;
2172		}
2173		opt = o->o_code;
2174		val = p;
2175	}
2176	else
2177	{
2178		for (o = OptionTab; o->o_name != NULL; o++)
2179		{
2180			if (o->o_code == opt)
2181				break;
2182		}
2183		if (o->o_name == NULL)
2184		{
2185			syserr("readcf: unknown option name 0x%x", opt & 0xff);
2186			return;
2187		}
2188		subopt = NULL;
2189	}
2190
2191	if (subopt != NULL && !bitset(OI_SUBOPT, o->o_flags))
2192	{
2193		if (tTd(37, 1))
2194			sm_dprintf("setoption: %s does not support suboptions, ignoring .%s\n",
2195				   OPTNAME, subopt);
2196		subopt = NULL;
2197	}
2198
2199	if (tTd(37, 1))
2200	{
2201		sm_dprintf(isascii(opt) && isprint(opt) ?
2202			   "setoption %s (%c)%s%s=" :
2203			   "setoption %s (0x%x)%s%s=",
2204			   OPTNAME, opt, subopt == NULL ? "" : ".",
2205			   subopt == NULL ? "" : subopt);
2206		xputs(val);
2207	}
2208
2209	/*
2210	**  See if this option is preset for us.
2211	*/
2212
2213	if (!sticky && bitnset(opt, StickyOpt))
2214	{
2215		if (tTd(37, 1))
2216			sm_dprintf(" (ignored)\n");
2217		return;
2218	}
2219
2220	/*
2221	**  Check to see if this option can be specified by this user.
2222	*/
2223
2224	if (!safe && RealUid == 0)
2225		safe = true;
2226	if (!safe && !bitset(OI_SAFE, o->o_flags))
2227	{
2228		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
2229		{
2230			int dp;
2231
2232			if (tTd(37, 1))
2233				sm_dprintf(" (unsafe)");
2234			dp = drop_privileges(true);
2235			setstat(dp);
2236		}
2237	}
2238	if (tTd(37, 1))
2239		sm_dprintf("\n");
2240
2241	switch (opt & 0xff)
2242	{
2243	  case '7':		/* force seven-bit input */
2244		SevenBitInput = atobool(val);
2245		break;
2246
2247	  case '8':		/* handling of 8-bit input */
2248#if MIME8TO7
2249		switch (*val)
2250		{
2251		  case 'p':		/* pass 8 bit, convert MIME */
2252			MimeMode = MM_CVTMIME|MM_PASS8BIT;
2253			break;
2254
2255		  case 'm':		/* convert 8-bit, convert MIME */
2256			MimeMode = MM_CVTMIME|MM_MIME8BIT;
2257			break;
2258
2259		  case 's':		/* strict adherence */
2260			MimeMode = MM_CVTMIME;
2261			break;
2262
2263# if 0
2264		  case 'r':		/* reject 8-bit, don't convert MIME */
2265			MimeMode = 0;
2266			break;
2267
2268		  case 'j':		/* "just send 8" */
2269			MimeMode = MM_PASS8BIT;
2270			break;
2271
2272		  case 'a':		/* encode 8 bit if available */
2273			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
2274			break;
2275
2276		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
2277			MimeMode = MM_MIME8BIT;
2278			break;
2279# endif /* 0 */
2280
2281		  default:
2282			syserr("Unknown 8-bit mode %c", *val);
2283			finis(false, true, EX_USAGE);
2284		}
2285#else /* MIME8TO7 */
2286		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2287				     "Warning: Option: %s requires MIME8TO7 support\n",
2288				     OPTNAME);
2289#endif /* MIME8TO7 */
2290		break;
2291
2292	  case 'A':		/* set default alias file */
2293		if (val[0] == '\0')
2294		{
2295			char *al;
2296
2297			SET_OPT_DEFAULT(al, "aliases");
2298			setalias(al);
2299		}
2300		else
2301			setalias(val);
2302		break;
2303
2304	  case 'a':		/* look N minutes for "@:@" in alias file */
2305		if (val[0] == '\0')
2306			SafeAlias = 5 MINUTES;
2307		else
2308			SafeAlias = convtime(val, 'm');
2309		break;
2310
2311	  case 'B':		/* substitution for blank character */
2312		SpaceSub = val[0];
2313		if (SpaceSub == '\0')
2314			SpaceSub = ' ';
2315		break;
2316
2317	  case 'b':		/* min blocks free on queue fs/max msg size */
2318		p = strchr(val, '/');
2319		if (p != NULL)
2320		{
2321			*p++ = '\0';
2322			MaxMessageSize = atol(p);
2323		}
2324		MinBlocksFree = atol(val);
2325		break;
2326
2327	  case 'c':		/* don't connect to "expensive" mailers */
2328		NoConnect = atobool(val);
2329		break;
2330
2331	  case 'C':		/* checkpoint every N addresses */
2332		CheckpointInterval = atoi(val);
2333		break;
2334
2335	  case 'd':		/* delivery mode */
2336		switch (*val)
2337		{
2338		  case '\0':
2339			set_delivery_mode(SM_DELIVER, e);
2340			break;
2341
2342		  case SM_QUEUE:	/* queue only */
2343		  case SM_DEFER:	/* queue only and defer map lookups */
2344		  case SM_DELIVER:	/* do everything */
2345		  case SM_FORK:		/* fork after verification */
2346			set_delivery_mode(*val, e);
2347			break;
2348
2349		  default:
2350			syserr("Unknown delivery mode %c", *val);
2351			finis(false, true, EX_USAGE);
2352		}
2353		break;
2354
2355	  case 'E':		/* error message header/header file */
2356		if (*val != '\0')
2357			ErrMsgFile = newstr(val);
2358		break;
2359
2360	  case 'e':		/* set error processing mode */
2361		switch (*val)
2362		{
2363		  case EM_QUIET:	/* be silent about it */
2364		  case EM_MAIL:		/* mail back */
2365		  case EM_BERKNET:	/* do berknet error processing */
2366		  case EM_WRITE:	/* write back (or mail) */
2367		  case EM_PRINT:	/* print errors normally (default) */
2368			e->e_errormode = *val;
2369			break;
2370		}
2371		break;
2372
2373	  case 'F':		/* file mode */
2374		FileMode = atooct(val) & 0777;
2375		break;
2376
2377	  case 'f':		/* save Unix-style From lines on front */
2378		SaveFrom = atobool(val);
2379		break;
2380
2381	  case 'G':		/* match recipients against GECOS field */
2382		MatchGecos = atobool(val);
2383		break;
2384
2385	  case 'g':		/* default gid */
2386  g_opt:
2387		if (isascii(*val) && isdigit(*val))
2388			DefGid = atoi(val);
2389		else
2390		{
2391			register struct group *gr;
2392
2393			DefGid = -1;
2394			gr = getgrnam(val);
2395			if (gr == NULL)
2396				syserr("readcf: option %c: unknown group %s",
2397					opt, val);
2398			else
2399				DefGid = gr->gr_gid;
2400		}
2401		break;
2402
2403	  case 'H':		/* help file */
2404		if (val[0] == '\0')
2405		{
2406			SET_OPT_DEFAULT(HelpFile, "helpfile");
2407		}
2408		else
2409		{
2410			CANONIFY(val);
2411			HelpFile = newstr(val);
2412		}
2413		break;
2414
2415	  case 'h':		/* maximum hop count */
2416		MaxHopCount = atoi(val);
2417		break;
2418
2419	  case 'I':		/* use internet domain name server */
2420#if NAMED_BIND
2421		for (p = val; *p != 0; )
2422		{
2423			bool clearmode;
2424			char *q;
2425			struct resolverflags *rfp;
2426
2427			while (*p == ' ')
2428				p++;
2429			if (*p == '\0')
2430				break;
2431			clearmode = false;
2432			if (*p == '-')
2433				clearmode = true;
2434			else if (*p != '+')
2435				p--;
2436			p++;
2437			q = p;
2438			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2439				p++;
2440			if (*p != '\0')
2441				*p++ = '\0';
2442			if (sm_strcasecmp(q, "HasWildcardMX") == 0)
2443			{
2444				HasWildcardMX = !clearmode;
2445				continue;
2446			}
2447			if (sm_strcasecmp(q, "WorkAroundBrokenAAAA") == 0)
2448			{
2449				WorkAroundBrokenAAAA = !clearmode;
2450				continue;
2451			}
2452			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
2453			{
2454				if (sm_strcasecmp(q, rfp->rf_name) == 0)
2455					break;
2456			}
2457			if (rfp->rf_name == NULL)
2458				syserr("readcf: I option value %s unrecognized", q);
2459			else if (clearmode)
2460				_res.options &= ~rfp->rf_bits;
2461			else
2462				_res.options |= rfp->rf_bits;
2463		}
2464		if (tTd(8, 2))
2465			sm_dprintf("_res.options = %x, HasWildcardMX = %d\n",
2466				   (unsigned int) _res.options, HasWildcardMX);
2467#else /* NAMED_BIND */
2468		usrerr("name server (I option) specified but BIND not compiled in");
2469#endif /* NAMED_BIND */
2470		break;
2471
2472	  case 'i':		/* ignore dot lines in message */
2473		IgnrDot = atobool(val);
2474		break;
2475
2476	  case 'j':		/* send errors in MIME (RFC 1341) format */
2477		SendMIMEErrors = atobool(val);
2478		break;
2479
2480	  case 'J':		/* .forward search path */
2481		CANONIFY(val);
2482		ForwardPath = newstr(val);
2483		break;
2484
2485	  case 'k':		/* connection cache size */
2486		MaxMciCache = atoi(val);
2487		if (MaxMciCache < 0)
2488			MaxMciCache = 0;
2489		break;
2490
2491	  case 'K':		/* connection cache timeout */
2492		MciCacheTimeout = convtime(val, 'm');
2493		break;
2494
2495	  case 'l':		/* use Errors-To: header */
2496		UseErrorsTo = atobool(val);
2497		break;
2498
2499	  case 'L':		/* log level */
2500		if (safe || LogLevel < atoi(val))
2501			LogLevel = atoi(val);
2502		break;
2503
2504	  case 'M':		/* define macro */
2505		sticky = false;
2506		mid = macid_parse(val, &ep);
2507		if (mid == 0)
2508			break;
2509		p = newstr(ep);
2510		if (!safe)
2511			cleanstrcpy(p, p, MAXNAME);
2512		macdefine(&CurEnv->e_macro, A_TEMP, mid, p);
2513		break;
2514
2515	  case 'm':		/* send to me too */
2516		MeToo = atobool(val);
2517		break;
2518
2519	  case 'n':		/* validate RHS in newaliases */
2520		CheckAliases = atobool(val);
2521		break;
2522
2523	    /* 'N' available -- was "net name" */
2524
2525	  case 'O':		/* daemon options */
2526		if (!setdaemonoptions(val))
2527			syserr("too many daemons defined (%d max)", MAXDAEMONS);
2528		break;
2529
2530	  case 'o':		/* assume old style headers */
2531		if (atobool(val))
2532			CurEnv->e_flags |= EF_OLDSTYLE;
2533		else
2534			CurEnv->e_flags &= ~EF_OLDSTYLE;
2535		break;
2536
2537	  case 'p':		/* select privacy level */
2538		p = val;
2539		for (;;)
2540		{
2541			register struct prival *pv;
2542			extern struct prival PrivacyValues[];
2543
2544			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
2545				p++;
2546			if (*p == '\0')
2547				break;
2548			val = p;
2549			while (isascii(*p) && isalnum(*p))
2550				p++;
2551			if (*p != '\0')
2552				*p++ = '\0';
2553
2554			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
2555			{
2556				if (sm_strcasecmp(val, pv->pv_name) == 0)
2557					break;
2558			}
2559			if (pv->pv_name == NULL)
2560				syserr("readcf: Op line: %s unrecognized", val);
2561			else
2562				PrivacyFlags |= pv->pv_flag;
2563		}
2564		sticky = false;
2565		break;
2566
2567	  case 'P':		/* postmaster copy address for returned mail */
2568		PostMasterCopy = newstr(val);
2569		break;
2570
2571	  case 'q':		/* slope of queue only function */
2572		QueueFactor = atoi(val);
2573		break;
2574
2575	  case 'Q':		/* queue directory */
2576		if (val[0] == '\0')
2577		{
2578			QueueDir = "mqueue";
2579		}
2580		else
2581		{
2582			QueueDir = newstr(val);
2583		}
2584		if (RealUid != 0 && !safe)
2585			Warn_Q_option = true;
2586		break;
2587
2588	  case 'R':		/* don't prune routes */
2589		DontPruneRoutes = atobool(val);
2590		break;
2591
2592	  case 'r':		/* read timeout */
2593		if (subopt == NULL)
2594			inittimeouts(val, sticky);
2595		else
2596			settimeout(subopt, val, sticky);
2597		break;
2598
2599	  case 'S':		/* status file */
2600		if (val[0] == '\0')
2601		{
2602			SET_OPT_DEFAULT(StatFile, "statistics");
2603		}
2604		else
2605		{
2606			CANONIFY(val);
2607			StatFile = newstr(val);
2608		}
2609		break;
2610
2611	  case 's':		/* be super safe, even if expensive */
2612		if (tolower(*val) == 'i')
2613			SuperSafe = SAFE_INTERACTIVE;
2614		else
2615			SuperSafe = atobool(val) ? SAFE_REALLY : SAFE_NO;
2616		break;
2617
2618	  case 'T':		/* queue timeout */
2619		p = strchr(val, '/');
2620		if (p != NULL)
2621		{
2622			*p++ = '\0';
2623			settimeout("queuewarn", p, sticky);
2624		}
2625		settimeout("queuereturn", val, sticky);
2626		break;
2627
2628	  case 't':		/* time zone name */
2629		TimeZoneSpec = newstr(val);
2630		break;
2631
2632	  case 'U':		/* location of user database */
2633		UdbSpec = newstr(val);
2634		break;
2635
2636	  case 'u':		/* set default uid */
2637		for (p = val; *p != '\0'; p++)
2638		{
2639#if _FFR_DOTTED_USERNAMES
2640			if (*p == '/' || *p == ':')
2641#else /* _FFR_DOTTED_USERNAMES */
2642			if (*p == '.' || *p == '/' || *p == ':')
2643#endif /* _FFR_DOTTED_USERNAMES */
2644			{
2645				*p++ = '\0';
2646				break;
2647			}
2648		}
2649		if (isascii(*val) && isdigit(*val))
2650		{
2651			DefUid = atoi(val);
2652			setdefuser();
2653		}
2654		else
2655		{
2656			register struct passwd *pw;
2657
2658			DefUid = -1;
2659			pw = sm_getpwnam(val);
2660			if (pw == NULL)
2661			{
2662				syserr("readcf: option u: unknown user %s", val);
2663				break;
2664			}
2665			else
2666			{
2667				DefUid = pw->pw_uid;
2668				DefGid = pw->pw_gid;
2669				DefUser = newstr(pw->pw_name);
2670			}
2671		}
2672
2673# ifdef UID_MAX
2674		if (DefUid > UID_MAX)
2675		{
2676			syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored",
2677				(long)DefUid, (long)UID_MAX);
2678			break;
2679		}
2680# endif /* UID_MAX */
2681
2682		/* handle the group if it is there */
2683		if (*p == '\0')
2684			break;
2685		val = p;
2686		goto g_opt;
2687
2688	  case 'V':		/* fallback MX host */
2689		if (val[0] != '\0')
2690			FallBackMX = newstr(val);
2691		break;
2692
2693	  case 'v':		/* run in verbose mode */
2694		Verbose = atobool(val) ? 1 : 0;
2695		break;
2696
2697	  case 'w':		/* if we are best MX, try host directly */
2698		TryNullMXList = atobool(val);
2699		break;
2700
2701	    /* 'W' available -- was wizard password */
2702
2703	  case 'x':		/* load avg at which to auto-queue msgs */
2704		QueueLA = atoi(val);
2705		break;
2706
2707	  case 'X':	/* load avg at which to auto-reject connections */
2708		RefuseLA = atoi(val);
2709		break;
2710
2711	  case O_DELAY_LA:	/* load avg at which to delay connections */
2712		DelayLA = atoi(val);
2713		break;
2714
2715	  case 'y':		/* work recipient factor */
2716		WkRecipFact = atoi(val);
2717		break;
2718
2719	  case 'Y':		/* fork jobs during queue runs */
2720		ForkQueueRuns = atobool(val);
2721		break;
2722
2723	  case 'z':		/* work message class factor */
2724		WkClassFact = atoi(val);
2725		break;
2726
2727	  case 'Z':		/* work time factor */
2728		WkTimeFact = atoi(val);
2729		break;
2730
2731
2732	  case O_QUEUESORTORD:	/* queue sorting order */
2733		switch (*val)
2734		{
2735		  case 'f':	/* File Name */
2736		  case 'F':
2737			QueueSortOrder = QSO_BYFILENAME;
2738			break;
2739
2740		  case 'h':	/* Host first */
2741		  case 'H':
2742			QueueSortOrder = QSO_BYHOST;
2743			break;
2744
2745		  case 'm':	/* Modification time */
2746		  case 'M':
2747			QueueSortOrder = QSO_BYMODTIME;
2748			break;
2749
2750		  case 'p':	/* Priority order */
2751		  case 'P':
2752			QueueSortOrder = QSO_BYPRIORITY;
2753			break;
2754
2755		  case 't':	/* Submission time */
2756		  case 'T':
2757			QueueSortOrder = QSO_BYTIME;
2758			break;
2759
2760		  case 'r':	/* Random */
2761		  case 'R':
2762			QueueSortOrder = QSO_RANDOM;
2763			break;
2764
2765#if _FFR_RHS
2766		  case 's':	/* Shuffled host name */
2767		  case 'S':
2768			QueueSortOrder = QSO_BYSHUFFLE;
2769			break;
2770#endif /* _FFR_RHS */
2771
2772		  default:
2773			syserr("Invalid queue sort order \"%s\"", val);
2774		}
2775		break;
2776
2777#if _FFR_QUEUEDELAY
2778	  case O_QUEUEDELAY:	/* queue delay algorithm */
2779		switch (*val)
2780		{
2781		  case 'e':	/* exponential */
2782		  case 'E':
2783			QueueAlg = QD_EXP;
2784			QueueInitDelay = 10 MINUTES;
2785			QueueMaxDelay = 2 HOURS;
2786			p = strchr(val, '/');
2787			if (p != NULL)
2788			{
2789				char *q;
2790
2791				*p++ = '\0';
2792				q = strchr(p, '/');
2793				if (q != NULL)
2794					*q++ = '\0';
2795				QueueInitDelay = convtime(p, 's');
2796				if (q != NULL)
2797				{
2798					QueueMaxDelay = convtime(q, 's');
2799				}
2800			}
2801			break;
2802
2803		  case 'l':	/* linear */
2804		  case 'L':
2805			QueueAlg = QD_LINEAR;
2806			break;
2807
2808		  default:
2809			syserr("Invalid queue delay algorithm \"%s\"", val);
2810		}
2811		break;
2812#endif /* _FFR_QUEUEDELAY */
2813
2814	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
2815		CANONIFY(val);
2816		HostsFile = newstr(val);
2817		break;
2818
2819	  case O_MQA:		/* minimum queue age between deliveries */
2820		MinQueueAge = convtime(val, 'm');
2821		break;
2822
2823	  case O_DEFCHARSET:	/* default character set for mimefying */
2824		DefaultCharSet = newstr(denlstring(val, true, true));
2825		break;
2826
2827	  case O_SSFILE:	/* service switch file */
2828		CANONIFY(val);
2829		ServiceSwitchFile = newstr(val);
2830		break;
2831
2832	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
2833		DialDelay = convtime(val, 's');
2834		break;
2835
2836	  case O_NORCPTACTION:	/* what to do if no recipient */
2837		if (sm_strcasecmp(val, "none") == 0)
2838			NoRecipientAction = NRA_NO_ACTION;
2839		else if (sm_strcasecmp(val, "add-to") == 0)
2840			NoRecipientAction = NRA_ADD_TO;
2841		else if (sm_strcasecmp(val, "add-apparently-to") == 0)
2842			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
2843		else if (sm_strcasecmp(val, "add-bcc") == 0)
2844			NoRecipientAction = NRA_ADD_BCC;
2845		else if (sm_strcasecmp(val, "add-to-undisclosed") == 0)
2846			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
2847		else
2848			syserr("Invalid NoRecipientAction: %s", val);
2849		break;
2850
2851	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
2852		SafeFileEnv = newstr(val);
2853		break;
2854
2855	  case O_MAXMSGSIZE:	/* maximum message size */
2856		MaxMessageSize = atol(val);
2857		break;
2858
2859	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
2860		ColonOkInAddr = atobool(val);
2861		break;
2862
2863	  case O_MAXQUEUERUN:	/* max # of jobs in a single queue run */
2864		MaxQueueRun = atoi(val);
2865		break;
2866
2867	  case O_MAXCHILDREN:	/* max # of children of daemon */
2868		MaxChildren = atoi(val);
2869		break;
2870
2871	  case O_MAXQUEUECHILDREN: /* max # of children of daemon */
2872		MaxQueueChildren = atoi(val);
2873		break;
2874
2875	  case O_MAXRUNNERSPERQUEUE: /* max # runners in a queue group */
2876		MaxRunnersPerQueue = atoi(val);
2877		break;
2878
2879	  case O_NICEQUEUERUN:		/* nice queue runs */
2880#if !HASNICE
2881		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2882				     "Warning: NiceQueueRun set on system that doesn't support nice()\n");
2883#endif /* !HASNICE */
2884
2885		/* XXX do we want to check the range? > 0 ? */
2886		NiceQueueRun = atoi(val);
2887		break;
2888
2889	  case O_SHMKEY	:		/* shared memory key */
2890#if SM_CONF_SHM
2891		ShmKey = atol(val);
2892#else /* SM_CONF_SHM */
2893		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2894				     "Warning: Option: %s requires shared memory support (-DSM_CONF_SHM)\n",
2895				     OPTNAME);
2896#endif /* SM_CONF_SHM */
2897		break;
2898
2899#if _FFR_MAX_FORWARD_ENTRIES
2900	  case O_MAXFORWARD:	/* max # of forward entries */
2901		MaxForwardEntries = atoi(val);
2902		break;
2903#endif /* _FFR_MAX_FORWARD_ENTRIES */
2904
2905	  case O_KEEPCNAMES:	/* don't expand CNAME records */
2906		DontExpandCnames = atobool(val);
2907		break;
2908
2909	  case O_MUSTQUOTE:	/* must quote these characters in phrases */
2910		(void) sm_strlcpy(buf, "@,;:\\()[]", sizeof buf);
2911		if (strlen(val) < sizeof buf - 10)
2912			(void) sm_strlcat(buf, val, sizeof buf);
2913		else
2914			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2915					     "Warning: MustQuoteChars too long, ignored.\n");
2916		MustQuoteChars = newstr(buf);
2917		break;
2918
2919	  case O_SMTPGREETING:	/* SMTP greeting message (old $e macro) */
2920		SmtpGreeting = newstr(munchstring(val, NULL, '\0'));
2921		break;
2922
2923	  case O_UNIXFROM:	/* UNIX From_ line (old $l macro) */
2924		UnixFromLine = newstr(munchstring(val, NULL, '\0'));
2925		break;
2926
2927	  case O_OPCHARS:	/* operator characters (old $o macro) */
2928		if (OperatorChars != NULL)
2929			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2930					     "Warning: OperatorChars is being redefined.\n         It should only be set before ruleset definitions.\n");
2931		OperatorChars = newstr(munchstring(val, NULL, '\0'));
2932		break;
2933
2934	  case O_DONTINITGRPS:	/* don't call initgroups(3) */
2935		DontInitGroups = atobool(val);
2936		break;
2937
2938	  case O_SLFH:		/* make sure from fits on one line */
2939		SingleLineFromHeader = atobool(val);
2940		break;
2941
2942	  case O_ABH:		/* allow HELO commands with syntax errors */
2943		AllowBogusHELO = atobool(val);
2944		break;
2945
2946	  case O_CONNTHROT:	/* connection rate throttle */
2947		ConnRateThrottle = atoi(val);
2948		break;
2949
2950	  case O_UGW:		/* group writable files are unsafe */
2951		if (!atobool(val))
2952		{
2953			setbitn(DBS_GROUPWRITABLEFORWARDFILESAFE,
2954				DontBlameSendmail);
2955			setbitn(DBS_GROUPWRITABLEINCLUDEFILESAFE,
2956				DontBlameSendmail);
2957		}
2958		break;
2959
2960	  case O_DBLBOUNCE:	/* address to which to send double bounces */
2961		DoubleBounceAddr = newstr(val);
2962		break;
2963
2964	  case O_HSDIR:		/* persistent host status directory */
2965		if (val[0] != '\0')
2966		{
2967			CANONIFY(val);
2968			HostStatDir = newstr(val);
2969		}
2970		break;
2971
2972	  case O_SINGTHREAD:	/* single thread deliveries (requires hsdir) */
2973		SingleThreadDelivery = atobool(val);
2974		break;
2975
2976	  case O_RUNASUSER:	/* run bulk of code as this user */
2977		for (p = val; *p != '\0'; p++)
2978		{
2979#if _FFR_DOTTED_USERNAMES
2980			if (*p == '/' || *p == ':')
2981#else /* _FFR_DOTTED_USERNAMES */
2982			if (*p == '.' || *p == '/' || *p == ':')
2983#endif /* _FFR_DOTTED_USERNAMES */
2984			{
2985				*p++ = '\0';
2986				break;
2987			}
2988		}
2989		if (isascii(*val) && isdigit(*val))
2990		{
2991			if (can_setuid)
2992				RunAsUid = atoi(val);
2993		}
2994		else
2995		{
2996			register struct passwd *pw;
2997
2998			pw = sm_getpwnam(val);
2999			if (pw == NULL)
3000			{
3001				syserr("readcf: option RunAsUser: unknown user %s", val);
3002				break;
3003			}
3004			else if (can_setuid)
3005			{
3006				if (*p == '\0')
3007					RunAsUserName = newstr(val);
3008				RunAsUid = pw->pw_uid;
3009				RunAsGid = pw->pw_gid;
3010			}
3011			else if (EffGid == pw->pw_gid)
3012				RunAsGid = pw->pw_gid;
3013			else if (UseMSP && *p == '\0')
3014				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3015						     "WARNING: RunAsGid for MSP ignored, check group ids (egid=%d, want=%d)\n",
3016						     (int) EffGid,
3017						     (int) pw->pw_gid);
3018		}
3019# ifdef UID_MAX
3020		if (RunAsUid > UID_MAX)
3021		{
3022			syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored",
3023				(long) RunAsUid, (long) UID_MAX);
3024			break;
3025		}
3026# endif /* UID_MAX */
3027		if (*p != '\0')
3028		{
3029			if (isascii(*p) && isdigit(*p))
3030			{
3031				gid_t runasgid;
3032
3033				runasgid = (gid_t) atoi(p);
3034				if (can_setuid || EffGid == runasgid)
3035					RunAsGid = runasgid;
3036				else if (UseMSP)
3037					(void) sm_io_fprintf(smioout,
3038							     SM_TIME_DEFAULT,
3039							     "WARNING: RunAsGid for MSP ignored, check group ids (egid=%d, want=%d)\n",
3040							     (int) EffGid,
3041							     (int) runasgid);
3042			}
3043			else
3044			{
3045				register struct group *gr;
3046
3047				gr = getgrnam(p);
3048				if (gr == NULL)
3049					syserr("readcf: option RunAsUser: unknown group %s",
3050						p);
3051				else if (can_setuid || EffGid == gr->gr_gid)
3052					RunAsGid = gr->gr_gid;
3053				else if (UseMSP)
3054					(void) sm_io_fprintf(smioout,
3055							     SM_TIME_DEFAULT,
3056							     "WARNING: RunAsGid for MSP ignored, check group ids (egid=%d, want=%d)\n",
3057							     (int) EffGid,
3058							     (int) gr->gr_gid);
3059			}
3060		}
3061		if (tTd(47, 5))
3062			sm_dprintf("readcf: RunAsUser = %d:%d\n",
3063				   (int) RunAsUid, (int) RunAsGid);
3064		break;
3065
3066	  case O_DSN_RRT:
3067		RrtImpliesDsn = atobool(val);
3068		break;
3069
3070	  case O_PIDFILE:
3071		PSTRSET(PidFile, val);
3072		break;
3073
3074	  case O_DONTBLAMESENDMAIL:
3075		p = val;
3076		for (;;)
3077		{
3078			register struct dbsval *dbs;
3079			extern struct dbsval DontBlameSendmailValues[];
3080
3081			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
3082				p++;
3083			if (*p == '\0')
3084				break;
3085			val = p;
3086			while (isascii(*p) && isalnum(*p))
3087				p++;
3088			if (*p != '\0')
3089				*p++ = '\0';
3090
3091			for (dbs = DontBlameSendmailValues;
3092			     dbs->dbs_name != NULL; dbs++)
3093			{
3094				if (sm_strcasecmp(val, dbs->dbs_name) == 0)
3095					break;
3096			}
3097			if (dbs->dbs_name == NULL)
3098				syserr("readcf: DontBlameSendmail option: %s unrecognized", val);
3099			else if (dbs->dbs_flag == DBS_SAFE)
3100				clrbitmap(DontBlameSendmail);
3101			else
3102				setbitn(dbs->dbs_flag, DontBlameSendmail);
3103		}
3104		sticky = false;
3105		break;
3106
3107	  case O_DPI:
3108		if (sm_strcasecmp(val, "loopback") == 0)
3109			DontProbeInterfaces = DPI_SKIPLOOPBACK;
3110		else if (atobool(val))
3111			DontProbeInterfaces = DPI_PROBENONE;
3112		else
3113			DontProbeInterfaces = DPI_PROBEALL;
3114		break;
3115
3116	  case O_MAXRCPT:
3117		MaxRcptPerMsg = atoi(val);
3118		break;
3119
3120	  case O_RCPTTHROT:
3121		BadRcptThrottle = atoi(val);
3122		break;
3123
3124	  case O_DEADLETTER:
3125		CANONIFY(val);
3126		PSTRSET(DeadLetterDrop, val);
3127		break;
3128
3129#if _FFR_DONTLOCKFILESFORREAD_OPTION
3130	  case O_DONTLOCK:
3131		DontLockReadFiles = atobool(val);
3132		break;
3133#endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */
3134
3135	  case O_MAXALIASRCSN:
3136		MaxAliasRecursion = atoi(val);
3137		break;
3138
3139	  case O_CNCTONLYTO:
3140		/* XXX should probably use gethostbyname */
3141#if NETINET || NETINET6
3142		ConnectOnlyTo.sa.sa_family = AF_UNSPEC;
3143# if NETINET6
3144		if (anynet_pton(AF_INET6, val,
3145				&ConnectOnlyTo.sin6.sin6_addr) != 1)
3146			ConnectOnlyTo.sa.sa_family = AF_INET6;
3147		else
3148# endif /* NETINET6 */
3149# if NETINET
3150		{
3151			ConnectOnlyTo.sin.sin_addr.s_addr = inet_addr(val);
3152			if (ConnectOnlyTo.sin.sin_addr.s_addr != INADDR_NONE)
3153				ConnectOnlyTo.sa.sa_family = AF_INET;
3154		}
3155
3156# endif /* NETINET */
3157		if (ConnectOnlyTo.sa.sa_family == AF_UNSPEC)
3158		{
3159			syserr("readcf: option ConnectOnlyTo: invalid IP address %s",
3160			       val);
3161			break;
3162		}
3163#endif /* NETINET || NETINET6 */
3164		break;
3165
3166	  case O_TRUSTUSER:
3167# if !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING)
3168		if (!UseMSP)
3169			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3170					     "readcf: option TrustedUser may cause problems on systems\n        which do not support fchown() if UseMSP is not set.\n");
3171# endif /* !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING) */
3172		if (isascii(*val) && isdigit(*val))
3173			TrustedUid = atoi(val);
3174		else
3175		{
3176			register struct passwd *pw;
3177
3178			TrustedUid = 0;
3179			pw = sm_getpwnam(val);
3180			if (pw == NULL)
3181			{
3182				syserr("readcf: option TrustedUser: unknown user %s", val);
3183				break;
3184			}
3185			else
3186				TrustedUid = pw->pw_uid;
3187		}
3188
3189# ifdef UID_MAX
3190		if (TrustedUid > UID_MAX)
3191		{
3192			syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)",
3193				(long) TrustedUid, (long) UID_MAX);
3194			TrustedUid = 0;
3195		}
3196# endif /* UID_MAX */
3197		break;
3198
3199	  case O_MAXMIMEHDRLEN:
3200		p = strchr(val, '/');
3201		if (p != NULL)
3202			*p++ = '\0';
3203		MaxMimeHeaderLength = atoi(val);
3204		if (p != NULL && *p != '\0')
3205			MaxMimeFieldLength = atoi(p);
3206		else
3207			MaxMimeFieldLength = MaxMimeHeaderLength / 2;
3208
3209		if (MaxMimeHeaderLength < 0)
3210			MaxMimeHeaderLength = 0;
3211		else if (MaxMimeHeaderLength < 128)
3212			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3213					     "Warning: MaxMimeHeaderLength: header length limit set lower than 128\n");
3214
3215		if (MaxMimeFieldLength < 0)
3216			MaxMimeFieldLength = 0;
3217		else if (MaxMimeFieldLength < 40)
3218			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3219					     "Warning: MaxMimeHeaderLength: field length limit set lower than 40\n");
3220		break;
3221
3222	  case O_CONTROLSOCKET:
3223		PSTRSET(ControlSocketName, val);
3224		break;
3225
3226	  case O_MAXHDRSLEN:
3227		MaxHeadersLength = atoi(val);
3228
3229		if (MaxHeadersLength > 0 &&
3230		    MaxHeadersLength < (MAXHDRSLEN / 2))
3231			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3232					     "Warning: MaxHeadersLength: headers length limit set lower than %d\n",
3233					     (MAXHDRSLEN / 2));
3234		break;
3235
3236	  case O_PROCTITLEPREFIX:
3237		PSTRSET(ProcTitlePrefix, val);
3238		break;
3239
3240#if SASL
3241	  case O_SASLINFO:
3242# if _FFR_ALLOW_SASLINFO
3243		/*
3244		**  Allow users to select their own authinfo file
3245		**  under certain circumstances, otherwise just ignore
3246		**  the option.  If the option isn't ignored, several
3247		**  commands don't work very well, e.g., mailq.
3248		**  However, this is not a "perfect" solution.
3249		**  If mail is queued, the authentication info
3250		**  will not be used in subsequent delivery attempts.
3251		**  If we really want to support this, then it has
3252		**  to be stored in the queue file.
3253		*/
3254		if (!bitset(SUBMIT_MSA, SubmitMode) && RealUid != 0 &&
3255		    RunAsUid != RealUid)
3256			break;
3257# endif /* _FFR_ALLOW_SASLINFO */
3258		PSTRSET(SASLInfo, val);
3259		break;
3260
3261	  case O_SASLMECH:
3262		if (AuthMechanisms != NULL)
3263			sm_free(AuthMechanisms); /* XXX */
3264		if (*val != '\0')
3265			AuthMechanisms = newstr(val);
3266		else
3267			AuthMechanisms = NULL;
3268		break;
3269
3270	  case O_SASLOPTS:
3271		while (val != NULL && *val != '\0')
3272		{
3273			switch (*val)
3274			{
3275			  case 'A':
3276				SASLOpts |= SASL_AUTH_AUTH;
3277				break;
3278			  case 'a':
3279				SASLOpts |= SASL_SEC_NOACTIVE;
3280				break;
3281			  case 'c':
3282				SASLOpts |= SASL_SEC_PASS_CREDENTIALS;
3283				break;
3284			  case 'd':
3285				SASLOpts |= SASL_SEC_NODICTIONARY;
3286				break;
3287			  case 'f':
3288				SASLOpts |= SASL_SEC_FORWARD_SECRECY;
3289				break;
3290			  case 'p':
3291				SASLOpts |= SASL_SEC_NOPLAINTEXT;
3292				break;
3293			  case 'y':
3294				SASLOpts |= SASL_SEC_NOANONYMOUS;
3295				break;
3296			  case ' ':	/* ignore */
3297			  case '\t':	/* ignore */
3298			  case ',':	/* ignore */
3299				break;
3300			  default:
3301				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3302						     "Warning: Option: %s unknown parameter '%c'\n",
3303						     OPTNAME,
3304						     (isascii(*val) &&
3305							isprint(*val))
3306							? *val : '?');
3307				break;
3308			}
3309			++val;
3310			val = strpbrk(val, ", \t");
3311			if (val != NULL)
3312				++val;
3313		}
3314		break;
3315	  case O_SASLBITS:
3316		MaxSLBits = atoi(val);
3317		break;
3318
3319#else /* SASL */
3320	  case O_SASLINFO:
3321	  case O_SASLMECH:
3322	  case O_SASLOPTS:
3323	  case O_SASLBITS:
3324		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3325				     "Warning: Option: %s requires SASL support (-DSASL)\n",
3326				     OPTNAME);
3327		break;
3328#endif /* SASL */
3329
3330#if STARTTLS
3331	  case O_SRVCERTFILE:
3332		SET_STRING_EXP(SrvCERTfile);
3333	  case O_SRVKEYFILE:
3334		SET_STRING_EXP(Srvkeyfile);
3335	  case O_CLTCERTFILE:
3336		SET_STRING_EXP(CltCERTfile);
3337	  case O_CLTKEYFILE:
3338		SET_STRING_EXP(Cltkeyfile);
3339	  case O_CACERTFILE:
3340		SET_STRING_EXP(CACERTfile);
3341	  case O_CACERTPATH:
3342		SET_STRING_EXP(CACERTpath);
3343	  case O_DHPARAMS:
3344		SET_STRING_EXP(DHParams);
3345# if _FFR_TLS_1
3346	  case O_DHPARAMS5:
3347		SET_STRING_EXP(DHParams5);
3348	  case O_CIPHERLIST:
3349		SET_STRING_EXP(CipherList);
3350# endif /* _FFR_TLS_1 */
3351
3352	/*
3353	**  XXX How about options per daemon/client instead of globally?
3354	**  This doesn't work well for some options, e.g., no server cert,
3355	**  but fine for others.
3356	**
3357	**  XXX Some people may want different certs per server.
3358	**
3359	**  See also srvfeatures()
3360	*/
3361
3362	  case O_TLS_SRV_OPTS:
3363		while (val != NULL && *val != '\0')
3364		{
3365			switch (*val)
3366			{
3367			  case 'V':
3368				TLS_Srv_Opts |= TLS_I_NO_VRFY;
3369				break;
3370# if _FFR_TLS_1
3371			/*
3372			**  Server without a cert? That works only if
3373			**  AnonDH is enabled as cipher, which is not in the
3374			**  default list. Hence the CipherList option must
3375			**  be available. Moreover: which clients support this
3376			**  besides sendmail with this setting?
3377			*/
3378
3379			  case 'C':
3380				TLS_Srv_Opts &= ~TLS_I_SRV_CERT;
3381				break;
3382# endif /* _FFR_TLS_1 */
3383			  case ' ':	/* ignore */
3384			  case '\t':	/* ignore */
3385			  case ',':	/* ignore */
3386				break;
3387			  default:
3388				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3389						     "Warning: Option: %s unknown parameter '%c'\n",
3390						     OPTNAME,
3391						     (isascii(*val) &&
3392							isprint(*val))
3393							? *val : '?');
3394				break;
3395			}
3396			++val;
3397			val = strpbrk(val, ", \t");
3398			if (val != NULL)
3399				++val;
3400		}
3401		break;
3402
3403	  case O_RANDFILE:
3404		PSTRSET(RandFile, val);
3405		break;
3406
3407#else /* STARTTLS */
3408	  case O_SRVCERTFILE:
3409	  case O_SRVKEYFILE:
3410	  case O_CLTCERTFILE:
3411	  case O_CLTKEYFILE:
3412	  case O_CACERTFILE:
3413	  case O_CACERTPATH:
3414	  case O_DHPARAMS:
3415# if _FFR_TLS_1
3416	  case O_DHPARAMS5:
3417	  case O_CIPHERLIST:
3418# endif /* _FFR_TLS_1 */
3419	  case O_RANDFILE:
3420		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3421				     "Warning: Option: %s requires TLS support\n",
3422				     OPTNAME);
3423		break;
3424
3425#endif /* STARTTLS */
3426
3427	  case O_CLIENTPORT:
3428		setclientoptions(val);
3429		break;
3430
3431	  case O_DF_BUFSIZE:
3432		DataFileBufferSize = atoi(val);
3433		break;
3434
3435	  case O_XF_BUFSIZE:
3436		XscriptFileBufferSize = atoi(val);
3437		break;
3438
3439	  case O_LDAPDEFAULTSPEC:
3440#if LDAPMAP
3441		ldapmap_set_defaults(val);
3442#else /* LDAPMAP */
3443		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3444				     "Warning: Option: %s requires LDAP support (-DLDAPMAP)\n",
3445				     OPTNAME);
3446#endif /* LDAPMAP */
3447		break;
3448
3449	  case O_INPUTMILTER:
3450#if MILTER
3451		InputFilterList = newstr(val);
3452#else /* MILTER */
3453		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3454				     "Warning: Option: %s requires Milter support (-DMILTER)\n",
3455				     OPTNAME);
3456#endif /* MILTER */
3457		break;
3458
3459	  case O_MILTER:
3460#if MILTER
3461		milter_set_option(subopt, val, sticky);
3462#else /* MILTER */
3463		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3464				     "Warning: Option: %s requires Milter support (-DMILTER)\n",
3465				     OPTNAME);
3466#endif /* MILTER */
3467		break;
3468
3469	  case O_QUEUE_FILE_MODE:	/* queue file mode */
3470		QueueFileMode = atooct(val) & 0777;
3471		break;
3472
3473	  case O_DLVR_MIN:	/* deliver by minimum time */
3474		DeliverByMin = convtime(val, 's');
3475		break;
3476
3477	  /* modifiers {daemon_flags} for direct submissions */
3478	  case O_DIRECTSUBMODIFIERS:
3479		{
3480			BITMAP256 m;	/* ignored */
3481			extern ENVELOPE BlankEnvelope;
3482
3483			macdefine(&BlankEnvelope.e_macro, A_PERM,
3484				  macid("{daemon_flags}"),
3485				  getmodifiers(val, m));
3486		}
3487		break;
3488
3489	  case O_FASTSPLIT:
3490		FastSplit = atoi(val);
3491		break;
3492
3493	  case O_MBDB:
3494		Mbdb = newstr(val);
3495		break;
3496
3497	  case O_MSQ:
3498		UseMSP = atobool(val);
3499		break;
3500
3501#if _FFR_SOFT_BOUNCE
3502	  case O_SOFTBOUNCE:
3503		SoftBounce = atobool(val);
3504		break;
3505#endif /* _FFR_SOFT_BOUNCE */
3506
3507	  default:
3508		if (tTd(37, 1))
3509		{
3510			if (isascii(opt) && isprint(opt))
3511				sm_dprintf("Warning: option %c unknown\n", opt);
3512			else
3513				sm_dprintf("Warning: option 0x%x unknown\n", opt);
3514		}
3515		break;
3516	}
3517
3518	/*
3519	**  Options with suboptions are responsible for taking care
3520	**  of sticky-ness (e.g., that a command line setting is kept
3521	**  when reading in the sendmail.cf file).  This has to be done
3522	**  when the suboptions are parsed since each suboption must be
3523	**  sticky, not the root option.
3524	*/
3525
3526	if (sticky && !bitset(OI_SUBOPT, o->o_flags))
3527		setbitn(opt, StickyOpt);
3528}
3529/*
3530**  SETCLASS -- set a string into a class
3531**
3532**	Parameters:
3533**		class -- the class to put the string in.
3534**		str -- the string to enter
3535**
3536**	Returns:
3537**		none.
3538**
3539**	Side Effects:
3540**		puts the word into the symbol table.
3541*/
3542
3543void
3544setclass(class, str)
3545	int class;
3546	char *str;
3547{
3548	register STAB *s;
3549
3550	if ((*str & 0377) == MATCHCLASS)
3551	{
3552		int mid;
3553
3554		str++;
3555		mid = macid(str);
3556		if (mid == 0)
3557			return;
3558
3559		if (tTd(37, 8))
3560			sm_dprintf("setclass(%s, $=%s)\n",
3561				   macname(class), macname(mid));
3562		copy_class(mid, class);
3563	}
3564	else
3565	{
3566		if (tTd(37, 8))
3567			sm_dprintf("setclass(%s, %s)\n", macname(class), str);
3568
3569		s = stab(str, ST_CLASS, ST_ENTER);
3570		setbitn(bitidx(class), s->s_class);
3571	}
3572}
3573/*
3574**  MAKEMAPENTRY -- create a map entry
3575**
3576**	Parameters:
3577**		line -- the config file line
3578**
3579**	Returns:
3580**		A pointer to the map that has been created.
3581**		NULL if there was a syntax error.
3582**
3583**	Side Effects:
3584**		Enters the map into the dictionary.
3585*/
3586
3587MAP *
3588makemapentry(line)
3589	char *line;
3590{
3591	register char *p;
3592	char *mapname;
3593	char *classname;
3594	register STAB *s;
3595	STAB *class;
3596
3597	for (p = line; isascii(*p) && isspace(*p); p++)
3598		continue;
3599	if (!(isascii(*p) && isalnum(*p)))
3600	{
3601		syserr("readcf: config K line: no map name");
3602		return NULL;
3603	}
3604
3605	mapname = p;
3606	while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.')
3607		continue;
3608	if (*p != '\0')
3609		*p++ = '\0';
3610	while (isascii(*p) && isspace(*p))
3611		p++;
3612	if (!(isascii(*p) && isalnum(*p)))
3613	{
3614		syserr("readcf: config K line, map %s: no map class", mapname);
3615		return NULL;
3616	}
3617	classname = p;
3618	while (isascii(*++p) && isalnum(*p))
3619		continue;
3620	if (*p != '\0')
3621		*p++ = '\0';
3622	while (isascii(*p) && isspace(*p))
3623		p++;
3624
3625	/* look up the class */
3626	class = stab(classname, ST_MAPCLASS, ST_FIND);
3627	if (class == NULL)
3628	{
3629		syserr("readcf: map %s: class %s not available", mapname,
3630			classname);
3631		return NULL;
3632	}
3633
3634	/* enter the map */
3635	s = stab(mapname, ST_MAP, ST_ENTER);
3636	s->s_map.map_class = &class->s_mapclass;
3637	s->s_map.map_mname = newstr(mapname);
3638
3639	if (class->s_mapclass.map_parse(&s->s_map, p))
3640		s->s_map.map_mflags |= MF_VALID;
3641
3642	if (tTd(37, 5))
3643	{
3644		sm_dprintf("map %s, class %s, flags %lx, file %s,\n",
3645			   s->s_map.map_mname, s->s_map.map_class->map_cname,
3646			   s->s_map.map_mflags, s->s_map.map_file);
3647		sm_dprintf("\tapp %s, domain %s, rebuild %s\n",
3648			   s->s_map.map_app, s->s_map.map_domain,
3649			   s->s_map.map_rebuild);
3650	}
3651	return &s->s_map;
3652}
3653/*
3654**  STRTORWSET -- convert string to rewriting set number
3655**
3656**	Parameters:
3657**		p -- the pointer to the string to decode.
3658**		endp -- if set, store the trailing delimiter here.
3659**		stabmode -- ST_ENTER to create this entry, ST_FIND if
3660**			it must already exist.
3661**
3662**	Returns:
3663**		The appropriate ruleset number.
3664**		-1 if it is not valid (error already printed)
3665*/
3666
3667int
3668strtorwset(p, endp, stabmode)
3669	char *p;
3670	char **endp;
3671	int stabmode;
3672{
3673	int ruleset;
3674	static int nextruleset = MAXRWSETS;
3675
3676	while (isascii(*p) && isspace(*p))
3677		p++;
3678	if (!isascii(*p))
3679	{
3680		syserr("invalid ruleset name: \"%.20s\"", p);
3681		return -1;
3682	}
3683	if (isdigit(*p))
3684	{
3685		ruleset = strtol(p, endp, 10);
3686		if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
3687		{
3688			syserr("bad ruleset %d (%d max)",
3689				ruleset, MAXRWSETS / 2);
3690			ruleset = -1;
3691		}
3692	}
3693	else
3694	{
3695		STAB *s;
3696		char delim;
3697		char *q = NULL;
3698
3699		q = p;
3700		while (*p != '\0' && isascii(*p) &&
3701		       (isalnum(*p) || *p == '_'))
3702			p++;
3703		if (q == p || !(isascii(*q) && isalpha(*q)))
3704		{
3705			/* no valid characters */
3706			syserr("invalid ruleset name: \"%.20s\"", q);
3707			return -1;
3708		}
3709		while (isascii(*p) && isspace(*p))
3710			*p++ = '\0';
3711		delim = *p;
3712		if (delim != '\0')
3713			*p = '\0';
3714		s = stab(q, ST_RULESET, stabmode);
3715		if (delim != '\0')
3716			*p = delim;
3717
3718		if (s == NULL)
3719			return -1;
3720
3721		if (stabmode == ST_ENTER && delim == '=')
3722		{
3723			while (isascii(*++p) && isspace(*p))
3724				continue;
3725			if (!(isascii(*p) && isdigit(*p)))
3726			{
3727				syserr("bad ruleset definition \"%s\" (number required after `=')", q);
3728				ruleset = -1;
3729			}
3730			else
3731			{
3732				ruleset = strtol(p, endp, 10);
3733				if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
3734				{
3735					syserr("bad ruleset number %d in \"%s\" (%d max)",
3736						ruleset, q, MAXRWSETS / 2);
3737					ruleset = -1;
3738				}
3739			}
3740		}
3741		else
3742		{
3743			if (endp != NULL)
3744				*endp = p;
3745			if (s->s_ruleset >= 0)
3746				ruleset = s->s_ruleset;
3747			else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
3748			{
3749				syserr("%s: too many named rulesets (%d max)",
3750					q, MAXRWSETS / 2);
3751				ruleset = -1;
3752			}
3753		}
3754		if (s->s_ruleset >= 0 &&
3755		    ruleset >= 0 &&
3756		    ruleset != s->s_ruleset)
3757		{
3758			syserr("%s: ruleset changed value (old %d, new %d)",
3759				q, s->s_ruleset, ruleset);
3760			ruleset = s->s_ruleset;
3761		}
3762		else if (ruleset >= 0)
3763		{
3764			s->s_ruleset = ruleset;
3765		}
3766		if (stabmode == ST_ENTER && ruleset >= 0)
3767		{
3768			char *h = NULL;
3769
3770			if (RuleSetNames[ruleset] != NULL)
3771				sm_free(RuleSetNames[ruleset]); /* XXX */
3772			if (delim != '\0' && (h = strchr(q, delim)) != NULL)
3773				*h = '\0';
3774			RuleSetNames[ruleset] = newstr(q);
3775			if (delim == '/' && h != NULL)
3776				*h = delim;	/* put back delim */
3777		}
3778	}
3779	return ruleset;
3780}
3781/*
3782**  SETTIMEOUT -- set an individual timeout
3783**
3784**	Parameters:
3785**		name -- the name of the timeout.
3786**		val -- the value of the timeout.
3787**		sticky -- if set, don't let other setoptions override
3788**			this value.
3789**
3790**	Returns:
3791**		none.
3792*/
3793
3794/* set if Timeout sub-option is stuck */
3795static BITMAP256	StickyTimeoutOpt;
3796
3797static struct timeoutinfo
3798{
3799	char		*to_name;	/* long name of timeout */
3800	unsigned char	to_code;	/* code for option */
3801} TimeOutTab[] =
3802{
3803#define TO_INITIAL			0x01
3804	{ "initial",			TO_INITIAL			},
3805#define TO_MAIL				0x02
3806	{ "mail",			TO_MAIL				},
3807#define TO_RCPT				0x03
3808	{ "rcpt",			TO_RCPT				},
3809#define TO_DATAINIT			0x04
3810	{ "datainit",			TO_DATAINIT			},
3811#define TO_DATABLOCK			0x05
3812	{ "datablock",			TO_DATABLOCK			},
3813#define TO_DATAFINAL			0x06
3814	{ "datafinal",			TO_DATAFINAL			},
3815#define TO_COMMAND			0x07
3816	{ "command",			TO_COMMAND			},
3817#define TO_RSET				0x08
3818	{ "rset",			TO_RSET				},
3819#define TO_HELO				0x09
3820	{ "helo",			TO_HELO				},
3821#define TO_QUIT				0x0A
3822	{ "quit",			TO_QUIT				},
3823#define TO_MISC				0x0B
3824	{ "misc",			TO_MISC				},
3825#define TO_IDENT			0x0C
3826	{ "ident",			TO_IDENT			},
3827#define TO_FILEOPEN			0x0D
3828	{ "fileopen",			TO_FILEOPEN			},
3829#define TO_CONNECT			0x0E
3830	{ "connect",			TO_CONNECT			},
3831#define TO_ICONNECT			0x0F
3832	{ "iconnect",			TO_ICONNECT			},
3833#define TO_QUEUEWARN			0x10
3834	{ "queuewarn",			TO_QUEUEWARN			},
3835	{ "queuewarn.*",		TO_QUEUEWARN			},
3836#define TO_QUEUEWARN_NORMAL		0x11
3837	{ "queuewarn.normal",		TO_QUEUEWARN_NORMAL		},
3838#define TO_QUEUEWARN_URGENT		0x12
3839	{ "queuewarn.urgent",		TO_QUEUEWARN_URGENT		},
3840#define TO_QUEUEWARN_NON_URGENT		0x13
3841	{ "queuewarn.non-urgent",	TO_QUEUEWARN_NON_URGENT		},
3842#define TO_QUEUERETURN			0x14
3843	{ "queuereturn",		TO_QUEUERETURN			},
3844	{ "queuereturn.*",		TO_QUEUERETURN			},
3845#define TO_QUEUERETURN_NORMAL		0x15
3846	{ "queuereturn.normal",		TO_QUEUERETURN_NORMAL		},
3847#define TO_QUEUERETURN_URGENT		0x16
3848	{ "queuereturn.urgent",		TO_QUEUERETURN_URGENT		},
3849#define TO_QUEUERETURN_NON_URGENT	0x17
3850	{ "queuereturn.non-urgent",	TO_QUEUERETURN_NON_URGENT	},
3851#define TO_HOSTSTATUS			0x18
3852	{ "hoststatus",			TO_HOSTSTATUS			},
3853#define TO_RESOLVER_RETRANS		0x19
3854	{ "resolver.retrans",		TO_RESOLVER_RETRANS		},
3855#define TO_RESOLVER_RETRANS_NORMAL	0x1A
3856	{ "resolver.retrans.normal",	TO_RESOLVER_RETRANS_NORMAL	},
3857#define TO_RESOLVER_RETRANS_FIRST	0x1B
3858	{ "resolver.retrans.first",	TO_RESOLVER_RETRANS_FIRST	},
3859#define TO_RESOLVER_RETRY		0x1C
3860	{ "resolver.retry",		TO_RESOLVER_RETRY		},
3861#define TO_RESOLVER_RETRY_NORMAL	0x1D
3862	{ "resolver.retry.normal",	TO_RESOLVER_RETRY_NORMAL	},
3863#define TO_RESOLVER_RETRY_FIRST		0x1E
3864	{ "resolver.retry.first",	TO_RESOLVER_RETRY_FIRST		},
3865#define TO_CONTROL			0x1F
3866	{ "control",			TO_CONTROL			},
3867#define TO_LHLO				0x20
3868	{ "lhlo",			TO_LHLO				},
3869#define TO_AUTH				0x21
3870	{ "auth",			TO_AUTH				},
3871#define TO_STARTTLS			0x22
3872	{ "starttls",			TO_STARTTLS			},
3873#define TO_ACONNECT			0x23
3874	{ "aconnect",			TO_ACONNECT			},
3875	{ NULL,				0				},
3876};
3877
3878
3879static void
3880settimeout(name, val, sticky)
3881	char *name;
3882	char *val;
3883	bool sticky;
3884{
3885	register struct timeoutinfo *to;
3886	int i, addopts;
3887	time_t toval;
3888
3889	if (tTd(37, 2))
3890		sm_dprintf("settimeout(%s = %s)", name, val);
3891
3892	for (to = TimeOutTab; to->to_name != NULL; to++)
3893	{
3894		if (sm_strcasecmp(to->to_name, name) == 0)
3895			break;
3896	}
3897
3898	if (to->to_name == NULL)
3899	{
3900		errno = 0; /* avoid bogus error text */
3901		syserr("settimeout: invalid timeout %s", name);
3902		return;
3903	}
3904
3905	/*
3906	**  See if this option is preset for us.
3907	*/
3908
3909	if (!sticky && bitnset(to->to_code, StickyTimeoutOpt))
3910	{
3911		if (tTd(37, 2))
3912			sm_dprintf(" (ignored)\n");
3913		return;
3914	}
3915
3916	if (tTd(37, 2))
3917		sm_dprintf("\n");
3918
3919	toval = convtime(val, 'm');
3920	addopts = 0;
3921
3922	switch (to->to_code)
3923	{
3924	  case TO_INITIAL:
3925		TimeOuts.to_initial = toval;
3926		break;
3927
3928	  case TO_MAIL:
3929		TimeOuts.to_mail = toval;
3930		break;
3931
3932	  case TO_RCPT:
3933		TimeOuts.to_rcpt = toval;
3934		break;
3935
3936	  case TO_DATAINIT:
3937		TimeOuts.to_datainit = toval;
3938		break;
3939
3940	  case TO_DATABLOCK:
3941		TimeOuts.to_datablock = toval;
3942		break;
3943
3944	  case TO_DATAFINAL:
3945		TimeOuts.to_datafinal = toval;
3946		break;
3947
3948	  case TO_COMMAND:
3949		TimeOuts.to_nextcommand = toval;
3950		break;
3951
3952	  case TO_RSET:
3953		TimeOuts.to_rset = toval;
3954		break;
3955
3956	  case TO_HELO:
3957		TimeOuts.to_helo = toval;
3958		break;
3959
3960	  case TO_QUIT:
3961		TimeOuts.to_quit = toval;
3962		break;
3963
3964	  case TO_MISC:
3965		TimeOuts.to_miscshort = toval;
3966		break;
3967
3968	  case TO_IDENT:
3969		TimeOuts.to_ident = toval;
3970		break;
3971
3972	  case TO_FILEOPEN:
3973		TimeOuts.to_fileopen = toval;
3974		break;
3975
3976	  case TO_CONNECT:
3977		TimeOuts.to_connect = toval;
3978		break;
3979
3980	  case TO_ICONNECT:
3981		TimeOuts.to_iconnect = toval;
3982		break;
3983
3984	  case TO_ACONNECT:
3985		TimeOuts.to_aconnect = toval;
3986		break;
3987
3988	  case TO_QUEUEWARN:
3989		toval = convtime(val, 'h');
3990		TimeOuts.to_q_warning[TOC_NORMAL] = toval;
3991		TimeOuts.to_q_warning[TOC_URGENT] = toval;
3992		TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
3993		addopts = 2;
3994		break;
3995
3996	  case TO_QUEUEWARN_NORMAL:
3997		toval = convtime(val, 'h');
3998		TimeOuts.to_q_warning[TOC_NORMAL] = toval;
3999		break;
4000
4001	  case TO_QUEUEWARN_URGENT:
4002		toval = convtime(val, 'h');
4003		TimeOuts.to_q_warning[TOC_URGENT] = toval;
4004		break;
4005
4006	  case TO_QUEUEWARN_NON_URGENT:
4007		toval = convtime(val, 'h');
4008		TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
4009		break;
4010
4011	  case TO_QUEUERETURN:
4012		toval = convtime(val, 'd');
4013		TimeOuts.to_q_return[TOC_NORMAL] = toval;
4014		TimeOuts.to_q_return[TOC_URGENT] = toval;
4015		TimeOuts.to_q_return[TOC_NONURGENT] = toval;
4016		addopts = 2;
4017		break;
4018
4019	  case TO_QUEUERETURN_NORMAL:
4020		toval = convtime(val, 'd');
4021		TimeOuts.to_q_return[TOC_NORMAL] = toval;
4022		break;
4023
4024	  case TO_QUEUERETURN_URGENT:
4025		toval = convtime(val, 'd');
4026		TimeOuts.to_q_return[TOC_URGENT] = toval;
4027		break;
4028
4029	  case TO_QUEUERETURN_NON_URGENT:
4030		toval = convtime(val, 'd');
4031		TimeOuts.to_q_return[TOC_NONURGENT] = toval;
4032		break;
4033
4034
4035	  case TO_HOSTSTATUS:
4036		MciInfoTimeout = toval;
4037		break;
4038
4039	  case TO_RESOLVER_RETRANS:
4040		toval = convtime(val, 's');
4041		TimeOuts.res_retrans[RES_TO_DEFAULT] = toval;
4042		TimeOuts.res_retrans[RES_TO_FIRST] = toval;
4043		TimeOuts.res_retrans[RES_TO_NORMAL] = toval;
4044		addopts = 2;
4045		break;
4046
4047	  case TO_RESOLVER_RETRY:
4048		i = atoi(val);
4049		TimeOuts.res_retry[RES_TO_DEFAULT] = i;
4050		TimeOuts.res_retry[RES_TO_FIRST] = i;
4051		TimeOuts.res_retry[RES_TO_NORMAL] = i;
4052		addopts = 2;
4053		break;
4054
4055	  case TO_RESOLVER_RETRANS_NORMAL:
4056		TimeOuts.res_retrans[RES_TO_NORMAL] = convtime(val, 's');
4057		break;
4058
4059	  case TO_RESOLVER_RETRY_NORMAL:
4060		TimeOuts.res_retry[RES_TO_NORMAL] = atoi(val);
4061		break;
4062
4063	  case TO_RESOLVER_RETRANS_FIRST:
4064		TimeOuts.res_retrans[RES_TO_FIRST] = convtime(val, 's');
4065		break;
4066
4067	  case TO_RESOLVER_RETRY_FIRST:
4068		TimeOuts.res_retry[RES_TO_FIRST] = atoi(val);
4069		break;
4070
4071	  case TO_CONTROL:
4072		TimeOuts.to_control = toval;
4073		break;
4074
4075	  case TO_LHLO:
4076		TimeOuts.to_lhlo = toval;
4077		break;
4078
4079#if SASL
4080	  case TO_AUTH:
4081		TimeOuts.to_auth = toval;
4082		break;
4083#endif /* SASL */
4084
4085#if STARTTLS
4086	  case TO_STARTTLS:
4087		TimeOuts.to_starttls = toval;
4088		break;
4089#endif /* STARTTLS */
4090
4091	  default:
4092		syserr("settimeout: invalid timeout %s", name);
4093		break;
4094	}
4095
4096	if (sticky)
4097	{
4098		for (i = 0; i <= addopts; i++)
4099			setbitn(to->to_code + i, StickyTimeoutOpt);
4100	}
4101}
4102/*
4103**  INITTIMEOUTS -- parse and set timeout values
4104**
4105**	Parameters:
4106**		val -- a pointer to the values.  If NULL, do initial
4107**			settings.
4108**		sticky -- if set, don't let other setoptions override
4109**			this suboption value.
4110**
4111**	Returns:
4112**		none.
4113**
4114**	Side Effects:
4115**		Initializes the TimeOuts structure
4116*/
4117
4118void
4119inittimeouts(val, sticky)
4120	register char *val;
4121	bool sticky;
4122{
4123	register char *p;
4124
4125	if (tTd(37, 2))
4126		sm_dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val);
4127	if (val == NULL)
4128	{
4129		TimeOuts.to_connect = (time_t) 0 SECONDS;
4130		TimeOuts.to_aconnect = (time_t) 0 SECONDS;
4131		TimeOuts.to_initial = (time_t) 5 MINUTES;
4132		TimeOuts.to_helo = (time_t) 5 MINUTES;
4133		TimeOuts.to_mail = (time_t) 10 MINUTES;
4134		TimeOuts.to_rcpt = (time_t) 1 HOUR;
4135		TimeOuts.to_datainit = (time_t) 5 MINUTES;
4136		TimeOuts.to_datablock = (time_t) 1 HOUR;
4137		TimeOuts.to_datafinal = (time_t) 1 HOUR;
4138		TimeOuts.to_rset = (time_t) 5 MINUTES;
4139		TimeOuts.to_quit = (time_t) 2 MINUTES;
4140		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
4141		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
4142#if IDENTPROTO
4143		TimeOuts.to_ident = (time_t) 5 SECONDS;
4144#else /* IDENTPROTO */
4145		TimeOuts.to_ident = (time_t) 0 SECONDS;
4146#endif /* IDENTPROTO */
4147		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
4148		TimeOuts.to_control = (time_t) 2 MINUTES;
4149		TimeOuts.to_lhlo = (time_t) 2 MINUTES;
4150#if SASL
4151		TimeOuts.to_auth = (time_t) 10 MINUTES;
4152#endif /* SASL */
4153#if STARTTLS
4154		TimeOuts.to_starttls = (time_t) 1 HOUR;
4155#endif /* STARTTLS */
4156		if (tTd(37, 5))
4157		{
4158			sm_dprintf("Timeouts:\n");
4159			sm_dprintf("  connect = %ld\n",
4160				   (long) TimeOuts.to_connect);
4161			sm_dprintf("  aconnect = %ld\n",
4162				   (long) TimeOuts.to_aconnect);
4163			sm_dprintf("  initial = %ld\n",
4164				   (long) TimeOuts.to_initial);
4165			sm_dprintf("  helo = %ld\n", (long) TimeOuts.to_helo);
4166			sm_dprintf("  mail = %ld\n", (long) TimeOuts.to_mail);
4167			sm_dprintf("  rcpt = %ld\n", (long) TimeOuts.to_rcpt);
4168			sm_dprintf("  datainit = %ld\n",
4169				   (long) TimeOuts.to_datainit);
4170			sm_dprintf("  datablock = %ld\n",
4171				   (long) TimeOuts.to_datablock);
4172			sm_dprintf("  datafinal = %ld\n",
4173				   (long) TimeOuts.to_datafinal);
4174			sm_dprintf("  rset = %ld\n", (long) TimeOuts.to_rset);
4175			sm_dprintf("  quit = %ld\n", (long) TimeOuts.to_quit);
4176			sm_dprintf("  nextcommand = %ld\n",
4177				   (long) TimeOuts.to_nextcommand);
4178			sm_dprintf("  miscshort = %ld\n",
4179				   (long) TimeOuts.to_miscshort);
4180			sm_dprintf("  ident = %ld\n", (long) TimeOuts.to_ident);
4181			sm_dprintf("  fileopen = %ld\n",
4182				   (long) TimeOuts.to_fileopen);
4183			sm_dprintf("  lhlo = %ld\n",
4184				   (long) TimeOuts.to_lhlo);
4185			sm_dprintf("  control = %ld\n",
4186				   (long) TimeOuts.to_control);
4187		}
4188		return;
4189	}
4190
4191	for (;; val = p)
4192	{
4193		while (isascii(*val) && isspace(*val))
4194			val++;
4195		if (*val == '\0')
4196			break;
4197		for (p = val; *p != '\0' && *p != ','; p++)
4198			continue;
4199		if (*p != '\0')
4200			*p++ = '\0';
4201
4202		if (isascii(*val) && isdigit(*val))
4203		{
4204			/* old syntax -- set everything */
4205			TimeOuts.to_mail = convtime(val, 'm');
4206			TimeOuts.to_rcpt = TimeOuts.to_mail;
4207			TimeOuts.to_datainit = TimeOuts.to_mail;
4208			TimeOuts.to_datablock = TimeOuts.to_mail;
4209			TimeOuts.to_datafinal = TimeOuts.to_mail;
4210			TimeOuts.to_nextcommand = TimeOuts.to_mail;
4211			if (sticky)
4212			{
4213				setbitn(TO_MAIL, StickyTimeoutOpt);
4214				setbitn(TO_RCPT, StickyTimeoutOpt);
4215				setbitn(TO_DATAINIT, StickyTimeoutOpt);
4216				setbitn(TO_DATABLOCK, StickyTimeoutOpt);
4217				setbitn(TO_DATAFINAL, StickyTimeoutOpt);
4218				setbitn(TO_COMMAND, StickyTimeoutOpt);
4219			}
4220			continue;
4221		}
4222		else
4223		{
4224			register char *q = strchr(val, ':');
4225
4226			if (q == NULL && (q = strchr(val, '=')) == NULL)
4227			{
4228				/* syntax error */
4229				continue;
4230			}
4231			*q++ = '\0';
4232			settimeout(val, q, sticky);
4233		}
4234	}
4235}
4236