xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 67711)
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.34 (Berkeley) 08/20/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 struct optioninfo
1070 {
1071 	char	*o_name;	/* long name of option */
1072 	char	o_code;		/* short name of option */
1073 	bool	o_safe;		/* safe for random people to use */
1074 } OptionTab[] =
1075 {
1076 	"SevenBitInput",	'7',		TRUE,
1077 	"EightBitMode",		'8',		TRUE,
1078 	"AliasFile",		'A',		FALSE,
1079 	"AliasWait",		'a',		FALSE,
1080 	"BlankSub",		'B',		FALSE,
1081 	"MinFreeBlocks",	'b',		TRUE,
1082 	"CheckpointInterval",	'C',		TRUE,
1083 	"HoldExpensive",	'c',		FALSE,
1084 	"AutoRebuildAliases",	'D',		FALSE,
1085 	"DeliveryMode",		'd',		TRUE,
1086 	"ErrorHeader",		'E',		FALSE,
1087 	"ErrorMode",		'e',		TRUE,
1088 	"TempFileMode",		'F',		FALSE,
1089 	"SaveFromLine",		'f',		FALSE,
1090 	"MatchGECOS",		'G',		FALSE,
1091 	"HelpFile",		'H',		FALSE,
1092 	"MaxHopCount",		'h',		FALSE,
1093 	"NameServerOptions",	'I',		FALSE,
1094 	"IgnoreDots",		'i',		TRUE,
1095 	"ForwardPath",		'J',		FALSE,
1096 	"SendMimeErrors",	'j',		TRUE,
1097 	"ConnectionCacheSize",	'k',		FALSE,
1098 	"ConnectionCacheTimeout", 'K',		FALSE,
1099 	"UseErrorsTo",		'l',		FALSE,
1100 	"LogLevel",		'L',		FALSE,
1101 	"MeToo",		'm',		TRUE,
1102 	"CheckAliases",		'n',		FALSE,
1103 	"OldStyleHeaders",	'o',		TRUE,
1104 	"DaemonPortOptions",	'O',		FALSE,
1105 	"PrivacyOptions",	'p',		TRUE,
1106 	"PostmasterCopy",	'P',		FALSE,
1107 	"QueueFactor",		'q',		FALSE,
1108 	"QueueDirectory",	'Q',		FALSE,
1109 	"DontPruneRoutes",	'R',		FALSE,
1110 	"Timeouts",		'r',		TRUE,
1111 	"StatusFile",		'S',		FALSE,
1112 	"SuperSafe",		's',		TRUE,
1113 	"QueueTimeout",		'T',		FALSE,
1114 	"TimeZoneSpec",		't',		FALSE,
1115 	"UserDatabaseSpec",	'U',		FALSE,
1116 	"DefaultUser",		'u',		FALSE,
1117 	"FallbackMXhost",	'V',		FALSE,
1118 	"Verbose",		'v',		TRUE,
1119 	"TryNullMXList",	'w',		TRUE,
1120 	"QueueLA",		'x',		FALSE,
1121 	"RefuseLA",		'X',		FALSE,
1122 	"RecipientFactor",	'y',		FALSE,
1123 	"ForkQueueRuns",	'Y',		FALSE,
1124 	"ClassFactor",		'z',		FALSE,
1125 	"TimeFactor",		'Z',		FALSE,
1126 #define O_BSP		0x80
1127 	"BrokenSmtpPeers",	O_BSP,		TRUE,
1128 #define O_SQBH		0x81
1129 	"SortQueueByHost",	O_SQBH,		TRUE,
1130 #define O_DNICE		0x82
1131 	"DeliveryNiceness",	O_DNICE,	TRUE,
1132 #define O_MQA		0x83
1133 	"MinQueueAge",		O_MQA,		TRUE,
1134 #define O_MHSA		0x84
1135 	"MaxHostStatAge",	O_MHSA,		TRUE,
1136 
1137 	NULL,			'\0',		FALSE,
1138 };
1139 
1140 
1141 
1142 setoption(opt, val, safe, sticky, e)
1143 	u_char opt;
1144 	char *val;
1145 	bool safe;
1146 	bool sticky;
1147 	register ENVELOPE *e;
1148 {
1149 	register char *p;
1150 	register struct optioninfo *o;
1151 	extern bool atobool();
1152 	extern time_t convtime();
1153 	extern int QueueLA;
1154 	extern int RefuseLA;
1155 	extern bool Warn_Q_option;
1156 	extern bool trusteduser();
1157 
1158 	if (opt == ' ')
1159 	{
1160 		/* full word options */
1161 
1162 		p = strchr(val, '=');
1163 		if (p == NULL)
1164 			p = &val[strlen(val)];
1165 		while (*--p == ' ')
1166 			continue;
1167 		while (*++p == ' ')
1168 			*p = '\0';
1169 		if (*p == '=')
1170 			*p++ = '\0';
1171 		while (*p == ' ')
1172 			p++;
1173 		for (o = OptionTab; o->o_name != NULL; o++)
1174 		{
1175 			if (strcasecmp(o->o_name, val) == 0)
1176 				break;
1177 		}
1178 		if (o->o_name == NULL)
1179 			syserr("readcf: unknown option name %s", val);
1180 		opt = o->o_code;
1181 		val = p;
1182 	}
1183 	else
1184 	{
1185 		for (o = OptionTab; o->o_name != NULL; o++)
1186 		{
1187 			if (o->o_code == opt)
1188 				break;
1189 		}
1190 	}
1191 
1192 	if (tTd(37, 1))
1193 		printf("setoption %s (0x%x)=%s",
1194 			o->o_name == NULL ? "<unknown>" : o->o_name,
1195 			opt, val);
1196 
1197 	/*
1198 	**  See if this option is preset for us.
1199 	*/
1200 
1201 	if (!sticky && bitnset(opt, StickyOpt))
1202 	{
1203 		if (tTd(37, 1))
1204 			printf(" (ignored)\n");
1205 		return;
1206 	}
1207 
1208 	/*
1209 	**  Check to see if this option can be specified by this user.
1210 	*/
1211 
1212 	if (!safe && RealUid == 0)
1213 		safe = TRUE;
1214 	if (!safe && !o->o_safe)
1215 	{
1216 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
1217 		{
1218 			if (tTd(37, 1))
1219 				printf(" (unsafe)");
1220 			if (RealUid != geteuid())
1221 			{
1222 				if (tTd(37, 1))
1223 					printf("(Resetting uid)");
1224 				(void) setgid(RealGid);
1225 				(void) setuid(RealUid);
1226 			}
1227 		}
1228 	}
1229 	if (tTd(37, 1))
1230 		printf("\n");
1231 
1232 	switch (opt & 0xff)
1233 	{
1234 	  case '7':		/* force seven-bit input */
1235 		SevenBitInput = atobool(val);
1236 		break;
1237 
1238 	  case '8':		/* handling of 8-bit input */
1239 		switch (*val)
1240 		{
1241 		  case 'r':		/* reject 8-bit, don't convert MIME */
1242 			MimeMode = 0;
1243 			break;
1244 
1245 		  case 'm':		/* convert 8-bit, convert MIME */
1246 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
1247 			break;
1248 
1249 		  case 'j':		/* "just send 8" */
1250 			MimeMode = MM_PASS8BIT;
1251 			break;
1252 
1253 		  case 'p':		/* pass 8 bit, convert MIME */
1254 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
1255 			break;
1256 
1257 		  case 's':		/* strict adherence */
1258 			MimeMode = MM_CVTMIME;
1259 			break;
1260 
1261 		  case 'a':		/* encode 8 bit if available */
1262 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
1263 			break;
1264 
1265 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
1266 			MimeMode = MM_MIME8BIT;
1267 			break;
1268 
1269 		  default:
1270 			syserr("Unknown 8-bit mode %c", *val);
1271 			exit(EX_USAGE);
1272 		}
1273 		break;
1274 
1275 	  case 'A':		/* set default alias file */
1276 		if (val[0] == '\0')
1277 			setalias("aliases");
1278 		else
1279 			setalias(val);
1280 		break;
1281 
1282 	  case 'a':		/* look N minutes for "@:@" in alias file */
1283 		if (val[0] == '\0')
1284 			SafeAlias = 5 * 60;		/* five minutes */
1285 		else
1286 			SafeAlias = convtime(val, 'm');
1287 		break;
1288 
1289 	  case 'B':		/* substitution for blank character */
1290 		SpaceSub = val[0];
1291 		if (SpaceSub == '\0')
1292 			SpaceSub = ' ';
1293 		break;
1294 
1295 	  case 'b':		/* min blocks free on queue fs/max msg size */
1296 		p = strchr(val, '/');
1297 		if (p != NULL)
1298 		{
1299 			*p++ = '\0';
1300 			MaxMessageSize = atol(p);
1301 		}
1302 		MinBlocksFree = atol(val);
1303 		break;
1304 
1305 	  case 'c':		/* don't connect to "expensive" mailers */
1306 		NoConnect = atobool(val);
1307 		break;
1308 
1309 	  case 'C':		/* checkpoint every N addresses */
1310 		CheckpointInterval = atoi(val);
1311 		break;
1312 
1313 	  case 'd':		/* delivery mode */
1314 		switch (*val)
1315 		{
1316 		  case '\0':
1317 			e->e_sendmode = SM_DELIVER;
1318 			break;
1319 
1320 		  case SM_QUEUE:	/* queue only */
1321 #ifndef QUEUE
1322 			syserr("need QUEUE to set -odqueue");
1323 #endif /* QUEUE */
1324 			/* fall through..... */
1325 
1326 		  case SM_DELIVER:	/* do everything */
1327 		  case SM_FORK:		/* fork after verification */
1328 			e->e_sendmode = *val;
1329 			break;
1330 
1331 		  default:
1332 			syserr("Unknown delivery mode %c", *val);
1333 			exit(EX_USAGE);
1334 		}
1335 		break;
1336 
1337 	  case 'D':		/* rebuild alias database as needed */
1338 		AutoRebuild = atobool(val);
1339 		break;
1340 
1341 	  case 'E':		/* error message header/header file */
1342 		if (*val != '\0')
1343 			ErrMsgFile = newstr(val);
1344 		break;
1345 
1346 	  case 'e':		/* set error processing mode */
1347 		switch (*val)
1348 		{
1349 		  case EM_QUIET:	/* be silent about it */
1350 		  case EM_MAIL:		/* mail back */
1351 		  case EM_BERKNET:	/* do berknet error processing */
1352 		  case EM_WRITE:	/* write back (or mail) */
1353 		  case EM_PRINT:	/* print errors normally (default) */
1354 			e->e_errormode = *val;
1355 			break;
1356 		}
1357 		break;
1358 
1359 	  case 'F':		/* file mode */
1360 		FileMode = atooct(val) & 0777;
1361 		break;
1362 
1363 	  case 'f':		/* save Unix-style From lines on front */
1364 		SaveFrom = atobool(val);
1365 		break;
1366 
1367 	  case 'G':		/* match recipients against GECOS field */
1368 		MatchGecos = atobool(val);
1369 		break;
1370 
1371 	  case 'g':		/* default gid */
1372 		if (isascii(*val) && isdigit(*val))
1373 			DefGid = atoi(val);
1374 		else
1375 		{
1376 			register struct group *gr;
1377 
1378 			DefGid = -1;
1379 			gr = getgrnam(val);
1380 			if (gr == NULL)
1381 				syserr("readcf: option g: unknown group %s", val);
1382 			else
1383 				DefGid = gr->gr_gid;
1384 		}
1385 		break;
1386 
1387 	  case 'H':		/* help file */
1388 		if (val[0] == '\0')
1389 			HelpFile = "sendmail.hf";
1390 		else
1391 			HelpFile = newstr(val);
1392 		break;
1393 
1394 	  case 'h':		/* maximum hop count */
1395 		MaxHopCount = atoi(val);
1396 		break;
1397 
1398 	  case 'I':		/* use internet domain name server */
1399 #if NAMED_BIND
1400 		UseNameServer = TRUE;
1401 		for (p = val; *p != 0; )
1402 		{
1403 			bool clearmode;
1404 			char *q;
1405 			struct resolverflags *rfp;
1406 
1407 			while (*p == ' ')
1408 				p++;
1409 			if (*p == '\0')
1410 				break;
1411 			clearmode = FALSE;
1412 			if (*p == '-')
1413 				clearmode = TRUE;
1414 			else if (*p != '+')
1415 				p--;
1416 			p++;
1417 			q = p;
1418 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1419 				p++;
1420 			if (*p != '\0')
1421 				*p++ = '\0';
1422 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
1423 			{
1424 				if (strcasecmp(q, rfp->rf_name) == 0)
1425 					break;
1426 			}
1427 			if (rfp->rf_name == NULL)
1428 				syserr("readcf: I option value %s unrecognized", q);
1429 			else if (clearmode)
1430 				_res.options &= ~rfp->rf_bits;
1431 			else
1432 				_res.options |= rfp->rf_bits;
1433 		}
1434 		if (tTd(8, 2))
1435 			printf("_res.options = %x\n", _res.options);
1436 #else
1437 		usrerr("name server (I option) specified but BIND not compiled in");
1438 #endif
1439 		break;
1440 
1441 	  case 'i':		/* ignore dot lines in message */
1442 		IgnrDot = atobool(val);
1443 		break;
1444 
1445 	  case 'j':		/* send errors in MIME (RFC 1341) format */
1446 		SendMIMEErrors = atobool(val);
1447 		break;
1448 
1449 	  case 'J':		/* .forward search path */
1450 		ForwardPath = newstr(val);
1451 		break;
1452 
1453 	  case 'k':		/* connection cache size */
1454 		MaxMciCache = atoi(val);
1455 		if (MaxMciCache < 0)
1456 			MaxMciCache = 0;
1457 		break;
1458 
1459 	  case 'K':		/* connection cache timeout */
1460 		MciCacheTimeout = convtime(val, 'm');
1461 		break;
1462 
1463 	  case 'l':		/* use Errors-To: header */
1464 		UseErrorsTo = atobool(val);
1465 		break;
1466 
1467 	  case 'L':		/* log level */
1468 		if (safe || LogLevel < atoi(val))
1469 			LogLevel = atoi(val);
1470 		break;
1471 
1472 	  case 'M':		/* define macro */
1473 		define(val[0], newstr(&val[1]), CurEnv);
1474 		sticky = FALSE;
1475 		break;
1476 
1477 	  case 'm':		/* send to me too */
1478 		MeToo = atobool(val);
1479 		break;
1480 
1481 	  case 'n':		/* validate RHS in newaliases */
1482 		CheckAliases = atobool(val);
1483 		break;
1484 
1485 	    /* 'N' available -- was "net name" */
1486 
1487 	  case 'O':		/* daemon options */
1488 		setdaemonoptions(val);
1489 		break;
1490 
1491 	  case 'o':		/* assume old style headers */
1492 		if (atobool(val))
1493 			CurEnv->e_flags |= EF_OLDSTYLE;
1494 		else
1495 			CurEnv->e_flags &= ~EF_OLDSTYLE;
1496 		break;
1497 
1498 	  case 'p':		/* select privacy level */
1499 		p = val;
1500 		for (;;)
1501 		{
1502 			register struct prival *pv;
1503 			extern struct prival PrivacyValues[];
1504 
1505 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
1506 				p++;
1507 			if (*p == '\0')
1508 				break;
1509 			val = p;
1510 			while (isascii(*p) && isalnum(*p))
1511 				p++;
1512 			if (*p != '\0')
1513 				*p++ = '\0';
1514 
1515 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
1516 			{
1517 				if (strcasecmp(val, pv->pv_name) == 0)
1518 					break;
1519 			}
1520 			if (pv->pv_name == NULL)
1521 				syserr("readcf: Op line: %s unrecognized", val);
1522 			PrivacyFlags |= pv->pv_flag;
1523 		}
1524 		break;
1525 
1526 	  case 'P':		/* postmaster copy address for returned mail */
1527 		PostMasterCopy = newstr(val);
1528 		break;
1529 
1530 	  case 'q':		/* slope of queue only function */
1531 		QueueFactor = atoi(val);
1532 		break;
1533 
1534 	  case 'Q':		/* queue directory */
1535 		if (val[0] == '\0')
1536 			QueueDir = "mqueue";
1537 		else
1538 			QueueDir = newstr(val);
1539 		if (RealUid != 0 && !safe)
1540 			Warn_Q_option = TRUE;
1541 		break;
1542 
1543 	  case 'R':		/* don't prune routes */
1544 		DontPruneRoutes = atobool(val);
1545 		break;
1546 
1547 	  case 'r':		/* read timeout */
1548 		settimeouts(val);
1549 		break;
1550 
1551 	  case 'S':		/* status file */
1552 		if (val[0] == '\0')
1553 			StatFile = "sendmail.st";
1554 		else
1555 			StatFile = newstr(val);
1556 		break;
1557 
1558 	  case 's':		/* be super safe, even if expensive */
1559 		SuperSafe = atobool(val);
1560 		break;
1561 
1562 	  case 'T':		/* queue timeout */
1563 		p = strchr(val, '/');
1564 		if (p != NULL)
1565 		{
1566 			*p++ = '\0';
1567 			TimeOuts.to_q_warning = convtime(p, 'd');
1568 		}
1569 		TimeOuts.to_q_return = convtime(val, 'h');
1570 		break;
1571 
1572 	  case 't':		/* time zone name */
1573 		TimeZoneSpec = newstr(val);
1574 		break;
1575 
1576 	  case 'U':		/* location of user database */
1577 		UdbSpec = newstr(val);
1578 		break;
1579 
1580 	  case 'u':		/* set default uid */
1581 		if (isascii(*val) && isdigit(*val))
1582 			DefUid = atoi(val);
1583 		else
1584 		{
1585 			register struct passwd *pw;
1586 
1587 			DefUid = -1;
1588 			pw = getpwnam(val);
1589 			if (pw == NULL)
1590 				syserr("readcf: option u: unknown user %s", val);
1591 			else
1592 				DefUid = pw->pw_uid;
1593 		}
1594 		setdefuser();
1595 		break;
1596 
1597 	  case 'V':		/* fallback MX host */
1598 		FallBackMX = newstr(val);
1599 		break;
1600 
1601 	  case 'v':		/* run in verbose mode */
1602 		Verbose = atobool(val);
1603 		break;
1604 
1605 	  case 'w':		/* if we are best MX, try host directly */
1606 		TryNullMXList = atobool(val);
1607 		break;
1608 
1609 	    /* 'W' available -- was wizard password */
1610 
1611 	  case 'x':		/* load avg at which to auto-queue msgs */
1612 		QueueLA = atoi(val);
1613 		break;
1614 
1615 	  case 'X':		/* load avg at which to auto-reject connections */
1616 		RefuseLA = atoi(val);
1617 		break;
1618 
1619 	  case 'y':		/* work recipient factor */
1620 		WkRecipFact = atoi(val);
1621 		break;
1622 
1623 	  case 'Y':		/* fork jobs during queue runs */
1624 		ForkQueueRuns = atobool(val);
1625 		break;
1626 
1627 	  case 'z':		/* work message class factor */
1628 		WkClassFact = atoi(val);
1629 		break;
1630 
1631 	  case 'Z':		/* work time factor */
1632 		WkTimeFact = atoi(val);
1633 		break;
1634 
1635 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
1636 		BrokenSmtpPeers = atobool(val);
1637 		break;
1638 
1639 	  case O_SQBH:		/* sort work queue by host first */
1640 		SortQueueByHost = atobool(val);
1641 		break;
1642 
1643 	  case O_DNICE:		/* delivery nice value */
1644 		DeliveryNiceness = atoi(val);
1645 		break;
1646 
1647 	  case O_MQA:		/* minimum queue age between deliveries */
1648 		MinQueueAge = convtime(val, 'm');
1649 		break;
1650 
1651 	  case O_MHSA:		/* maximum age of cached host status */
1652 		MaxHostStatAge = convtime(val, 'm');
1653 		break;
1654 
1655 	  default:
1656 		break;
1657 	}
1658 	if (sticky)
1659 		setbitn(opt, StickyOpt);
1660 	return;
1661 }
1662 /*
1663 **  SETCLASS -- set a word into a class
1664 **
1665 **	Parameters:
1666 **		class -- the class to put the word in.
1667 **		word -- the word to enter
1668 **
1669 **	Returns:
1670 **		none.
1671 **
1672 **	Side Effects:
1673 **		puts the word into the symbol table.
1674 */
1675 
1676 setclass(class, word)
1677 	int class;
1678 	char *word;
1679 {
1680 	register STAB *s;
1681 
1682 	if (tTd(37, 8))
1683 		printf("setclass(%c, %s)\n", class, word);
1684 	s = stab(word, ST_CLASS, ST_ENTER);
1685 	setbitn(class, s->s_class);
1686 }
1687 /*
1688 **  MAKEMAPENTRY -- create a map entry
1689 **
1690 **	Parameters:
1691 **		line -- the config file line
1692 **
1693 **	Returns:
1694 **		TRUE if it successfully entered the map entry.
1695 **		FALSE otherwise (usually syntax error).
1696 **
1697 **	Side Effects:
1698 **		Enters the map into the dictionary.
1699 */
1700 
1701 void
1702 makemapentry(line)
1703 	char *line;
1704 {
1705 	register char *p;
1706 	char *mapname;
1707 	char *classname;
1708 	register STAB *s;
1709 	STAB *class;
1710 
1711 	for (p = line; isascii(*p) && isspace(*p); p++)
1712 		continue;
1713 	if (!(isascii(*p) && isalnum(*p)))
1714 	{
1715 		syserr("readcf: config K line: no map name");
1716 		return;
1717 	}
1718 
1719 	mapname = p;
1720 	while (isascii(*++p) && isalnum(*p))
1721 		continue;
1722 	if (*p != '\0')
1723 		*p++ = '\0';
1724 	while (isascii(*p) && isspace(*p))
1725 		p++;
1726 	if (!(isascii(*p) && isalnum(*p)))
1727 	{
1728 		syserr("readcf: config K line, map %s: no map class", mapname);
1729 		return;
1730 	}
1731 	classname = p;
1732 	while (isascii(*++p) && isalnum(*p))
1733 		continue;
1734 	if (*p != '\0')
1735 		*p++ = '\0';
1736 	while (isascii(*p) && isspace(*p))
1737 		p++;
1738 
1739 	/* look up the class */
1740 	class = stab(classname, ST_MAPCLASS, ST_FIND);
1741 	if (class == NULL)
1742 	{
1743 		syserr("readcf: map %s: class %s not available", mapname, classname);
1744 		return;
1745 	}
1746 
1747 	/* enter the map */
1748 	s = stab(mapname, ST_MAP, ST_ENTER);
1749 	s->s_map.map_class = &class->s_mapclass;
1750 	s->s_map.map_mname = newstr(mapname);
1751 
1752 	if (class->s_mapclass.map_parse(&s->s_map, p))
1753 		s->s_map.map_mflags |= MF_VALID;
1754 
1755 	if (tTd(37, 5))
1756 	{
1757 		printf("map %s, class %s, flags %x, file %s,\n",
1758 			s->s_map.map_mname, s->s_map.map_class->map_cname,
1759 			s->s_map.map_mflags,
1760 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
1761 		printf("\tapp %s, domain %s, rebuild %s\n",
1762 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
1763 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
1764 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
1765 	}
1766 }
1767 /*
1768 **  SETTIMEOUTS -- parse and set timeout values
1769 **
1770 **	Parameters:
1771 **		val -- a pointer to the values.  If NULL, do initial
1772 **			settings.
1773 **
1774 **	Returns:
1775 **		none.
1776 **
1777 **	Side Effects:
1778 **		Initializes the TimeOuts structure
1779 */
1780 
1781 #define SECONDS
1782 #define MINUTES	* 60
1783 #define HOUR	* 3600
1784 
1785 settimeouts(val)
1786 	register char *val;
1787 {
1788 	register char *p;
1789 	extern time_t convtime();
1790 
1791 	if (val == NULL)
1792 	{
1793 		TimeOuts.to_initial = (time_t) 5 MINUTES;
1794 		TimeOuts.to_helo = (time_t) 5 MINUTES;
1795 		TimeOuts.to_mail = (time_t) 10 MINUTES;
1796 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
1797 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
1798 		TimeOuts.to_datablock = (time_t) 1 HOUR;
1799 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
1800 		TimeOuts.to_rset = (time_t) 5 MINUTES;
1801 		TimeOuts.to_quit = (time_t) 2 MINUTES;
1802 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
1803 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
1804 		TimeOuts.to_ident = (time_t) 30 SECONDS;
1805 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
1806 		return;
1807 	}
1808 
1809 	for (;; val = p)
1810 	{
1811 		while (isascii(*val) && isspace(*val))
1812 			val++;
1813 		if (*val == '\0')
1814 			break;
1815 		for (p = val; *p != '\0' && *p != ','; p++)
1816 			continue;
1817 		if (*p != '\0')
1818 			*p++ = '\0';
1819 
1820 		if (isascii(*val) && isdigit(*val))
1821 		{
1822 			/* old syntax -- set everything */
1823 			TimeOuts.to_mail = convtime(val, 'm');
1824 			TimeOuts.to_rcpt = TimeOuts.to_mail;
1825 			TimeOuts.to_datainit = TimeOuts.to_mail;
1826 			TimeOuts.to_datablock = TimeOuts.to_mail;
1827 			TimeOuts.to_datafinal = TimeOuts.to_mail;
1828 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
1829 			continue;
1830 		}
1831 		else
1832 		{
1833 			register char *q = strchr(val, ':');
1834 			time_t to;
1835 
1836 			if (q == NULL && (q = strchr(val, '=')) == NULL)
1837 			{
1838 				/* syntax error */
1839 				continue;
1840 			}
1841 			*q++ = '\0';
1842 			to = convtime(q, 'm');
1843 
1844 			if (strcasecmp(val, "initial") == 0)
1845 				TimeOuts.to_initial = to;
1846 			else if (strcasecmp(val, "mail") == 0)
1847 				TimeOuts.to_mail = to;
1848 			else if (strcasecmp(val, "rcpt") == 0)
1849 				TimeOuts.to_rcpt = to;
1850 			else if (strcasecmp(val, "datainit") == 0)
1851 				TimeOuts.to_datainit = to;
1852 			else if (strcasecmp(val, "datablock") == 0)
1853 				TimeOuts.to_datablock = to;
1854 			else if (strcasecmp(val, "datafinal") == 0)
1855 				TimeOuts.to_datafinal = to;
1856 			else if (strcasecmp(val, "command") == 0)
1857 				TimeOuts.to_nextcommand = to;
1858 			else if (strcasecmp(val, "rset") == 0)
1859 				TimeOuts.to_rset = to;
1860 			else if (strcasecmp(val, "helo") == 0)
1861 				TimeOuts.to_helo = to;
1862 			else if (strcasecmp(val, "quit") == 0)
1863 				TimeOuts.to_quit = to;
1864 			else if (strcasecmp(val, "misc") == 0)
1865 				TimeOuts.to_miscshort = to;
1866 			else if (strcasecmp(val, "ident") == 0)
1867 				TimeOuts.to_ident = to;
1868 			else if (strcasecmp(val, "fileopen") == 0)
1869 				TimeOuts.to_fileopen = to;
1870 			else if (strcasecmp(val, "queuewarn") == 0)
1871 				TimeOuts.to_q_warning = to;
1872 			else if (strcasecmp(val, "queuereturn") == 0)
1873 				TimeOuts.to_q_return = to;
1874 			else
1875 				syserr("settimeouts: invalid timeout %s", val);
1876 		}
1877 	}
1878 }
1879