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