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