xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 68267)
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.63 (Berkeley) 02/10/95";
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 			p = &buf[1];
479 			while (*p != '\0')
480 			{
481 				while (isspace(*p))
482 					p++;
483 				q = p;
484 				while (*p != '\0' && !isspace(*p))
485 					p++;
486 				if (*p != '\0')
487 					*p++ = '\0';
488 				if (*q == '\0')
489 					continue;
490 				(void) stab(q, ST_TRUSTED, ST_ENTER);
491 			}
492 			break;
493 
494 		  case 'V':		/* configuration syntax version */
495 			for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
496 				continue;
497 			if (!isascii(*p) || !isdigit(*p))
498 			{
499 				syserr("invalid argument to V line: \"%.20s\"",
500 					&bp[1]);
501 				break;
502 			}
503 			ConfigLevel = strtol(p, &ep, 10);
504 			if (ConfigLevel >= 5)
505 			{
506 				/* level 5 configs have short name in $w */
507 				p = macvalue('w', e);
508 				if (p != NULL && (p = strchr(p, '.')) != NULL)
509 					*p = '\0';
510 			}
511 			if (*ep++ == '/')
512 			{
513 				/* extract vendor code */
514 				for (p = ep; isascii(*p) && isalpha(*p); )
515 					p++;
516 				*p = '\0';
517 
518 				if (!setvendor(ep))
519 					syserr("invalid V line vendor code: \"%s\"",
520 						ep);
521 			}
522 			break;
523 
524 		  case 'K':
525 			makemapentry(&bp[1]);
526 			break;
527 
528 		  default:
529 		  badline:
530 			syserr("unknown control line \"%s\"", bp);
531 		}
532 		if (bp != buf)
533 			free(bp);
534 	}
535 	if (ferror(cf))
536 	{
537 		syserr("I/O read error", cfname);
538 		exit(EX_OSFILE);
539 	}
540 	fclose(cf);
541 	FileName = NULL;
542 
543 	/* initialize host maps from local service tables */
544 	inithostmaps();
545 
546 	/* determine if we need to do special name-server frotz */
547 	{
548 		int nmaps;
549 		char *maptype[MAXMAPSTACK];
550 		short mapreturn[MAXMAPACTIONS];
551 
552 		nmaps = switch_map_find("hosts", maptype, mapreturn);
553 		UseNameServer = FALSE;
554 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
555 		{
556 			register int mapno;
557 
558 			for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
559 			{
560 				if (strcmp(maptype[mapno], "dns") == 0)
561 					UseNameServer = TRUE;
562 			}
563 		}
564 
565 #ifdef HESIOD
566 		nmaps = switch_map_find("passwd", maptype, mapreturn);
567 		UseHesiod = FALSE;
568 		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
569 		{
570 			register int mapno;
571 
572 			for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
573 			{
574 				if (strcmp(maptype[mapno], "hesiod") == 0)
575 					UseHesiod = TRUE;
576 			}
577 		}
578 #endif
579 	}
580 }
581 /*
582 **  TOOMANY -- signal too many of some option
583 **
584 **	Parameters:
585 **		id -- the id of the error line
586 **		maxcnt -- the maximum possible values
587 **
588 **	Returns:
589 **		none.
590 **
591 **	Side Effects:
592 **		gives a syserr.
593 */
594 
595 toomany(id, maxcnt)
596 	char id;
597 	int maxcnt;
598 {
599 	syserr("too many %c lines, %d max", id, maxcnt);
600 }
601 /*
602 **  FILECLASS -- read members of a class from a file
603 **
604 **	Parameters:
605 **		class -- class to define.
606 **		filename -- name of file to read.
607 **		fmt -- scanf string to use for match.
608 **		safe -- if set, this is a safe read.
609 **		optional -- if set, it is not an error for the file to
610 **			not exist.
611 **
612 **	Returns:
613 **		none
614 **
615 **	Side Effects:
616 **
617 **		puts all lines in filename that match a scanf into
618 **			the named class.
619 */
620 
621 fileclass(class, filename, fmt, safe, optional)
622 	int class;
623 	char *filename;
624 	char *fmt;
625 	bool safe;
626 	bool optional;
627 {
628 	FILE *f;
629 	struct stat stbuf;
630 	char buf[MAXLINE];
631 
632 	if (tTd(37, 2))
633 		printf("fileclass(%s, fmt=%s)\n", filename, fmt);
634 
635 	if (filename[0] == '|')
636 	{
637 		syserr("fileclass: pipes (F%c%s) not supported due to security problems",
638 			class, filename);
639 		return;
640 	}
641 	if (stat(filename, &stbuf) < 0)
642 	{
643 		if (tTd(37, 2))
644 			printf("  cannot stat (%s)\n", errstring(errno));
645 		if (!optional)
646 			syserr("fileclass: cannot stat %s", filename);
647 		return;
648 	}
649 	if (!S_ISREG(stbuf.st_mode))
650 	{
651 		syserr("fileclass: %s not a regular file", filename);
652 		return;
653 	}
654 	if (!safe && access(filename, R_OK) < 0)
655 	{
656 		syserr("fileclass: access denied on %s", filename);
657 		return;
658 	}
659 	f = fopen(filename, "r");
660 	if (f == NULL)
661 	{
662 		syserr("fileclass: cannot open %s", filename);
663 		return;
664 	}
665 
666 	while (fgets(buf, sizeof buf, f) != NULL)
667 	{
668 		register STAB *s;
669 		register char *p;
670 # ifdef SCANF
671 		char wordbuf[MAXNAME+1];
672 
673 		if (sscanf(buf, fmt, wordbuf) != 1)
674 			continue;
675 		p = wordbuf;
676 # else /* SCANF */
677 		p = buf;
678 # endif /* SCANF */
679 
680 		/*
681 		**  Break up the match into words.
682 		*/
683 
684 		while (*p != '\0')
685 		{
686 			register char *q;
687 
688 			/* strip leading spaces */
689 			while (isascii(*p) && isspace(*p))
690 				p++;
691 			if (*p == '\0')
692 				break;
693 
694 			/* find the end of the word */
695 			q = p;
696 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
697 				p++;
698 			if (*p != '\0')
699 				*p++ = '\0';
700 
701 			/* enter the word in the symbol table */
702 			setclass(class, q);
703 		}
704 	}
705 
706 	(void) fclose(f);
707 }
708 /*
709 **  MAKEMAILER -- define a new mailer.
710 **
711 **	Parameters:
712 **		line -- description of mailer.  This is in labeled
713 **			fields.  The fields are:
714 **			   A -- the argv for this mailer
715 **			   C -- the character set for MIME conversions
716 **			   D -- the directory to run in
717 **			   E -- the eol string
718 **			   F -- the flags associated with the mailer
719 **			   L -- the maximum line length
720 **			   M -- the maximum message size
721 **			   P -- the path to the mailer
722 **			   R -- the recipient rewriting set
723 **			   S -- the sender rewriting set
724 **			   T -- the mailer type (for DSNs)
725 **			   U -- the uid to run as
726 **			The first word is the canonical name of the mailer.
727 **
728 **	Returns:
729 **		none.
730 **
731 **	Side Effects:
732 **		enters the mailer into the mailer table.
733 */
734 
735 makemailer(line)
736 	char *line;
737 {
738 	register char *p;
739 	register struct mailer *m;
740 	register STAB *s;
741 	int i;
742 	char fcode;
743 	auto char *endp;
744 	extern int NextMailer;
745 	extern char **makeargv();
746 	extern char *munchstring();
747 	extern long atol();
748 
749 	/* allocate a mailer and set up defaults */
750 	m = (struct mailer *) xalloc(sizeof *m);
751 	bzero((char *) m, sizeof *m);
752 	m->m_eol = "\n";
753 	m->m_uid = m->m_gid = 0;
754 
755 	/* collect the mailer name */
756 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
757 		continue;
758 	if (*p != '\0')
759 		*p++ = '\0';
760 	m->m_name = newstr(line);
761 
762 	/* now scan through and assign info from the fields */
763 	while (*p != '\0')
764 	{
765 		auto char *delimptr;
766 
767 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
768 			p++;
769 
770 		/* p now points to field code */
771 		fcode = *p;
772 		while (*p != '\0' && *p != '=' && *p != ',')
773 			p++;
774 		if (*p++ != '=')
775 		{
776 			syserr("mailer %s: `=' expected", m->m_name);
777 			return;
778 		}
779 		while (isascii(*p) && isspace(*p))
780 			p++;
781 
782 		/* p now points to the field body */
783 		p = munchstring(p, &delimptr);
784 
785 		/* install the field into the mailer struct */
786 		switch (fcode)
787 		{
788 		  case 'P':		/* pathname */
789 			m->m_mailer = newstr(p);
790 			break;
791 
792 		  case 'F':		/* flags */
793 			for (; *p != '\0'; p++)
794 				if (!(isascii(*p) && isspace(*p)))
795 					setbitn(*p, m->m_flags);
796 			break;
797 
798 		  case 'S':		/* sender rewriting ruleset */
799 		  case 'R':		/* recipient rewriting ruleset */
800 			i = strtol(p, &endp, 10);
801 			if (i < 0 || i >= MAXRWSETS)
802 			{
803 				syserr("invalid rewrite set, %d max", MAXRWSETS);
804 				return;
805 			}
806 			if (fcode == 'S')
807 				m->m_sh_rwset = m->m_se_rwset = i;
808 			else
809 				m->m_rh_rwset = m->m_re_rwset = i;
810 
811 			p = endp;
812 			if (*p++ == '/')
813 			{
814 				i = strtol(p, NULL, 10);
815 				if (i < 0 || i >= MAXRWSETS)
816 				{
817 					syserr("invalid rewrite set, %d max",
818 						MAXRWSETS);
819 					return;
820 				}
821 				if (fcode == 'S')
822 					m->m_sh_rwset = i;
823 				else
824 					m->m_rh_rwset = i;
825 			}
826 			break;
827 
828 		  case 'E':		/* end of line string */
829 			m->m_eol = newstr(p);
830 			break;
831 
832 		  case 'A':		/* argument vector */
833 			m->m_argv = makeargv(p);
834 			break;
835 
836 		  case 'M':		/* maximum message size */
837 			m->m_maxsize = atol(p);
838 			break;
839 
840 		  case 'L':		/* maximum line length */
841 			m->m_linelimit = atoi(p);
842 			break;
843 
844 		  case 'D':		/* working directory */
845 			m->m_execdir = newstr(p);
846 			break;
847 
848 		  case 'C':		/* default charset */
849 			m->m_defcharset = newstr(p);
850 			break;
851 
852 		  case 'T':		/* MTA Type */
853 			m->m_mtatype = newstr(p);
854 			p = strchr(m->m_mtatype, '/');
855 			if (p != NULL)
856 			{
857 				*p++ = '\0';
858 				if (*p == '\0')
859 					p = NULL;
860 			}
861 			if (p == NULL)
862 				m->m_addrtype = m->m_mtatype;
863 			else
864 			{
865 				m->m_addrtype = p;
866 				p = strchr(p, '/');
867 			}
868 			if (p != NULL)
869 			{
870 				*p++ = '\0';
871 				if (*p == '\0')
872 					p = NULL;
873 			}
874 			if (p == NULL)
875 				m->m_diagtype = m->m_mtatype;
876 			else
877 				m->m_diagtype = p;
878 			break;
879 
880 		  case 'U':		/* user id */
881 			if (isascii(*p) && !isdigit(*p))
882 			{
883 				char *q = p;
884 				struct passwd *pw;
885 
886 				while (isascii(*p) && isalnum(*p))
887 					p++;
888 				while (isascii(*p) && isspace(*p))
889 					*p++ = '\0';
890 				if (*p != '\0')
891 					*p++ = '\0';
892 				pw = getpwnam(q);
893 				if (pw == NULL)
894 					syserr("readcf: mailer U= flag: unknown user %s", q);
895 				else
896 				{
897 					m->m_uid = pw->pw_uid;
898 					m->m_gid = pw->pw_gid;
899 				}
900 			}
901 			else
902 			{
903 				auto char *q;
904 
905 				m->m_uid = strtol(p, &q, 0);
906 				p = q;
907 			}
908 			while (isascii(*p) && isspace(*p))
909 				p++;
910 			if (*p == '\0')
911 				break;
912 			if (isascii(*p) && !isdigit(*p))
913 			{
914 				char *q = p;
915 				struct group *gr;
916 
917 				while (isascii(*p) && isalnum(*p))
918 					p++;
919 				*p++ = '\0';
920 				gr = getgrnam(q);
921 				if (gr == NULL)
922 					syserr("readcf: mailer U= flag: unknown group %s", q);
923 				else
924 					m->m_gid = gr->gr_gid;
925 			}
926 			else
927 			{
928 				m->m_gid = strtol(p, NULL, 0);
929 			}
930 			break;
931 		}
932 
933 		p = delimptr;
934 	}
935 
936 	/* do some rationality checking */
937 	if (m->m_argv == NULL)
938 	{
939 		syserr("M%s: A= argument required", m->m_name);
940 		return;
941 	}
942 	if (m->m_mailer == NULL)
943 	{
944 		syserr("M%s: P= argument required", m->m_name);
945 		return;
946 	}
947 
948 	if (NextMailer >= MAXMAILERS)
949 	{
950 		syserr("too many mailers defined (%d max)", MAXMAILERS);
951 		return;
952 	}
953 
954 	/* do some heuristic cleanup for back compatibility */
955 	if (bitnset(M_LIMITS, m->m_flags))
956 	{
957 		if (m->m_linelimit == 0)
958 			m->m_linelimit = SMTPLINELIM;
959 		if (ConfigLevel < 2)
960 			setbitn(M_7BITS, m->m_flags);
961 	}
962 
963 	if (ConfigLevel < 6 &&
964 	    (strcmp(m->m_mailer, "[IPC]") == 0 ||
965 	     strcmp(m->m_mailer, "[TCP]") == 0))
966 	{
967 		if (m->m_mtatype == NULL)
968 			m->m_mtatype = "dns";
969 		if (m->m_addrtype == NULL)
970 			m->m_addrtype = "rfc822";
971 		if (m->m_diagtype == NULL)
972 			m->m_diagtype = "smtp";
973 	}
974 
975 	/* enter the mailer into the symbol table */
976 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
977 	if (s->s_mailer != NULL)
978 	{
979 		i = s->s_mailer->m_mno;
980 		free(s->s_mailer);
981 	}
982 	else
983 	{
984 		i = NextMailer++;
985 	}
986 	Mailer[i] = s->s_mailer = m;
987 	m->m_mno = i;
988 }
989 /*
990 **  MUNCHSTRING -- translate a string into internal form.
991 **
992 **	Parameters:
993 **		p -- the string to munch.
994 **		delimptr -- if non-NULL, set to the pointer of the
995 **			field delimiter character.
996 **
997 **	Returns:
998 **		the munched string.
999 */
1000 
1001 char *
1002 munchstring(p, delimptr)
1003 	register char *p;
1004 	char **delimptr;
1005 {
1006 	register char *q;
1007 	bool backslash = FALSE;
1008 	bool quotemode = FALSE;
1009 	static char buf[MAXLINE];
1010 
1011 	for (q = buf; *p != '\0'; p++)
1012 	{
1013 		if (backslash)
1014 		{
1015 			/* everything is roughly literal */
1016 			backslash = FALSE;
1017 			switch (*p)
1018 			{
1019 			  case 'r':		/* carriage return */
1020 				*q++ = '\r';
1021 				continue;
1022 
1023 			  case 'n':		/* newline */
1024 				*q++ = '\n';
1025 				continue;
1026 
1027 			  case 'f':		/* form feed */
1028 				*q++ = '\f';
1029 				continue;
1030 
1031 			  case 'b':		/* backspace */
1032 				*q++ = '\b';
1033 				continue;
1034 			}
1035 			*q++ = *p;
1036 		}
1037 		else
1038 		{
1039 			if (*p == '\\')
1040 				backslash = TRUE;
1041 			else if (*p == '"')
1042 				quotemode = !quotemode;
1043 			else if (quotemode || *p != ',')
1044 				*q++ = *p;
1045 			else
1046 				break;
1047 		}
1048 	}
1049 
1050 	if (delimptr != NULL)
1051 		*delimptr = p;
1052 	*q++ = '\0';
1053 	return (buf);
1054 }
1055 /*
1056 **  MAKEARGV -- break up a string into words
1057 **
1058 **	Parameters:
1059 **		p -- the string to break up.
1060 **
1061 **	Returns:
1062 **		a char **argv (dynamically allocated)
1063 **
1064 **	Side Effects:
1065 **		munges p.
1066 */
1067 
1068 char **
1069 makeargv(p)
1070 	register char *p;
1071 {
1072 	char *q;
1073 	int i;
1074 	char **avp;
1075 	char *argv[MAXPV + 1];
1076 
1077 	/* take apart the words */
1078 	i = 0;
1079 	while (*p != '\0' && i < MAXPV)
1080 	{
1081 		q = p;
1082 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1083 			p++;
1084 		while (isascii(*p) && isspace(*p))
1085 			*p++ = '\0';
1086 		argv[i++] = newstr(q);
1087 	}
1088 	argv[i++] = NULL;
1089 
1090 	/* now make a copy of the argv */
1091 	avp = (char **) xalloc(sizeof *avp * i);
1092 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
1093 
1094 	return (avp);
1095 }
1096 /*
1097 **  PRINTRULES -- print rewrite rules (for debugging)
1098 **
1099 **	Parameters:
1100 **		none.
1101 **
1102 **	Returns:
1103 **		none.
1104 **
1105 **	Side Effects:
1106 **		prints rewrite rules.
1107 */
1108 
1109 printrules()
1110 {
1111 	register struct rewrite *rwp;
1112 	register int ruleset;
1113 
1114 	for (ruleset = 0; ruleset < 10; ruleset++)
1115 	{
1116 		if (RewriteRules[ruleset] == NULL)
1117 			continue;
1118 		printf("\n----Rule Set %d:", ruleset);
1119 
1120 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
1121 		{
1122 			printf("\nLHS:");
1123 			printav(rwp->r_lhs);
1124 			printf("RHS:");
1125 			printav(rwp->r_rhs);
1126 		}
1127 	}
1128 }
1129 /*
1130 **  PRINTMAILER -- print mailer structure (for debugging)
1131 **
1132 **	Parameters:
1133 **		m -- the mailer to print
1134 **
1135 **	Returns:
1136 **		none.
1137 */
1138 
1139 printmailer(m)
1140 	register MAILER *m;
1141 {
1142 	int j;
1143 
1144 	printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
1145 		m->m_mno, m->m_name,
1146 		m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
1147 		m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
1148 		m->m_uid, m->m_gid);
1149 	for (j = '\0'; j <= '\177'; j++)
1150 		if (bitnset(j, m->m_flags))
1151 			(void) putchar(j);
1152 	printf(" L=%d E=", m->m_linelimit);
1153 	xputs(m->m_eol);
1154 	if (m->m_defcharset != NULL)
1155 		printf(" C=%s", m->m_defcharset);
1156 	printf(" T=%s/%s/%s",
1157 		m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
1158 		m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
1159 		m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
1160 	if (m->m_argv != NULL)
1161 	{
1162 		char **a = m->m_argv;
1163 
1164 		printf(" A=");
1165 		while (*a != NULL)
1166 		{
1167 			if (a != m->m_argv)
1168 				printf(" ");
1169 			xputs(*a++);
1170 		}
1171 	}
1172 	printf("\n");
1173 }
1174 /*
1175 **  SETOPTION -- set global processing option
1176 **
1177 **	Parameters:
1178 **		opt -- option name.
1179 **		val -- option value (as a text string).
1180 **		safe -- set if this came from a configuration file.
1181 **			Some options (if set from the command line) will
1182 **			reset the user id to avoid security problems.
1183 **		sticky -- if set, don't let other setoptions override
1184 **			this value.
1185 **		e -- the main envelope.
1186 **
1187 **	Returns:
1188 **		none.
1189 **
1190 **	Side Effects:
1191 **		Sets options as implied by the arguments.
1192 */
1193 
1194 static BITMAP	StickyOpt;		/* set if option is stuck */
1195 
1196 
1197 #if NAMED_BIND
1198 
1199 struct resolverflags
1200 {
1201 	char	*rf_name;	/* name of the flag */
1202 	long	rf_bits;	/* bits to set/clear */
1203 } ResolverFlags[] =
1204 {
1205 	"debug",	RES_DEBUG,
1206 	"aaonly",	RES_AAONLY,
1207 	"usevc",	RES_USEVC,
1208 	"primary",	RES_PRIMARY,
1209 	"igntc",	RES_IGNTC,
1210 	"recurse",	RES_RECURSE,
1211 	"defnames",	RES_DEFNAMES,
1212 	"stayopen",	RES_STAYOPEN,
1213 	"dnsrch",	RES_DNSRCH,
1214 	"true",		0,		/* to avoid error on old syntax */
1215 	NULL,		0
1216 };
1217 
1218 #endif
1219 
1220 struct optioninfo
1221 {
1222 	char	*o_name;	/* long name of option */
1223 	u_char	o_code;		/* short name of option */
1224 	bool	o_safe;		/* safe for random people to use */
1225 } OptionTab[] =
1226 {
1227 	"SevenBitInput",	'7',		TRUE,
1228 	"EightBitMode",		'8',		TRUE,
1229 	"AliasFile",		'A',		FALSE,
1230 	"AliasWait",		'a',		FALSE,
1231 	"BlankSub",		'B',		FALSE,
1232 	"MinFreeBlocks",	'b',		TRUE,
1233 	"CheckpointInterval",	'C',		TRUE,
1234 	"HoldExpensive",	'c',		FALSE,
1235 	"AutoRebuildAliases",	'D',		FALSE,
1236 	"DeliveryMode",		'd',		TRUE,
1237 	"ErrorHeader",		'E',		FALSE,
1238 	"ErrorMode",		'e',		TRUE,
1239 	"TempFileMode",		'F',		FALSE,
1240 	"SaveFromLine",		'f',		FALSE,
1241 	"MatchGECOS",		'G',		FALSE,
1242 	"HelpFile",		'H',		FALSE,
1243 	"MaxHopCount",		'h',		FALSE,
1244 	"NameServerOptions",	'I',		FALSE,
1245 	"IgnoreDots",		'i',		TRUE,
1246 	"ForwardPath",		'J',		FALSE,
1247 	"SendMimeErrors",	'j',		TRUE,
1248 	"ConnectionCacheSize",	'k',		FALSE,
1249 	"ConnectionCacheTimeout", 'K',		FALSE,
1250 	"UseErrorsTo",		'l',		FALSE,
1251 	"LogLevel",		'L',		FALSE,
1252 	"MeToo",		'm',		TRUE,
1253 	"CheckAliases",		'n',		FALSE,
1254 	"OldStyleHeaders",	'o',		TRUE,
1255 	"DaemonPortOptions",	'O',		FALSE,
1256 	"PrivacyOptions",	'p',		TRUE,
1257 	"PostmasterCopy",	'P',		FALSE,
1258 	"QueueFactor",		'q',		FALSE,
1259 	"QueueDirectory",	'Q',		FALSE,
1260 	"DontPruneRoutes",	'R',		FALSE,
1261 	"Timeout",		'r',		TRUE,
1262 	"StatusFile",		'S',		FALSE,
1263 	"SuperSafe",		's',		TRUE,
1264 	"QueueTimeout",		'T',		FALSE,
1265 	"TimeZoneSpec",		't',		FALSE,
1266 	"UserDatabaseSpec",	'U',		FALSE,
1267 	"DefaultUser",		'u',		FALSE,
1268 	"FallbackMXhost",	'V',		FALSE,
1269 	"Verbose",		'v',		TRUE,
1270 	"TryNullMXList",	'w',		TRUE,
1271 	"QueueLA",		'x',		FALSE,
1272 	"RefuseLA",		'X',		FALSE,
1273 	"RecipientFactor",	'y',		FALSE,
1274 	"ForkQueueRuns",	'Y',		FALSE,
1275 	"ClassFactor",		'z',		FALSE,
1276 	"TimeFactor",		'Z',		FALSE,
1277 #define O_BSP		0x80
1278 	"BrokenSmtpPeers",	O_BSP,		TRUE,
1279 #define O_QUEUESORTORD	0x81
1280 	"QueueSortOrder",	O_QUEUESORTORD,	TRUE,
1281 #define O_MQA		0x83
1282 	"MinQueueAge",		O_MQA,		TRUE,
1283 #define O_MHSA		0x84
1284 /*
1285 	"MaxHostStatAge",	O_MHSA,		TRUE,
1286 */
1287 #define O_DEFCHARSET	0x85
1288 	"DefaultCharSet",	O_DEFCHARSET,	TRUE,
1289 #define O_SSFILE	0x86
1290 	"ServiceSwitchFile",	O_SSFILE,	FALSE,
1291 #define O_DIALDELAY	0x87
1292 	"DialDelay",		O_DIALDELAY,	TRUE,
1293 
1294 	NULL,			'\0',		FALSE,
1295 };
1296 
1297 
1298 
1299 setoption(opt, val, safe, sticky, e)
1300 	u_char opt;
1301 	char *val;
1302 	bool safe;
1303 	bool sticky;
1304 	register ENVELOPE *e;
1305 {
1306 	register char *p;
1307 	register struct optioninfo *o;
1308 	char *subopt;
1309 	int len;
1310 	extern bool atobool();
1311 	extern time_t convtime();
1312 	extern int QueueLA;
1313 	extern int RefuseLA;
1314 	extern bool Warn_Q_option;
1315 
1316 	errno = 0;
1317 	if (opt == ' ')
1318 	{
1319 		/* full word options */
1320 		struct optioninfo *sel;
1321 
1322 		p = strchr(val, '=');
1323 		if (p == NULL)
1324 			p = &val[strlen(val)];
1325 		while (*--p == ' ')
1326 			continue;
1327 		while (*++p == ' ')
1328 			*p = '\0';
1329 		if (p == val)
1330 		{
1331 			syserr("readcf: null option name");
1332 			return;
1333 		}
1334 		if (*p == '=')
1335 			*p++ = '\0';
1336 		while (*p == ' ')
1337 			p++;
1338 		subopt = strchr(val, '.');
1339 		if (subopt != NULL)
1340 			*subopt++ = '\0';
1341 		sel = NULL;
1342 		for (o = OptionTab; o->o_name != NULL; o++)
1343 		{
1344 			if (strncasecmp(o->o_name, val, strlen(val)) != 0)
1345 				continue;
1346 			if (strlen(o->o_name) == strlen(val))
1347 			{
1348 				/* completely specified -- this must be it */
1349 				sel = NULL;
1350 				break;
1351 			}
1352 			if (sel != NULL)
1353 				break;
1354 			sel = o;
1355 		}
1356 		if (sel != NULL && o->o_name == NULL)
1357 			o = sel;
1358 		else if (o->o_name == NULL)
1359 		{
1360 			syserr("readcf: unknown option name %s", val);
1361 			return;
1362 		}
1363 		else if (sel != NULL)
1364 		{
1365 			syserr("readcf: ambiguous option name %s (matches %s and %s)",
1366 				val, sel->o_name, o->o_name);
1367 			return;
1368 		}
1369 		if (strlen(val) != strlen(o->o_name))
1370 		{
1371 			bool oldVerbose = Verbose;
1372 
1373 			Verbose = TRUE;
1374 			message("Option %s used as abbreviation for %s",
1375 				val, o->o_name);
1376 			Verbose = oldVerbose;
1377 		}
1378 		opt = o->o_code;
1379 		val = p;
1380 	}
1381 	else
1382 	{
1383 		for (o = OptionTab; o->o_name != NULL; o++)
1384 		{
1385 			if (o->o_code == opt)
1386 				break;
1387 		}
1388 		subopt = NULL;
1389 	}
1390 
1391 	if (tTd(37, 1))
1392 	{
1393 		printf(isascii(opt) && isprint(opt) ?
1394 			    "setoption %s (%c).%s=%s" :
1395 			    "setoption %s (0x%x).%s=%s",
1396 			o->o_name == NULL ? "<unknown>" : o->o_name,
1397 			opt,
1398 			subopt == NULL ? "" : subopt,
1399 			val);
1400 	}
1401 
1402 	/*
1403 	**  See if this option is preset for us.
1404 	*/
1405 
1406 	if (!sticky && bitnset(opt, StickyOpt))
1407 	{
1408 		if (tTd(37, 1))
1409 			printf(" (ignored)\n");
1410 		return;
1411 	}
1412 
1413 	/*
1414 	**  Check to see if this option can be specified by this user.
1415 	*/
1416 
1417 	if (!safe && RealUid == 0)
1418 		safe = TRUE;
1419 	if (!safe && !o->o_safe)
1420 	{
1421 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
1422 		{
1423 			if (tTd(37, 1))
1424 				printf(" (unsafe)");
1425 			if (RealUid != geteuid())
1426 			{
1427 				if (tTd(37, 1))
1428 					printf("(Resetting uid)");
1429 				(void) setgid(RealGid);
1430 				(void) setuid(RealUid);
1431 			}
1432 		}
1433 	}
1434 	if (tTd(37, 1))
1435 		printf("\n");
1436 
1437 	switch (opt & 0xff)
1438 	{
1439 	  case '7':		/* force seven-bit input */
1440 		SevenBitInput = atobool(val);
1441 		break;
1442 
1443 	  case '8':		/* handling of 8-bit input */
1444 		switch (*val)
1445 		{
1446 		  case 'r':		/* reject 8-bit, don't convert MIME */
1447 			MimeMode = 0;
1448 			break;
1449 
1450 		  case 'm':		/* convert 8-bit, convert MIME */
1451 			MimeMode = MM_CVTMIME|MM_MIME8BIT;
1452 			break;
1453 
1454 		  case 'j':		/* "just send 8" */
1455 			MimeMode = MM_PASS8BIT;
1456 			break;
1457 
1458 		  case 'p':		/* pass 8 bit, convert MIME */
1459 			MimeMode = MM_PASS8BIT|MM_CVTMIME;
1460 			break;
1461 
1462 		  case 's':		/* strict adherence */
1463 			MimeMode = MM_CVTMIME;
1464 			break;
1465 
1466 		  case 'a':		/* encode 8 bit if available */
1467 			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
1468 			break;
1469 
1470 		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
1471 			MimeMode = MM_MIME8BIT;
1472 			break;
1473 
1474 		  default:
1475 			syserr("Unknown 8-bit mode %c", *val);
1476 			exit(EX_USAGE);
1477 		}
1478 		break;
1479 
1480 	  case 'A':		/* set default alias file */
1481 		if (val[0] == '\0')
1482 			setalias("aliases");
1483 		else
1484 			setalias(val);
1485 		break;
1486 
1487 	  case 'a':		/* look N minutes for "@:@" in alias file */
1488 		if (val[0] == '\0')
1489 			SafeAlias = 5 * 60;		/* five minutes */
1490 		else
1491 			SafeAlias = convtime(val, 'm');
1492 		break;
1493 
1494 	  case 'B':		/* substitution for blank character */
1495 		SpaceSub = val[0];
1496 		if (SpaceSub == '\0')
1497 			SpaceSub = ' ';
1498 		break;
1499 
1500 	  case 'b':		/* min blocks free on queue fs/max msg size */
1501 		p = strchr(val, '/');
1502 		if (p != NULL)
1503 		{
1504 			*p++ = '\0';
1505 			MaxMessageSize = atol(p);
1506 		}
1507 		MinBlocksFree = atol(val);
1508 		break;
1509 
1510 	  case 'c':		/* don't connect to "expensive" mailers */
1511 		NoConnect = atobool(val);
1512 		break;
1513 
1514 	  case 'C':		/* checkpoint every N addresses */
1515 		CheckpointInterval = atoi(val);
1516 		break;
1517 
1518 	  case 'd':		/* delivery mode */
1519 		switch (*val)
1520 		{
1521 		  case '\0':
1522 			e->e_sendmode = SM_DELIVER;
1523 			break;
1524 
1525 		  case SM_QUEUE:	/* queue only */
1526 #ifndef QUEUE
1527 			syserr("need QUEUE to set -odqueue");
1528 #endif /* QUEUE */
1529 			/* fall through..... */
1530 
1531 		  case SM_DELIVER:	/* do everything */
1532 		  case SM_FORK:		/* fork after verification */
1533 			e->e_sendmode = *val;
1534 			break;
1535 
1536 		  default:
1537 			syserr("Unknown delivery mode %c", *val);
1538 			exit(EX_USAGE);
1539 		}
1540 		break;
1541 
1542 	  case 'D':		/* rebuild alias database as needed */
1543 		AutoRebuild = atobool(val);
1544 		break;
1545 
1546 	  case 'E':		/* error message header/header file */
1547 		if (*val != '\0')
1548 			ErrMsgFile = newstr(val);
1549 		break;
1550 
1551 	  case 'e':		/* set error processing mode */
1552 		switch (*val)
1553 		{
1554 		  case EM_QUIET:	/* be silent about it */
1555 		  case EM_MAIL:		/* mail back */
1556 		  case EM_BERKNET:	/* do berknet error processing */
1557 		  case EM_WRITE:	/* write back (or mail) */
1558 		  case EM_PRINT:	/* print errors normally (default) */
1559 			e->e_errormode = *val;
1560 			break;
1561 		}
1562 		break;
1563 
1564 	  case 'F':		/* file mode */
1565 		FileMode = atooct(val) & 0777;
1566 		break;
1567 
1568 	  case 'f':		/* save Unix-style From lines on front */
1569 		SaveFrom = atobool(val);
1570 		break;
1571 
1572 	  case 'G':		/* match recipients against GECOS field */
1573 		MatchGecos = atobool(val);
1574 		break;
1575 
1576 	  case 'g':		/* default gid */
1577   g_opt:
1578 		if (isascii(*val) && isdigit(*val))
1579 			DefGid = atoi(val);
1580 		else
1581 		{
1582 			register struct group *gr;
1583 
1584 			DefGid = -1;
1585 			gr = getgrnam(val);
1586 			if (gr == NULL)
1587 				syserr("readcf: option %c: unknown group %s",
1588 					opt, val);
1589 			else
1590 				DefGid = gr->gr_gid;
1591 		}
1592 		break;
1593 
1594 	  case 'H':		/* help file */
1595 		if (val[0] == '\0')
1596 			HelpFile = "sendmail.hf";
1597 		else
1598 			HelpFile = newstr(val);
1599 		break;
1600 
1601 	  case 'h':		/* maximum hop count */
1602 		MaxHopCount = atoi(val);
1603 		break;
1604 
1605 	  case 'I':		/* use internet domain name server */
1606 #if NAMED_BIND
1607 		for (p = val; *p != 0; )
1608 		{
1609 			bool clearmode;
1610 			char *q;
1611 			struct resolverflags *rfp;
1612 
1613 			while (*p == ' ')
1614 				p++;
1615 			if (*p == '\0')
1616 				break;
1617 			clearmode = FALSE;
1618 			if (*p == '-')
1619 				clearmode = TRUE;
1620 			else if (*p != '+')
1621 				p--;
1622 			p++;
1623 			q = p;
1624 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1625 				p++;
1626 			if (*p != '\0')
1627 				*p++ = '\0';
1628 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
1629 			{
1630 				if (strcasecmp(q, rfp->rf_name) == 0)
1631 					break;
1632 			}
1633 			if (rfp->rf_name == NULL)
1634 				syserr("readcf: I option value %s unrecognized", q);
1635 			else if (clearmode)
1636 				_res.options &= ~rfp->rf_bits;
1637 			else
1638 				_res.options |= rfp->rf_bits;
1639 		}
1640 		if (tTd(8, 2))
1641 			printf("_res.options = %x\n", _res.options);
1642 #else
1643 		usrerr("name server (I option) specified but BIND not compiled in");
1644 #endif
1645 		break;
1646 
1647 	  case 'i':		/* ignore dot lines in message */
1648 		IgnrDot = atobool(val);
1649 		break;
1650 
1651 	  case 'j':		/* send errors in MIME (RFC 1341) format */
1652 		SendMIMEErrors = atobool(val);
1653 		break;
1654 
1655 	  case 'J':		/* .forward search path */
1656 		ForwardPath = newstr(val);
1657 		break;
1658 
1659 	  case 'k':		/* connection cache size */
1660 		MaxMciCache = atoi(val);
1661 		if (MaxMciCache < 0)
1662 			MaxMciCache = 0;
1663 		break;
1664 
1665 	  case 'K':		/* connection cache timeout */
1666 		MciCacheTimeout = convtime(val, 'm');
1667 		break;
1668 
1669 	  case 'l':		/* use Errors-To: header */
1670 		UseErrorsTo = atobool(val);
1671 		break;
1672 
1673 	  case 'L':		/* log level */
1674 		if (safe || LogLevel < atoi(val))
1675 			LogLevel = atoi(val);
1676 		break;
1677 
1678 	  case 'M':		/* define macro */
1679 		p = newstr(&val[1]);
1680 		if (!safe)
1681 			cleanstrcpy(p, p, MAXNAME);
1682 		define(val[0], p, CurEnv);
1683 		sticky = FALSE;
1684 		break;
1685 
1686 	  case 'm':		/* send to me too */
1687 		MeToo = atobool(val);
1688 		break;
1689 
1690 	  case 'n':		/* validate RHS in newaliases */
1691 		CheckAliases = atobool(val);
1692 		break;
1693 
1694 	    /* 'N' available -- was "net name" */
1695 
1696 	  case 'O':		/* daemon options */
1697 		setdaemonoptions(val);
1698 		break;
1699 
1700 	  case 'o':		/* assume old style headers */
1701 		if (atobool(val))
1702 			CurEnv->e_flags |= EF_OLDSTYLE;
1703 		else
1704 			CurEnv->e_flags &= ~EF_OLDSTYLE;
1705 		break;
1706 
1707 	  case 'p':		/* select privacy level */
1708 		p = val;
1709 		for (;;)
1710 		{
1711 			register struct prival *pv;
1712 			extern struct prival PrivacyValues[];
1713 
1714 			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
1715 				p++;
1716 			if (*p == '\0')
1717 				break;
1718 			val = p;
1719 			while (isascii(*p) && isalnum(*p))
1720 				p++;
1721 			if (*p != '\0')
1722 				*p++ = '\0';
1723 
1724 			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
1725 			{
1726 				if (strcasecmp(val, pv->pv_name) == 0)
1727 					break;
1728 			}
1729 			if (pv->pv_name == NULL)
1730 				syserr("readcf: Op line: %s unrecognized", val);
1731 			PrivacyFlags |= pv->pv_flag;
1732 		}
1733 		break;
1734 
1735 	  case 'P':		/* postmaster copy address for returned mail */
1736 		PostMasterCopy = newstr(val);
1737 		break;
1738 
1739 	  case 'q':		/* slope of queue only function */
1740 		QueueFactor = atoi(val);
1741 		break;
1742 
1743 	  case 'Q':		/* queue directory */
1744 		if (val[0] == '\0')
1745 			QueueDir = "mqueue";
1746 		else
1747 			QueueDir = newstr(val);
1748 		if (RealUid != 0 && !safe)
1749 			Warn_Q_option = TRUE;
1750 		break;
1751 
1752 	  case 'R':		/* don't prune routes */
1753 		DontPruneRoutes = atobool(val);
1754 		break;
1755 
1756 	  case 'r':		/* read timeout */
1757 		if (subopt == NULL)
1758 			inittimeouts(val);
1759 		else
1760 			settimeout(subopt, val);
1761 		break;
1762 
1763 	  case 'S':		/* status file */
1764 		if (val[0] == '\0')
1765 			StatFile = "sendmail.st";
1766 		else
1767 			StatFile = newstr(val);
1768 		break;
1769 
1770 	  case 's':		/* be super safe, even if expensive */
1771 		SuperSafe = atobool(val);
1772 		break;
1773 
1774 	  case 'T':		/* queue timeout */
1775 		p = strchr(val, '/');
1776 		if (p != NULL)
1777 		{
1778 			*p++ = '\0';
1779 			settimeout("queuewarn", p);
1780 		}
1781 		settimeout("queuereturn", val);
1782 		break;
1783 
1784 	  case 't':		/* time zone name */
1785 		TimeZoneSpec = newstr(val);
1786 		break;
1787 
1788 	  case 'U':		/* location of user database */
1789 		UdbSpec = newstr(val);
1790 		break;
1791 
1792 	  case 'u':		/* set default uid */
1793 		for (p = val; *p != '\0'; p++)
1794 		{
1795 			if (*p == '.' || *p == '/' || *p == ':')
1796 			{
1797 				*p++ = '\0';
1798 				break;
1799 			}
1800 		}
1801 		if (isascii(*val) && isdigit(*val))
1802 			DefUid = atoi(val);
1803 		else
1804 		{
1805 			register struct passwd *pw;
1806 
1807 			DefUid = -1;
1808 			pw = getpwnam(val);
1809 			if (pw == NULL)
1810 				syserr("readcf: option u: unknown user %s", val);
1811 			else
1812 			{
1813 				DefUid = pw->pw_uid;
1814 				DefGid = pw->pw_gid;
1815 			}
1816 		}
1817 		setdefuser();
1818 
1819 		/* handle the group if it is there */
1820 		if (*p == '\0')
1821 			break;
1822 		val = p;
1823 		goto g_opt;
1824 
1825 	  case 'V':		/* fallback MX host */
1826 		FallBackMX = newstr(val);
1827 		break;
1828 
1829 	  case 'v':		/* run in verbose mode */
1830 		Verbose = atobool(val);
1831 		break;
1832 
1833 	  case 'w':		/* if we are best MX, try host directly */
1834 		TryNullMXList = atobool(val);
1835 		break;
1836 
1837 	    /* 'W' available -- was wizard password */
1838 
1839 	  case 'x':		/* load avg at which to auto-queue msgs */
1840 		QueueLA = atoi(val);
1841 		break;
1842 
1843 	  case 'X':		/* load avg at which to auto-reject connections */
1844 		RefuseLA = atoi(val);
1845 		break;
1846 
1847 	  case 'y':		/* work recipient factor */
1848 		WkRecipFact = atoi(val);
1849 		break;
1850 
1851 	  case 'Y':		/* fork jobs during queue runs */
1852 		ForkQueueRuns = atobool(val);
1853 		break;
1854 
1855 	  case 'z':		/* work message class factor */
1856 		WkClassFact = atoi(val);
1857 		break;
1858 
1859 	  case 'Z':		/* work time factor */
1860 		WkTimeFact = atoi(val);
1861 		break;
1862 
1863 	  case O_BSP:		/* SMTP Peers can't handle 2-line greeting */
1864 		BrokenSmtpPeers = atobool(val);
1865 		break;
1866 
1867 	  case O_QUEUESORTORD:	/* queue sorting order */
1868 		switch (*val)
1869 		{
1870 		  case 'h':	/* Host first */
1871 		  case 'H':
1872 			QueueSortOrder = QS_BYHOST;
1873 			break;
1874 
1875 		  case 'p':	/* Priority order */
1876 		  case 'P':
1877 			QueueSortOrder = QS_BYPRIORITY;
1878 			break;
1879 
1880 		  default:
1881 			syserr("Invalid queue sort order \"%s\"", val);
1882 		}
1883 		break;
1884 
1885 	  case O_MQA:		/* minimum queue age between deliveries */
1886 		MinQueueAge = convtime(val, 'm');
1887 		break;
1888 
1889 	  case O_MHSA:		/* maximum age of cached host status */
1890 		MaxHostStatAge = convtime(val, 'm');
1891 		break;
1892 
1893 	  case O_DEFCHARSET:	/* default character set for mimefying */
1894 		DefaultCharSet = newstr(val);
1895 		break;
1896 
1897 	  case O_SSFILE:	/* service switch file */
1898 		ServiceSwitchFile = newstr(val);
1899 		break;
1900 
1901 	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
1902 		DialDelay = convtime(val, 's');
1903 		break;
1904 
1905 	  default:
1906 		if (tTd(37, 1))
1907 		{
1908 			if (isascii(opt) && isprint(opt))
1909 				printf("Warning: option %c unknown\n", opt);
1910 			else
1911 				printf("Warning: option 0x%x unknown\n", opt);
1912 		}
1913 		break;
1914 	}
1915 	if (sticky)
1916 		setbitn(opt, StickyOpt);
1917 	return;
1918 }
1919 /*
1920 **  SETCLASS -- set a word into a class
1921 **
1922 **	Parameters:
1923 **		class -- the class to put the word in.
1924 **		word -- the word to enter
1925 **
1926 **	Returns:
1927 **		none.
1928 **
1929 **	Side Effects:
1930 **		puts the word into the symbol table.
1931 */
1932 
1933 setclass(class, word)
1934 	int class;
1935 	char *word;
1936 {
1937 	register STAB *s;
1938 
1939 	if (tTd(37, 8))
1940 		printf("setclass(%c, %s)\n", class, word);
1941 	s = stab(word, ST_CLASS, ST_ENTER);
1942 	setbitn(class, s->s_class);
1943 }
1944 /*
1945 **  MAKEMAPENTRY -- create a map entry
1946 **
1947 **	Parameters:
1948 **		line -- the config file line
1949 **
1950 **	Returns:
1951 **		TRUE if it successfully entered the map entry.
1952 **		FALSE otherwise (usually syntax error).
1953 **
1954 **	Side Effects:
1955 **		Enters the map into the dictionary.
1956 */
1957 
1958 void
1959 makemapentry(line)
1960 	char *line;
1961 {
1962 	register char *p;
1963 	char *mapname;
1964 	char *classname;
1965 	register STAB *s;
1966 	STAB *class;
1967 
1968 	for (p = line; isascii(*p) && isspace(*p); p++)
1969 		continue;
1970 	if (!(isascii(*p) && isalnum(*p)))
1971 	{
1972 		syserr("readcf: config K line: no map name");
1973 		return;
1974 	}
1975 
1976 	mapname = p;
1977 	while ((isascii(*++p) && isalnum(*p)) || *p == '.')
1978 		continue;
1979 	if (*p != '\0')
1980 		*p++ = '\0';
1981 	while (isascii(*p) && isspace(*p))
1982 		p++;
1983 	if (!(isascii(*p) && isalnum(*p)))
1984 	{
1985 		syserr("readcf: config K line, map %s: no map class", mapname);
1986 		return;
1987 	}
1988 	classname = p;
1989 	while (isascii(*++p) && isalnum(*p))
1990 		continue;
1991 	if (*p != '\0')
1992 		*p++ = '\0';
1993 	while (isascii(*p) && isspace(*p))
1994 		p++;
1995 
1996 	/* look up the class */
1997 	class = stab(classname, ST_MAPCLASS, ST_FIND);
1998 	if (class == NULL)
1999 	{
2000 		syserr("readcf: map %s: class %s not available", mapname, classname);
2001 		return;
2002 	}
2003 
2004 	/* enter the map */
2005 	s = stab(mapname, ST_MAP, ST_ENTER);
2006 	s->s_map.map_class = &class->s_mapclass;
2007 	s->s_map.map_mname = newstr(mapname);
2008 
2009 	if (class->s_mapclass.map_parse(&s->s_map, p))
2010 		s->s_map.map_mflags |= MF_VALID;
2011 
2012 	if (tTd(37, 5))
2013 	{
2014 		printf("map %s, class %s, flags %x, file %s,\n",
2015 			s->s_map.map_mname, s->s_map.map_class->map_cname,
2016 			s->s_map.map_mflags,
2017 			s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
2018 		printf("\tapp %s, domain %s, rebuild %s\n",
2019 			s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
2020 			s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
2021 			s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
2022 	}
2023 }
2024 /*
2025 **  INITTIMEOUTS -- parse and set timeout values
2026 **
2027 **	Parameters:
2028 **		val -- a pointer to the values.  If NULL, do initial
2029 **			settings.
2030 **
2031 **	Returns:
2032 **		none.
2033 **
2034 **	Side Effects:
2035 **		Initializes the TimeOuts structure
2036 */
2037 
2038 #define SECONDS
2039 #define MINUTES	* 60
2040 #define HOUR	* 3600
2041 
2042 inittimeouts(val)
2043 	register char *val;
2044 {
2045 	register char *p;
2046 	extern time_t convtime();
2047 
2048 	if (val == NULL)
2049 	{
2050 		TimeOuts.to_initial = (time_t) 5 MINUTES;
2051 		TimeOuts.to_helo = (time_t) 5 MINUTES;
2052 		TimeOuts.to_mail = (time_t) 10 MINUTES;
2053 		TimeOuts.to_rcpt = (time_t) 1 HOUR;
2054 		TimeOuts.to_datainit = (time_t) 5 MINUTES;
2055 		TimeOuts.to_datablock = (time_t) 1 HOUR;
2056 		TimeOuts.to_datafinal = (time_t) 1 HOUR;
2057 		TimeOuts.to_rset = (time_t) 5 MINUTES;
2058 		TimeOuts.to_quit = (time_t) 2 MINUTES;
2059 		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
2060 		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
2061 #if IDENTPROTO
2062 		TimeOuts.to_ident = (time_t) 30 SECONDS;
2063 #else
2064 		TimeOuts.to_ident = (time_t) 0 SECONDS;
2065 #endif
2066 		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
2067 		return;
2068 	}
2069 
2070 	for (;; val = p)
2071 	{
2072 		while (isascii(*val) && isspace(*val))
2073 			val++;
2074 		if (*val == '\0')
2075 			break;
2076 		for (p = val; *p != '\0' && *p != ','; p++)
2077 			continue;
2078 		if (*p != '\0')
2079 			*p++ = '\0';
2080 
2081 		if (isascii(*val) && isdigit(*val))
2082 		{
2083 			/* old syntax -- set everything */
2084 			TimeOuts.to_mail = convtime(val, 'm');
2085 			TimeOuts.to_rcpt = TimeOuts.to_mail;
2086 			TimeOuts.to_datainit = TimeOuts.to_mail;
2087 			TimeOuts.to_datablock = TimeOuts.to_mail;
2088 			TimeOuts.to_datafinal = TimeOuts.to_mail;
2089 			TimeOuts.to_nextcommand = TimeOuts.to_mail;
2090 			continue;
2091 		}
2092 		else
2093 		{
2094 			register char *q = strchr(val, ':');
2095 
2096 			if (q == NULL && (q = strchr(val, '=')) == NULL)
2097 			{
2098 				/* syntax error */
2099 				continue;
2100 			}
2101 			*q++ = '\0';
2102 			settimeout(val, q);
2103 		}
2104 	}
2105 }
2106 /*
2107 **  SETTIMEOUT -- set an individual timeout
2108 **
2109 **	Parameters:
2110 **		name -- the name of the timeout.
2111 **		val -- the value of the timeout.
2112 **
2113 **	Returns:
2114 **		none.
2115 */
2116 
2117 settimeout(name, val)
2118 	char *name;
2119 	char *val;
2120 {
2121 	register char *p;
2122 	time_t to;
2123 	extern time_t convtime();
2124 
2125 	to = convtime(val, 'm');
2126 	p = strchr(name, '.');
2127 	if (p != NULL)
2128 		*p++ = '\0';
2129 
2130 	if (strcasecmp(name, "initial") == 0)
2131 		TimeOuts.to_initial = to;
2132 	else if (strcasecmp(name, "mail") == 0)
2133 		TimeOuts.to_mail = to;
2134 	else if (strcasecmp(name, "rcpt") == 0)
2135 		TimeOuts.to_rcpt = to;
2136 	else if (strcasecmp(name, "datainit") == 0)
2137 		TimeOuts.to_datainit = to;
2138 	else if (strcasecmp(name, "datablock") == 0)
2139 		TimeOuts.to_datablock = to;
2140 	else if (strcasecmp(name, "datafinal") == 0)
2141 		TimeOuts.to_datafinal = to;
2142 	else if (strcasecmp(name, "command") == 0)
2143 		TimeOuts.to_nextcommand = to;
2144 	else if (strcasecmp(name, "rset") == 0)
2145 		TimeOuts.to_rset = to;
2146 	else if (strcasecmp(name, "helo") == 0)
2147 		TimeOuts.to_helo = to;
2148 	else if (strcasecmp(name, "quit") == 0)
2149 		TimeOuts.to_quit = to;
2150 	else if (strcasecmp(name, "misc") == 0)
2151 		TimeOuts.to_miscshort = to;
2152 	else if (strcasecmp(name, "ident") == 0)
2153 		TimeOuts.to_ident = to;
2154 	else if (strcasecmp(name, "fileopen") == 0)
2155 		TimeOuts.to_fileopen = to;
2156 	else if (strcasecmp(name, "queuewarn") == 0)
2157 	{
2158 		to = convtime(val, 'h');
2159 		if (p == NULL || strcmp(p, "*") == 0)
2160 		{
2161 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2162 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2163 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2164 		}
2165 		else if (strcasecmp(p, "normal") == 0)
2166 			TimeOuts.to_q_warning[TOC_NORMAL] = to;
2167 		else if (strcasecmp(p, "urgent") == 0)
2168 			TimeOuts.to_q_warning[TOC_URGENT] = to;
2169 		else if (strcasecmp(p, "non-urgent") == 0)
2170 			TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2171 		else
2172 			syserr("settimeout: invalid queuewarn subtimeout %s", p);
2173 	}
2174 	else if (strcasecmp(name, "queuereturn") == 0)
2175 	{
2176 		to = convtime(val, 'd');
2177 		if (p == NULL || strcmp(p, "*") == 0)
2178 		{
2179 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2180 			TimeOuts.to_q_return[TOC_URGENT] = to;
2181 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2182 		}
2183 		else if (strcasecmp(p, "normal") == 0)
2184 			TimeOuts.to_q_return[TOC_NORMAL] = to;
2185 		else if (strcasecmp(p, "urgent") == 0)
2186 			TimeOuts.to_q_return[TOC_URGENT] = to;
2187 		else if (strcasecmp(p, "non-urgent") == 0)
2188 			TimeOuts.to_q_return[TOC_NONURGENT] = to;
2189 		else
2190 			syserr("settimeout: invalid queuereturn subtimeout %s", p);
2191 	}
2192 	else
2193 		syserr("settimeout: invalid timeout %s", name);
2194 }
2195