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