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