xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 69838)
1 /*
2  * Copyright (c) 1983, 1995 Eric P. Allman
3  * Copyright (c) 1988, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)readcf.c	8.100 (Berkeley) 06/10/95";
11 #endif /* not lint */
12 
13 # include "sendmail.h"
14 # include <grp.h>
15 #if NAMED_BIND
16 # include <resolv.h>
17 #endif
18 
19 /*
20 **  READCF -- read control file.
21 **
22 **	This routine reads the control file and builds the internal
23 **	form.
24 **
25 **	The file is formatted as a sequence of lines, each taken
26 **	atomically.  The first character of each line describes how
27 **	the line is to be interpreted.  The lines are:
28 **		Dxval		Define macro x to have value val.
29 **		Cxword		Put word into class x.
30 **		Fxfile [fmt]	Read file for lines to put into
31 **				class x.  Use scanf string 'fmt'
32 **				or "%s" if not present.  Fmt should
33 **				only produce one string-valued result.
34 **		Hname: value	Define header with field-name 'name'
35 **				and value as specified; this will be
36 **				macro expanded immediately before
37 **				use.
38 **		Sn		Use rewriting set n.
39 **		Rlhs rhs	Rewrite addresses that match lhs to
40 **				be rhs.
41 **		Mn arg=val...	Define mailer.  n is the internal name.
42 **				Args specify mailer parameters.
43 **		Oxvalue		Set option x to value.
44 **		Pname=value	Set precedence name to value.
45 **		Vversioncode[/vendorcode]
46 **				Version level/vendor name of
47 **				configuration syntax.
48 **		Kmapname mapclass arguments....
49 **				Define keyed lookup of a given class.
50 **				Arguments are class dependent.
51 **		Eenvar=value	Set the environment value to the given value.
52 **
53 **	Parameters:
54 **		cfname -- control file name.
55 **		safe -- TRUE if this is the system config file;
56 **			FALSE otherwise.
57 **		e -- the main envelope.
58 **
59 **	Returns:
60 **		none.
61 **
62 **	Side Effects:
63 **		Builds several internal tables.
64 */
65 
66 void
67 readcf(cfname, safe, e)
68 	char *cfname;
69 	bool safe;
70 	register ENVELOPE *e;
71 {
72 	FILE *cf;
73 	int ruleset = 0;
74 	char *q;
75 	struct rewrite *rwp = NULL;
76 	char *bp;
77 	auto char *ep;
78 	int nfuzzy;
79 	char *file;
80 	bool optional;
81 	int mid;
82 	char buf[MAXLINE];
83 	register char *p;
84 	extern char **copyplist();
85 	struct stat statb;
86 	char exbuf[MAXLINE];
87 	char pvpbuf[MAXLINE + MAXATOM];
88 	static char *null_list[1] = { NULL };
89 	extern char *munchstring __P((char *, char **));
90 	extern void fileclass __P((int, char *, char *, bool, bool));
91 	extern void toomany __P((int, int));
92 
93 	FileName = cfname;
94 	LineNumber = 0;
95 
96 	cf = fopen(cfname, "r");
97 	if (cf == NULL)
98 	{
99 		syserr("cannot open");
100 		exit(EX_OSFILE);
101 	}
102 
103 	if (fstat(fileno(cf), &statb) < 0)
104 	{
105 		syserr("cannot fstat");
106 		exit(EX_OSFILE);
107 	}
108 
109 	if (!S_ISREG(statb.st_mode))
110 	{
111 		syserr("not a plain file");
112 		exit(EX_OSFILE);
113 	}
114 
115 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
116 	{
117 		if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS)
118 			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
119 				FileName);
120 #ifdef LOG
121 		if (LogLevel > 0)
122 			syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
123 				FileName);
124 #endif
125 	}
126 
127 #ifdef XLA
128 	xla_zero();
129 #endif
130 
131 	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
132 	{
133 		if (bp[0] == '#')
134 		{
135 			if (bp != buf)
136 				free(bp);
137 			continue;
138 		}
139 
140 		/* do macro expansion mappings */
141 		for (p = bp; *p != '\0'; p++)
142 		{
143 			if (*p == '#' && p > bp && ConfigLevel >= 3)
144 			{
145 				/* this is an on-line comment */
146 				register char *e;
147 
148 				switch (*--p & 0377)
149 				{
150 				  case MACROEXPAND:
151 					/* it's from $# -- let it go through */
152 					p++;
153 					break;
154 
155 				  case '\\':
156 					/* it's backslash escaped */
157 					(void) strcpy(p, p + 1);
158 					break;
159 
160 				  default:
161 					/* delete preceeding white space */
162 					while (isascii(*p) && isspace(*p) && p > bp)
163 						p--;
164 					if ((e = strchr(++p, '\n')) != NULL)
165 						(void) strcpy(p, e);
166 					else
167 						p[0] = p[1] = '\0';
168 					break;
169 				}
170 				continue;
171 			}
172 
173 			if (*p != '$' || p[1] == '\0')
174 				continue;
175 
176 			if (p[1] == '$')
177 			{
178 				/* actual dollar sign.... */
179 				(void) strcpy(p, p + 1);
180 				continue;
181 			}
182 
183 			/* convert to macro expansion character */
184 			*p++ = MACROEXPAND;
185 
186 			/* convert macro name to code */
187 			*p = macid(p, &ep);
188 			if (ep != p)
189 				strcpy(p + 1, ep);
190 		}
191 
192 		/* interpret this line */
193 		errno = 0;
194 		switch (bp[0])
195 		{
196 		  case '\0':
197 		  case '#':		/* comment */
198 			break;
199 
200 		  case 'R':		/* rewriting rule */
201 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
202 				continue;
203 
204 			if (*p == '\0')
205 			{
206 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
207 				break;
208 			}
209 
210 			/* allocate space for the rule header */
211 			if (rwp == NULL)
212 			{
213 				RewriteRules[ruleset] = rwp =
214 					(struct rewrite *) xalloc(sizeof *rwp);
215 			}
216 			else
217 			{
218 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
219 				rwp = rwp->r_next;
220 			}
221 			rwp->r_next = NULL;
222 
223 			/* expand and save the LHS */
224 			*p = '\0';
225 			expand(&bp[1], exbuf, sizeof exbuf, e);
226 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
227 					     sizeof pvpbuf, NULL, NULL);
228 			nfuzzy = 0;
229 			if (rwp->r_lhs != NULL)
230 			{
231 				register char **ap;
232 
233 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
234 
235 				/* count the number of fuzzy matches in LHS */
236 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
237 				{
238 					char *botch;
239 
240 					botch = NULL;
241 					switch (**ap & 0377)
242 					{
243 					  case MATCHZANY:
244 					  case MATCHANY:
245 					  case MATCHONE:
246 					  case MATCHCLASS:
247 					  case MATCHNCLASS:
248 						nfuzzy++;
249 						break;
250 
251 					  case MATCHREPL:
252 						botch = "$0-$9";
253 						break;
254 
255 					  case CANONNET:
256 						botch = "$#";
257 						break;
258 
259 					  case CANONUSER:
260 						botch = "$:";
261 						break;
262 
263 					  case CALLSUBR:
264 						botch = "$>";
265 						break;
266 
267 					  case CONDIF:
268 						botch = "$?";
269 						break;
270 
271 					  case CONDELSE:
272 						botch = "$|";
273 						break;
274 
275 					  case CONDFI:
276 						botch = "$.";
277 						break;
278 
279 					  case HOSTBEGIN:
280 						botch = "$[";
281 						break;
282 
283 					  case HOSTEND:
284 						botch = "$]";
285 						break;
286 
287 					  case LOOKUPBEGIN:
288 						botch = "$(";
289 						break;
290 
291 					  case LOOKUPEND:
292 						botch = "$)";
293 						break;
294 					}
295 					if (botch != NULL)
296 						syserr("Inappropriate use of %s on LHS",
297 							botch);
298 				}
299 			}
300 			else
301 			{
302 				syserr("R line: null LHS");
303 				rwp->r_lhs = null_list;
304 			}
305 
306 			/* expand and save the RHS */
307 			while (*++p == '\t')
308 				continue;
309 			q = p;
310 			while (*p != '\0' && *p != '\t')
311 				p++;
312 			*p = '\0';
313 			expand(q, exbuf, sizeof exbuf, e);
314 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
315 					     sizeof pvpbuf, NULL, NULL);
316 			if (rwp->r_rhs != NULL)
317 			{
318 				register char **ap;
319 
320 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
321 
322 				/* check no out-of-bounds replacements */
323 				nfuzzy += '0';
324 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
325 				{
326 					char *botch;
327 
328 					botch = NULL;
329 					switch (**ap & 0377)
330 					{
331 					  case MATCHREPL:
332 						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
333 						{
334 							syserr("replacement $%c out of bounds",
335 								(*ap)[1]);
336 						}
337 						break;
338 
339 					  case MATCHZANY:
340 						botch = "$*";
341 						break;
342 
343 					  case MATCHANY:
344 						botch = "$+";
345 						break;
346 
347 					  case MATCHONE:
348 						botch = "$-";
349 						break;
350 
351 					  case MATCHCLASS:
352 						botch = "$=";
353 						break;
354 
355 					  case MATCHNCLASS:
356 						botch = "$~";
357 						break;
358 					}
359 					if (botch != NULL)
360 						syserr("Inappropriate use of %s on RHS",
361 							botch);
362 				}
363 			}
364 			else
365 			{
366 				syserr("R line: null RHS");
367 				rwp->r_rhs = null_list;
368 			}
369 			break;
370 
371 		  case 'S':		/* select rewriting set */
372 			ruleset = strtorwset(&bp[1], NULL, ST_ENTER);
373 			rwp = RewriteRules[ruleset];
374 			if (rwp != NULL)
375 			{
376 				while (rwp->r_next != NULL)
377 					rwp = rwp->r_next;
378 				fprintf(stderr, "WARNING: Ruleset %s redefined\n",
379 					&bp[1]);
380 			}
381 			break;
382 
383 		  case 'D':		/* macro definition */
384 			mid = macid(&bp[1], &ep);
385 			p = munchstring(ep, NULL);
386 			define(mid, newstr(p), e);
387 			break;
388 
389 		  case 'H':		/* required header line */
390 			(void) chompheader(&bp[1], TRUE, NULL, e);
391 			break;
392 
393 		  case 'C':		/* word class */
394 		  case 'T':		/* trusted user (set class `t') */
395 			if (bp[0] == 'C')
396 			{
397 				mid = macid(&bp[1], &ep);
398 				expand(ep, exbuf, sizeof exbuf, e);
399 				p = exbuf;
400 			}
401 			else
402 			{
403 				mid = 't';
404 				p = &bp[1];
405 			}
406 			while (*p != '\0')
407 			{
408 				register char *wd;
409 				char delim;
410 
411 				while (*p != '\0' && isascii(*p) && isspace(*p))
412 					p++;
413 				wd = p;
414 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
415 					p++;
416 				delim = *p;
417 				*p = '\0';
418 				if (wd[0] != '\0')
419 					setclass(mid, wd);
420 				*p = delim;
421 			}
422 			break;
423 
424 		  case 'F':		/* word class from file */
425 			mid = macid(&bp[1], &ep);
426 			for (p = ep; isascii(*p) && isspace(*p); )
427 				p++;
428 			if (p[0] == '-' && p[1] == 'o')
429 			{
430 				optional = TRUE;
431 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
432 					p++;
433 				while (isascii(*p) && isspace(*p))
434 					p++;
435 			}
436 			else
437 				optional = FALSE;
438 			file = p;
439 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
440 				p++;
441 			if (*p == '\0')
442 				p = "%s";
443 			else
444 			{
445 				*p = '\0';
446 				while (isascii(*++p) && isspace(*p))
447 					continue;
448 			}
449 			fileclass(bp[1], file, p, safe, optional);
450 			break;
451 
452 #ifdef XLA
453 		  case 'L':		/* extended load average description */
454 			xla_init(&bp[1]);
455 			break;
456 #endif
457 
458 		  case 'M':		/* define mailer */
459 			makemailer(&bp[1]);
460 			break;
461 
462 		  case 'O':		/* set option */
463 			setoption(bp[1], &bp[2], safe, FALSE, e);
464 			break;
465 
466 		  case 'P':		/* set precedence */
467 			if (NumPriorities >= MAXPRIORITIES)
468 			{
469 				toomany('P', MAXPRIORITIES);
470 				break;
471 			}
472 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
473 				continue;
474 			if (*p == '\0')
475 				goto badline;
476 			*p = '\0';
477 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
478 			Priorities[NumPriorities].pri_val = atoi(++p);
479 			NumPriorities++;
480 			break;
481 
482 		  case 'V':		/* configuration syntax version */
483 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
484 				continue;
485 			if (!isascii(*p) || !isdigit(*p))
486 			{
487 				syserr("invalid argument to V line: \"%.20s\"",
488 					&bp[1]);
489 				break;
490 			}
491 			ConfigLevel = strtol(p, &ep, 10);
492 
493 			/*
494 			**  Do heuristic tweaking for back compatibility.
495 			*/
496 
497 			if (ConfigLevel >= 5)
498 			{
499 				/* level 5 configs have short name in $w */
500 				p = macvalue('w', e);
501 				if (p != NULL && (p = strchr(p, '.')) != NULL)
502 					*p = '\0';
503 			}
504 			if (ConfigLevel >= 6)
505 			{
506 				ColonOkInAddr = FALSE;
507 			}
508 
509 			/*
510 			**  Look for vendor code.
511 			*/
512 
513 			if (*ep++ == '/')
514 			{
515 				/* extract vendor code */
516 				for (p = ep; isascii(*p) && isalpha(*p); )
517 					p++;
518 				*p = '\0';
519 
520 				if (!setvendor(ep))
521 					syserr("invalid V line vendor code: \"%s\"",
522 						ep);
523 			}
524 			break;
525 
526 		  case 'K':
527 			(void) makemapentry(&bp[1]);
528 			break;
529 
530 		  case 'E':
531 			p = strchr(bp, '=');
532 			if (p != NULL)
533 				*p++ = '\0';
534 			setuserenv(&bp[1], p);
535 			break;
536 
537 		  default:
538 		  badline:
539 			syserr("unknown control line \"%s\"", bp);
540 		}
541 		if (bp != buf)
542 			free(bp);
543 	}
544 	if (ferror(cf))
545 	{
546 		syserr("I/O read error", cfname);
547 		exit(EX_OSFILE);
548 	}
549 	fclose(cf);
550 	FileName = NULL;
551 
552 	/* initialize host maps from local service tables */
553 	inithostmaps();
554 
555 	/* determine if we need to do special name-server frotz */
556 	{
557 		int nmaps;
558 		char *maptype[MAXMAPSTACK];
559 		short mapreturn[MAXMAPACTIONS];
560 
561 		nmaps = switch_map_find("hosts", maptype, mapreturn);
562 		UseNameServer = FALSE;
563 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
564 		{
565 			register int mapno;
566 
567 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
568 			{
569 				if (strcmp(maptype[mapno], "dns") == 0)
570 					UseNameServer = TRUE;
571 			}
572 		}
573 
574 #ifdef HESIOD
575 		nmaps = switch_map_find("passwd", maptype, mapreturn);
576 		UseHesiod = FALSE;
577 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
578 		{
579 			register int mapno;
580 
581 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
582 			{
583 				if (strcmp(maptype[mapno], "hesiod") == 0)
584 					UseHesiod = TRUE;
585 			}
586 		}
587 #endif
588 	}
589 }
590 /*
591 **  TOOMANY -- signal too many of some option
592 **
593 **	Parameters:
594 **		id -- the id of the error line
595 **		maxcnt -- the maximum possible values
596 **
597 **	Returns:
598 **		none.
599 **
600 **	Side Effects:
601 **		gives a syserr.
602 */
603 
604 void
605 toomany(id, maxcnt)
606 	int id;
607 	int maxcnt;
608 {
609 	syserr("too many %c lines, %d max", id, maxcnt);
610 }
611 /*
612 **  FILECLASS -- read members of a class from a file
613 **
614 **	Parameters:
615 **		class -- class to define.
616 **		filename -- name of file to read.
617 **		fmt -- scanf string to use for match.
618 **		safe -- if set, this is a safe read.
619 **		optional -- if set, it is not an error for the file to
620 **			not exist.
621 **
622 **	Returns:
623 **		none
624 **
625 **	Side Effects:
626 **
627 **		puts all lines in filename that match a scanf into
628 **			the named class.
629 */
630 
631 void
632 fileclass(class, filename, fmt, safe, optional)
633 	int class;
634 	char *filename;
635 	char *fmt;
636 	bool safe;
637 	bool optional;
638 {
639 	FILE *f;
640 	int sff;
641 	int pid;
642 	register char *p;
643 	char buf[MAXLINE];
644 
645 	if (tTd(37, 2))
646 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
647 
648 	if (filename[0] == '|')
649 	{
650 		auto int fd;
651 		int i;
652 		char *argv[MAXPV + 1];
653 
654 		i = 0;
655 		for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t"))
656 		{
657 			if (i >= MAXPV)
658 				break;
659 			argv[i++] = p;
660 		}
661 		argv[i] = NULL;
662 		pid = prog_open(argv, &fd, CurEnv);
663 		if (pid < 0)
664 			f = NULL;
665 		else
666 			f = fdopen(fd, "r");
667 	}
668 	else
669 	{
670 		pid = -1;
671 		sff = SFF_REGONLY;
672 		if (safe)
673 			sff |= SFF_OPENASROOT;
674 		f = safefopen(filename, O_RDONLY, 0, sff);
675 	}
676 	if (f == NULL)
677 	{
678 		if (!optional)
679 			syserr("fileclass: cannot open %s", filename);
680 		return;
681 	}
682 
683 	while (fgets(buf, sizeof buf, f) != NULL)
684 	{
685 		register char *p;
686 # ifdef SCANF
687 		char wordbuf[MAXNAME+1];
688 
689 		if (sscanf(buf, fmt, wordbuf) != 1)
690 			continue;
691 		p = wordbuf;
692 # else /* SCANF */
693 		p = buf;
694 # endif /* SCANF */
695 
696 		/*
697 		**  Break up the match into words.
698 		*/
699 
700 		while (*p != '\0')
701 		{
702 			register char *q;
703 
704 			/* strip leading spaces */
705 			while (isascii(*p) && isspace(*p))
706 				p++;
707 			if (*p == '\0')
708 				break;
709 
710 			/* find the end of the word */
711 			q = p;
712 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
713 				p++;
714 			if (*p != '\0')
715 				*p++ = '\0';
716 
717 			/* enter the word in the symbol table */
718 			setclass(class, q);
719 		}
720 	}
721 
722 	(void) fclose(f);
723 	if (pid > 0)
724 		(void) waitfor(pid);
725 }
726 /*
727 **  MAKEMAILER -- define a new mailer.
728 **
729 **	Parameters:
730 **		line -- description of mailer.  This is in labeled
731 **			fields.  The fields are:
732 **			   A -- the argv for this mailer
733 **			   C -- the character set for MIME conversions
734 **			   D -- the directory to run in
735 **			   E -- the eol string
736 **			   F -- the flags associated with the mailer
737 **			   L -- the maximum line length
738 **			   M -- the maximum message size
739 **			   N -- the niceness at which to run
740 **			   P -- the path to the mailer
741 **			   R -- the recipient rewriting set
742 **			   S -- the sender rewriting set
743 **			   T -- the mailer type (for DSNs)
744 **			   U -- the uid to run as
745 **			The first word is the canonical name of the mailer.
746 **
747 **	Returns:
748 **		none.
749 **
750 **	Side Effects:
751 **		enters the mailer into the mailer table.
752 */
753 
754 void
755 makemailer(line)
756 	char *line;
757 {
758 	register char *p;
759 	register struct mailer *m;
760 	register STAB *s;
761 	int i;
762 	char fcode;
763 	auto char *endp;
764 	extern int NextMailer;
765 	extern char **makeargv();
766 	extern char *munchstring();
767 
768 	/* allocate a mailer and set up defaults */
769 	m = (struct mailer *) xalloc(sizeof *m);
770 	bzero((char *) m, sizeof *m);
771 	m->m_eol = "\n";
772 	m->m_uid = m->m_gid = 0;
773 
774 	/* collect the mailer name */
775 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
776 		continue;
777 	if (*p != '\0')
778 		*p++ = '\0';
779 	m->m_name = newstr(line);
780 
781 	/* now scan through and assign info from the fields */
782 	while (*p != '\0')
783 	{
784 		auto char *delimptr;
785 
786 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
787 			p++;
788 
789 		/* p now points to field code */
790 		fcode = *p;
791 		while (*p != '\0' && *p != '=' && *p != ',')
792 			p++;
793 		if (*p++ != '=')
794 		{
795 			syserr("mailer %s: `=' expected", m->m_name);
796 			return;
797 		}
798 		while (isascii(*p) && isspace(*p))
799 			p++;
800 
801 		/* p now points to the field body */
802 		p = munchstring(p, &delimptr);
803 
804 		/* install the field into the mailer struct */
805 		switch (fcode)
806 		{
807 		  case 'P':		/* pathname */
808 			m->m_mailer = newstr(p);
809 			break;
810 
811 		  case 'F':		/* flags */
812 			for (; *p != '\0'; p++)
813 				if (!(isascii(*p) && isspace(*p)))
814 					setbitn(*p, m->m_flags);
815 			break;
816 
817 		  case 'S':		/* sender rewriting ruleset */
818 		  case 'R':		/* recipient rewriting ruleset */
819 			i = strtorwset(p, &endp, ST_ENTER);
820 			if (i < 0)
821 				return;
822 			if (fcode == 'S')
823 				m->m_sh_rwset = m->m_se_rwset = i;
824 			else
825 				m->m_rh_rwset = m->m_re_rwset = i;
826 
827 			p = endp;
828 			if (*p++ == '/')
829 			{
830 				i = strtorwset(p, NULL);
831 				if (i < 0)
832 					return;
833 				if (fcode == 'S')
834 					m->m_sh_rwset = i;
835 				else
836 					m->m_rh_rwset = i;
837 			}
838 			break;
839 
840 		  case 'E':		/* end of line string */
841 			m->m_eol = newstr(p);
842 			break;
843 
844 		  case 'A':		/* argument vector */
845 			m->m_argv = makeargv(p);
846 			break;
847 
848 		  case 'M':		/* maximum message size */
849 			m->m_maxsize = atol(p);
850 			break;
851 
852 		  case 'L':		/* maximum line length */
853 			m->m_linelimit = atoi(p);
854 			break;
855 
856 		  case 'N':		/* run niceness */
857 			m->m_nice = atoi(p);
858 			break;
859 
860 		  case 'D':		/* working directory */
861 			m->m_execdir = newstr(p);
862 			break;
863 
864 		  case 'C':		/* default charset */
865 			m->m_defcharset = newstr(p);
866 			break;
867 
868 		  case 'T':		/* MTA-Name/Address/Diagnostic types */
869 			m->m_mtatype = newstr(p);
870 			p = strchr(m->m_mtatype, '/');
871 			if (p != NULL)
872 			{
873 				*p++ = '\0';
874 				if (*p == '\0')
875 					p = NULL;
876 			}
877 			if (p == NULL)
878 				m->m_addrtype = m->m_mtatype;
879 			else
880 			{
881 				m->m_addrtype = p;
882 				p = strchr(p, '/');
883 			}
884 			if (p != NULL)
885 			{
886 				*p++ = '\0';
887 				if (*p == '\0')
888 					p = NULL;
889 			}
890 			if (p == NULL)
891 				m->m_diagtype = m->m_mtatype;
892 			else
893 				m->m_diagtype = p;
894 			break;
895 
896 		  case 'U':		/* user id */
897 			if (isascii(*p) && !isdigit(*p))
898 			{
899 				char *q = p;
900 				struct passwd *pw;
901 
902 				while (isascii(*p) && isalnum(*p))
903 					p++;
904 				while (isascii(*p) && isspace(*p))
905 					*p++ = '\0';
906 				if (*p != '\0')
907 					*p++ = '\0';
908 				pw = sm_getpwnam(q);
909 				if (pw == NULL)
910 					syserr("readcf: mailer U= flag: unknown user %s", q);
911 				else
912 				{
913 					m->m_uid = pw->pw_uid;
914 					m->m_gid = pw->pw_gid;
915 				}
916 			}
917 			else
918 			{
919 				auto char *q;
920 
921 				m->m_uid = strtol(p, &q, 0);
922 				p = q;
923 			}
924 			while (isascii(*p) && isspace(*p))
925 				p++;
926 			if (*p == '\0')
927 				break;
928 			if (isascii(*p) && !isdigit(*p))
929 			{
930 				char *q = p;
931 				struct group *gr;
932 
933 				while (isascii(*p) && isalnum(*p))
934 					p++;
935 				*p++ = '\0';
936 				gr = getgrnam(q);
937 				if (gr == NULL)
938 					syserr("readcf: mailer U= flag: unknown group %s", q);
939 				else
940 					m->m_gid = gr->gr_gid;
941 			}
942 			else
943 			{
944 				m->m_gid = strtol(p, NULL, 0);
945 			}
946 			break;
947 		}
948 
949 		p = delimptr;
950 	}
951 
952 	/* do some rationality checking */
953 	if (m->m_argv == NULL)
954 	{
955 		syserr("M%s: A= argument required", m->m_name);
956 		return;
957 	}
958 	if (m->m_mailer == NULL)
959 	{
960 		syserr("M%s: P= argument required", m->m_name);
961 		return;
962 	}
963 
964 	if (NextMailer >= MAXMAILERS)
965 	{
966 		syserr("too many mailers defined (%d max)", MAXMAILERS);
967 		return;
968 	}
969 
970 	/* do some heuristic cleanup for back compatibility */
971 	if (bitnset(M_LIMITS, m->m_flags))
972 	{
973 		if (m->m_linelimit == 0)
974 			m->m_linelimit = SMTPLINELIM;
975 		if (ConfigLevel < 2)
976 			setbitn(M_7BITS, m->m_flags);
977 	}
978 
979 	if (ConfigLevel < 6 &&
980 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
981 	     strcmp(m->m_mailer, "[TCP]") == 0))
982 	{
983 		if (m->m_mtatype == NULL)
984 			m->m_mtatype = "dns";
985 		if (m->m_addrtype == NULL)
986 			m->m_addrtype = "rfc822";
987 		if (m->m_diagtype == NULL)
988 			m->m_diagtype = "smtp";
989 	}
990 
991 	/* enter the mailer into the symbol table */
992 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
993 	if (s->s_mailer != NULL)
994 	{
995 		i = s->s_mailer->m_mno;
996 		free(s->s_mailer);
997 	}
998 	else
999 	{
1000 		i = NextMailer++;
1001 	}
1002 	Mailer[i] = s->s_mailer = m;
1003 	m->m_mno = i;
1004 }
1005 /*
1006 **  MUNCHSTRING -- translate a string into internal form.
1007 **
1008 **	Parameters:
1009 **		p -- the string to munch.
1010 **		delimptr -- if non-NULL, set to the pointer of the
1011 **			field delimiter character.
1012 **
1013 **	Returns:
1014 **		the munched string.
1015 */
1016 
1017 char *
1018 munchstring(p, delimptr)
1019 	register char *p;
1020 	char **delimptr;
1021 {
1022 	register char *q;
1023 	bool backslash = FALSE;
1024 	bool quotemode = FALSE;
1025 	static char buf[MAXLINE];
1026 
1027 	for (q = buf; *p != '\0'; p++)
1028 	{
1029 		if (backslash)
1030 		{
1031 			/* everything is roughly literal */
1032 			backslash = FALSE;
1033 			switch (*p)
1034 			{
1035 			  case 'r':		/* carriage return */
1036 				*q++ = '\r';
1037 				continue;
1038 
1039 			  case 'n':		/* newline */
1040 				*q++ = '\n';
1041 				continue;
1042 
1043 			  case 'f':		/* form feed */
1044 				*q++ = '\f';
1045 				continue;
1046 
1047 			  case 'b':		/* backspace */
1048 				*q++ = '\b';
1049 				continue;
1050 			}
1051 			*q++ = *p;
1052 		}
1053 		else
1054 		{
1055 			if (*p == '\\')
1056 				backslash = TRUE;
1057 			else if (*p == '"')
1058 				quotemode = !quotemode;
1059 			else if (quotemode || *p != ',')
1060 				*q++ = *p;
1061 			else
1062 				break;
1063 		}
1064 	}
1065 
1066 	if (delimptr != NULL)
1067 		*delimptr = p;
1068 	*q++ = '\0';
1069 	return (buf);
1070 }
1071 /*
1072 **  MAKEARGV -- break up a string into words
1073 **
1074 **	Parameters:
1075 **		p -- the string to break up.
1076 **
1077 **	Returns:
1078 **		a char **argv (dynamically allocated)
1079 **
1080 **	Side Effects:
1081 **		munges p.
1082 */
1083 
1084 char **
1085 makeargv(p)
1086 	register char *p;
1087 {
1088 	char *q;
1089 	int i;
1090 	char **avp;
1091 	char *argv[MAXPV + 1];
1092 
1093 	/* take apart the words */
1094 	i = 0;
1095 	while (*p != '\0' && i < MAXPV)
1096 	{
1097 		q = p;
1098 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1099 			p++;
1100 		while (isascii(*p) && isspace(*p))
1101 			*p++ = '\0';
1102 		argv[i++] = newstr(q);
1103 	}
1104 	argv[i++] = NULL;
1105 
1106 	/* now make a copy of the argv */
1107 	avp = (char **) xalloc(sizeof *avp * i);
1108 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
1109 
1110 	return (avp);
1111 }
1112 /*
1113 **  PRINTRULES -- print rewrite rules (for debugging)
1114 **
1115 **	Parameters:
1116 **		none.
1117 **
1118 **	Returns:
1119 **		none.
1120 **
1121 **	Side Effects:
1122 **		prints rewrite rules.
1123 */
1124 
1125 void
1126 printrules()
1127 {
1128 	register struct rewrite *rwp;
1129 	register int ruleset;
1130 
1131 	for (ruleset = 0; ruleset < 10; ruleset++)
1132 	{
1133 		if (RewriteRules[ruleset] == NULL)
1134 			continue;
1135 		printf("\n----Rule Set %d:", ruleset);
1136 
1137 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
1138 		{
1139 			printf("\nLHS:");
1140 			printav(rwp->r_lhs);
1141 			printf("RHS:");
1142 			printav(rwp->r_rhs);
1143 		}
1144 	}
1145 }
1146 /*
1147 **  PRINTMAILER -- print mailer structure (for debugging)
1148 **
1149 **	Parameters:
1150 **		m -- the mailer to print
1151 **
1152 **	Returns:
1153 **		none.
1154 */
1155 
1156 void
1157 printmailer(m)
1158 	register MAILER *m;
1159 {
1160 	int j;
1161 
1162 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
1163 		m->m_mno, m->m_name,
1164 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
1165 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
1166 		m->m_uid, m->m_gid);
1167 	for (j = '\0'; j <= '\177'; j++)
1168 		if (bitnset(j, m->m_flags))
1169 			(void) putchar(j);
1170 	printf(" L=%d E=", m->m_linelimit);
1171 	xputs(m->m_eol);
1172 	if (m->m_defcharset != NULL)
1173 		printf(" C=%s", m->m_defcharset);
1174 	printf(" T=%s/%s/%s",
1175 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
1176 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
1177 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
1178 	if (m->m_argv != NULL)
1179 	{
1180 		char **a = m->m_argv;
1181 
1182 		printf(" A=");
1183 		while (*a != NULL)
1184 		{
1185 			if (a != m->m_argv)
1186 				printf(" ");
1187 			xputs(*a++);
1188 		}
1189 	}
1190 	printf("\n");
1191 }
1192 /*
1193 **  SETOPTION -- set global processing option
1194 **
1195 **	Parameters:
1196 **		opt -- option name.
1197 **		val -- option value (as a text string).
1198 **		safe -- set if this came from a configuration file.
1199 **			Some options (if set from the command line) will
1200 **			reset the user id to avoid security problems.
1201 **		sticky -- if set, don't let other setoptions override
1202 **			this value.
1203 **		e -- the main envelope.
1204 **
1205 **	Returns:
1206 **		none.
1207 **
1208 **	Side Effects:
1209 **		Sets options as implied by the arguments.
1210 */
1211 
1212 static BITMAP	StickyOpt;		/* set if option is stuck */
1213 extern void	settimeout __P((char *, char *));
1214 
1215 
1216 #if NAMED_BIND
1217 
1218 struct resolverflags
1219 {
1220 	char	*rf_name;	/* name of the flag */
1221 	long	rf_bits;	/* bits to set/clear */
1222 } ResolverFlags[] =
1223 {
1224 	"debug",	RES_DEBUG,
1225 	"aaonly",	RES_AAONLY,
1226 	"usevc",	RES_USEVC,
1227 	"primary",	RES_PRIMARY,
1228 	"igntc",	RES_IGNTC,
1229 	"recurse",	RES_RECURSE,
1230 	"defnames",	RES_DEFNAMES,
1231 	"stayopen",	RES_STAYOPEN,
1232 	"dnsrch",	RES_DNSRCH,
1233 	"true",		0,		/* to avoid error on old syntax */
1234 	NULL,		0
1235 };
1236 
1237 #endif
1238 
1239 struct optioninfo
1240 {
1241 	char	*o_name;	/* long name of option */
1242 	u_char	o_code;		/* short name of option */
1243 	bool	o_safe;		/* safe for random people to use */
1244 } OptionTab[] =
1245 {
1246 	"SevenBitInput",	'7',		TRUE,
1247 #if MIME8TO7
1248 	"EightBitMode",		'8',		TRUE,
1249 #endif
1250 	"AliasFile",		'A',		FALSE,
1251 	"AliasWait",		'a',		FALSE,
1252 	"BlankSub",		'B',		FALSE,
1253 	"MinFreeBlocks",	'b',		TRUE,
1254 	"CheckpointInterval",	'C',		TRUE,
1255 	"HoldExpensive",	'c',		FALSE,
1256 	"AutoRebuildAliases",	'D',		FALSE,
1257 	"DeliveryMode",		'd',		TRUE,
1258 	"ErrorHeader",		'E',		FALSE,
1259 	"ErrorMode",		'e',		TRUE,
1260 	"TempFileMode",		'F',		FALSE,
1261 	"SaveFromLine",		'f',		FALSE,
1262 	"MatchGECOS",		'G',		FALSE,
1263 	"HelpFile",		'H',		FALSE,
1264 	"MaxHopCount",		'h',		FALSE,
1265 	"ResolverOptions",	'I',		FALSE,
1266 	"IgnoreDots",		'i',		TRUE,
1267 	"ForwardPath",		'J',		FALSE,
1268 	"SendMimeErrors",	'j',		TRUE,
1269 	"ConnectionCacheSize",	'k',		FALSE,
1270 	"ConnectionCacheTimeout", 'K',		FALSE,
1271 	"UseErrorsTo",		'l',		FALSE,
1272 	"LogLevel",		'L',		FALSE,
1273 	"MeToo",		'm',		TRUE,
1274 	"CheckAliases",		'n',		FALSE,
1275 	"OldStyleHeaders",	'o',		TRUE,
1276 	"DaemonPortOptions",	'O',		FALSE,
1277 	"PrivacyOptions",	'p',		TRUE,
1278 	"PostmasterCopy",	'P',		FALSE,
1279 	"QueueFactor",		'q',		FALSE,
1280 	"QueueDirectory",	'Q',		FALSE,
1281 	"DontPruneRoutes",	'R',		FALSE,
1282 	"Timeout",		'r',		TRUE,
1283 	"StatusFile",		'S',		FALSE,
1284 	"SuperSafe",		's',		TRUE,
1285 	"QueueTimeout",		'T',		FALSE,
1286 	"TimeZoneSpec",		't',		FALSE,
1287 	"UserDatabaseSpec",	'U',		FALSE,
1288 	"DefaultUser",		'u',		FALSE,
1289 	"FallbackMXhost",	'V',		FALSE,
1290 	"Verbose",		'v',		TRUE,
1291 	"TryNullMXList",	'w',		TRUE,
1292 	"QueueLA",		'x',		FALSE,
1293 	"RefuseLA",		'X',		FALSE,
1294 	"RecipientFactor",	'y',		FALSE,
1295 	"ForkEachJob",		'Y',		FALSE,
1296 	"ClassFactor",		'z',		FALSE,
1297 	"RetryFactor",		'Z',		FALSE,
1298 #define O_QUEUESORTORD	0x81
1299 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
1300 #define O_HOSTSFILE	0x82
1301 	"HostsFile",		O_HOSTSFILE,	FALSE,
1302 #define O_MQA		0x83
1303 	"MinQueueAge",		O_MQA,		TRUE,
1304 #define O_MHSA		0x84
1305 /*
1306 	"MaxHostStatAge",	O_MHSA,		TRUE,
1307 */
1308 #define O_DEFCHARSET	0x85
1309 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
1310 #define O_SSFILE	0x86
1311 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
1312 #define O_DIALDELAY	0x87
1313 	"DialDelay",		O_DIALDELAY,	TRUE,
1314 #define O_NORCPTACTION	0x88
1315 	"NoRecipientAction",	O_NORCPTACTION,	TRUE,
1316 #define O_SAFEFILEENV	0x89
1317 	"SafeFileEnvironment",	O_SAFEFILEENV,	FALSE,
1318 #define O_MAXMSGSIZE	0x8a
1319 	"MaxMessageSize",	O_MAXMSGSIZE,	FALSE,
1320 #define O_COLONOKINADDR	0x8b
1321 	"ColonOkInAddr",	O_COLONOKINADDR, TRUE,
1322 #define O_MAXQUEUERUN	0x8c
1323 	"MaxQueueRunSize",	O_MAXQUEUERUN,	TRUE,
1324 #define O_MAXCHILDREN	0x8d
1325 /*
1326 	"MaxDaemonChildren",	O_MAXCHILDREN,	FALSE,
1327 */
1328 
1329 	NULL,			'\0',		FALSE,
1330 };
1331 
1332 
1333 
1334 void
1335 setoption(opt, val, safe, sticky, e)
1336 	int opt;
1337 	char *val;
1338 	bool safe;
1339 	bool sticky;
1340 	register ENVELOPE *e;
1341 {
1342 	register char *p;
1343 	register struct optioninfo *o;
1344 	char *subopt;
1345 	extern bool atobool();
1346 	extern time_t convtime();
1347 	extern int QueueLA;
1348 	extern int RefuseLA;
1349 	extern bool Warn_Q_option;
1350 
1351 	errno = 0;
1352 	if (opt == ' ')
1353 	{
1354 		/* full word options */
1355 		struct optioninfo *sel;
1356 
1357 		p = strchr(val, '=');
1358 		if (p == NULL)
1359 			p = &val[strlen(val)];
1360 		while (*--p == ' ')
1361 			continue;
1362 		while (*++p == ' ')
1363 			*p = '\0';
1364 		if (p == val)
1365 		{
1366 			syserr("readcf: null option name");
1367 			return;
1368 		}
1369 		if (*p == '=')
1370 			*p++ = '\0';
1371 		while (*p == ' ')
1372 			p++;
1373 		subopt = strchr(val, '.');
1374 		if (subopt != NULL)
1375 			*subopt++ = '\0';
1376 		sel = NULL;
1377 		for (o = OptionTab; o->o_name != NULL; o++)
1378 		{
1379 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
1380 				continue;
1381 			if (strlen(o->o_name) == strlen(val))
1382 			{
1383 				/* completely specified -- this must be it */
1384 				sel = NULL;
1385 				break;
1386 			}
1387 			if (sel != NULL)
1388 				break;
1389 			sel = o;
1390 		}
1391 		if (sel != NULL && o->o_name == NULL)
1392 			o = sel;
1393 		else if (o->o_name == NULL)
1394 		{
1395 			syserr("readcf: unknown option name %s", val);
1396 			return;
1397 		}
1398 		else if (sel != NULL)
1399 		{
1400 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
1401 				val, sel->o_name, o->o_name);
1402 			return;
1403 		}
1404 		if (strlen(val) != strlen(o->o_name))
1405 		{
1406 			bool oldVerbose = Verbose;
1407 
1408 			Verbose = TRUE;
1409 			message("Option %s used as abbreviation for %s",
1410 				val, o->o_name);
1411 			Verbose = oldVerbose;
1412 		}
1413 		opt = o->o_code;
1414 		val = p;
1415 	}
1416 	else
1417 	{
1418 		for (o = OptionTab; o->o_name != NULL; o++)
1419 		{
1420 			if (o->o_code == opt)
1421 				break;
1422 		}
1423 		subopt = NULL;
1424 	}
1425 
1426 	if (tTd(37, 1))
1427 	{
1428 		printf(isascii(opt) && isprint(opt) ?
1429 			    "setoption %s (%c).%s=%s" :
1430 			    "setoption %s (0x%x).%s=%s",
1431 			o->o_name == NULL ? "<unknown>" : o->o_name,
1432 			opt,
1433 			subopt == NULL ? "" : subopt,
1434 			val);
1435 	}
1436 
1437 	/*
1438 	**  See if this option is preset for us.
1439 	*/
1440 
1441 	if (!sticky && bitnset(opt, StickyOpt))
1442 	{
1443 		if (tTd(37, 1))
1444 			printf(" (ignored)\n");
1445 		return;
1446 	}
1447 
1448 	/*
1449 	**  Check to see if this option can be specified by this user.
1450 	*/
1451 
1452 	if (!safe && RealUid == 0)
1453 		safe = TRUE;
1454 	if (!safe && !o->o_safe)
1455 	{
1456 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
1457 		{
1458 			if (tTd(37, 1))
1459 				printf(" (unsafe)");
1460 			if (RealUid != geteuid())
1461 			{
1462 				if (tTd(37, 1))
1463 					printf("(Resetting uid)");
1464 				(void) setgid(RealGid);
1465 				(void) setuid(RealUid);
1466 			}
1467 		}
1468 	}
1469 	if (tTd(37, 1))
1470 		printf("\n");
1471 
1472 	switch (opt & 0xff)
1473 	{
1474 	  case '7':		/* force seven-bit input */
1475 		SevenBitInput = atobool(val);
1476 		break;
1477 
1478 #if MIME8TO7
1479 	  case '8':		/* handling of 8-bit input */
1480 		switch (*val)
1481 		{
1482 		  case 'm':		/* convert 8-bit, convert MIME */
1483 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
1484 			break;
1485 
1486 		  case 'p':		/* pass 8 bit, convert MIME */
1487 			MimeMode = MM_CVTMIME|MM_PASS8BIT;
1488 			break;
1489 
1490 		  case 's':		/* strict adherence */
1491 			MimeMode = MM_CVTMIME;
1492 			break;
1493 
1494 #if 0
1495 		  case 'r':		/* reject 8-bit, don't convert MIME */
1496 			MimeMode = 0;
1497 			break;
1498 
1499 		  case 'j':		/* "just send 8" */
1500 			MimeMode = MM_PASS8BIT;
1501 			break;
1502 
1503 		  case 'a':		/* encode 8 bit if available */
1504 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
1505 			break;
1506 
1507 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
1508 			MimeMode = MM_MIME8BIT;
1509 			break;
1510 #endif
1511 
1512 		  default:
1513 			syserr("Unknown 8-bit mode %c", *val);
1514 			exit(EX_USAGE);
1515 		}
1516 		break;
1517 #endif
1518 
1519 	  case 'A':		/* set default alias file */
1520 		if (val[0] == '\0')
1521 			setalias("aliases");
1522 		else
1523 			setalias(val);
1524 		break;
1525 
1526 	  case 'a':		/* look N minutes for "@:@" in alias file */
1527 		if (val[0] == '\0')
1528 			SafeAlias = 5 * 60;		/* five minutes */
1529 		else
1530 			SafeAlias = convtime(val, 'm');
1531 		break;
1532 
1533 	  case 'B':		/* substitution for blank character */
1534 		SpaceSub = val[0];
1535 		if (SpaceSub == '\0')
1536 			SpaceSub = ' ';
1537 		break;
1538 
1539 	  case 'b':		/* min blocks free on queue fs/max msg size */
1540 		p = strchr(val, '/');
1541 		if (p != NULL)
1542 		{
1543 			*p++ = '\0';
1544 			MaxMessageSize = atol(p);
1545 		}
1546 		MinBlocksFree = atol(val);
1547 		break;
1548 
1549 	  case 'c':		/* don't connect to "expensive" mailers */
1550 		NoConnect = atobool(val);
1551 		break;
1552 
1553 	  case 'C':		/* checkpoint every N addresses */
1554 		CheckpointInterval = atoi(val);
1555 		break;
1556 
1557 	  case 'd':		/* delivery mode */
1558 		switch (*val)
1559 		{
1560 		  case '\0':
1561 			e->e_sendmode = SM_DELIVER;
1562 			break;
1563 
1564 		  case SM_QUEUE:	/* queue only */
1565 #ifndef QUEUE
1566 			syserr("need QUEUE to set -odqueue");
1567 #endif /* QUEUE */
1568 			/* fall through..... */
1569 
1570 		  case SM_DELIVER:	/* do everything */
1571 		  case SM_FORK:		/* fork after verification */
1572 			e->e_sendmode = *val;
1573 			break;
1574 
1575 		  default:
1576 			syserr("Unknown delivery mode %c", *val);
1577 			exit(EX_USAGE);
1578 		}
1579 		break;
1580 
1581 	  case 'D':		/* rebuild alias database as needed */
1582 		AutoRebuild = atobool(val);
1583 		break;
1584 
1585 	  case 'E':		/* error message header/header file */
1586 		if (*val != '\0')
1587 			ErrMsgFile = newstr(val);
1588 		break;
1589 
1590 	  case 'e':		/* set error processing mode */
1591 		switch (*val)
1592 		{
1593 		  case EM_QUIET:	/* be silent about it */
1594 		  case EM_MAIL:		/* mail back */
1595 		  case EM_BERKNET:	/* do berknet error processing */
1596 		  case EM_WRITE:	/* write back (or mail) */
1597 		  case EM_PRINT:	/* print errors normally (default) */
1598 			e->e_errormode = *val;
1599 			break;
1600 		}
1601 		break;
1602 
1603 	  case 'F':		/* file mode */
1604 		FileMode = atooct(val) & 0777;
1605 		break;
1606 
1607 	  case 'f':		/* save Unix-style From lines on front */
1608 		SaveFrom = atobool(val);
1609 		break;
1610 
1611 	  case 'G':		/* match recipients against GECOS field */
1612 		MatchGecos = atobool(val);
1613 		break;
1614 
1615 	  case 'g':		/* default gid */
1616   g_opt:
1617 		if (isascii(*val) && isdigit(*val))
1618 			DefGid = atoi(val);
1619 		else
1620 		{
1621 			register struct group *gr;
1622 
1623 			DefGid = -1;
1624 			gr = getgrnam(val);
1625 			if (gr == NULL)
1626 				syserr("readcf: option %c: unknown group %s",
1627 					opt, val);
1628 			else
1629 				DefGid = gr->gr_gid;
1630 		}
1631 		break;
1632 
1633 	  case 'H':		/* help file */
1634 		if (val[0] == '\0')
1635 			HelpFile = "sendmail.hf";
1636 		else
1637 			HelpFile = newstr(val);
1638 		break;
1639 
1640 	  case 'h':		/* maximum hop count */
1641 		MaxHopCount = atoi(val);
1642 		break;
1643 
1644 	  case 'I':		/* use internet domain name server */
1645 #if NAMED_BIND
1646 		for (p = val; *p != 0; )
1647 		{
1648 			bool clearmode;
1649 			char *q;
1650 			struct resolverflags *rfp;
1651 
1652 			while (*p == ' ')
1653 				p++;
1654 			if (*p == '\0')
1655 				break;
1656 			clearmode = FALSE;
1657 			if (*p == '-')
1658 				clearmode = TRUE;
1659 			else if (*p != '+')
1660 				p--;
1661 			p++;
1662 			q = p;
1663 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1664 				p++;
1665 			if (*p != '\0')
1666 				*p++ = '\0';
1667 			if (strcasecmp(q, "HasWildcardMX") == 0)
1668 			{
1669 				NoMXforCanon = !clearmode;
1670 				continue;
1671 			}
1672 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
1673 			{
1674 				if (strcasecmp(q, rfp->rf_name) == 0)
1675 					break;
1676 			}
1677 			if (rfp->rf_name == NULL)
1678 				syserr("readcf: I option value %s unrecognized", q);
1679 			else if (clearmode)
1680 				_res.options &= ~rfp->rf_bits;
1681 			else
1682 				_res.options |= rfp->rf_bits;
1683 		}
1684 		if (tTd(8, 2))
1685 			printf("_res.options = %x, HasWildcardMX = %d\n",
1686 				_res.options, !NoMXforCanon);
1687 #else
1688 		usrerr("name server (I option) specified but BIND not compiled in");
1689 #endif
1690 		break;
1691 
1692 	  case 'i':		/* ignore dot lines in message */
1693 		IgnrDot = atobool(val);
1694 		break;
1695 
1696 	  case 'j':		/* send errors in MIME (RFC 1341) format */
1697 		SendMIMEErrors = atobool(val);
1698 		break;
1699 
1700 	  case 'J':		/* .forward search path */
1701 		ForwardPath = newstr(val);
1702 		break;
1703 
1704 	  case 'k':		/* connection cache size */
1705 		MaxMciCache = atoi(val);
1706 		if (MaxMciCache < 0)
1707 			MaxMciCache = 0;
1708 		break;
1709 
1710 	  case 'K':		/* connection cache timeout */
1711 		MciCacheTimeout = convtime(val, 'm');
1712 		break;
1713 
1714 	  case 'l':		/* use Errors-To: header */
1715 		UseErrorsTo = atobool(val);
1716 		break;
1717 
1718 	  case 'L':		/* log level */
1719 		if (safe || LogLevel < atoi(val))
1720 			LogLevel = atoi(val);
1721 		break;
1722 
1723 	  case 'M':		/* define macro */
1724 		p = newstr(&val[1]);
1725 		if (!safe)
1726 			cleanstrcpy(p, p, MAXNAME);
1727 		define(val[0], p, CurEnv);
1728 		sticky = FALSE;
1729 		break;
1730 
1731 	  case 'm':		/* send to me too */
1732 		MeToo = atobool(val);
1733 		break;
1734 
1735 	  case 'n':		/* validate RHS in newaliases */
1736 		CheckAliases = atobool(val);
1737 		break;
1738 
1739 	    /* 'N' available -- was "net name" */
1740 
1741 	  case 'O':		/* daemon options */
1742 		setdaemonoptions(val);
1743 		break;
1744 
1745 	  case 'o':		/* assume old style headers */
1746 		if (atobool(val))
1747 			CurEnv->e_flags |= EF_OLDSTYLE;
1748 		else
1749 			CurEnv->e_flags &= ~EF_OLDSTYLE;
1750 		break;
1751 
1752 	  case 'p':		/* select privacy level */
1753 		p = val;
1754 		for (;;)
1755 		{
1756 			register struct prival *pv;
1757 			extern struct prival PrivacyValues[];
1758 
1759 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
1760 				p++;
1761 			if (*p == '\0')
1762 				break;
1763 			val = p;
1764 			while (isascii(*p) && isalnum(*p))
1765 				p++;
1766 			if (*p != '\0')
1767 				*p++ = '\0';
1768 
1769 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
1770 			{
1771 				if (strcasecmp(val, pv->pv_name) == 0)
1772 					break;
1773 			}
1774 			if (pv->pv_name == NULL)
1775 				syserr("readcf: Op line: %s unrecognized", val);
1776 			PrivacyFlags |= pv->pv_flag;
1777 		}
1778 		sticky = FALSE;
1779 		break;
1780 
1781 	  case 'P':		/* postmaster copy address for returned mail */
1782 		PostMasterCopy = newstr(val);
1783 		break;
1784 
1785 	  case 'q':		/* slope of queue only function */
1786 		QueueFactor = atoi(val);
1787 		break;
1788 
1789 	  case 'Q':		/* queue directory */
1790 		if (val[0] == '\0')
1791 			QueueDir = "mqueue";
1792 		else
1793 			QueueDir = newstr(val);
1794 		if (RealUid != 0 && !safe)
1795 			Warn_Q_option = TRUE;
1796 		break;
1797 
1798 	  case 'R':		/* don't prune routes */
1799 		DontPruneRoutes = atobool(val);
1800 		break;
1801 
1802 	  case 'r':		/* read timeout */
1803 		if (subopt == NULL)
1804 			inittimeouts(val);
1805 		else
1806 			settimeout(subopt, val);
1807 		break;
1808 
1809 	  case 'S':		/* status file */
1810 		if (val[0] == '\0')
1811 			StatFile = "sendmail.st";
1812 		else
1813 			StatFile = newstr(val);
1814 		break;
1815 
1816 	  case 's':		/* be super safe, even if expensive */
1817 		SuperSafe = atobool(val);
1818 		break;
1819 
1820 	  case 'T':		/* queue timeout */
1821 		p = strchr(val, '/');
1822 		if (p != NULL)
1823 		{
1824 			*p++ = '\0';
1825 			settimeout("queuewarn", p);
1826 		}
1827 		settimeout("queuereturn", val);
1828 		break;
1829 
1830 	  case 't':		/* time zone name */
1831 		TimeZoneSpec = newstr(val);
1832 		break;
1833 
1834 	  case 'U':		/* location of user database */
1835 		UdbSpec = newstr(val);
1836 		break;
1837 
1838 	  case 'u':		/* set default uid */
1839 		for (p = val; *p != '\0'; p++)
1840 		{
1841 			if (*p == '.' || *p == '/' || *p == ':')
1842 			{
1843 				*p++ = '\0';
1844 				break;
1845 			}
1846 		}
1847 		if (isascii(*val) && isdigit(*val))
1848 			DefUid = atoi(val);
1849 		else
1850 		{
1851 			register struct passwd *pw;
1852 
1853 			DefUid = -1;
1854 			pw = sm_getpwnam(val);
1855 			if (pw == NULL)
1856 				syserr("readcf: option u: unknown user %s", val);
1857 			else
1858 			{
1859 				DefUid = pw->pw_uid;
1860 				DefGid = pw->pw_gid;
1861 			}
1862 		}
1863 		setdefuser();
1864 
1865 		/* handle the group if it is there */
1866 		if (*p == '\0')
1867 			break;
1868 		val = p;
1869 		goto g_opt;
1870 
1871 	  case 'V':		/* fallback MX host */
1872 		FallBackMX = newstr(val);
1873 		break;
1874 
1875 	  case 'v':		/* run in verbose mode */
1876 		Verbose = atobool(val);
1877 		break;
1878 
1879 	  case 'w':		/* if we are best MX, try host directly */
1880 		TryNullMXList = atobool(val);
1881 		break;
1882 
1883 	    /* 'W' available -- was wizard password */
1884 
1885 	  case 'x':		/* load avg at which to auto-queue msgs */
1886 		QueueLA = atoi(val);
1887 		break;
1888 
1889 	  case 'X':		/* load avg at which to auto-reject connections */
1890 		RefuseLA = atoi(val);
1891 		break;
1892 
1893 	  case 'y':		/* work recipient factor */
1894 		WkRecipFact = atoi(val);
1895 		break;
1896 
1897 	  case 'Y':		/* fork jobs during queue runs */
1898 		ForkQueueRuns = atobool(val);
1899 		break;
1900 
1901 	  case 'z':		/* work message class factor */
1902 		WkClassFact = atoi(val);
1903 		break;
1904 
1905 	  case 'Z':		/* work time factor */
1906 		WkTimeFact = atoi(val);
1907 		break;
1908 
1909 	  case O_QUEUESORTORD:	/* queue sorting order */
1910 		switch (*val)
1911 		{
1912 		  case 'h':	/* Host first */
1913 		  case 'H':
1914 			QueueSortOrder = QS_BYHOST;
1915 			break;
1916 
1917 		  case 'p':	/* Priority order */
1918 		  case 'P':
1919 			QueueSortOrder = QS_BYPRIORITY;
1920 			break;
1921 
1922 		  default:
1923 			syserr("Invalid queue sort order \"%s\"", val);
1924 		}
1925 		break;
1926 
1927 	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
1928 		HostsFile = newstr(val);
1929 		break;
1930 
1931 	  case O_MQA:		/* minimum queue age between deliveries */
1932 		MinQueueAge = convtime(val, 'm');
1933 		break;
1934 
1935 	  case O_MHSA:		/* maximum age of cached host status */
1936 		MaxHostStatAge = convtime(val, 'm');
1937 		break;
1938 
1939 	  case O_DEFCHARSET:	/* default character set for mimefying */
1940 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
1941 		break;
1942 
1943 	  case O_SSFILE:	/* service switch file */
1944 		ServiceSwitchFile = newstr(val);
1945 		break;
1946 
1947 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
1948 		DialDelay = convtime(val, 's');
1949 		break;
1950 
1951 	  case O_NORCPTACTION:	/* what to do if no recipient */
1952 		if (strcasecmp(val, "none") == 0)
1953 			NoRecipientAction = NRA_NO_ACTION;
1954 		else if (strcasecmp(val, "add-to") == 0)
1955 			NoRecipientAction = NRA_ADD_TO;
1956 		else if (strcasecmp(val, "add-apparently-to") == 0)
1957 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
1958 		else if (strcasecmp(val, "add-bcc") == 0)
1959 			NoRecipientAction = NRA_ADD_BCC;
1960 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
1961 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
1962 		else
1963 			syserr("Invalid NoRecipientAction: %s", val);
1964 
1965 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
1966 		SafeFileEnv = newstr(val);
1967 		break;
1968 
1969 	  case O_MAXMSGSIZE:	/* maximum message size */
1970 		MaxMessageSize = atol(val);
1971 		break;
1972 
1973 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
1974 		ColonOkInAddr = atobool(val);
1975 		break;
1976 
1977 	  case O_MAXQUEUERUN:	/* max # of jobs in a single queue run */
1978 		MaxQueueRun = atol(val);
1979 		break;
1980 
1981 	  case O_MAXCHILDREN:	/* max # of children of daemon */
1982 		MaxChildren = atoi(val);
1983 
1984 	  default:
1985 		if (tTd(37, 1))
1986 		{
1987 			if (isascii(opt) && isprint(opt))
1988 				printf("Warning: option %c unknown\n", opt);
1989 			else
1990 				printf("Warning: option 0x%x unknown\n", opt);
1991 		}
1992 		break;
1993 	}
1994 	if (sticky)
1995 		setbitn(opt, StickyOpt);
1996 }
1997 /*
1998 **  SETCLASS -- set a string into a class
1999 **
2000 **	Parameters:
2001 **		class -- the class to put the string in.
2002 **		str -- the string to enter
2003 **
2004 **	Returns:
2005 **		none.
2006 **
2007 **	Side Effects:
2008 **		puts the word into the symbol table.
2009 */
2010 
2011 void
2012 setclass(class, str)
2013 	int class;
2014 	char *str;
2015 {
2016 	register STAB *s;
2017 
2018 	if (tTd(37, 8))
2019 		printf("setclass(%c, %s)\n", class, str);
2020 	s = stab(str, ST_CLASS, ST_ENTER);
2021 	setbitn(class, s->s_class);
2022 }
2023 /*
2024 **  MAKEMAPENTRY -- create a map entry
2025 **
2026 **	Parameters:
2027 **		line -- the config file line
2028 **
2029 **	Returns:
2030 **		A pointer to the map that has been created.
2031 **		NULL if there was a syntax error.
2032 **
2033 **	Side Effects:
2034 **		Enters the map into the dictionary.
2035 */
2036 
2037 MAP *
2038 makemapentry(line)
2039 	char *line;
2040 {
2041 	register char *p;
2042 	char *mapname;
2043 	char *classname;
2044 	register STAB *s;
2045 	STAB *class;
2046 
2047 	for (p = line; isascii(*p) && isspace(*p); p++)
2048 		continue;
2049 	if (!(isascii(*p) && isalnum(*p)))
2050 	{
2051 		syserr("readcf: config K line: no map name");
2052 		return NULL;
2053 	}
2054 
2055 	mapname = p;
2056 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
2057 		continue;
2058 	if (*p != '\0')
2059 		*p++ = '\0';
2060 	while (isascii(*p) && isspace(*p))
2061 		p++;
2062 	if (!(isascii(*p) && isalnum(*p)))
2063 	{
2064 		syserr("readcf: config K line, map %s: no map class", mapname);
2065 		return NULL;
2066 	}
2067 	classname = p;
2068 	while (isascii(*++p) && isalnum(*p))
2069 		continue;
2070 	if (*p != '\0')
2071 		*p++ = '\0';
2072 	while (isascii(*p) && isspace(*p))
2073 		p++;
2074 
2075 	/* look up the class */
2076 	class = stab(classname, ST_MAPCLASS, ST_FIND);
2077 	if (class == NULL)
2078 	{
2079 		syserr("readcf: map %s: class %s not available", mapname, classname);
2080 		return NULL;
2081 	}
2082 
2083 	/* enter the map */
2084 	s = stab(mapname, ST_MAP, ST_ENTER);
2085 	s->s_map.map_class = &class->s_mapclass;
2086 	s->s_map.map_mname = newstr(mapname);
2087 
2088 	if (class->s_mapclass.map_parse(&s->s_map, p))
2089 		s->s_map.map_mflags |= MF_VALID;
2090 
2091 	if (tTd(37, 5))
2092 	{
2093 		printf("map %s, class %s, flags %x, file %s,\n",
2094 			s->s_map.map_mname, s->s_map.map_class->map_cname,
2095 			s->s_map.map_mflags,
2096 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
2097 		printf("\tapp %s, domain %s, rebuild %s\n",
2098 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
2099 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
2100 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
2101 	}
2102 
2103 	return &s->s_map;
2104 }
2105 /*
2106 **  STRTORWSET -- convert string to rewriting set number
2107 **
2108 **	Parameters:
2109 **		p -- the pointer to the string to decode.
2110 **		endp -- if set, store the trailing delimiter here.
2111 **		stabmode -- ST_ENTER to create this entry, ST_FIND if
2112 **			it must already exist.
2113 **
2114 **	Returns:
2115 **		The appropriate ruleset number.
2116 **		-1 if it is not valid (error already printed)
2117 */
2118 
2119 int
2120 strtorwset(p, endp, stabmode)
2121 	char *p;
2122 	char **endp;
2123 	int stabmode;
2124 {
2125 	int ruleset;
2126 	static int nextruleset = MAXRWSETS;
2127 
2128 	while (isascii(*p) && isspace(*p))
2129 		p++;
2130 	if (!isascii(*p))
2131 	{
2132 		syserr("invalid ruleset name: \"%.20s\"", p);
2133 		return -1;
2134 	}
2135 	if (isdigit(*p))
2136 	{
2137 		ruleset = strtol(p, endp, 10);
2138 		if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
2139 		{
2140 			syserr("bad ruleset %d (%d max)",
2141 				ruleset, MAXRWSETS / 2);
2142 			ruleset = -1;
2143 		}
2144 	}
2145 	else
2146 	{
2147 		STAB *s;
2148 		char delim;
2149 		char *q;
2150 
2151 		q = p;
2152 		while (*p != '\0' && isascii(*p) &&
2153 		       (isalnum(*p) || strchr("-_$", *p) != NULL))
2154 			p++;
2155 		while (isascii(*p) && isspace(*p))
2156 			*p++ = '\0';
2157 		delim = *p;
2158 		if (delim != '\0')
2159 			*p = '\0';
2160 		s = stab(q, ST_RULESET, stabmode);
2161 		if (delim != '\0')
2162 			*p = delim;
2163 
2164 		if (s == NULL)
2165 		{
2166 			syserr("unknown ruleset %s", q);
2167 			return -1;
2168 		}
2169 
2170 		if (stabmode == ST_ENTER && delim == '=')
2171 		{
2172 			ruleset = strtol(p, endp, 10);
2173 			if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
2174 			{
2175 				syserr("bad ruleset %s = %d (%d max)",
2176 					q, ruleset, MAXRWSETS / 2);
2177 				ruleset = -1;
2178 			}
2179 		}
2180 		else
2181 		{
2182 			if (endp != NULL)
2183 				*endp = p;
2184 			if (s->s_ruleset > 0)
2185 				ruleset = s->s_ruleset;
2186 			else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
2187 			{
2188 				syserr("%s: too many named rulesets (%d max)",
2189 					q, MAXRWSETS / 2);
2190 				ruleset = -1;
2191 			}
2192 		}
2193 		if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset)
2194 		{
2195 			syserr("%s: ruleset changed value (old %d, new %d)",
2196 				q, ruleset, s->s_ruleset);
2197 			ruleset = s->s_ruleset;
2198 		}
2199 		else if (ruleset > 0)
2200 		{
2201 			s->s_ruleset = ruleset;
2202 		}
2203 	}
2204 	return ruleset;
2205 }
2206 /*
2207 **  INITTIMEOUTS -- parse and set timeout values
2208 **
2209 **	Parameters:
2210 **		val -- a pointer to the values.  If NULL, do initial
2211 **			settings.
2212 **
2213 **	Returns:
2214 **		none.
2215 **
2216 **	Side Effects:
2217 **		Initializes the TimeOuts structure
2218 */
2219 
2220 #define SECONDS
2221 #define MINUTES	* 60
2222 #define HOUR	* 3600
2223 
2224 void
2225 inittimeouts(val)
2226 	register char *val;
2227 {
2228 	register char *p;
2229 	extern time_t convtime();
2230 
2231 	if (val == NULL)
2232 	{
2233 		TimeOuts.to_initial = (time_t) 5 MINUTES;
2234 		TimeOuts.to_helo = (time_t) 5 MINUTES;
2235 		TimeOuts.to_mail = (time_t) 10 MINUTES;
2236 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
2237 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
2238 		TimeOuts.to_datablock = (time_t) 1 HOUR;
2239 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
2240 		TimeOuts.to_rset = (time_t) 5 MINUTES;
2241 		TimeOuts.to_quit = (time_t) 2 MINUTES;
2242 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
2243 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
2244 #if IDENTPROTO
2245 		TimeOuts.to_ident = (time_t) 30 SECONDS;
2246 #else
2247 		TimeOuts.to_ident = (time_t) 0 SECONDS;
2248 #endif
2249 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
2250 		return;
2251 	}
2252 
2253 	for (;; val = p)
2254 	{
2255 		while (isascii(*val) && isspace(*val))
2256 			val++;
2257 		if (*val == '\0')
2258 			break;
2259 		for (p = val; *p != '\0' && *p != ','; p++)
2260 			continue;
2261 		if (*p != '\0')
2262 			*p++ = '\0';
2263 
2264 		if (isascii(*val) && isdigit(*val))
2265 		{
2266 			/* old syntax -- set everything */
2267 			TimeOuts.to_mail = convtime(val, 'm');
2268 			TimeOuts.to_rcpt = TimeOuts.to_mail;
2269 			TimeOuts.to_datainit = TimeOuts.to_mail;
2270 			TimeOuts.to_datablock = TimeOuts.to_mail;
2271 			TimeOuts.to_datafinal = TimeOuts.to_mail;
2272 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
2273 			continue;
2274 		}
2275 		else
2276 		{
2277 			register char *q = strchr(val, ':');
2278 
2279 			if (q == NULL && (q = strchr(val, '=')) == NULL)
2280 			{
2281 				/* syntax error */
2282 				continue;
2283 			}
2284 			*q++ = '\0';
2285 			settimeout(val, q);
2286 		}
2287 	}
2288 }
2289 /*
2290 **  SETTIMEOUT -- set an individual timeout
2291 **
2292 **	Parameters:
2293 **		name -- the name of the timeout.
2294 **		val -- the value of the timeout.
2295 **
2296 **	Returns:
2297 **		none.
2298 */
2299 
2300 void
2301 settimeout(name, val)
2302 	char *name;
2303 	char *val;
2304 {
2305 	register char *p;
2306 	time_t to;
2307 	extern time_t convtime();
2308 
2309 	to = convtime(val, 'm');
2310 	p = strchr(name, '.');
2311 	if (p != NULL)
2312 		*p++ = '\0';
2313 
2314 	if (strcasecmp(name, "initial") == 0)
2315 		TimeOuts.to_initial = to;
2316 	else if (strcasecmp(name, "mail") == 0)
2317 		TimeOuts.to_mail = to;
2318 	else if (strcasecmp(name, "rcpt") == 0)
2319 		TimeOuts.to_rcpt = to;
2320 	else if (strcasecmp(name, "datainit") == 0)
2321 		TimeOuts.to_datainit = to;
2322 	else if (strcasecmp(name, "datablock") == 0)
2323 		TimeOuts.to_datablock = to;
2324 	else if (strcasecmp(name, "datafinal") == 0)
2325 		TimeOuts.to_datafinal = to;
2326 	else if (strcasecmp(name, "command") == 0)
2327 		TimeOuts.to_nextcommand = to;
2328 	else if (strcasecmp(name, "rset") == 0)
2329 		TimeOuts.to_rset = to;
2330 	else if (strcasecmp(name, "helo") == 0)
2331 		TimeOuts.to_helo = to;
2332 	else if (strcasecmp(name, "quit") == 0)
2333 		TimeOuts.to_quit = to;
2334 	else if (strcasecmp(name, "misc") == 0)
2335 		TimeOuts.to_miscshort = to;
2336 	else if (strcasecmp(name, "ident") == 0)
2337 		TimeOuts.to_ident = to;
2338 	else if (strcasecmp(name, "fileopen") == 0)
2339 		TimeOuts.to_fileopen = to;
2340 	else if (strcasecmp(name, "queuewarn") == 0)
2341 	{
2342 		to = convtime(val, 'h');
2343 		if (p == NULL || strcmp(p, "*") == 0)
2344 		{
2345 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2346 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2347 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2348 		}
2349 		else if (strcasecmp(p, "normal") == 0)
2350 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2351 		else if (strcasecmp(p, "urgent") == 0)
2352 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2353 		else if (strcasecmp(p, "non-urgent") == 0)
2354 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2355 		else
2356 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
2357 	}
2358 	else if (strcasecmp(name, "queuereturn") == 0)
2359 	{
2360 		to = convtime(val, 'd');
2361 		if (p == NULL || strcmp(p, "*") == 0)
2362 		{
2363 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2364 			TimeOuts.to_q_return[TOC_URGENT] = to;
2365 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2366 		}
2367 		else if (strcasecmp(p, "normal") == 0)
2368 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2369 		else if (strcasecmp(p, "urgent") == 0)
2370 			TimeOuts.to_q_return[TOC_URGENT] = to;
2371 		else if (strcasecmp(p, "non-urgent") == 0)
2372 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2373 		else
2374 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
2375 	}
2376 	else
2377 		syserr("settimeout: invalid timeout %s", name);
2378 }
2379