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