xref: /original-bsd/usr.sbin/sendmail/src/readcf.c (revision 18c592ef)
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.107 (Berkeley) 06/20/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++)
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 # if 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 			/* extract MTA name type; default to "dns" */
870 			m->m_mtatype = newstr(p);
871 			p = strchr(m->m_mtatype, '/');
872 			if (p != NULL)
873 			{
874 				*p++ = '\0';
875 				if (*p == '\0')
876 					p = NULL;
877 			}
878 			if (*m->m_mtatype == '\0')
879 				m->m_mtatype = "dns";
880 
881 			/* extract address type; default to "rfc822" */
882 			m->m_addrtype = p;
883 			if (p != NULL)
884 				p = strchr(p, '/');
885 			if (p != NULL)
886 			{
887 				*p++ = '\0';
888 				if (*p == '\0')
889 					p = NULL;
890 			}
891 			if (m->m_addrtype == NULL || *m->m_addrtype == '\0')
892 				m->m_addrtype = "rfc822";
893 
894 			/* extract diagnostic type; default to "smtp" */
895 			m->m_diagtype = p;
896 			if (m->m_diagtype == NULL || *m->m_diagtype == '\0')
897 				m->m_diagtype = "smtp";
898 			break;
899 
900 		  case 'U':		/* user id */
901 			if (isascii(*p) && !isdigit(*p))
902 			{
903 				char *q = p;
904 				struct passwd *pw;
905 
906 				while (isascii(*p) && isalnum(*p))
907 					p++;
908 				while (isascii(*p) && isspace(*p))
909 					*p++ = '\0';
910 				if (*p != '\0')
911 					*p++ = '\0';
912 				pw = sm_getpwnam(q);
913 				if (pw == NULL)
914 					syserr("readcf: mailer U= flag: unknown user %s", q);
915 				else
916 				{
917 					m->m_uid = pw->pw_uid;
918 					m->m_gid = pw->pw_gid;
919 				}
920 			}
921 			else
922 			{
923 				auto char *q;
924 
925 				m->m_uid = strtol(p, &q, 0);
926 				p = q;
927 			}
928 			while (isascii(*p) && isspace(*p))
929 				p++;
930 			if (*p == '\0')
931 				break;
932 			if (isascii(*p) && !isdigit(*p))
933 			{
934 				char *q = p;
935 				struct group *gr;
936 
937 				while (isascii(*p) && isalnum(*p))
938 					p++;
939 				*p++ = '\0';
940 				gr = getgrnam(q);
941 				if (gr == NULL)
942 					syserr("readcf: mailer U= flag: unknown group %s", q);
943 				else
944 					m->m_gid = gr->gr_gid;
945 			}
946 			else
947 			{
948 				m->m_gid = strtol(p, NULL, 0);
949 			}
950 			break;
951 		}
952 
953 		p = delimptr;
954 	}
955 
956 	/* do some rationality checking */
957 	if (m->m_argv == NULL)
958 	{
959 		syserr("M%s: A= argument required", m->m_name);
960 		return;
961 	}
962 	if (m->m_mailer == NULL)
963 	{
964 		syserr("M%s: P= argument required", m->m_name);
965 		return;
966 	}
967 
968 	if (NextMailer >= MAXMAILERS)
969 	{
970 		syserr("too many mailers defined (%d max)", MAXMAILERS);
971 		return;
972 	}
973 
974 	/* do some heuristic cleanup for back compatibility */
975 	if (bitnset(M_LIMITS, m->m_flags))
976 	{
977 		if (m->m_linelimit == 0)
978 			m->m_linelimit = SMTPLINELIM;
979 		if (ConfigLevel < 2)
980 			setbitn(M_7BITS, m->m_flags);
981 	}
982 
983 	if (ConfigLevel < 6 &&
984 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
985 	     strcmp(m->m_mailer, "[TCP]") == 0))
986 	{
987 		if (m->m_mtatype == NULL)
988 			m->m_mtatype = "dns";
989 		if (m->m_addrtype == NULL)
990 			m->m_addrtype = "rfc822";
991 		if (m->m_diagtype == NULL)
992 			m->m_diagtype = "smtp";
993 	}
994 
995 	/* enter the mailer into the symbol table */
996 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
997 	if (s->s_mailer != NULL)
998 	{
999 		i = s->s_mailer->m_mno;
1000 		free(s->s_mailer);
1001 	}
1002 	else
1003 	{
1004 		i = NextMailer++;
1005 	}
1006 	Mailer[i] = s->s_mailer = m;
1007 	m->m_mno = i;
1008 }
1009 /*
1010 **  MUNCHSTRING -- translate a string into internal form.
1011 **
1012 **	Parameters:
1013 **		p -- the string to munch.
1014 **		delimptr -- if non-NULL, set to the pointer of the
1015 **			field delimiter character.
1016 **
1017 **	Returns:
1018 **		the munched string.
1019 */
1020 
1021 char *
1022 munchstring(p, delimptr)
1023 	register char *p;
1024 	char **delimptr;
1025 {
1026 	register char *q;
1027 	bool backslash = FALSE;
1028 	bool quotemode = FALSE;
1029 	static char buf[MAXLINE];
1030 
1031 	for (q = buf; *p != '\0'; p++)
1032 	{
1033 		if (backslash)
1034 		{
1035 			/* everything is roughly literal */
1036 			backslash = FALSE;
1037 			switch (*p)
1038 			{
1039 			  case 'r':		/* carriage return */
1040 				*q++ = '\r';
1041 				continue;
1042 
1043 			  case 'n':		/* newline */
1044 				*q++ = '\n';
1045 				continue;
1046 
1047 			  case 'f':		/* form feed */
1048 				*q++ = '\f';
1049 				continue;
1050 
1051 			  case 'b':		/* backspace */
1052 				*q++ = '\b';
1053 				continue;
1054 			}
1055 			*q++ = *p;
1056 		}
1057 		else
1058 		{
1059 			if (*p == '\\')
1060 				backslash = TRUE;
1061 			else if (*p == '"')
1062 				quotemode = !quotemode;
1063 			else if (quotemode || *p != ',')
1064 				*q++ = *p;
1065 			else
1066 				break;
1067 		}
1068 	}
1069 
1070 	if (delimptr != NULL)
1071 		*delimptr = p;
1072 	*q++ = '\0';
1073 	return (buf);
1074 }
1075 /*
1076 **  MAKEARGV -- break up a string into words
1077 **
1078 **	Parameters:
1079 **		p -- the string to break up.
1080 **
1081 **	Returns:
1082 **		a char **argv (dynamically allocated)
1083 **
1084 **	Side Effects:
1085 **		munges p.
1086 */
1087 
1088 char **
1089 makeargv(p)
1090 	register char *p;
1091 {
1092 	char *q;
1093 	int i;
1094 	char **avp;
1095 	char *argv[MAXPV + 1];
1096 
1097 	/* take apart the words */
1098 	i = 0;
1099 	while (*p != '\0' && i < MAXPV)
1100 	{
1101 		q = p;
1102 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1103 			p++;
1104 		while (isascii(*p) && isspace(*p))
1105 			*p++ = '\0';
1106 		argv[i++] = newstr(q);
1107 	}
1108 	argv[i++] = NULL;
1109 
1110 	/* now make a copy of the argv */
1111 	avp = (char **) xalloc(sizeof *avp * i);
1112 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
1113 
1114 	return (avp);
1115 }
1116 /*
1117 **  PRINTRULES -- print rewrite rules (for debugging)
1118 **
1119 **	Parameters:
1120 **		none.
1121 **
1122 **	Returns:
1123 **		none.
1124 **
1125 **	Side Effects:
1126 **		prints rewrite rules.
1127 */
1128 
1129 void
1130 printrules()
1131 {
1132 	register struct rewrite *rwp;
1133 	register int ruleset;
1134 
1135 	for (ruleset = 0; ruleset < 10; ruleset++)
1136 	{
1137 		if (RewriteRules[ruleset] == NULL)
1138 			continue;
1139 		printf("\n----Rule Set %d:", ruleset);
1140 
1141 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
1142 		{
1143 			printf("\nLHS:");
1144 			printav(rwp->r_lhs);
1145 			printf("RHS:");
1146 			printav(rwp->r_rhs);
1147 		}
1148 	}
1149 }
1150 /*
1151 **  PRINTMAILER -- print mailer structure (for debugging)
1152 **
1153 **	Parameters:
1154 **		m -- the mailer to print
1155 **
1156 **	Returns:
1157 **		none.
1158 */
1159 
1160 void
1161 printmailer(m)
1162 	register MAILER *m;
1163 {
1164 	int j;
1165 
1166 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
1167 		m->m_mno, m->m_name,
1168 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
1169 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
1170 		m->m_uid, m->m_gid);
1171 	for (j = '\0'; j <= '\177'; j++)
1172 		if (bitnset(j, m->m_flags))
1173 			(void) putchar(j);
1174 	printf(" L=%d E=", m->m_linelimit);
1175 	xputs(m->m_eol);
1176 	if (m->m_defcharset != NULL)
1177 		printf(" C=%s", m->m_defcharset);
1178 	printf(" T=%s/%s/%s",
1179 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
1180 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
1181 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
1182 	if (m->m_argv != NULL)
1183 	{
1184 		char **a = m->m_argv;
1185 
1186 		printf(" A=");
1187 		while (*a != NULL)
1188 		{
1189 			if (a != m->m_argv)
1190 				printf(" ");
1191 			xputs(*a++);
1192 		}
1193 	}
1194 	printf("\n");
1195 }
1196 /*
1197 **  SETOPTION -- set global processing option
1198 **
1199 **	Parameters:
1200 **		opt -- option name.
1201 **		val -- option value (as a text string).
1202 **		safe -- set if this came from a configuration file.
1203 **			Some options (if set from the command line) will
1204 **			reset the user id to avoid security problems.
1205 **		sticky -- if set, don't let other setoptions override
1206 **			this value.
1207 **		e -- the main envelope.
1208 **
1209 **	Returns:
1210 **		none.
1211 **
1212 **	Side Effects:
1213 **		Sets options as implied by the arguments.
1214 */
1215 
1216 static BITMAP	StickyOpt;		/* set if option is stuck */
1217 extern void	settimeout __P((char *, char *));
1218 
1219 
1220 #if NAMED_BIND
1221 
1222 struct resolverflags
1223 {
1224 	char	*rf_name;	/* name of the flag */
1225 	long	rf_bits;	/* bits to set/clear */
1226 } ResolverFlags[] =
1227 {
1228 	"debug",	RES_DEBUG,
1229 	"aaonly",	RES_AAONLY,
1230 	"usevc",	RES_USEVC,
1231 	"primary",	RES_PRIMARY,
1232 	"igntc",	RES_IGNTC,
1233 	"recurse",	RES_RECURSE,
1234 	"defnames",	RES_DEFNAMES,
1235 	"stayopen",	RES_STAYOPEN,
1236 	"dnsrch",	RES_DNSRCH,
1237 	"true",		0,		/* to avoid error on old syntax */
1238 	NULL,		0
1239 };
1240 
1241 #endif
1242 
1243 struct optioninfo
1244 {
1245 	char	*o_name;	/* long name of option */
1246 	u_char	o_code;		/* short name of option */
1247 	bool	o_safe;		/* safe for random people to use */
1248 } OptionTab[] =
1249 {
1250 	"SevenBitInput",	'7',		TRUE,
1251 #if MIME8TO7
1252 	"EightBitMode",		'8',		TRUE,
1253 #endif
1254 	"AliasFile",		'A',		FALSE,
1255 	"AliasWait",		'a',		FALSE,
1256 	"BlankSub",		'B',		FALSE,
1257 	"MinFreeBlocks",	'b',		TRUE,
1258 	"CheckpointInterval",	'C',		TRUE,
1259 	"HoldExpensive",	'c',		FALSE,
1260 	"AutoRebuildAliases",	'D',		FALSE,
1261 	"DeliveryMode",		'd',		TRUE,
1262 	"ErrorHeader",		'E',		FALSE,
1263 	"ErrorMode",		'e',		TRUE,
1264 	"TempFileMode",		'F',		FALSE,
1265 	"SaveFromLine",		'f',		FALSE,
1266 	"MatchGECOS",		'G',		FALSE,
1267 	"HelpFile",		'H',		FALSE,
1268 	"MaxHopCount",		'h',		FALSE,
1269 	"ResolverOptions",	'I',		FALSE,
1270 	"IgnoreDots",		'i',		TRUE,
1271 	"ForwardPath",		'J',		FALSE,
1272 	"SendMimeErrors",	'j',		TRUE,
1273 	"ConnectionCacheSize",	'k',		FALSE,
1274 	"ConnectionCacheTimeout", 'K',		FALSE,
1275 	"UseErrorsTo",		'l',		FALSE,
1276 	"LogLevel",		'L',		FALSE,
1277 	"MeToo",		'm',		TRUE,
1278 	"CheckAliases",		'n',		FALSE,
1279 	"OldStyleHeaders",	'o',		TRUE,
1280 	"DaemonPortOptions",	'O',		FALSE,
1281 	"PrivacyOptions",	'p',		TRUE,
1282 	"PostmasterCopy",	'P',		FALSE,
1283 	"QueueFactor",		'q',		FALSE,
1284 	"QueueDirectory",	'Q',		FALSE,
1285 	"DontPruneRoutes",	'R',		FALSE,
1286 	"Timeout",		'r',		TRUE,
1287 	"StatusFile",		'S',		FALSE,
1288 	"SuperSafe",		's',		TRUE,
1289 	"QueueTimeout",		'T',		FALSE,
1290 	"TimeZoneSpec",		't',		FALSE,
1291 	"UserDatabaseSpec",	'U',		FALSE,
1292 	"DefaultUser",		'u',		FALSE,
1293 	"FallbackMXhost",	'V',		FALSE,
1294 	"Verbose",		'v',		TRUE,
1295 	"TryNullMXList",	'w',		TRUE,
1296 	"QueueLA",		'x',		FALSE,
1297 	"RefuseLA",		'X',		FALSE,
1298 	"RecipientFactor",	'y',		FALSE,
1299 	"ForkEachJob",		'Y',		FALSE,
1300 	"ClassFactor",		'z',		FALSE,
1301 	"RetryFactor",		'Z',		FALSE,
1302 #define O_QUEUESORTORD	0x81
1303 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
1304 #define O_HOSTSFILE	0x82
1305 	"HostsFile",		O_HOSTSFILE,	FALSE,
1306 #define O_MQA		0x83
1307 	"MinQueueAge",		O_MQA,		TRUE,
1308 #define O_MHSA		0x84
1309 /*
1310 	"MaxHostStatAge",	O_MHSA,		TRUE,
1311 */
1312 #define O_DEFCHARSET	0x85
1313 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
1314 #define O_SSFILE	0x86
1315 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
1316 #define O_DIALDELAY	0x87
1317 	"DialDelay",		O_DIALDELAY,	TRUE,
1318 #define O_NORCPTACTION	0x88
1319 	"NoRecipientAction",	O_NORCPTACTION,	TRUE,
1320 #define O_SAFEFILEENV	0x89
1321 	"SafeFileEnvironment",	O_SAFEFILEENV,	FALSE,
1322 #define O_MAXMSGSIZE	0x8a
1323 	"MaxMessageSize",	O_MAXMSGSIZE,	FALSE,
1324 #define O_COLONOKINADDR	0x8b
1325 	"ColonOkInAddr",	O_COLONOKINADDR, TRUE,
1326 #define O_MAXQUEUERUN	0x8c
1327 	"MaxQueueRunSize",	O_MAXQUEUERUN,	TRUE,
1328 #define O_MAXCHILDREN	0x8d
1329 /*
1330 	"MaxDaemonChildren",	O_MAXCHILDREN,	FALSE,
1331 */
1332 #define O_KEEPCNAMES	0x8e
1333 	"DontExpandCnames",	O_KEEPCNAMES,	FALSE,
1334 
1335 	NULL,			'\0',		FALSE,
1336 };
1337 
1338 
1339 
1340 void
1341 setoption(opt, val, safe, sticky, e)
1342 	int opt;
1343 	char *val;
1344 	bool safe;
1345 	bool sticky;
1346 	register ENVELOPE *e;
1347 {
1348 	register char *p;
1349 	register struct optioninfo *o;
1350 	char *subopt;
1351 	extern bool atobool();
1352 	extern time_t convtime();
1353 	extern int QueueLA;
1354 	extern int RefuseLA;
1355 	extern bool Warn_Q_option;
1356 
1357 	errno = 0;
1358 	if (opt == ' ')
1359 	{
1360 		/* full word options */
1361 		struct optioninfo *sel;
1362 
1363 		p = strchr(val, '=');
1364 		if (p == NULL)
1365 			p = &val[strlen(val)];
1366 		while (*--p == ' ')
1367 			continue;
1368 		while (*++p == ' ')
1369 			*p = '\0';
1370 		if (p == val)
1371 		{
1372 			syserr("readcf: null option name");
1373 			return;
1374 		}
1375 		if (*p == '=')
1376 			*p++ = '\0';
1377 		while (*p == ' ')
1378 			p++;
1379 		subopt = strchr(val, '.');
1380 		if (subopt != NULL)
1381 			*subopt++ = '\0';
1382 		sel = NULL;
1383 		for (o = OptionTab; o->o_name != NULL; o++)
1384 		{
1385 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
1386 				continue;
1387 			if (strlen(o->o_name) == strlen(val))
1388 			{
1389 				/* completely specified -- this must be it */
1390 				sel = NULL;
1391 				break;
1392 			}
1393 			if (sel != NULL)
1394 				break;
1395 			sel = o;
1396 		}
1397 		if (sel != NULL && o->o_name == NULL)
1398 			o = sel;
1399 		else if (o->o_name == NULL)
1400 		{
1401 			syserr("readcf: unknown option name %s", val);
1402 			return;
1403 		}
1404 		else if (sel != NULL)
1405 		{
1406 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
1407 				val, sel->o_name, o->o_name);
1408 			return;
1409 		}
1410 		if (strlen(val) != strlen(o->o_name))
1411 		{
1412 			bool oldVerbose = Verbose;
1413 
1414 			Verbose = TRUE;
1415 			message("Option %s used as abbreviation for %s",
1416 				val, o->o_name);
1417 			Verbose = oldVerbose;
1418 		}
1419 		opt = o->o_code;
1420 		val = p;
1421 	}
1422 	else
1423 	{
1424 		for (o = OptionTab; o->o_name != NULL; o++)
1425 		{
1426 			if (o->o_code == opt)
1427 				break;
1428 		}
1429 		subopt = NULL;
1430 	}
1431 
1432 	if (tTd(37, 1))
1433 	{
1434 		printf(isascii(opt) && isprint(opt) ?
1435 			    "setoption %s (%c).%s=" :
1436 			    "setoption %s (0x%x).%s=",
1437 			o->o_name == NULL ? "<unknown>" : o->o_name,
1438 			opt,
1439 			subopt == NULL ? "" : subopt);
1440 		xputs(val);
1441 	}
1442 
1443 	/*
1444 	**  See if this option is preset for us.
1445 	*/
1446 
1447 	if (!sticky && bitnset(opt, StickyOpt))
1448 	{
1449 		if (tTd(37, 1))
1450 			printf(" (ignored)\n");
1451 		return;
1452 	}
1453 
1454 	/*
1455 	**  Check to see if this option can be specified by this user.
1456 	*/
1457 
1458 	if (!safe && RealUid == 0)
1459 		safe = TRUE;
1460 	if (!safe && !o->o_safe)
1461 	{
1462 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
1463 		{
1464 			if (tTd(37, 1))
1465 				printf(" (unsafe)");
1466 			if (RealUid != geteuid())
1467 			{
1468 				if (tTd(37, 1))
1469 					printf("(Resetting uid)");
1470 				(void) setgid(RealGid);
1471 				(void) setuid(RealUid);
1472 			}
1473 		}
1474 	}
1475 	if (tTd(37, 1))
1476 		printf("\n");
1477 
1478 	switch (opt & 0xff)
1479 	{
1480 	  case '7':		/* force seven-bit input */
1481 		SevenBitInput = atobool(val);
1482 		break;
1483 
1484 #if MIME8TO7
1485 	  case '8':		/* handling of 8-bit input */
1486 		switch (*val)
1487 		{
1488 		  case 'm':		/* convert 8-bit, convert MIME */
1489 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
1490 			break;
1491 
1492 		  case 'p':		/* pass 8 bit, convert MIME */
1493 			MimeMode = MM_CVTMIME|MM_PASS8BIT;
1494 			break;
1495 
1496 		  case 's':		/* strict adherence */
1497 			MimeMode = MM_CVTMIME;
1498 			break;
1499 
1500 #if 0
1501 		  case 'r':		/* reject 8-bit, don't convert MIME */
1502 			MimeMode = 0;
1503 			break;
1504 
1505 		  case 'j':		/* "just send 8" */
1506 			MimeMode = MM_PASS8BIT;
1507 			break;
1508 
1509 		  case 'a':		/* encode 8 bit if available */
1510 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
1511 			break;
1512 
1513 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
1514 			MimeMode = MM_MIME8BIT;
1515 			break;
1516 #endif
1517 
1518 		  default:
1519 			syserr("Unknown 8-bit mode %c", *val);
1520 			exit(EX_USAGE);
1521 		}
1522 		break;
1523 #endif
1524 
1525 	  case 'A':		/* set default alias file */
1526 		if (val[0] == '\0')
1527 			setalias("aliases");
1528 		else
1529 			setalias(val);
1530 		break;
1531 
1532 	  case 'a':		/* look N minutes for "@:@" in alias file */
1533 		if (val[0] == '\0')
1534 			SafeAlias = 5 * 60;		/* five minutes */
1535 		else
1536 			SafeAlias = convtime(val, 'm');
1537 		break;
1538 
1539 	  case 'B':		/* substitution for blank character */
1540 		SpaceSub = val[0];
1541 		if (SpaceSub == '\0')
1542 			SpaceSub = ' ';
1543 		break;
1544 
1545 	  case 'b':		/* min blocks free on queue fs/max msg size */
1546 		p = strchr(val, '/');
1547 		if (p != NULL)
1548 		{
1549 			*p++ = '\0';
1550 			MaxMessageSize = atol(p);
1551 		}
1552 		MinBlocksFree = atol(val);
1553 		break;
1554 
1555 	  case 'c':		/* don't connect to "expensive" mailers */
1556 		NoConnect = atobool(val);
1557 		break;
1558 
1559 	  case 'C':		/* checkpoint every N addresses */
1560 		CheckpointInterval = atoi(val);
1561 		break;
1562 
1563 	  case 'd':		/* delivery mode */
1564 		switch (*val)
1565 		{
1566 		  case '\0':
1567 			e->e_sendmode = SM_DELIVER;
1568 			break;
1569 
1570 		  case SM_QUEUE:	/* queue only */
1571 #ifndef QUEUE
1572 			syserr("need QUEUE to set -odqueue");
1573 #endif /* QUEUE */
1574 			/* fall through..... */
1575 
1576 		  case SM_DELIVER:	/* do everything */
1577 		  case SM_FORK:		/* fork after verification */
1578 			e->e_sendmode = *val;
1579 			break;
1580 
1581 		  default:
1582 			syserr("Unknown delivery mode %c", *val);
1583 			exit(EX_USAGE);
1584 		}
1585 		break;
1586 
1587 	  case 'D':		/* rebuild alias database as needed */
1588 		AutoRebuild = atobool(val);
1589 		break;
1590 
1591 	  case 'E':		/* error message header/header file */
1592 		if (*val != '\0')
1593 			ErrMsgFile = newstr(val);
1594 		break;
1595 
1596 	  case 'e':		/* set error processing mode */
1597 		switch (*val)
1598 		{
1599 		  case EM_QUIET:	/* be silent about it */
1600 		  case EM_MAIL:		/* mail back */
1601 		  case EM_BERKNET:	/* do berknet error processing */
1602 		  case EM_WRITE:	/* write back (or mail) */
1603 		  case EM_PRINT:	/* print errors normally (default) */
1604 			e->e_errormode = *val;
1605 			break;
1606 		}
1607 		break;
1608 
1609 	  case 'F':		/* file mode */
1610 		FileMode = atooct(val) & 0777;
1611 		break;
1612 
1613 	  case 'f':		/* save Unix-style From lines on front */
1614 		SaveFrom = atobool(val);
1615 		break;
1616 
1617 	  case 'G':		/* match recipients against GECOS field */
1618 		MatchGecos = atobool(val);
1619 		break;
1620 
1621 	  case 'g':		/* default gid */
1622   g_opt:
1623 		if (isascii(*val) && isdigit(*val))
1624 			DefGid = atoi(val);
1625 		else
1626 		{
1627 			register struct group *gr;
1628 
1629 			DefGid = -1;
1630 			gr = getgrnam(val);
1631 			if (gr == NULL)
1632 				syserr("readcf: option %c: unknown group %s",
1633 					opt, val);
1634 			else
1635 				DefGid = gr->gr_gid;
1636 		}
1637 		break;
1638 
1639 	  case 'H':		/* help file */
1640 		if (val[0] == '\0')
1641 			HelpFile = "sendmail.hf";
1642 		else
1643 			HelpFile = newstr(val);
1644 		break;
1645 
1646 	  case 'h':		/* maximum hop count */
1647 		MaxHopCount = atoi(val);
1648 		break;
1649 
1650 	  case 'I':		/* use internet domain name server */
1651 #if NAMED_BIND
1652 		for (p = val; *p != 0; )
1653 		{
1654 			bool clearmode;
1655 			char *q;
1656 			struct resolverflags *rfp;
1657 
1658 			while (*p == ' ')
1659 				p++;
1660 			if (*p == '\0')
1661 				break;
1662 			clearmode = FALSE;
1663 			if (*p == '-')
1664 				clearmode = TRUE;
1665 			else if (*p != '+')
1666 				p--;
1667 			p++;
1668 			q = p;
1669 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1670 				p++;
1671 			if (*p != '\0')
1672 				*p++ = '\0';
1673 			if (strcasecmp(q, "HasWildcardMX") == 0)
1674 			{
1675 				HasWildcardMX = !clearmode;
1676 				continue;
1677 			}
1678 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
1679 			{
1680 				if (strcasecmp(q, rfp->rf_name) == 0)
1681 					break;
1682 			}
1683 			if (rfp->rf_name == NULL)
1684 				syserr("readcf: I option value %s unrecognized", q);
1685 			else if (clearmode)
1686 				_res.options &= ~rfp->rf_bits;
1687 			else
1688 				_res.options |= rfp->rf_bits;
1689 		}
1690 		if (tTd(8, 2))
1691 			printf("_res.options = %x, HasWildcardMX = %d\n",
1692 				_res.options, HasWildcardMX);
1693 #else
1694 		usrerr("name server (I option) specified but BIND not compiled in");
1695 #endif
1696 		break;
1697 
1698 	  case 'i':		/* ignore dot lines in message */
1699 		IgnrDot = atobool(val);
1700 		break;
1701 
1702 	  case 'j':		/* send errors in MIME (RFC 1341) format */
1703 		SendMIMEErrors = atobool(val);
1704 		break;
1705 
1706 	  case 'J':		/* .forward search path */
1707 		ForwardPath = newstr(val);
1708 		break;
1709 
1710 	  case 'k':		/* connection cache size */
1711 		MaxMciCache = atoi(val);
1712 		if (MaxMciCache < 0)
1713 			MaxMciCache = 0;
1714 		break;
1715 
1716 	  case 'K':		/* connection cache timeout */
1717 		MciCacheTimeout = convtime(val, 'm');
1718 		break;
1719 
1720 	  case 'l':		/* use Errors-To: header */
1721 		UseErrorsTo = atobool(val);
1722 		break;
1723 
1724 	  case 'L':		/* log level */
1725 		if (safe || LogLevel < atoi(val))
1726 			LogLevel = atoi(val);
1727 		break;
1728 
1729 	  case 'M':		/* define macro */
1730 		p = newstr(&val[1]);
1731 		if (!safe)
1732 			cleanstrcpy(p, p, MAXNAME);
1733 		define(val[0], p, CurEnv);
1734 		sticky = FALSE;
1735 		break;
1736 
1737 	  case 'm':		/* send to me too */
1738 		MeToo = atobool(val);
1739 		break;
1740 
1741 	  case 'n':		/* validate RHS in newaliases */
1742 		CheckAliases = atobool(val);
1743 		break;
1744 
1745 	    /* 'N' available -- was "net name" */
1746 
1747 	  case 'O':		/* daemon options */
1748 		setdaemonoptions(val);
1749 		break;
1750 
1751 	  case 'o':		/* assume old style headers */
1752 		if (atobool(val))
1753 			CurEnv->e_flags |= EF_OLDSTYLE;
1754 		else
1755 			CurEnv->e_flags &= ~EF_OLDSTYLE;
1756 		break;
1757 
1758 	  case 'p':		/* select privacy level */
1759 		p = val;
1760 		for (;;)
1761 		{
1762 			register struct prival *pv;
1763 			extern struct prival PrivacyValues[];
1764 
1765 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
1766 				p++;
1767 			if (*p == '\0')
1768 				break;
1769 			val = p;
1770 			while (isascii(*p) && isalnum(*p))
1771 				p++;
1772 			if (*p != '\0')
1773 				*p++ = '\0';
1774 
1775 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
1776 			{
1777 				if (strcasecmp(val, pv->pv_name) == 0)
1778 					break;
1779 			}
1780 			if (pv->pv_name == NULL)
1781 				syserr("readcf: Op line: %s unrecognized", val);
1782 			PrivacyFlags |= pv->pv_flag;
1783 		}
1784 		sticky = FALSE;
1785 		break;
1786 
1787 	  case 'P':		/* postmaster copy address for returned mail */
1788 		PostMasterCopy = newstr(val);
1789 		break;
1790 
1791 	  case 'q':		/* slope of queue only function */
1792 		QueueFactor = atoi(val);
1793 		break;
1794 
1795 	  case 'Q':		/* queue directory */
1796 		if (val[0] == '\0')
1797 			QueueDir = "mqueue";
1798 		else
1799 			QueueDir = newstr(val);
1800 		if (RealUid != 0 && !safe)
1801 			Warn_Q_option = TRUE;
1802 		break;
1803 
1804 	  case 'R':		/* don't prune routes */
1805 		DontPruneRoutes = atobool(val);
1806 		break;
1807 
1808 	  case 'r':		/* read timeout */
1809 		if (subopt == NULL)
1810 			inittimeouts(val);
1811 		else
1812 			settimeout(subopt, val);
1813 		break;
1814 
1815 	  case 'S':		/* status file */
1816 		if (val[0] == '\0')
1817 			StatFile = "sendmail.st";
1818 		else
1819 			StatFile = newstr(val);
1820 		break;
1821 
1822 	  case 's':		/* be super safe, even if expensive */
1823 		SuperSafe = atobool(val);
1824 		break;
1825 
1826 	  case 'T':		/* queue timeout */
1827 		p = strchr(val, '/');
1828 		if (p != NULL)
1829 		{
1830 			*p++ = '\0';
1831 			settimeout("queuewarn", p);
1832 		}
1833 		settimeout("queuereturn", val);
1834 		break;
1835 
1836 	  case 't':		/* time zone name */
1837 		TimeZoneSpec = newstr(val);
1838 		break;
1839 
1840 	  case 'U':		/* location of user database */
1841 		UdbSpec = newstr(val);
1842 		break;
1843 
1844 	  case 'u':		/* set default uid */
1845 		for (p = val; *p != '\0'; p++)
1846 		{
1847 			if (*p == '.' || *p == '/' || *p == ':')
1848 			{
1849 				*p++ = '\0';
1850 				break;
1851 			}
1852 		}
1853 		if (isascii(*val) && isdigit(*val))
1854 			DefUid = atoi(val);
1855 		else
1856 		{
1857 			register struct passwd *pw;
1858 
1859 			DefUid = -1;
1860 			pw = sm_getpwnam(val);
1861 			if (pw == NULL)
1862 				syserr("readcf: option u: unknown user %s", val);
1863 			else
1864 			{
1865 				DefUid = pw->pw_uid;
1866 				DefGid = pw->pw_gid;
1867 			}
1868 		}
1869 		setdefuser();
1870 
1871 		/* handle the group if it is there */
1872 		if (*p == '\0')
1873 			break;
1874 		val = p;
1875 		goto g_opt;
1876 
1877 	  case 'V':		/* fallback MX host */
1878 		FallBackMX = newstr(val);
1879 		break;
1880 
1881 	  case 'v':		/* run in verbose mode */
1882 		Verbose = atobool(val);
1883 		break;
1884 
1885 	  case 'w':		/* if we are best MX, try host directly */
1886 		TryNullMXList = atobool(val);
1887 		break;
1888 
1889 	    /* 'W' available -- was wizard password */
1890 
1891 	  case 'x':		/* load avg at which to auto-queue msgs */
1892 		QueueLA = atoi(val);
1893 		break;
1894 
1895 	  case 'X':		/* load avg at which to auto-reject connections */
1896 		RefuseLA = atoi(val);
1897 		break;
1898 
1899 	  case 'y':		/* work recipient factor */
1900 		WkRecipFact = atoi(val);
1901 		break;
1902 
1903 	  case 'Y':		/* fork jobs during queue runs */
1904 		ForkQueueRuns = atobool(val);
1905 		break;
1906 
1907 	  case 'z':		/* work message class factor */
1908 		WkClassFact = atoi(val);
1909 		break;
1910 
1911 	  case 'Z':		/* work time factor */
1912 		WkTimeFact = atoi(val);
1913 		break;
1914 
1915 	  case O_QUEUESORTORD:	/* queue sorting order */
1916 		switch (*val)
1917 		{
1918 		  case 'h':	/* Host first */
1919 		  case 'H':
1920 			QueueSortOrder = QS_BYHOST;
1921 			break;
1922 
1923 		  case 'p':	/* Priority order */
1924 		  case 'P':
1925 			QueueSortOrder = QS_BYPRIORITY;
1926 			break;
1927 
1928 		  default:
1929 			syserr("Invalid queue sort order \"%s\"", val);
1930 		}
1931 		break;
1932 
1933 	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
1934 		HostsFile = newstr(val);
1935 		break;
1936 
1937 	  case O_MQA:		/* minimum queue age between deliveries */
1938 		MinQueueAge = convtime(val, 'm');
1939 		break;
1940 
1941 	  case O_MHSA:		/* maximum age of cached host status */
1942 		MaxHostStatAge = convtime(val, 'm');
1943 		break;
1944 
1945 	  case O_DEFCHARSET:	/* default character set for mimefying */
1946 		DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
1947 		break;
1948 
1949 	  case O_SSFILE:	/* service switch file */
1950 		ServiceSwitchFile = newstr(val);
1951 		break;
1952 
1953 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
1954 		DialDelay = convtime(val, 's');
1955 		break;
1956 
1957 	  case O_NORCPTACTION:	/* what to do if no recipient */
1958 		if (strcasecmp(val, "none") == 0)
1959 			NoRecipientAction = NRA_NO_ACTION;
1960 		else if (strcasecmp(val, "add-to") == 0)
1961 			NoRecipientAction = NRA_ADD_TO;
1962 		else if (strcasecmp(val, "add-apparently-to") == 0)
1963 			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
1964 		else if (strcasecmp(val, "add-bcc") == 0)
1965 			NoRecipientAction = NRA_ADD_BCC;
1966 		else if (strcasecmp(val, "add-to-undisclosed") == 0)
1967 			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
1968 		else
1969 			syserr("Invalid NoRecipientAction: %s", val);
1970 
1971 	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
1972 		SafeFileEnv = newstr(val);
1973 		break;
1974 
1975 	  case O_MAXMSGSIZE:	/* maximum message size */
1976 		MaxMessageSize = atol(val);
1977 		break;
1978 
1979 	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
1980 		ColonOkInAddr = atobool(val);
1981 		break;
1982 
1983 	  case O_MAXQUEUERUN:	/* max # of jobs in a single queue run */
1984 		MaxQueueRun = atol(val);
1985 		break;
1986 
1987 	  case O_MAXCHILDREN:	/* max # of children of daemon */
1988 		MaxChildren = atoi(val);
1989 		break;
1990 
1991 	  case O_KEEPCNAMES:	/* don't expand CNAME records */
1992 		DontExpandCnames = atobool(val);
1993 		break;
1994 
1995 	  default:
1996 		if (tTd(37, 1))
1997 		{
1998 			if (isascii(opt) && isprint(opt))
1999 				printf("Warning: option %c unknown\n", opt);
2000 			else
2001 				printf("Warning: option 0x%x unknown\n", opt);
2002 		}
2003 		break;
2004 	}
2005 	if (sticky)
2006 		setbitn(opt, StickyOpt);
2007 }
2008 /*
2009 **  SETCLASS -- set a string into a class
2010 **
2011 **	Parameters:
2012 **		class -- the class to put the string in.
2013 **		str -- the string to enter
2014 **
2015 **	Returns:
2016 **		none.
2017 **
2018 **	Side Effects:
2019 **		puts the word into the symbol table.
2020 */
2021 
2022 void
2023 setclass(class, str)
2024 	int class;
2025 	char *str;
2026 {
2027 	register STAB *s;
2028 
2029 	if (tTd(37, 8))
2030 		printf("setclass(%c, %s)\n", class, str);
2031 	s = stab(str, ST_CLASS, ST_ENTER);
2032 	setbitn(class, s->s_class);
2033 }
2034 /*
2035 **  MAKEMAPENTRY -- create a map entry
2036 **
2037 **	Parameters:
2038 **		line -- the config file line
2039 **
2040 **	Returns:
2041 **		A pointer to the map that has been created.
2042 **		NULL if there was a syntax error.
2043 **
2044 **	Side Effects:
2045 **		Enters the map into the dictionary.
2046 */
2047 
2048 MAP *
2049 makemapentry(line)
2050 	char *line;
2051 {
2052 	register char *p;
2053 	char *mapname;
2054 	char *classname;
2055 	register STAB *s;
2056 	STAB *class;
2057 
2058 	for (p = line; isascii(*p) && isspace(*p); p++)
2059 		continue;
2060 	if (!(isascii(*p) && isalnum(*p)))
2061 	{
2062 		syserr("readcf: config K line: no map name");
2063 		return NULL;
2064 	}
2065 
2066 	mapname = p;
2067 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
2068 		continue;
2069 	if (*p != '\0')
2070 		*p++ = '\0';
2071 	while (isascii(*p) && isspace(*p))
2072 		p++;
2073 	if (!(isascii(*p) && isalnum(*p)))
2074 	{
2075 		syserr("readcf: config K line, map %s: no map class", mapname);
2076 		return NULL;
2077 	}
2078 	classname = p;
2079 	while (isascii(*++p) && isalnum(*p))
2080 		continue;
2081 	if (*p != '\0')
2082 		*p++ = '\0';
2083 	while (isascii(*p) && isspace(*p))
2084 		p++;
2085 
2086 	/* look up the class */
2087 	class = stab(classname, ST_MAPCLASS, ST_FIND);
2088 	if (class == NULL)
2089 	{
2090 		syserr("readcf: map %s: class %s not available", mapname, classname);
2091 		return NULL;
2092 	}
2093 
2094 	/* enter the map */
2095 	s = stab(mapname, ST_MAP, ST_ENTER);
2096 	s->s_map.map_class = &class->s_mapclass;
2097 	s->s_map.map_mname = newstr(mapname);
2098 
2099 	if (class->s_mapclass.map_parse(&s->s_map, p))
2100 		s->s_map.map_mflags |= MF_VALID;
2101 
2102 	if (tTd(37, 5))
2103 	{
2104 		printf("map %s, class %s, flags %x, file %s,\n",
2105 			s->s_map.map_mname, s->s_map.map_class->map_cname,
2106 			s->s_map.map_mflags,
2107 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
2108 		printf("\tapp %s, domain %s, rebuild %s\n",
2109 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
2110 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
2111 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
2112 	}
2113 
2114 	return &s->s_map;
2115 }
2116 /*
2117 **  STRTORWSET -- convert string to rewriting set number
2118 **
2119 **	Parameters:
2120 **		p -- the pointer to the string to decode.
2121 **		endp -- if set, store the trailing delimiter here.
2122 **		stabmode -- ST_ENTER to create this entry, ST_FIND if
2123 **			it must already exist.
2124 **
2125 **	Returns:
2126 **		The appropriate ruleset number.
2127 **		-1 if it is not valid (error already printed)
2128 */
2129 
2130 int
2131 strtorwset(p, endp, stabmode)
2132 	char *p;
2133 	char **endp;
2134 	int stabmode;
2135 {
2136 	int ruleset;
2137 	static int nextruleset = MAXRWSETS;
2138 
2139 	while (isascii(*p) && isspace(*p))
2140 		p++;
2141 	if (!isascii(*p))
2142 	{
2143 		syserr("invalid ruleset name: \"%.20s\"", p);
2144 		return -1;
2145 	}
2146 	if (isdigit(*p))
2147 	{
2148 		ruleset = strtol(p, endp, 10);
2149 		if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
2150 		{
2151 			syserr("bad ruleset %d (%d max)",
2152 				ruleset, MAXRWSETS / 2);
2153 			ruleset = -1;
2154 		}
2155 	}
2156 	else
2157 	{
2158 		STAB *s;
2159 		char delim;
2160 		char *q;
2161 
2162 		q = p;
2163 		while (*p != '\0' && isascii(*p) &&
2164 		       (isalnum(*p) || strchr("-_$", *p) != NULL))
2165 			p++;
2166 		while (isascii(*p) && isspace(*p))
2167 			*p++ = '\0';
2168 		delim = *p;
2169 		if (delim != '\0')
2170 			*p = '\0';
2171 		s = stab(q, ST_RULESET, stabmode);
2172 		if (delim != '\0')
2173 			*p = delim;
2174 
2175 		if (s == NULL)
2176 		{
2177 			syserr("unknown ruleset %s", q);
2178 			return -1;
2179 		}
2180 
2181 		if (stabmode == ST_ENTER && delim == '=')
2182 		{
2183 			ruleset = strtol(p, endp, 10);
2184 			if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
2185 			{
2186 				syserr("bad ruleset %s = %d (%d max)",
2187 					q, ruleset, MAXRWSETS / 2);
2188 				ruleset = -1;
2189 			}
2190 		}
2191 		else
2192 		{
2193 			if (endp != NULL)
2194 				*endp = p;
2195 			if (s->s_ruleset > 0)
2196 				ruleset = s->s_ruleset;
2197 			else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
2198 			{
2199 				syserr("%s: too many named rulesets (%d max)",
2200 					q, MAXRWSETS / 2);
2201 				ruleset = -1;
2202 			}
2203 		}
2204 		if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset)
2205 		{
2206 			syserr("%s: ruleset changed value (old %d, new %d)",
2207 				q, ruleset, s->s_ruleset);
2208 			ruleset = s->s_ruleset;
2209 		}
2210 		else if (ruleset > 0)
2211 		{
2212 			s->s_ruleset = ruleset;
2213 		}
2214 	}
2215 	return ruleset;
2216 }
2217 /*
2218 **  INITTIMEOUTS -- parse and set timeout values
2219 **
2220 **	Parameters:
2221 **		val -- a pointer to the values.  If NULL, do initial
2222 **			settings.
2223 **
2224 **	Returns:
2225 **		none.
2226 **
2227 **	Side Effects:
2228 **		Initializes the TimeOuts structure
2229 */
2230 
2231 #define SECONDS
2232 #define MINUTES	* 60
2233 #define HOUR	* 3600
2234 
2235 void
2236 inittimeouts(val)
2237 	register char *val;
2238 {
2239 	register char *p;
2240 	extern time_t convtime();
2241 
2242 	if (val == NULL)
2243 	{
2244 		TimeOuts.to_initial = (time_t) 5 MINUTES;
2245 		TimeOuts.to_helo = (time_t) 5 MINUTES;
2246 		TimeOuts.to_mail = (time_t) 10 MINUTES;
2247 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
2248 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
2249 		TimeOuts.to_datablock = (time_t) 1 HOUR;
2250 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
2251 		TimeOuts.to_rset = (time_t) 5 MINUTES;
2252 		TimeOuts.to_quit = (time_t) 2 MINUTES;
2253 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
2254 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
2255 #if IDENTPROTO
2256 		TimeOuts.to_ident = (time_t) 30 SECONDS;
2257 #else
2258 		TimeOuts.to_ident = (time_t) 0 SECONDS;
2259 #endif
2260 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
2261 		return;
2262 	}
2263 
2264 	for (;; val = p)
2265 	{
2266 		while (isascii(*val) && isspace(*val))
2267 			val++;
2268 		if (*val == '\0')
2269 			break;
2270 		for (p = val; *p != '\0' && *p != ','; p++)
2271 			continue;
2272 		if (*p != '\0')
2273 			*p++ = '\0';
2274 
2275 		if (isascii(*val) && isdigit(*val))
2276 		{
2277 			/* old syntax -- set everything */
2278 			TimeOuts.to_mail = convtime(val, 'm');
2279 			TimeOuts.to_rcpt = TimeOuts.to_mail;
2280 			TimeOuts.to_datainit = TimeOuts.to_mail;
2281 			TimeOuts.to_datablock = TimeOuts.to_mail;
2282 			TimeOuts.to_datafinal = TimeOuts.to_mail;
2283 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
2284 			continue;
2285 		}
2286 		else
2287 		{
2288 			register char *q = strchr(val, ':');
2289 
2290 			if (q == NULL && (q = strchr(val, '=')) == NULL)
2291 			{
2292 				/* syntax error */
2293 				continue;
2294 			}
2295 			*q++ = '\0';
2296 			settimeout(val, q);
2297 		}
2298 	}
2299 }
2300 /*
2301 **  SETTIMEOUT -- set an individual timeout
2302 **
2303 **	Parameters:
2304 **		name -- the name of the timeout.
2305 **		val -- the value of the timeout.
2306 **
2307 **	Returns:
2308 **		none.
2309 */
2310 
2311 void
2312 settimeout(name, val)
2313 	char *name;
2314 	char *val;
2315 {
2316 	register char *p;
2317 	time_t to;
2318 	extern time_t convtime();
2319 
2320 	to = convtime(val, 'm');
2321 	p = strchr(name, '.');
2322 	if (p != NULL)
2323 		*p++ = '\0';
2324 
2325 	if (strcasecmp(name, "initial") == 0)
2326 		TimeOuts.to_initial = to;
2327 	else if (strcasecmp(name, "mail") == 0)
2328 		TimeOuts.to_mail = to;
2329 	else if (strcasecmp(name, "rcpt") == 0)
2330 		TimeOuts.to_rcpt = to;
2331 	else if (strcasecmp(name, "datainit") == 0)
2332 		TimeOuts.to_datainit = to;
2333 	else if (strcasecmp(name, "datablock") == 0)
2334 		TimeOuts.to_datablock = to;
2335 	else if (strcasecmp(name, "datafinal") == 0)
2336 		TimeOuts.to_datafinal = to;
2337 	else if (strcasecmp(name, "command") == 0)
2338 		TimeOuts.to_nextcommand = to;
2339 	else if (strcasecmp(name, "rset") == 0)
2340 		TimeOuts.to_rset = to;
2341 	else if (strcasecmp(name, "helo") == 0)
2342 		TimeOuts.to_helo = to;
2343 	else if (strcasecmp(name, "quit") == 0)
2344 		TimeOuts.to_quit = to;
2345 	else if (strcasecmp(name, "misc") == 0)
2346 		TimeOuts.to_miscshort = to;
2347 	else if (strcasecmp(name, "ident") == 0)
2348 		TimeOuts.to_ident = to;
2349 	else if (strcasecmp(name, "fileopen") == 0)
2350 		TimeOuts.to_fileopen = to;
2351 	else if (strcasecmp(name, "queuewarn") == 0)
2352 	{
2353 		to = convtime(val, 'h');
2354 		if (p == NULL || strcmp(p, "*") == 0)
2355 		{
2356 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2357 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2358 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2359 		}
2360 		else if (strcasecmp(p, "normal") == 0)
2361 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2362 		else if (strcasecmp(p, "urgent") == 0)
2363 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2364 		else if (strcasecmp(p, "non-urgent") == 0)
2365 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2366 		else
2367 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
2368 	}
2369 	else if (strcasecmp(name, "queuereturn") == 0)
2370 	{
2371 		to = convtime(val, 'd');
2372 		if (p == NULL || strcmp(p, "*") == 0)
2373 		{
2374 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2375 			TimeOuts.to_q_return[TOC_URGENT] = to;
2376 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2377 		}
2378 		else if (strcasecmp(p, "normal") == 0)
2379 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2380 		else if (strcasecmp(p, "urgent") == 0)
2381 			TimeOuts.to_q_return[TOC_URGENT] = to;
2382 		else if (strcasecmp(p, "non-urgent") == 0)
2383 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2384 		else
2385 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
2386 	}
2387 	else
2388 		syserr("settimeout: invalid timeout %s", name);
2389 }
2390