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