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