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