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