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