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