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