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