xref: /original-bsd/usr.sbin/sendmail/src/readcf.c (revision c94cb6e0)
1 /*
2  * Copyright (c) 1983 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.32 (Berkeley) 08/07/94";
11 #endif /* not lint */
12 
13 # include "sendmail.h"
14 # include <pwd.h>
15 # include <grp.h>
16 #if NAMED_BIND
17 # include <resolv.h>
18 #endif
19 
20 /*
21 **  READCF -- read control file.
22 **
23 **	This routine reads the control file and builds the internal
24 **	form.
25 **
26 **	The file is formatted as a sequence of lines, each taken
27 **	atomically.  The first character of each line describes how
28 **	the line is to be interpreted.  The lines are:
29 **		Dxval		Define macro x to have value val.
30 **		Cxword		Put word into class x.
31 **		Fxfile [fmt]	Read file for lines to put into
32 **				class x.  Use scanf string 'fmt'
33 **				or "%s" if not present.  Fmt should
34 **				only produce one string-valued result.
35 **		Hname: value	Define header with field-name 'name'
36 **				and value as specified; this will be
37 **				macro expanded immediately before
38 **				use.
39 **		Sn		Use rewriting set n.
40 **		Rlhs rhs	Rewrite addresses that match lhs to
41 **				be rhs.
42 **		Mn arg=val...	Define mailer.  n is the internal name.
43 **				Args specify mailer parameters.
44 **		Oxvalue		Set option x to value.
45 **		Pname=value	Set precedence name to value.
46 **		Vversioncode[/vendorcode]
47 **				Version level/vendor name of
48 **				configuration syntax.
49 **		Kmapname mapclass arguments....
50 **				Define keyed lookup of a given class.
51 **				Arguments are class dependent.
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 readcf(cfname, safe, e)
67 	char *cfname;
68 	bool safe;
69 	register ENVELOPE *e;
70 {
71 	FILE *cf;
72 	int ruleset = 0;
73 	char *q;
74 	struct rewrite *rwp = NULL;
75 	char *bp;
76 	auto char *ep;
77 	int nfuzzy;
78 	char *file;
79 	bool optional;
80 	char buf[MAXLINE];
81 	register char *p;
82 	extern char **copyplist();
83 	struct stat statb;
84 	char exbuf[MAXLINE];
85 	char pvpbuf[MAXLINE + MAXATOM];
86 	extern char *munchstring();
87 	extern void makemapentry();
88 
89 	FileName = cfname;
90 	LineNumber = 0;
91 
92 	cf = fopen(cfname, "r");
93 	if (cf == NULL)
94 	{
95 		syserr("cannot open");
96 		exit(EX_OSFILE);
97 	}
98 
99 	if (fstat(fileno(cf), &statb) < 0)
100 	{
101 		syserr("cannot fstat");
102 		exit(EX_OSFILE);
103 	}
104 
105 	if (!S_ISREG(statb.st_mode))
106 	{
107 		syserr("not a plain file");
108 		exit(EX_OSFILE);
109 	}
110 
111 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
112 	{
113 		if (OpMode == MD_DAEMON || OpMode == MD_FREEZE)
114 			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
115 				FileName);
116 #ifdef LOG
117 		if (LogLevel > 0)
118 			syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
119 				FileName);
120 #endif
121 	}
122 
123 #ifdef XLA
124 	xla_zero();
125 #endif
126 
127 	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
128 	{
129 		if (bp[0] == '#')
130 		{
131 			if (bp != buf)
132 				free(bp);
133 			continue;
134 		}
135 
136 		/* map $ into \201 for macro expansion */
137 		for (p = bp; *p != '\0'; p++)
138 		{
139 			if (*p == '#' && p > bp && ConfigLevel >= 3)
140 			{
141 				/* this is an on-line comment */
142 				register char *e;
143 
144 				switch (*--p & 0377)
145 				{
146 				  case MACROEXPAND:
147 					/* it's from $# -- let it go through */
148 					p++;
149 					break;
150 
151 				  case '\\':
152 					/* it's backslash escaped */
153 					(void) strcpy(p, p + 1);
154 					break;
155 
156 				  default:
157 					/* delete preceeding white space */
158 					while (isascii(*p) && isspace(*p) && p > bp)
159 						p--;
160 					if ((e = strchr(++p, '\n')) != NULL)
161 						(void) strcpy(p, e);
162 					else
163 						p[0] = p[1] = '\0';
164 					break;
165 				}
166 				continue;
167 			}
168 
169 			if (*p != '$')
170 				continue;
171 
172 			if (p[1] == '$')
173 			{
174 				/* actual dollar sign.... */
175 				(void) strcpy(p, p + 1);
176 				continue;
177 			}
178 
179 			/* convert to macro expansion character */
180 			*p = MACROEXPAND;
181 		}
182 
183 		/* interpret this line */
184 		errno = 0;
185 		switch (bp[0])
186 		{
187 		  case '\0':
188 		  case '#':		/* comment */
189 			break;
190 
191 		  case 'R':		/* rewriting rule */
192 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
193 				continue;
194 
195 			if (*p == '\0')
196 			{
197 				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
198 				break;
199 			}
200 
201 			/* allocate space for the rule header */
202 			if (rwp == NULL)
203 			{
204 				RewriteRules[ruleset] = rwp =
205 					(struct rewrite *) xalloc(sizeof *rwp);
206 			}
207 			else
208 			{
209 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
210 				rwp = rwp->r_next;
211 			}
212 			rwp->r_next = NULL;
213 
214 			/* expand and save the LHS */
215 			*p = '\0';
216 			expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e);
217 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
218 					     sizeof pvpbuf, NULL);
219 			nfuzzy = 0;
220 			if (rwp->r_lhs != NULL)
221 			{
222 				register char **ap;
223 
224 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
225 
226 				/* count the number of fuzzy matches in LHS */
227 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
228 				{
229 					char *botch;
230 
231 					botch = NULL;
232 					switch (**ap & 0377)
233 					{
234 					  case MATCHZANY:
235 					  case MATCHANY:
236 					  case MATCHONE:
237 					  case MATCHCLASS:
238 					  case MATCHNCLASS:
239 						nfuzzy++;
240 						break;
241 
242 					  case MATCHREPL:
243 						botch = "$0-$9";
244 						break;
245 
246 					  case CANONNET:
247 						botch = "$#";
248 						break;
249 
250 					  case CANONUSER:
251 						botch = "$:";
252 						break;
253 
254 					  case CALLSUBR:
255 						botch = "$>";
256 						break;
257 
258 					  case CONDIF:
259 						botch = "$?";
260 						break;
261 
262 					  case CONDELSE:
263 						botch = "$|";
264 						break;
265 
266 					  case CONDFI:
267 						botch = "$.";
268 						break;
269 
270 					  case HOSTBEGIN:
271 						botch = "$[";
272 						break;
273 
274 					  case HOSTEND:
275 						botch = "$]";
276 						break;
277 
278 					  case LOOKUPBEGIN:
279 						botch = "$(";
280 						break;
281 
282 					  case LOOKUPEND:
283 						botch = "$)";
284 						break;
285 					}
286 					if (botch != NULL)
287 						syserr("Inappropriate use of %s on LHS",
288 							botch);
289 				}
290 			}
291 			else
292 				syserr("R line: null LHS");
293 
294 			/* expand and save the RHS */
295 			while (*++p == '\t')
296 				continue;
297 			q = p;
298 			while (*p != '\0' && *p != '\t')
299 				p++;
300 			*p = '\0';
301 			expand(q, exbuf, &exbuf[sizeof exbuf], e);
302 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
303 					     sizeof pvpbuf, NULL);
304 			if (rwp->r_rhs != NULL)
305 			{
306 				register char **ap;
307 
308 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
309 
310 				/* check no out-of-bounds replacements */
311 				nfuzzy += '0';
312 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
313 				{
314 					char *botch;
315 
316 					botch = NULL;
317 					switch (**ap & 0377)
318 					{
319 					  case MATCHREPL:
320 						if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
321 						{
322 							syserr("replacement $%c out of bounds",
323 								(*ap)[1]);
324 						}
325 						break;
326 
327 					  case MATCHZANY:
328 						botch = "$*";
329 						break;
330 
331 					  case MATCHANY:
332 						botch = "$+";
333 						break;
334 
335 					  case MATCHONE:
336 						botch = "$-";
337 						break;
338 
339 					  case MATCHCLASS:
340 						botch = "$=";
341 						break;
342 
343 					  case MATCHNCLASS:
344 						botch = "$~";
345 						break;
346 					}
347 					if (botch != NULL)
348 						syserr("Inappropriate use of %s on RHS",
349 							botch);
350 				}
351 			}
352 			else
353 				syserr("R line: null RHS");
354 			break;
355 
356 		  case 'S':		/* select rewriting set */
357 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
358 				continue;
359 			if (!isascii(*p) || !isdigit(*p))
360 			{
361 				syserr("invalid argument to S line: \"%.20s\"",
362 					&bp[1]);
363 				break;
364 			}
365 			ruleset = atoi(p);
366 			if (ruleset >= MAXRWSETS || ruleset < 0)
367 			{
368 				syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
369 				ruleset = 0;
370 			}
371 			rwp = NULL;
372 			break;
373 
374 		  case 'D':		/* macro definition */
375 			p = munchstring(&bp[2], NULL);
376 			define(bp[1], newstr(p), e);
377 			break;
378 
379 		  case 'H':		/* required header line */
380 			(void) chompheader(&bp[1], TRUE, e);
381 			break;
382 
383 		  case 'C':		/* word class */
384 			/* scan the list of words and set class for all */
385 			expand(&bp[2], exbuf, &exbuf[sizeof exbuf], e);
386 			for (p = exbuf; *p != '\0'; )
387 			{
388 				register char *wd;
389 				char delim;
390 
391 				while (*p != '\0' && isascii(*p) && isspace(*p))
392 					p++;
393 				wd = p;
394 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
395 					p++;
396 				delim = *p;
397 				*p = '\0';
398 				if (wd[0] != '\0')
399 					setclass(bp[1], wd);
400 				*p = delim;
401 			}
402 			break;
403 
404 		  case 'F':		/* word class from file */
405 			for (p = &bp[2]; isascii(*p) && isspace(*p); )
406 				p++;
407 			if (p[0] == '-' && p[1] == 'o')
408 			{
409 				optional = TRUE;
410 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
411 					p++;
412 				while (isascii(*p) && isspace(*p))
413 					p++;
414 			}
415 			else
416 				optional = FALSE;
417 			file = p;
418 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
419 				p++;
420 			if (*p == '\0')
421 				p = "%s";
422 			else
423 			{
424 				*p = '\0';
425 				while (isascii(*++p) && isspace(*p))
426 					continue;
427 			}
428 			fileclass(bp[1], file, p, safe, optional);
429 			break;
430 
431 #ifdef XLA
432 		  case 'L':		/* extended load average description */
433 			xla_init(&bp[1]);
434 			break;
435 #endif
436 
437 		  case 'M':		/* define mailer */
438 			makemailer(&bp[1]);
439 			break;
440 
441 		  case 'O':		/* set option */
442 			setoption(bp[1], &bp[2], safe, FALSE, e);
443 			break;
444 
445 		  case 'P':		/* set precedence */
446 			if (NumPriorities >= MAXPRIORITIES)
447 			{
448 				toomany('P', MAXPRIORITIES);
449 				break;
450 			}
451 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
452 				continue;
453 			if (*p == '\0')
454 				goto badline;
455 			*p = '\0';
456 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
457 			Priorities[NumPriorities].pri_val = atoi(++p);
458 			NumPriorities++;
459 			break;
460 
461 		  case 'T':		/* trusted user(s) */
462 			/* this option is obsolete, but will be ignored */
463 			break;
464 
465 		  case 'V':		/* configuration syntax version */
466 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
467 				continue;
468 			if (!isascii(*p) || !isdigit(*p))
469 			{
470 				syserr("invalid argument to V line: \"%.20s\"",
471 					&bp[1]);
472 				break;
473 			}
474 			ConfigLevel = strtol(p, &ep, 10);
475 			if (ConfigLevel >= 5)
476 			{
477 				/* level 5 configs have short name in $w */
478 				p = macvalue('w', e);
479 				if (p != NULL && (p = strchr(p, '.')) != NULL)
480 					*p = '\0';
481 			}
482 			if (*ep++ == '/')
483 			{
484 				/* extract vendor code */
485 				for (p = ep; isascii(*p) && isalpha(*p); )
486 					p++;
487 				*p = '\0';
488 
489 				if (!setvendor(ep))
490 					syserr("invalid V line vendor code: \"%s\"",
491 						ep);
492 			}
493 			break;
494 
495 		  case 'K':
496 			makemapentry(&bp[1]);
497 			break;
498 
499 		  default:
500 		  badline:
501 			syserr("unknown control line \"%s\"", bp);
502 		}
503 		if (bp != buf)
504 			free(bp);
505 	}
506 	if (ferror(cf))
507 	{
508 		syserr("I/O read error", cfname);
509 		exit(EX_OSFILE);
510 	}
511 	fclose(cf);
512 	FileName = NULL;
513 
514 	if (stab("host", ST_MAP, ST_FIND) == NULL)
515 	{
516 		/* user didn't initialize: set up host map */
517 		strcpy(buf, "host host");
518 #if NAMED_BIND
519 		if (ConfigLevel >= 2)
520 			strcat(buf, " -a.");
521 #endif
522 		makemapentry(buf);
523 	}
524 }
525 /*
526 **  TOOMANY -- signal too many of some option
527 **
528 **	Parameters:
529 **		id -- the id of the error line
530 **		maxcnt -- the maximum possible values
531 **
532 **	Returns:
533 **		none.
534 **
535 **	Side Effects:
536 **		gives a syserr.
537 */
538 
539 toomany(id, maxcnt)
540 	char id;
541 	int maxcnt;
542 {
543 	syserr("too many %c lines, %d max", id, maxcnt);
544 }
545 /*
546 **  FILECLASS -- read members of a class from a file
547 **
548 **	Parameters:
549 **		class -- class to define.
550 **		filename -- name of file to read.
551 **		fmt -- scanf string to use for match.
552 **		safe -- if set, this is a safe read.
553 **		optional -- if set, it is not an error for the file to
554 **			not exist.
555 **
556 **	Returns:
557 **		none
558 **
559 **	Side Effects:
560 **
561 **		puts all lines in filename that match a scanf into
562 **			the named class.
563 */
564 
565 fileclass(class, filename, fmt, safe, optional)
566 	int class;
567 	char *filename;
568 	char *fmt;
569 	bool safe;
570 	bool optional;
571 {
572 	FILE *f;
573 	struct stat stbuf;
574 	char buf[MAXLINE];
575 
576 	if (tTd(37, 2))
577 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
578 
579 	if (filename[0] == '|')
580 	{
581 		syserr("fileclass: pipes (F%c%s) not supported due to security problems",
582 			class, filename);
583 		return;
584 	}
585 	if (stat(filename, &stbuf) < 0)
586 	{
587 		if (tTd(37, 2))
588 			printf("  cannot stat (%s)\n", errstring(errno));
589 		if (!optional)
590 			syserr("fileclass: cannot stat %s", filename);
591 		return;
592 	}
593 	if (!S_ISREG(stbuf.st_mode))
594 	{
595 		syserr("fileclass: %s not a regular file", filename);
596 		return;
597 	}
598 	if (!safe && access(filename, R_OK) < 0)
599 	{
600 		syserr("fileclass: access denied on %s", filename);
601 		return;
602 	}
603 	f = fopen(filename, "r");
604 	if (f == NULL)
605 	{
606 		syserr("fileclass: cannot open %s", filename);
607 		return;
608 	}
609 
610 	while (fgets(buf, sizeof buf, f) != NULL)
611 	{
612 		register STAB *s;
613 		register char *p;
614 # ifdef SCANF
615 		char wordbuf[MAXNAME+1];
616 
617 		if (sscanf(buf, fmt, wordbuf) != 1)
618 			continue;
619 		p = wordbuf;
620 # else /* SCANF */
621 		p = buf;
622 # endif /* SCANF */
623 
624 		/*
625 		**  Break up the match into words.
626 		*/
627 
628 		while (*p != '\0')
629 		{
630 			register char *q;
631 
632 			/* strip leading spaces */
633 			while (isascii(*p) && isspace(*p))
634 				p++;
635 			if (*p == '\0')
636 				break;
637 
638 			/* find the end of the word */
639 			q = p;
640 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
641 				p++;
642 			if (*p != '\0')
643 				*p++ = '\0';
644 
645 			/* enter the word in the symbol table */
646 			setclass(class, q);
647 		}
648 	}
649 
650 	(void) fclose(f);
651 }
652 /*
653 **  MAKEMAILER -- define a new mailer.
654 **
655 **	Parameters:
656 **		line -- description of mailer.  This is in labeled
657 **			fields.  The fields are:
658 **			   P -- the path to the mailer
659 **			   F -- the flags associated with the mailer
660 **			   A -- the argv for this mailer
661 **			   S -- the sender rewriting set
662 **			   R -- the recipient rewriting set
663 **			   E -- the eol string
664 **			The first word is the canonical name of the mailer.
665 **
666 **	Returns:
667 **		none.
668 **
669 **	Side Effects:
670 **		enters the mailer into the mailer table.
671 */
672 
673 makemailer(line)
674 	char *line;
675 {
676 	register char *p;
677 	register struct mailer *m;
678 	register STAB *s;
679 	int i;
680 	char fcode;
681 	auto char *endp;
682 	extern int NextMailer;
683 	extern char **makeargv();
684 	extern char *munchstring();
685 	extern long atol();
686 
687 	/* allocate a mailer and set up defaults */
688 	m = (struct mailer *) xalloc(sizeof *m);
689 	bzero((char *) m, sizeof *m);
690 	m->m_eol = "\n";
691 	m->m_uid = m->m_gid = 0;
692 
693 	/* collect the mailer name */
694 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
695 		continue;
696 	if (*p != '\0')
697 		*p++ = '\0';
698 	m->m_name = newstr(line);
699 
700 	/* now scan through and assign info from the fields */
701 	while (*p != '\0')
702 	{
703 		auto char *delimptr;
704 
705 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
706 			p++;
707 
708 		/* p now points to field code */
709 		fcode = *p;
710 		while (*p != '\0' && *p != '=' && *p != ',')
711 			p++;
712 		if (*p++ != '=')
713 		{
714 			syserr("mailer %s: `=' expected", m->m_name);
715 			return;
716 		}
717 		while (isascii(*p) && isspace(*p))
718 			p++;
719 
720 		/* p now points to the field body */
721 		p = munchstring(p, &delimptr);
722 
723 		/* install the field into the mailer struct */
724 		switch (fcode)
725 		{
726 		  case 'P':		/* pathname */
727 			m->m_mailer = newstr(p);
728 			break;
729 
730 		  case 'F':		/* flags */
731 			for (; *p != '\0'; p++)
732 				if (!(isascii(*p) && isspace(*p)))
733 					setbitn(*p, m->m_flags);
734 			break;
735 
736 		  case 'S':		/* sender rewriting ruleset */
737 		  case 'R':		/* recipient rewriting ruleset */
738 			i = strtol(p, &endp, 10);
739 			if (i < 0 || i >= MAXRWSETS)
740 			{
741 				syserr("invalid rewrite set, %d max", MAXRWSETS);
742 				return;
743 			}
744 			if (fcode == 'S')
745 				m->m_sh_rwset = m->m_se_rwset = i;
746 			else
747 				m->m_rh_rwset = m->m_re_rwset = i;
748 
749 			p = endp;
750 			if (*p++ == '/')
751 			{
752 				i = strtol(p, NULL, 10);
753 				if (i < 0 || i >= MAXRWSETS)
754 				{
755 					syserr("invalid rewrite set, %d max",
756 						MAXRWSETS);
757 					return;
758 				}
759 				if (fcode == 'S')
760 					m->m_sh_rwset = i;
761 				else
762 					m->m_rh_rwset = i;
763 			}
764 			break;
765 
766 		  case 'E':		/* end of line string */
767 			m->m_eol = newstr(p);
768 			break;
769 
770 		  case 'A':		/* argument vector */
771 			m->m_argv = makeargv(p);
772 			break;
773 
774 		  case 'M':		/* maximum message size */
775 			m->m_maxsize = atol(p);
776 			break;
777 
778 		  case 'L':		/* maximum line length */
779 			m->m_linelimit = atoi(p);
780 			break;
781 
782 		  case 'D':		/* working directory */
783 			m->m_execdir = newstr(p);
784 			break;
785 
786 		  case 'U':		/* user id */
787 			if (isascii(*p) && !isdigit(*p))
788 			{
789 				char *q = p;
790 				struct passwd *pw;
791 
792 				while (isascii(*p) && isalnum(*p))
793 					p++;
794 				while (isascii(*p) && isspace(*p))
795 					*p++ = '\0';
796 				if (*p != '\0')
797 					*p++ = '\0';
798 				pw = getpwnam(q);
799 				if (pw == NULL)
800 					syserr("readcf: mailer U= flag: unknown user %s", q);
801 				else
802 				{
803 					m->m_uid = pw->pw_uid;
804 					m->m_gid = pw->pw_gid;
805 				}
806 			}
807 			else
808 			{
809 				auto char *q;
810 
811 				m->m_uid = strtol(p, &q, 0);
812 				p = q;
813 			}
814 			while (isascii(*p) && isspace(*p))
815 				p++;
816 			if (*p == '\0')
817 				break;
818 			if (isascii(*p) && !isdigit(*p))
819 			{
820 				char *q = p;
821 				struct group *gr;
822 
823 				while (isascii(*p) && isalnum(*p))
824 					p++;
825 				*p++ = '\0';
826 				gr = getgrnam(q);
827 				if (gr == NULL)
828 					syserr("readcf: mailer U= flag: unknown group %s", q);
829 				else
830 					m->m_gid = gr->gr_gid;
831 			}
832 			else
833 			{
834 				m->m_gid = strtol(p, NULL, 0);
835 			}
836 			break;
837 		}
838 
839 		p = delimptr;
840 	}
841 
842 	/* do some heuristic cleanup for back compatibility */
843 	if (bitnset(M_LIMITS, m->m_flags))
844 	{
845 		if (m->m_linelimit == 0)
846 			m->m_linelimit = SMTPLINELIM;
847 		if (ConfigLevel < 2)
848 			setbitn(M_7BITS, m->m_flags);
849 	}
850 
851 	/* do some rationality checking */
852 	if (m->m_argv == NULL)
853 	{
854 		syserr("M%s: A= argument required", m->m_name);
855 		return;
856 	}
857 	if (m->m_mailer == NULL)
858 	{
859 		syserr("M%s: P= argument required", m->m_name);
860 		return;
861 	}
862 
863 	if (NextMailer >= MAXMAILERS)
864 	{
865 		syserr("too many mailers defined (%d max)", MAXMAILERS);
866 		return;
867 	}
868 
869 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
870 	if (s->s_mailer != NULL)
871 	{
872 		i = s->s_mailer->m_mno;
873 		free(s->s_mailer);
874 	}
875 	else
876 	{
877 		i = NextMailer++;
878 	}
879 	Mailer[i] = s->s_mailer = m;
880 	m->m_mno = i;
881 }
882 /*
883 **  MUNCHSTRING -- translate a string into internal form.
884 **
885 **	Parameters:
886 **		p -- the string to munch.
887 **		delimptr -- if non-NULL, set to the pointer of the
888 **			field delimiter character.
889 **
890 **	Returns:
891 **		the munched string.
892 */
893 
894 char *
895 munchstring(p, delimptr)
896 	register char *p;
897 	char **delimptr;
898 {
899 	register char *q;
900 	bool backslash = FALSE;
901 	bool quotemode = FALSE;
902 	static char buf[MAXLINE];
903 
904 	for (q = buf; *p != '\0'; p++)
905 	{
906 		if (backslash)
907 		{
908 			/* everything is roughly literal */
909 			backslash = FALSE;
910 			switch (*p)
911 			{
912 			  case 'r':		/* carriage return */
913 				*q++ = '\r';
914 				continue;
915 
916 			  case 'n':		/* newline */
917 				*q++ = '\n';
918 				continue;
919 
920 			  case 'f':		/* form feed */
921 				*q++ = '\f';
922 				continue;
923 
924 			  case 'b':		/* backspace */
925 				*q++ = '\b';
926 				continue;
927 			}
928 			*q++ = *p;
929 		}
930 		else
931 		{
932 			if (*p == '\\')
933 				backslash = TRUE;
934 			else if (*p == '"')
935 				quotemode = !quotemode;
936 			else if (quotemode || *p != ',')
937 				*q++ = *p;
938 			else
939 				break;
940 		}
941 	}
942 
943 	if (delimptr != NULL)
944 		*delimptr = p;
945 	*q++ = '\0';
946 	return (buf);
947 }
948 /*
949 **  MAKEARGV -- break up a string into words
950 **
951 **	Parameters:
952 **		p -- the string to break up.
953 **
954 **	Returns:
955 **		a char **argv (dynamically allocated)
956 **
957 **	Side Effects:
958 **		munges p.
959 */
960 
961 char **
962 makeargv(p)
963 	register char *p;
964 {
965 	char *q;
966 	int i;
967 	char **avp;
968 	char *argv[MAXPV + 1];
969 
970 	/* take apart the words */
971 	i = 0;
972 	while (*p != '\0' && i < MAXPV)
973 	{
974 		q = p;
975 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
976 			p++;
977 		while (isascii(*p) && isspace(*p))
978 			*p++ = '\0';
979 		argv[i++] = newstr(q);
980 	}
981 	argv[i++] = NULL;
982 
983 	/* now make a copy of the argv */
984 	avp = (char **) xalloc(sizeof *avp * i);
985 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
986 
987 	return (avp);
988 }
989 /*
990 **  PRINTRULES -- print rewrite rules (for debugging)
991 **
992 **	Parameters:
993 **		none.
994 **
995 **	Returns:
996 **		none.
997 **
998 **	Side Effects:
999 **		prints rewrite rules.
1000 */
1001 
1002 printrules()
1003 {
1004 	register struct rewrite *rwp;
1005 	register int ruleset;
1006 
1007 	for (ruleset = 0; ruleset < 10; ruleset++)
1008 	{
1009 		if (RewriteRules[ruleset] == NULL)
1010 			continue;
1011 		printf("\n----Rule Set %d:", ruleset);
1012 
1013 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
1014 		{
1015 			printf("\nLHS:");
1016 			printav(rwp->r_lhs);
1017 			printf("RHS:");
1018 			printav(rwp->r_rhs);
1019 		}
1020 	}
1021 }
1022 
1023 /*
1024 **  SETOPTION -- set global processing option
1025 **
1026 **	Parameters:
1027 **		opt -- option name.
1028 **		val -- option value (as a text string).
1029 **		safe -- set if this came from a configuration file.
1030 **			Some options (if set from the command line) will
1031 **			reset the user id to avoid security problems.
1032 **		sticky -- if set, don't let other setoptions override
1033 **			this value.
1034 **		e -- the main envelope.
1035 **
1036 **	Returns:
1037 **		none.
1038 **
1039 **	Side Effects:
1040 **		Sets options as implied by the arguments.
1041 */
1042 
1043 static BITMAP	StickyOpt;		/* set if option is stuck */
1044 
1045 
1046 #if NAMED_BIND
1047 
1048 struct resolverflags
1049 {
1050 	char	*rf_name;	/* name of the flag */
1051 	long	rf_bits;	/* bits to set/clear */
1052 } ResolverFlags[] =
1053 {
1054 	"debug",	RES_DEBUG,
1055 	"aaonly",	RES_AAONLY,
1056 	"usevc",	RES_USEVC,
1057 	"primary",	RES_PRIMARY,
1058 	"igntc",	RES_IGNTC,
1059 	"recurse",	RES_RECURSE,
1060 	"defnames",	RES_DEFNAMES,
1061 	"stayopen",	RES_STAYOPEN,
1062 	"dnsrch",	RES_DNSRCH,
1063 	"true",		0,		/* to avoid error on old syntax */
1064 	NULL,		0
1065 };
1066 
1067 #endif
1068 
1069 /* codes for options that have no short name */
1070 /* NOTE: some of these values may be in the list of "safe" options below */
1071 #define O_BSP		0x80	/* have broken SMTP peers */
1072 #define O_SQBH		0x81	/* sort queue by host */
1073 
1074 struct optioninfo
1075 {
1076 	char	*o_name;	/* long name of option */
1077 	char	o_code;		/* short name of option */
1078 	bool	o_safe;		/* safe for random people to use */
1079 } OptionTab[] =
1080 {
1081 	"SevenBitInput",	'7',	TRUE,
1082 	"EightBitMode",		'8',	TRUE,
1083 	"AliasFile",		'A',	FALSE,
1084 	"AliasWait",		'a',	FALSE,
1085 	"BlankSub",		'B',	FALSE,
1086 	"MinFreeBlocks",	'b',	TRUE,
1087 	"CheckpointInterval",	'C',	TRUE,
1088 	"HoldExpensive",	'c',	FALSE,
1089 	"AutoRebuildAliases",	'D',	FALSE,
1090 	"DeliveryMode",		'd',	TRUE,
1091 	"ErrorHeader",		'E',	FALSE,
1092 	"ErrorMode",		'e',	TRUE,
1093 	"TempFileMode",		'F',	FALSE,
1094 	"SaveFromLine",		'f',	FALSE,
1095 	"MatchGECOS",		'G',	FALSE,
1096 	"HelpFile",		'H',	FALSE,
1097 	"MaxHopCount",		'h',	FALSE,
1098 	"NameServerOptions",	'I',	FALSE,
1099 	"IgnoreDots",		'i',	TRUE,
1100 	"ForwardPath",		'J',	FALSE,
1101 	"SendMimeErrors",	'j',	TRUE,
1102 	"ConnectionCacheSize",	'k',	FALSE,
1103 	"ConnectionCacheTimeout", 'K',	FALSE,
1104 	"UseErrorsTo",		'l',	FALSE,
1105 	"LogLevel",		'L',	FALSE,
1106 	"MeToo",		'm',	TRUE,
1107 	"CheckAliases",		'n',	FALSE,
1108 	"OldStyleHeaders",	'o',	TRUE,
1109 	"DaemonPortOptions",	'O',	FALSE,
1110 	"PrivacyOptions",	'p',	TRUE,
1111 	"PostmasterCopy",	'P',	FALSE,
1112 	"QueueFactor",		'q',	FALSE,
1113 	"QueueDirectory",	'Q',	FALSE,
1114 	"DontPruneRoutes",	'R',	FALSE,
1115 	"ReadTimeout",		'r',	TRUE,
1116 	"StatusFile",		'S',	FALSE,
1117 	"SuperSafe",		's',	TRUE,
1118 	"QueueTimeout",		'T',	FALSE,
1119 	"TimeZoneSpec",		't',	FALSE,
1120 	"UserDatabaseSpec",	'U',	FALSE,
1121 	"DefaultUser",		'u',	FALSE,
1122 	"FallbackMXhost",	'V',	FALSE,
1123 	"Verbose",		'v',	TRUE,
1124 	"TryNullMXList",	'w',	TRUE,
1125 	"QueueLA",		'x',	FALSE,
1126 	"RefuseLA",		'X',	FALSE,
1127 	"RecipientFactor",	'y',	FALSE,
1128 	"ForkQueueRuns",	'Y',	FALSE,
1129 	"ClassFactor",		'z',	FALSE,
1130 	"TimeFactor",		'Z',	FALSE,
1131 	"BrokenSmtpPeers",	O_BSP,	TRUE,
1132 	"SortQueueByHost",	O_SQBH,	TRUE,
1133 	NULL,			'\0',	FALSE,
1134 };
1135 
1136 
1137 
1138 setoption(opt, val, safe, sticky, e)
1139 	u_char opt;
1140 	char *val;
1141 	bool safe;
1142 	bool sticky;
1143 	register ENVELOPE *e;
1144 {
1145 	register char *p;
1146 	register struct optioninfo *o;
1147 	extern bool atobool();
1148 	extern time_t convtime();
1149 	extern int QueueLA;
1150 	extern int RefuseLA;
1151 	extern bool Warn_Q_option;
1152 	extern bool trusteduser();
1153 
1154 	if (opt == ' ')
1155 	{
1156 		/* full word options */
1157 
1158 		p = strchr(val, '=');
1159 		if (p == NULL)
1160 			p = &val[strlen(val)];
1161 		while (*--p == ' ')
1162 			continue;
1163 		while (*++p == ' ')
1164 			*p = '\0';
1165 		if (*p == '=')
1166 			*p++ = '\0';
1167 		while (*p == ' ')
1168 			p++;
1169 		for (o = OptionTab; o->o_name != NULL; o++)
1170 		{
1171 			if (strcasecmp(o->o_name, val) == 0)
1172 				break;
1173 		}
1174 		if (o->o_name == NULL)
1175 			syserr("readcf: unknown option name %s", val);
1176 		opt = o->o_code;
1177 		val = p;
1178 	}
1179 	else
1180 	{
1181 		for (o = OptionTab; o->o_name != NULL; o++)
1182 		{
1183 			if (o->o_code == opt)
1184 				break;
1185 		}
1186 	}
1187 
1188 	if (tTd(37, 1))
1189 		printf("setoption %s (0x%x)=%s",
1190 			o->o_name == NULL ? "<unknown>" : o->o_name,
1191 			opt, val);
1192 
1193 	/*
1194 	**  See if this option is preset for us.
1195 	*/
1196 
1197 	if (!sticky && bitnset(opt, StickyOpt))
1198 	{
1199 		if (tTd(37, 1))
1200 			printf(" (ignored)\n");
1201 		return;
1202 	}
1203 
1204 	/*
1205 	**  Check to see if this option can be specified by this user.
1206 	*/
1207 
1208 	if (!safe && RealUid == 0)
1209 		safe = TRUE;
1210 	if (!safe && !o->o_safe)
1211 	{
1212 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
1213 		{
1214 			if (tTd(37, 1))
1215 				printf(" (unsafe)");
1216 			if (RealUid != geteuid())
1217 			{
1218 				if (tTd(37, 1))
1219 					printf("(Resetting uid)");
1220 				(void) setgid(RealGid);
1221 				(void) setuid(RealUid);
1222 			}
1223 		}
1224 	}
1225 	if (tTd(37, 1))
1226 		printf("\n");
1227 
1228 	switch (opt & 0xff)
1229 	{
1230 	  case '7':		/* force seven-bit input */
1231 		SevenBitInput = atobool(val);
1232 		break;
1233 
1234 	  case '8':		/* handling of 8-bit input */
1235 		switch (*val)
1236 		{
1237 		  case 'r':		/* reject 8-bit, don't convert MIME */
1238 			MimeMode = 0;
1239 			break;
1240 
1241 		  case 'm':		/* convert 8-bit, convert MIME */
1242 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
1243 			break;
1244 
1245 		  case 'j':		/* "just send 8" */
1246 			MimeMode = MM_PASS8BIT;
1247 			break;
1248 
1249 		  case 'p':		/* pass 8 bit, convert MIME */
1250 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
1251 			break;
1252 
1253 		  case 's':		/* strict adherence */
1254 			MimeMode = MM_CVTMIME;
1255 			break;
1256 
1257 		  case 'a':		/* encode 8 bit if available */
1258 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
1259 			break;
1260 
1261 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
1262 			MimeMode = MM_MIME8BIT;
1263 			break;
1264 
1265 		  default:
1266 			syserr("Unknown 8-bit mode %c", *val);
1267 			exit(EX_USAGE);
1268 		}
1269 		break;
1270 
1271 	  case 'A':		/* set default alias file */
1272 		if (val[0] == '\0')
1273 			setalias("aliases");
1274 		else
1275 			setalias(val);
1276 		break;
1277 
1278 	  case 'a':		/* look N minutes for "@:@" in alias file */
1279 		if (val[0] == '\0')
1280 			SafeAlias = 5 * 60;		/* five minutes */
1281 		else
1282 			SafeAlias = convtime(val, 'm');
1283 		break;
1284 
1285 	  case 'B':		/* substitution for blank character */
1286 		SpaceSub = val[0];
1287 		if (SpaceSub == '\0')
1288 			SpaceSub = ' ';
1289 		break;
1290 
1291 	  case 'b':		/* min blocks free on queue fs/max msg size */
1292 		p = strchr(val, '/');
1293 		if (p != NULL)
1294 		{
1295 			*p++ = '\0';
1296 			MaxMessageSize = atol(p);
1297 		}
1298 		MinBlocksFree = atol(val);
1299 		break;
1300 
1301 	  case 'c':		/* don't connect to "expensive" mailers */
1302 		NoConnect = atobool(val);
1303 		break;
1304 
1305 	  case 'C':		/* checkpoint every N addresses */
1306 		CheckpointInterval = atoi(val);
1307 		break;
1308 
1309 	  case 'd':		/* delivery mode */
1310 		switch (*val)
1311 		{
1312 		  case '\0':
1313 			e->e_sendmode = SM_DELIVER;
1314 			break;
1315 
1316 		  case SM_QUEUE:	/* queue only */
1317 #ifndef QUEUE
1318 			syserr("need QUEUE to set -odqueue");
1319 #endif /* QUEUE */
1320 			/* fall through..... */
1321 
1322 		  case SM_DELIVER:	/* do everything */
1323 		  case SM_FORK:		/* fork after verification */
1324 			e->e_sendmode = *val;
1325 			break;
1326 
1327 		  default:
1328 			syserr("Unknown delivery mode %c", *val);
1329 			exit(EX_USAGE);
1330 		}
1331 		break;
1332 
1333 	  case 'D':		/* rebuild alias database as needed */
1334 		AutoRebuild = atobool(val);
1335 		break;
1336 
1337 	  case 'E':		/* error message header/header file */
1338 		if (*val != '\0')
1339 			ErrMsgFile = newstr(val);
1340 		break;
1341 
1342 	  case 'e':		/* set error processing mode */
1343 		switch (*val)
1344 		{
1345 		  case EM_QUIET:	/* be silent about it */
1346 		  case EM_MAIL:		/* mail back */
1347 		  case EM_BERKNET:	/* do berknet error processing */
1348 		  case EM_WRITE:	/* write back (or mail) */
1349 		  case EM_PRINT:	/* print errors normally (default) */
1350 			e->e_errormode = *val;
1351 			break;
1352 		}
1353 		break;
1354 
1355 	  case 'F':		/* file mode */
1356 		FileMode = atooct(val) & 0777;
1357 		break;
1358 
1359 	  case 'f':		/* save Unix-style From lines on front */
1360 		SaveFrom = atobool(val);
1361 		break;
1362 
1363 	  case 'G':		/* match recipients against GECOS field */
1364 		MatchGecos = atobool(val);
1365 		break;
1366 
1367 	  case 'g':		/* default gid */
1368 		if (isascii(*val) && isdigit(*val))
1369 			DefGid = atoi(val);
1370 		else
1371 		{
1372 			register struct group *gr;
1373 
1374 			DefGid = -1;
1375 			gr = getgrnam(val);
1376 			if (gr == NULL)
1377 				syserr("readcf: option g: unknown group %s", val);
1378 			else
1379 				DefGid = gr->gr_gid;
1380 		}
1381 		break;
1382 
1383 	  case 'H':		/* help file */
1384 		if (val[0] == '\0')
1385 			HelpFile = "sendmail.hf";
1386 		else
1387 			HelpFile = newstr(val);
1388 		break;
1389 
1390 	  case 'h':		/* maximum hop count */
1391 		MaxHopCount = atoi(val);
1392 		break;
1393 
1394 	  case 'I':		/* use internet domain name server */
1395 #if NAMED_BIND
1396 		UseNameServer = TRUE;
1397 		for (p = val; *p != 0; )
1398 		{
1399 			bool clearmode;
1400 			char *q;
1401 			struct resolverflags *rfp;
1402 
1403 			while (*p == ' ')
1404 				p++;
1405 			if (*p == '\0')
1406 				break;
1407 			clearmode = FALSE;
1408 			if (*p == '-')
1409 				clearmode = TRUE;
1410 			else if (*p != '+')
1411 				p--;
1412 			p++;
1413 			q = p;
1414 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1415 				p++;
1416 			if (*p != '\0')
1417 				*p++ = '\0';
1418 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
1419 			{
1420 				if (strcasecmp(q, rfp->rf_name) == 0)
1421 					break;
1422 			}
1423 			if (rfp->rf_name == NULL)
1424 				syserr("readcf: I option value %s unrecognized", q);
1425 			else if (clearmode)
1426 				_res.options &= ~rfp->rf_bits;
1427 			else
1428 				_res.options |= rfp->rf_bits;
1429 		}
1430 		if (tTd(8, 2))
1431 			printf("_res.options = %x\n", _res.options);
1432 #else
1433 		usrerr("name server (I option) specified but BIND not compiled in");
1434 #endif
1435 		break;
1436 
1437 	  case 'i':		/* ignore dot lines in message */
1438 		IgnrDot = atobool(val);
1439 		break;
1440 
1441 	  case 'j':		/* send errors in MIME (RFC 1341) format */
1442 		SendMIMEErrors = atobool(val);
1443 		break;
1444 
1445 	  case 'J':		/* .forward search path */
1446 		ForwardPath = newstr(val);
1447 		break;
1448 
1449 	  case 'k':		/* connection cache size */
1450 		MaxMciCache = atoi(val);
1451 		if (MaxMciCache < 0)
1452 			MaxMciCache = 0;
1453 		break;
1454 
1455 	  case 'K':		/* connection cache timeout */
1456 		MciCacheTimeout = convtime(val, 'm');
1457 		break;
1458 
1459 	  case 'l':		/* use Errors-To: header */
1460 		UseErrorsTo = atobool(val);
1461 		break;
1462 
1463 	  case 'L':		/* log level */
1464 		if (safe || LogLevel < atoi(val))
1465 			LogLevel = atoi(val);
1466 		break;
1467 
1468 	  case 'M':		/* define macro */
1469 		define(val[0], newstr(&val[1]), CurEnv);
1470 		sticky = FALSE;
1471 		break;
1472 
1473 	  case 'm':		/* send to me too */
1474 		MeToo = atobool(val);
1475 		break;
1476 
1477 	  case 'n':		/* validate RHS in newaliases */
1478 		CheckAliases = atobool(val);
1479 		break;
1480 
1481 	    /* 'N' available -- was "net name" */
1482 
1483 	  case 'O':		/* daemon options */
1484 		setdaemonoptions(val);
1485 		break;
1486 
1487 	  case 'o':		/* assume old style headers */
1488 		if (atobool(val))
1489 			CurEnv->e_flags |= EF_OLDSTYLE;
1490 		else
1491 			CurEnv->e_flags &= ~EF_OLDSTYLE;
1492 		break;
1493 
1494 	  case 'p':		/* select privacy level */
1495 		p = val;
1496 		for (;;)
1497 		{
1498 			register struct prival *pv;
1499 			extern struct prival PrivacyValues[];
1500 
1501 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
1502 				p++;
1503 			if (*p == '\0')
1504 				break;
1505 			val = p;
1506 			while (isascii(*p) && isalnum(*p))
1507 				p++;
1508 			if (*p != '\0')
1509 				*p++ = '\0';
1510 
1511 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
1512 			{
1513 				if (strcasecmp(val, pv->pv_name) == 0)
1514 					break;
1515 			}
1516 			if (pv->pv_name == NULL)
1517 				syserr("readcf: Op line: %s unrecognized", val);
1518 			PrivacyFlags |= pv->pv_flag;
1519 		}
1520 		break;
1521 
1522 	  case 'P':		/* postmaster copy address for returned mail */
1523 		PostMasterCopy = newstr(val);
1524 		break;
1525 
1526 	  case 'q':		/* slope of queue only function */
1527 		QueueFactor = atoi(val);
1528 		break;
1529 
1530 	  case 'Q':		/* queue directory */
1531 		if (val[0] == '\0')
1532 			QueueDir = "mqueue";
1533 		else
1534 			QueueDir = newstr(val);
1535 		if (RealUid != 0 && !safe)
1536 			Warn_Q_option = TRUE;
1537 		break;
1538 
1539 	  case 'R':		/* don't prune routes */
1540 		DontPruneRoutes = atobool(val);
1541 		break;
1542 
1543 	  case 'r':		/* read timeout */
1544 		settimeouts(val);
1545 		break;
1546 
1547 	  case 'S':		/* status file */
1548 		if (val[0] == '\0')
1549 			StatFile = "sendmail.st";
1550 		else
1551 			StatFile = newstr(val);
1552 		break;
1553 
1554 	  case 's':		/* be super safe, even if expensive */
1555 		SuperSafe = atobool(val);
1556 		break;
1557 
1558 	  case 'T':		/* queue timeout */
1559 		p = strchr(val, '/');
1560 		if (p != NULL)
1561 		{
1562 			*p++ = '\0';
1563 			TimeOuts.to_q_warning = convtime(p, 'd');
1564 		}
1565 		TimeOuts.to_q_return = convtime(val, 'h');
1566 		break;
1567 
1568 	  case 't':		/* time zone name */
1569 		TimeZoneSpec = newstr(val);
1570 		break;
1571 
1572 	  case 'U':		/* location of user database */
1573 		UdbSpec = newstr(val);
1574 		break;
1575 
1576 	  case 'u':		/* set default uid */
1577 		if (isascii(*val) && isdigit(*val))
1578 			DefUid = atoi(val);
1579 		else
1580 		{
1581 			register struct passwd *pw;
1582 
1583 			DefUid = -1;
1584 			pw = getpwnam(val);
1585 			if (pw == NULL)
1586 				syserr("readcf: option u: unknown user %s", val);
1587 			else
1588 				DefUid = pw->pw_uid;
1589 		}
1590 		setdefuser();
1591 		break;
1592 
1593 	  case 'V':		/* fallback MX host */
1594 		FallBackMX = newstr(val);
1595 		break;
1596 
1597 	  case 'v':		/* run in verbose mode */
1598 		Verbose = atobool(val);
1599 		break;
1600 
1601 	  case 'w':		/* if we are best MX, try host directly */
1602 		TryNullMXList = atobool(val);
1603 		break;
1604 
1605 	    /* 'W' available -- was wizard password */
1606 
1607 	  case 'x':		/* load avg at which to auto-queue msgs */
1608 		QueueLA = atoi(val);
1609 		break;
1610 
1611 	  case 'X':		/* load avg at which to auto-reject connections */
1612 		RefuseLA = atoi(val);
1613 		break;
1614 
1615 	  case 'y':		/* work recipient factor */
1616 		WkRecipFact = atoi(val);
1617 		break;
1618 
1619 	  case 'Y':		/* fork jobs during queue runs */
1620 		ForkQueueRuns = atobool(val);
1621 		break;
1622 
1623 	  case 'z':		/* work message class factor */
1624 		WkClassFact = atoi(val);
1625 		break;
1626 
1627 	  case 'Z':		/* work time factor */
1628 		WkTimeFact = atoi(val);
1629 		break;
1630 
1631 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
1632 		BrokenSmtpPeers = atobool(val);
1633 		break;
1634 
1635 	  case O_SQBH:		/* sort work queue by host first */
1636 		SortQueueByHost = atobool(val);
1637 		break;
1638 
1639 	  default:
1640 		break;
1641 	}
1642 	if (sticky)
1643 		setbitn(opt, StickyOpt);
1644 	return;
1645 }
1646 /*
1647 **  SETCLASS -- set a word into a class
1648 **
1649 **	Parameters:
1650 **		class -- the class to put the word in.
1651 **		word -- the word to enter
1652 **
1653 **	Returns:
1654 **		none.
1655 **
1656 **	Side Effects:
1657 **		puts the word into the symbol table.
1658 */
1659 
1660 setclass(class, word)
1661 	int class;
1662 	char *word;
1663 {
1664 	register STAB *s;
1665 
1666 	if (tTd(37, 8))
1667 		printf("setclass(%c, %s)\n", class, word);
1668 	s = stab(word, ST_CLASS, ST_ENTER);
1669 	setbitn(class, s->s_class);
1670 }
1671 /*
1672 **  MAKEMAPENTRY -- create a map entry
1673 **
1674 **	Parameters:
1675 **		line -- the config file line
1676 **
1677 **	Returns:
1678 **		TRUE if it successfully entered the map entry.
1679 **		FALSE otherwise (usually syntax error).
1680 **
1681 **	Side Effects:
1682 **		Enters the map into the dictionary.
1683 */
1684 
1685 void
1686 makemapentry(line)
1687 	char *line;
1688 {
1689 	register char *p;
1690 	char *mapname;
1691 	char *classname;
1692 	register STAB *s;
1693 	STAB *class;
1694 
1695 	for (p = line; isascii(*p) && isspace(*p); p++)
1696 		continue;
1697 	if (!(isascii(*p) && isalnum(*p)))
1698 	{
1699 		syserr("readcf: config K line: no map name");
1700 		return;
1701 	}
1702 
1703 	mapname = p;
1704 	while (isascii(*++p) && isalnum(*p))
1705 		continue;
1706 	if (*p != '\0')
1707 		*p++ = '\0';
1708 	while (isascii(*p) && isspace(*p))
1709 		p++;
1710 	if (!(isascii(*p) && isalnum(*p)))
1711 	{
1712 		syserr("readcf: config K line, map %s: no map class", mapname);
1713 		return;
1714 	}
1715 	classname = p;
1716 	while (isascii(*++p) && isalnum(*p))
1717 		continue;
1718 	if (*p != '\0')
1719 		*p++ = '\0';
1720 	while (isascii(*p) && isspace(*p))
1721 		p++;
1722 
1723 	/* look up the class */
1724 	class = stab(classname, ST_MAPCLASS, ST_FIND);
1725 	if (class == NULL)
1726 	{
1727 		syserr("readcf: map %s: class %s not available", mapname, classname);
1728 		return;
1729 	}
1730 
1731 	/* enter the map */
1732 	s = stab(mapname, ST_MAP, ST_ENTER);
1733 	s->s_map.map_class = &class->s_mapclass;
1734 	s->s_map.map_mname = newstr(mapname);
1735 
1736 	if (class->s_mapclass.map_parse(&s->s_map, p))
1737 		s->s_map.map_mflags |= MF_VALID;
1738 
1739 	if (tTd(37, 5))
1740 	{
1741 		printf("map %s, class %s, flags %x, file %s,\n",
1742 			s->s_map.map_mname, s->s_map.map_class->map_cname,
1743 			s->s_map.map_mflags,
1744 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
1745 		printf("\tapp %s, domain %s, rebuild %s\n",
1746 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
1747 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
1748 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
1749 	}
1750 }
1751 /*
1752 **  SETTIMEOUTS -- parse and set timeout values
1753 **
1754 **	Parameters:
1755 **		val -- a pointer to the values.  If NULL, do initial
1756 **			settings.
1757 **
1758 **	Returns:
1759 **		none.
1760 **
1761 **	Side Effects:
1762 **		Initializes the TimeOuts structure
1763 */
1764 
1765 #define SECONDS
1766 #define MINUTES	* 60
1767 #define HOUR	* 3600
1768 
1769 settimeouts(val)
1770 	register char *val;
1771 {
1772 	register char *p;
1773 	extern time_t convtime();
1774 
1775 	if (val == NULL)
1776 	{
1777 		TimeOuts.to_initial = (time_t) 5 MINUTES;
1778 		TimeOuts.to_helo = (time_t) 5 MINUTES;
1779 		TimeOuts.to_mail = (time_t) 10 MINUTES;
1780 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
1781 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
1782 		TimeOuts.to_datablock = (time_t) 1 HOUR;
1783 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
1784 		TimeOuts.to_rset = (time_t) 5 MINUTES;
1785 		TimeOuts.to_quit = (time_t) 2 MINUTES;
1786 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
1787 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
1788 		TimeOuts.to_ident = (time_t) 30 SECONDS;
1789 		return;
1790 	}
1791 
1792 	for (;; val = p)
1793 	{
1794 		while (isascii(*val) && isspace(*val))
1795 			val++;
1796 		if (*val == '\0')
1797 			break;
1798 		for (p = val; *p != '\0' && *p != ','; p++)
1799 			continue;
1800 		if (*p != '\0')
1801 			*p++ = '\0';
1802 
1803 		if (isascii(*val) && isdigit(*val))
1804 		{
1805 			/* old syntax -- set everything */
1806 			TimeOuts.to_mail = convtime(val, 'm');
1807 			TimeOuts.to_rcpt = TimeOuts.to_mail;
1808 			TimeOuts.to_datainit = TimeOuts.to_mail;
1809 			TimeOuts.to_datablock = TimeOuts.to_mail;
1810 			TimeOuts.to_datafinal = TimeOuts.to_mail;
1811 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
1812 			continue;
1813 		}
1814 		else
1815 		{
1816 			register char *q = strchr(val, '=');
1817 			time_t to;
1818 
1819 			if (q == NULL)
1820 			{
1821 				/* syntax error */
1822 				continue;
1823 			}
1824 			*q++ = '\0';
1825 			to = convtime(q, 'm');
1826 
1827 			if (strcasecmp(val, "initial") == 0)
1828 				TimeOuts.to_initial = to;
1829 			else if (strcasecmp(val, "mail") == 0)
1830 				TimeOuts.to_mail = to;
1831 			else if (strcasecmp(val, "rcpt") == 0)
1832 				TimeOuts.to_rcpt = to;
1833 			else if (strcasecmp(val, "datainit") == 0)
1834 				TimeOuts.to_datainit = to;
1835 			else if (strcasecmp(val, "datablock") == 0)
1836 				TimeOuts.to_datablock = to;
1837 			else if (strcasecmp(val, "datafinal") == 0)
1838 				TimeOuts.to_datafinal = to;
1839 			else if (strcasecmp(val, "command") == 0)
1840 				TimeOuts.to_nextcommand = to;
1841 			else if (strcasecmp(val, "rset") == 0)
1842 				TimeOuts.to_rset = to;
1843 			else if (strcasecmp(val, "helo") == 0)
1844 				TimeOuts.to_helo = to;
1845 			else if (strcasecmp(val, "quit") == 0)
1846 				TimeOuts.to_quit = to;
1847 			else if (strcasecmp(val, "misc") == 0)
1848 				TimeOuts.to_miscshort = to;
1849 			else if (strcasecmp(val, "ident") == 0)
1850 				TimeOuts.to_ident = to;
1851 			else
1852 				syserr("settimeouts: invalid timeout %s", val);
1853 		}
1854 	}
1855 }
1856