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