xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 69852)
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.101 (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 #define O_KEEPCNAMES	0x8e
1329 	"DontExpandCnames",	O_KEEPCNAMES,	FALSE,
1330 
1331 	NULL,			'\0',		FALSE,
1332 };
1333 
1334 
1335 
1336 void
1337 setoption(opt, val, safe, sticky, e)
1338 	int opt;
1339 	char *val;
1340 	bool safe;
1341 	bool sticky;
1342 	register ENVELOPE *e;
1343 {
1344 	register char *p;
1345 	register struct optioninfo *o;
1346 	char *subopt;
1347 	extern bool atobool();
1348 	extern time_t convtime();
1349 	extern int QueueLA;
1350 	extern int RefuseLA;
1351 	extern bool Warn_Q_option;
1352 
1353 	errno = 0;
1354 	if (opt == ' ')
1355 	{
1356 		/* full word options */
1357 		struct optioninfo *sel;
1358 
1359 		p = strchr(val, '=');
1360 		if (p == NULL)
1361 			p = &val[strlen(val)];
1362 		while (*--p == ' ')
1363 			continue;
1364 		while (*++p == ' ')
1365 			*p = '\0';
1366 		if (p == val)
1367 		{
1368 			syserr("readcf: null option name");
1369 			return;
1370 		}
1371 		if (*p == '=')
1372 			*p++ = '\0';
1373 		while (*p == ' ')
1374 			p++;
1375 		subopt = strchr(val, '.');
1376 		if (subopt != NULL)
1377 			*subopt++ = '\0';
1378 		sel = NULL;
1379 		for (o = OptionTab; o->o_name != NULL; o++)
1380 		{
1381 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
1382 				continue;
1383 			if (strlen(o->o_name) == strlen(val))
1384 			{
1385 				/* completely specified -- this must be it */
1386 				sel = NULL;
1387 				break;
1388 			}
1389 			if (sel != NULL)
1390 				break;
1391 			sel = o;
1392 		}
1393 		if (sel != NULL && o->o_name == NULL)
1394 			o = sel;
1395 		else if (o->o_name == NULL)
1396 		{
1397 			syserr("readcf: unknown option name %s", val);
1398 			return;
1399 		}
1400 		else if (sel != NULL)
1401 		{
1402 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
1403 				val, sel->o_name, o->o_name);
1404 			return;
1405 		}
1406 		if (strlen(val) != strlen(o->o_name))
1407 		{
1408 			bool oldVerbose = Verbose;
1409 
1410 			Verbose = TRUE;
1411 			message("Option %s used as abbreviation for %s",
1412 				val, o->o_name);
1413 			Verbose = oldVerbose;
1414 		}
1415 		opt = o->o_code;
1416 		val = p;
1417 	}
1418 	else
1419 	{
1420 		for (o = OptionTab; o->o_name != NULL; o++)
1421 		{
1422 			if (o->o_code == opt)
1423 				break;
1424 		}
1425 		subopt = NULL;
1426 	}
1427 
1428 	if (tTd(37, 1))
1429 	{
1430 		printf(isascii(opt) && isprint(opt) ?
1431 			    "setoption %s (%c).%s=%s" :
1432 			    "setoption %s (0x%x).%s=%s",
1433 			o->o_name == NULL ? "<unknown>" : o->o_name,
1434 			opt,
1435 			subopt == NULL ? "" : subopt,
1436 			val);
1437 	}
1438 
1439 	/*
1440 	**  See if this option is preset for us.
1441 	*/
1442 
1443 	if (!sticky && bitnset(opt, StickyOpt))
1444 	{
1445 		if (tTd(37, 1))
1446 			printf(" (ignored)\n");
1447 		return;
1448 	}
1449 
1450 	/*
1451 	**  Check to see if this option can be specified by this user.
1452 	*/
1453 
1454 	if (!safe && RealUid == 0)
1455 		safe = TRUE;
1456 	if (!safe && !o->o_safe)
1457 	{
1458 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
1459 		{
1460 			if (tTd(37, 1))
1461 				printf(" (unsafe)");
1462 			if (RealUid != geteuid())
1463 			{
1464 				if (tTd(37, 1))
1465 					printf("(Resetting uid)");
1466 				(void) setgid(RealGid);
1467 				(void) setuid(RealUid);
1468 			}
1469 		}
1470 	}
1471 	if (tTd(37, 1))
1472 		printf("\n");
1473 
1474 	switch (opt & 0xff)
1475 	{
1476 	  case '7':		/* force seven-bit input */
1477 		SevenBitInput = atobool(val);
1478 		break;
1479 
1480 #if MIME8TO7
1481 	  case '8':		/* handling of 8-bit input */
1482 		switch (*val)
1483 		{
1484 		  case 'm':		/* convert 8-bit, convert MIME */
1485 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
1486 			break;
1487 
1488 		  case 'p':		/* pass 8 bit, convert MIME */
1489 			MimeMode = MM_CVTMIME|MM_PASS8BIT;
1490 			break;
1491 
1492 		  case 's':		/* strict adherence */
1493 			MimeMode = MM_CVTMIME;
1494 			break;
1495 
1496 #if 0
1497 		  case 'r':		/* reject 8-bit, don't convert MIME */
1498 			MimeMode = 0;
1499 			break;
1500 
1501 		  case 'j':		/* "just send 8" */
1502 			MimeMode = MM_PASS8BIT;
1503 			break;
1504 
1505 		  case 'a':		/* encode 8 bit if available */
1506 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
1507 			break;
1508 
1509 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
1510 			MimeMode = MM_MIME8BIT;
1511 			break;
1512 #endif
1513 
1514 		  default:
1515 			syserr("Unknown 8-bit mode %c", *val);
1516 			exit(EX_USAGE);
1517 		}
1518 		break;
1519 #endif
1520 
1521 	  case 'A':		/* set default alias file */
1522 		if (val[0] == '\0')
1523 			setalias("aliases");
1524 		else
1525 			setalias(val);
1526 		break;
1527 
1528 	  case 'a':		/* look N minutes for "@:@" in alias file */
1529 		if (val[0] == '\0')
1530 			SafeAlias = 5 * 60;		/* five minutes */
1531 		else
1532 			SafeAlias = convtime(val, 'm');
1533 		break;
1534 
1535 	  case 'B':		/* substitution for blank character */
1536 		SpaceSub = val[0];
1537 		if (SpaceSub == '\0')
1538 			SpaceSub = ' ';
1539 		break;
1540 
1541 	  case 'b':		/* min blocks free on queue fs/max msg size */
1542 		p = strchr(val, '/');
1543 		if (p != NULL)
1544 		{
1545 			*p++ = '\0';
1546 			MaxMessageSize = atol(p);
1547 		}
1548 		MinBlocksFree = atol(val);
1549 		break;
1550 
1551 	  case 'c':		/* don't connect to "expensive" mailers */
1552 		NoConnect = atobool(val);
1553 		break;
1554 
1555 	  case 'C':		/* checkpoint every N addresses */
1556 		CheckpointInterval = atoi(val);
1557 		break;
1558 
1559 	  case 'd':		/* delivery mode */
1560 		switch (*val)
1561 		{
1562 		  case '\0':
1563 			e->e_sendmode = SM_DELIVER;
1564 			break;
1565 
1566 		  case SM_QUEUE:	/* queue only */
1567 #ifndef QUEUE
1568 			syserr("need QUEUE to set -odqueue");
1569 #endif /* QUEUE */
1570 			/* fall through..... */
1571 
1572 		  case SM_DELIVER:	/* do everything */
1573 		  case SM_FORK:		/* fork after verification */
1574 			e->e_sendmode = *val;
1575 			break;
1576 
1577 		  default:
1578 			syserr("Unknown delivery mode %c", *val);
1579 			exit(EX_USAGE);
1580 		}
1581 		break;
1582 
1583 	  case 'D':		/* rebuild alias database as needed */
1584 		AutoRebuild = atobool(val);
1585 		break;
1586 
1587 	  case 'E':		/* error message header/header file */
1588 		if (*val != '\0')
1589 			ErrMsgFile = newstr(val);
1590 		break;
1591 
1592 	  case 'e':		/* set error processing mode */
1593 		switch (*val)
1594 		{
1595 		  case EM_QUIET:	/* be silent about it */
1596 		  case EM_MAIL:		/* mail back */
1597 		  case EM_BERKNET:	/* do berknet error processing */
1598 		  case EM_WRITE:	/* write back (or mail) */
1599 		  case EM_PRINT:	/* print errors normally (default) */
1600 			e->e_errormode = *val;
1601 			break;
1602 		}
1603 		break;
1604 
1605 	  case 'F':		/* file mode */
1606 		FileMode = atooct(val) & 0777;
1607 		break;
1608 
1609 	  case 'f':		/* save Unix-style From lines on front */
1610 		SaveFrom = atobool(val);
1611 		break;
1612 
1613 	  case 'G':		/* match recipients against GECOS field */
1614 		MatchGecos = atobool(val);
1615 		break;
1616 
1617 	  case 'g':		/* default gid */
1618   g_opt:
1619 		if (isascii(*val) && isdigit(*val))
1620 			DefGid = atoi(val);
1621 		else
1622 		{
1623 			register struct group *gr;
1624 
1625 			DefGid = -1;
1626 			gr = getgrnam(val);
1627 			if (gr == NULL)
1628 				syserr("readcf: option %c: unknown group %s",
1629 					opt, val);
1630 			else
1631 				DefGid = gr->gr_gid;
1632 		}
1633 		break;
1634 
1635 	  case 'H':		/* help file */
1636 		if (val[0] == '\0')
1637 			HelpFile = "sendmail.hf";
1638 		else
1639 			HelpFile = newstr(val);
1640 		break;
1641 
1642 	  case 'h':		/* maximum hop count */
1643 		MaxHopCount = atoi(val);
1644 		break;
1645 
1646 	  case 'I':		/* use internet domain name server */
1647 #if NAMED_BIND
1648 		for (p = val; *p != 0; )
1649 		{
1650 			bool clearmode;
1651 			char *q;
1652 			struct resolverflags *rfp;
1653 
1654 			while (*p == ' ')
1655 				p++;
1656 			if (*p == '\0')
1657 				break;
1658 			clearmode = FALSE;
1659 			if (*p == '-')
1660 				clearmode = TRUE;
1661 			else if (*p != '+')
1662 				p--;
1663 			p++;
1664 			q = p;
1665 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1666 				p++;
1667 			if (*p != '\0')
1668 				*p++ = '\0';
1669 			if (strcasecmp(q, "HasWildcardMX") == 0)
1670 			{
1671 				NoMXforCanon = !clearmode;
1672 				continue;
1673 			}
1674 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
1675 			{
1676 				if (strcasecmp(q, rfp->rf_name) == 0)
1677 					break;
1678 			}
1679 			if (rfp->rf_name == NULL)
1680 				syserr("readcf: I option value %s unrecognized", q);
1681 			else if (clearmode)
1682 				_res.options &= ~rfp->rf_bits;
1683 			else
1684 				_res.options |= rfp->rf_bits;
1685 		}
1686 		if (tTd(8, 2))
1687 			printf("_res.options = %x, HasWildcardMX = %d\n",
1688 				_res.options, !NoMXforCanon);
1689 #else
1690 		usrerr("name server (I option) specified but BIND not compiled in");
1691 #endif
1692 		break;
1693 
1694 	  case 'i':		/* ignore dot lines in message */
1695 		IgnrDot = atobool(val);
1696 		break;
1697 
1698 	  case 'j':		/* send errors in MIME (RFC 1341) format */
1699 		SendMIMEErrors = atobool(val);
1700 		break;
1701 
1702 	  case 'J':		/* .forward search path */
1703 		ForwardPath = newstr(val);
1704 		break;
1705 
1706 	  case 'k':		/* connection cache size */
1707 		MaxMciCache = atoi(val);
1708 		if (MaxMciCache < 0)
1709 			MaxMciCache = 0;
1710 		break;
1711 
1712 	  case 'K':		/* connection cache timeout */
1713 		MciCacheTimeout = convtime(val, 'm');
1714 		break;
1715 
1716 	  case 'l':		/* use Errors-To: header */
1717 		UseErrorsTo = atobool(val);
1718 		break;
1719 
1720 	  case 'L':		/* log level */
1721 		if (safe || LogLevel < atoi(val))
1722 			LogLevel = atoi(val);
1723 		break;
1724 
1725 	  case 'M':		/* define macro */
1726 		p = newstr(&val[1]);
1727 		if (!safe)
1728 			cleanstrcpy(p, p, MAXNAME);
1729 		define(val[0], p, CurEnv);
1730 		sticky = FALSE;
1731 		break;
1732 
1733 	  case 'm':		/* send to me too */
1734 		MeToo = atobool(val);
1735 		break;
1736 
1737 	  case 'n':		/* validate RHS in newaliases */
1738 		CheckAliases = atobool(val);
1739 		break;
1740 
1741 	    /* 'N' available -- was "net name" */
1742 
1743 	  case 'O':		/* daemon options */
1744 		setdaemonoptions(val);
1745 		break;
1746 
1747 	  case 'o':		/* assume old style headers */
1748 		if (atobool(val))
1749 			CurEnv->e_flags |= EF_OLDSTYLE;
1750 		else
1751 			CurEnv->e_flags &= ~EF_OLDSTYLE;
1752 		break;
1753 
1754 	  case 'p':		/* select privacy level */
1755 		p = val;
1756 		for (;;)
1757 		{
1758 			register struct prival *pv;
1759 			extern struct prival PrivacyValues[];
1760 
1761 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
1762 				p++;
1763 			if (*p == '\0')
1764 				break;
1765 			val = p;
1766 			while (isascii(*p) && isalnum(*p))
1767 				p++;
1768 			if (*p != '\0')
1769 				*p++ = '\0';
1770 
1771 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
1772 			{
1773 				if (strcasecmp(val, pv->pv_name) == 0)
1774 					break;
1775 			}
1776 			if (pv->pv_name == NULL)
1777 				syserr("readcf: Op line: %s unrecognized", val);
1778 			PrivacyFlags |= pv->pv_flag;
1779 		}
1780 		sticky = FALSE;
1781 		break;
1782 
1783 	  case 'P':		/* postmaster copy address for returned mail */
1784 		PostMasterCopy = newstr(val);
1785 		break;
1786 
1787 	  case 'q':		/* slope of queue only function */
1788 		QueueFactor = atoi(val);
1789 		break;
1790 
1791 	  case 'Q':		/* queue directory */
1792 		if (val[0] == '\0')
1793 			QueueDir = "mqueue";
1794 		else
1795 			QueueDir = newstr(val);
1796 		if (RealUid != 0 && !safe)
1797 			Warn_Q_option = TRUE;
1798 		break;
1799 
1800 	  case 'R':		/* don't prune routes */
1801 		DontPruneRoutes = atobool(val);
1802 		break;
1803 
1804 	  case 'r':		/* read timeout */
1805 		if (subopt == NULL)
1806 			inittimeouts(val);
1807 		else
1808 			settimeout(subopt, val);
1809 		break;
1810 
1811 	  case 'S':		/* status file */
1812 		if (val[0] == '\0')
1813 			StatFile = "sendmail.st";
1814 		else
1815 			StatFile = newstr(val);
1816 		break;
1817 
1818 	  case 's':		/* be super safe, even if expensive */
1819 		SuperSafe = atobool(val);
1820 		break;
1821 
1822 	  case 'T':		/* queue timeout */
1823 		p = strchr(val, '/');
1824 		if (p != NULL)
1825 		{
1826 			*p++ = '\0';
1827 			settimeout("queuewarn", p);
1828 		}
1829 		settimeout("queuereturn", val);
1830 		break;
1831 
1832 	  case 't':		/* time zone name */
1833 		TimeZoneSpec = newstr(val);
1834 		break;
1835 
1836 	  case 'U':		/* location of user database */
1837 		UdbSpec = newstr(val);
1838 		break;
1839 
1840 	  case 'u':		/* set default uid */
1841 		for (p = val; *p != '\0'; p++)
1842 		{
1843 			if (*p == '.' || *p == '/' || *p == ':')
1844 			{
1845 				*p++ = '\0';
1846 				break;
1847 			}
1848 		}
1849 		if (isascii(*val) && isdigit(*val))
1850 			DefUid = atoi(val);
1851 		else
1852 		{
1853 			register struct passwd *pw;
1854 
1855 			DefUid = -1;
1856 			pw = sm_getpwnam(val);
1857 			if (pw == NULL)
1858 				syserr("readcf: option u: unknown user %s", val);
1859 			else
1860 			{
1861 				DefUid = pw->pw_uid;
1862 				DefGid = pw->pw_gid;
1863 			}
1864 		}
1865 		setdefuser();
1866 
1867 		/* handle the group if it is there */
1868 		if (*p == '\0')
1869 			break;
1870 		val = p;
1871 		goto g_opt;
1872 
1873 	  case 'V':		/* fallback MX host */
1874 		FallBackMX = newstr(val);
1875 		break;
1876 
1877 	  case 'v':		/* run in verbose mode */
1878 		Verbose = atobool(val);
1879 		break;
1880 
1881 	  case 'w':		/* if we are best MX, try host directly */
1882 		TryNullMXList = atobool(val);
1883 		break;
1884 
1885 	    /* 'W' available -- was wizard password */
1886 
1887 	  case 'x':		/* load avg at which to auto-queue msgs */
1888 		QueueLA = atoi(val);
1889 		break;
1890 
1891 	  case 'X':		/* load avg at which to auto-reject connections */
1892 		RefuseLA = atoi(val);
1893 		break;
1894 
1895 	  case 'y':		/* work recipient factor */
1896 		WkRecipFact = atoi(val);
1897 		break;
1898 
1899 	  case 'Y':		/* fork jobs during queue runs */
1900 		ForkQueueRuns = atobool(val);
1901 		break;
1902 
1903 	  case 'z':		/* work message class factor */
1904 		WkClassFact = atoi(val);
1905 		break;
1906 
1907 	  case 'Z':		/* work time factor */
1908 		WkTimeFact = atoi(val);
1909 		break;
1910 
1911 	  case O_QUEUESORTORD:	/* queue sorting order */
1912 		switch (*val)
1913 		{
1914 		  case 'h':	/* Host first */
1915 		  case 'H':
1916 			QueueSortOrder = QS_BYHOST;
1917 			break;
1918 
1919 		  case 'p':	/* Priority order */
1920 		  case 'P':
1921 			QueueSortOrder = QS_BYPRIORITY;
1922 			break;
1923 
1924 		  default:
1925 			syserr("Invalid queue sort order \"%s\"", val);
1926 		}
1927 		break;
1928 
1929 	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
1930 		HostsFile = newstr(val);
1931 		break;
1932 
1933 	  case O_MQA:		/* minimum queue age between deliveries */
1934 		MinQueueAge = convtime(val, 'm');
1935 		break;
1936 
1937 	  case O_MHSA:		/* maximum age of cached host status */
1938 		MaxHostStatAge = convtime(val, 'm');
1939 		break;
1940 
1941 	  case O_DEFCHARSET:	/* default character set for mimefying */
1942 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
1943 		break;
1944 
1945 	  case O_SSFILE:	/* service switch file */
1946 		ServiceSwitchFile = newstr(val);
1947 		break;
1948 
1949 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
1950 		DialDelay = convtime(val, 's');
1951 		break;
1952 
1953 	  case O_NORCPTACTION:	/* what to do if no recipient */
1954 		if (strcasecmp(val, "none") == 0)
1955 			NoRecipientAction = NRA_NO_ACTION;
1956 		else if (strcasecmp(val, "add-to") == 0)
1957 			NoRecipientAction = NRA_ADD_TO;
1958 		else if (strcasecmp(val, "add-apparently-to") == 0)
1959 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
1960 		else if (strcasecmp(val, "add-bcc") == 0)
1961 			NoRecipientAction = NRA_ADD_BCC;
1962 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
1963 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
1964 		else
1965 			syserr("Invalid NoRecipientAction: %s", val);
1966 
1967 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
1968 		SafeFileEnv = newstr(val);
1969 		break;
1970 
1971 	  case O_MAXMSGSIZE:	/* maximum message size */
1972 		MaxMessageSize = atol(val);
1973 		break;
1974 
1975 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
1976 		ColonOkInAddr = atobool(val);
1977 		break;
1978 
1979 	  case O_MAXQUEUERUN:	/* max # of jobs in a single queue run */
1980 		MaxQueueRun = atol(val);
1981 		break;
1982 
1983 	  case O_MAXCHILDREN:	/* max # of children of daemon */
1984 		MaxChildren = atoi(val);
1985 		break;
1986 
1987 	  case O_KEEPCNAMES:	/* don't expand CNAME records */
1988 		DontExpandCnames = atobool(val);
1989 		break;
1990 
1991 	  default:
1992 		if (tTd(37, 1))
1993 		{
1994 			if (isascii(opt) && isprint(opt))
1995 				printf("Warning: option %c unknown\n", opt);
1996 			else
1997 				printf("Warning: option 0x%x unknown\n", opt);
1998 		}
1999 		break;
2000 	}
2001 	if (sticky)
2002 		setbitn(opt, StickyOpt);
2003 }
2004 /*
2005 **  SETCLASS -- set a string into a class
2006 **
2007 **	Parameters:
2008 **		class -- the class to put the string in.
2009 **		str -- the string to enter
2010 **
2011 **	Returns:
2012 **		none.
2013 **
2014 **	Side Effects:
2015 **		puts the word into the symbol table.
2016 */
2017 
2018 void
2019 setclass(class, str)
2020 	int class;
2021 	char *str;
2022 {
2023 	register STAB *s;
2024 
2025 	if (tTd(37, 8))
2026 		printf("setclass(%c, %s)\n", class, str);
2027 	s = stab(str, ST_CLASS, ST_ENTER);
2028 	setbitn(class, s->s_class);
2029 }
2030 /*
2031 **  MAKEMAPENTRY -- create a map entry
2032 **
2033 **	Parameters:
2034 **		line -- the config file line
2035 **
2036 **	Returns:
2037 **		A pointer to the map that has been created.
2038 **		NULL if there was a syntax error.
2039 **
2040 **	Side Effects:
2041 **		Enters the map into the dictionary.
2042 */
2043 
2044 MAP *
2045 makemapentry(line)
2046 	char *line;
2047 {
2048 	register char *p;
2049 	char *mapname;
2050 	char *classname;
2051 	register STAB *s;
2052 	STAB *class;
2053 
2054 	for (p = line; isascii(*p) && isspace(*p); p++)
2055 		continue;
2056 	if (!(isascii(*p) && isalnum(*p)))
2057 	{
2058 		syserr("readcf: config K line: no map name");
2059 		return NULL;
2060 	}
2061 
2062 	mapname = p;
2063 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
2064 		continue;
2065 	if (*p != '\0')
2066 		*p++ = '\0';
2067 	while (isascii(*p) && isspace(*p))
2068 		p++;
2069 	if (!(isascii(*p) && isalnum(*p)))
2070 	{
2071 		syserr("readcf: config K line, map %s: no map class", mapname);
2072 		return NULL;
2073 	}
2074 	classname = p;
2075 	while (isascii(*++p) && isalnum(*p))
2076 		continue;
2077 	if (*p != '\0')
2078 		*p++ = '\0';
2079 	while (isascii(*p) && isspace(*p))
2080 		p++;
2081 
2082 	/* look up the class */
2083 	class = stab(classname, ST_MAPCLASS, ST_FIND);
2084 	if (class == NULL)
2085 	{
2086 		syserr("readcf: map %s: class %s not available", mapname, classname);
2087 		return NULL;
2088 	}
2089 
2090 	/* enter the map */
2091 	s = stab(mapname, ST_MAP, ST_ENTER);
2092 	s->s_map.map_class = &class->s_mapclass;
2093 	s->s_map.map_mname = newstr(mapname);
2094 
2095 	if (class->s_mapclass.map_parse(&s->s_map, p))
2096 		s->s_map.map_mflags |= MF_VALID;
2097 
2098 	if (tTd(37, 5))
2099 	{
2100 		printf("map %s, class %s, flags %x, file %s,\n",
2101 			s->s_map.map_mname, s->s_map.map_class->map_cname,
2102 			s->s_map.map_mflags,
2103 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
2104 		printf("\tapp %s, domain %s, rebuild %s\n",
2105 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
2106 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
2107 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
2108 	}
2109 
2110 	return &s->s_map;
2111 }
2112 /*
2113 **  STRTORWSET -- convert string to rewriting set number
2114 **
2115 **	Parameters:
2116 **		p -- the pointer to the string to decode.
2117 **		endp -- if set, store the trailing delimiter here.
2118 **		stabmode -- ST_ENTER to create this entry, ST_FIND if
2119 **			it must already exist.
2120 **
2121 **	Returns:
2122 **		The appropriate ruleset number.
2123 **		-1 if it is not valid (error already printed)
2124 */
2125 
2126 int
2127 strtorwset(p, endp, stabmode)
2128 	char *p;
2129 	char **endp;
2130 	int stabmode;
2131 {
2132 	int ruleset;
2133 	static int nextruleset = MAXRWSETS;
2134 
2135 	while (isascii(*p) && isspace(*p))
2136 		p++;
2137 	if (!isascii(*p))
2138 	{
2139 		syserr("invalid ruleset name: \"%.20s\"", p);
2140 		return -1;
2141 	}
2142 	if (isdigit(*p))
2143 	{
2144 		ruleset = strtol(p, endp, 10);
2145 		if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
2146 		{
2147 			syserr("bad ruleset %d (%d max)",
2148 				ruleset, MAXRWSETS / 2);
2149 			ruleset = -1;
2150 		}
2151 	}
2152 	else
2153 	{
2154 		STAB *s;
2155 		char delim;
2156 		char *q;
2157 
2158 		q = p;
2159 		while (*p != '\0' && isascii(*p) &&
2160 		       (isalnum(*p) || strchr("-_$", *p) != NULL))
2161 			p++;
2162 		while (isascii(*p) && isspace(*p))
2163 			*p++ = '\0';
2164 		delim = *p;
2165 		if (delim != '\0')
2166 			*p = '\0';
2167 		s = stab(q, ST_RULESET, stabmode);
2168 		if (delim != '\0')
2169 			*p = delim;
2170 
2171 		if (s == NULL)
2172 		{
2173 			syserr("unknown ruleset %s", q);
2174 			return -1;
2175 		}
2176 
2177 		if (stabmode == ST_ENTER && delim == '=')
2178 		{
2179 			ruleset = strtol(p, endp, 10);
2180 			if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
2181 			{
2182 				syserr("bad ruleset %s = %d (%d max)",
2183 					q, ruleset, MAXRWSETS / 2);
2184 				ruleset = -1;
2185 			}
2186 		}
2187 		else
2188 		{
2189 			if (endp != NULL)
2190 				*endp = p;
2191 			if (s->s_ruleset > 0)
2192 				ruleset = s->s_ruleset;
2193 			else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
2194 			{
2195 				syserr("%s: too many named rulesets (%d max)",
2196 					q, MAXRWSETS / 2);
2197 				ruleset = -1;
2198 			}
2199 		}
2200 		if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset)
2201 		{
2202 			syserr("%s: ruleset changed value (old %d, new %d)",
2203 				q, ruleset, s->s_ruleset);
2204 			ruleset = s->s_ruleset;
2205 		}
2206 		else if (ruleset > 0)
2207 		{
2208 			s->s_ruleset = ruleset;
2209 		}
2210 	}
2211 	return ruleset;
2212 }
2213 /*
2214 **  INITTIMEOUTS -- parse and set timeout values
2215 **
2216 **	Parameters:
2217 **		val -- a pointer to the values.  If NULL, do initial
2218 **			settings.
2219 **
2220 **	Returns:
2221 **		none.
2222 **
2223 **	Side Effects:
2224 **		Initializes the TimeOuts structure
2225 */
2226 
2227 #define SECONDS
2228 #define MINUTES	* 60
2229 #define HOUR	* 3600
2230 
2231 void
2232 inittimeouts(val)
2233 	register char *val;
2234 {
2235 	register char *p;
2236 	extern time_t convtime();
2237 
2238 	if (val == NULL)
2239 	{
2240 		TimeOuts.to_initial = (time_t) 5 MINUTES;
2241 		TimeOuts.to_helo = (time_t) 5 MINUTES;
2242 		TimeOuts.to_mail = (time_t) 10 MINUTES;
2243 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
2244 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
2245 		TimeOuts.to_datablock = (time_t) 1 HOUR;
2246 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
2247 		TimeOuts.to_rset = (time_t) 5 MINUTES;
2248 		TimeOuts.to_quit = (time_t) 2 MINUTES;
2249 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
2250 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
2251 #if IDENTPROTO
2252 		TimeOuts.to_ident = (time_t) 30 SECONDS;
2253 #else
2254 		TimeOuts.to_ident = (time_t) 0 SECONDS;
2255 #endif
2256 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
2257 		return;
2258 	}
2259 
2260 	for (;; val = p)
2261 	{
2262 		while (isascii(*val) && isspace(*val))
2263 			val++;
2264 		if (*val == '\0')
2265 			break;
2266 		for (p = val; *p != '\0' && *p != ','; p++)
2267 			continue;
2268 		if (*p != '\0')
2269 			*p++ = '\0';
2270 
2271 		if (isascii(*val) && isdigit(*val))
2272 		{
2273 			/* old syntax -- set everything */
2274 			TimeOuts.to_mail = convtime(val, 'm');
2275 			TimeOuts.to_rcpt = TimeOuts.to_mail;
2276 			TimeOuts.to_datainit = TimeOuts.to_mail;
2277 			TimeOuts.to_datablock = TimeOuts.to_mail;
2278 			TimeOuts.to_datafinal = TimeOuts.to_mail;
2279 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
2280 			continue;
2281 		}
2282 		else
2283 		{
2284 			register char *q = strchr(val, ':');
2285 
2286 			if (q == NULL && (q = strchr(val, '=')) == NULL)
2287 			{
2288 				/* syntax error */
2289 				continue;
2290 			}
2291 			*q++ = '\0';
2292 			settimeout(val, q);
2293 		}
2294 	}
2295 }
2296 /*
2297 **  SETTIMEOUT -- set an individual timeout
2298 **
2299 **	Parameters:
2300 **		name -- the name of the timeout.
2301 **		val -- the value of the timeout.
2302 **
2303 **	Returns:
2304 **		none.
2305 */
2306 
2307 void
2308 settimeout(name, val)
2309 	char *name;
2310 	char *val;
2311 {
2312 	register char *p;
2313 	time_t to;
2314 	extern time_t convtime();
2315 
2316 	to = convtime(val, 'm');
2317 	p = strchr(name, '.');
2318 	if (p != NULL)
2319 		*p++ = '\0';
2320 
2321 	if (strcasecmp(name, "initial") == 0)
2322 		TimeOuts.to_initial = to;
2323 	else if (strcasecmp(name, "mail") == 0)
2324 		TimeOuts.to_mail = to;
2325 	else if (strcasecmp(name, "rcpt") == 0)
2326 		TimeOuts.to_rcpt = to;
2327 	else if (strcasecmp(name, "datainit") == 0)
2328 		TimeOuts.to_datainit = to;
2329 	else if (strcasecmp(name, "datablock") == 0)
2330 		TimeOuts.to_datablock = to;
2331 	else if (strcasecmp(name, "datafinal") == 0)
2332 		TimeOuts.to_datafinal = to;
2333 	else if (strcasecmp(name, "command") == 0)
2334 		TimeOuts.to_nextcommand = to;
2335 	else if (strcasecmp(name, "rset") == 0)
2336 		TimeOuts.to_rset = to;
2337 	else if (strcasecmp(name, "helo") == 0)
2338 		TimeOuts.to_helo = to;
2339 	else if (strcasecmp(name, "quit") == 0)
2340 		TimeOuts.to_quit = to;
2341 	else if (strcasecmp(name, "misc") == 0)
2342 		TimeOuts.to_miscshort = to;
2343 	else if (strcasecmp(name, "ident") == 0)
2344 		TimeOuts.to_ident = to;
2345 	else if (strcasecmp(name, "fileopen") == 0)
2346 		TimeOuts.to_fileopen = to;
2347 	else if (strcasecmp(name, "queuewarn") == 0)
2348 	{
2349 		to = convtime(val, 'h');
2350 		if (p == NULL || strcmp(p, "*") == 0)
2351 		{
2352 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2353 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2354 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2355 		}
2356 		else if (strcasecmp(p, "normal") == 0)
2357 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2358 		else if (strcasecmp(p, "urgent") == 0)
2359 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2360 		else if (strcasecmp(p, "non-urgent") == 0)
2361 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2362 		else
2363 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
2364 	}
2365 	else if (strcasecmp(name, "queuereturn") == 0)
2366 	{
2367 		to = convtime(val, 'd');
2368 		if (p == NULL || strcmp(p, "*") == 0)
2369 		{
2370 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2371 			TimeOuts.to_q_return[TOC_URGENT] = to;
2372 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2373 		}
2374 		else if (strcasecmp(p, "normal") == 0)
2375 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2376 		else if (strcasecmp(p, "urgent") == 0)
2377 			TimeOuts.to_q_return[TOC_URGENT] = to;
2378 		else if (strcasecmp(p, "non-urgent") == 0)
2379 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2380 		else
2381 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
2382 	}
2383 	else
2384 		syserr("settimeout: invalid timeout %s", name);
2385 }
2386