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