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