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