source: trunk/third/sendmail/sendmail/readcf.c @ 19204

Revision 19204, 96.9 KB checked in by zacheiss, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19203, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
3 *      All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *      The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#include <sendmail.h>
15
16SM_RCSID("@(#)$Id: readcf.c,v 1.1.1.1 2003-04-08 15:09:07 zacheiss Exp $")
17
18#if NETINET || NETINET6
19# include <arpa/inet.h>
20#endif /* NETINET || NETINET6 */
21
22#define SECONDS
23#define MINUTES * 60
24#define HOUR    * 3600
25#define HOURS   HOUR
26
27static void     fileclass __P((int, char *, char *, bool, bool, bool));
28static char     **makeargv __P((char *));
29static void     settimeout __P((char *, char *, bool));
30static void     toomany __P((int, int));
31static char     *extrquotstr __P((char *, char **, char *, bool *));
32
33/*
34**  READCF -- read configuration file.
35**
36**      This routine reads the configuration file and builds the internal
37**      form.
38**
39**      The file is formatted as a sequence of lines, each taken
40**      atomically.  The first character of each line describes how
41**      the line is to be interpreted.  The lines are:
42**              Dxval           Define macro x to have value val.
43**              Cxword          Put word into class x.
44**              Fxfile [fmt]    Read file for lines to put into
45**                              class x.  Use scanf string 'fmt'
46**                              or "%s" if not present.  Fmt should
47**                              only produce one string-valued result.
48**              Hname: value    Define header with field-name 'name'
49**                              and value as specified; this will be
50**                              macro expanded immediately before
51**                              use.
52**              Sn              Use rewriting set n.
53**              Rlhs rhs        Rewrite addresses that match lhs to
54**                              be rhs.
55**              Mn arg=val...   Define mailer.  n is the internal name.
56**                              Args specify mailer parameters.
57**              Oxvalue         Set option x to value.
58**              O option value  Set option (long name) to value.
59**              Pname=value     Set precedence name to value.
60**              Qn arg=val...   Define queue groups.  n is the internal name.
61**                              Args specify queue parameters.
62**              Vversioncode[/vendorcode]
63**                              Version level/vendor name of
64**                              configuration syntax.
65**              Kmapname mapclass arguments....
66**                              Define keyed lookup of a given class.
67**                              Arguments are class dependent.
68**              Eenvar=value    Set the environment value to the given value.
69**
70**      Parameters:
71**              cfname -- configuration file name.
72**              safe -- true if this is the system config file;
73**                      false otherwise.
74**              e -- the main envelope.
75**
76**      Returns:
77**              none.
78**
79**      Side Effects:
80**              Builds several internal tables.
81*/
82
83void
84readcf(cfname, safe, e)
85        char *cfname;
86        bool safe;
87        register ENVELOPE *e;
88{
89        SM_FILE_T *cf;
90        int ruleset = -1;
91        char *q;
92        struct rewrite *rwp = NULL;
93        char *bp;
94        auto char *ep;
95        int nfuzzy;
96        char *file;
97        bool optional;
98        bool ok;
99        bool ismap;
100        int mid;
101        register char *p;
102        long sff = SFF_OPENASROOT;
103        struct stat statb;
104        char buf[MAXLINE];
105        char exbuf[MAXLINE];
106        char pvpbuf[MAXLINE + MAXATOM];
107        static char *null_list[1] = { NULL };
108        extern unsigned char TokTypeNoC[];
109
110        FileName = cfname;
111        LineNumber = 0;
112
113        if (DontLockReadFiles)
114                sff |= SFF_NOLOCK;
115        cf = safefopen(cfname, O_RDONLY, 0444, sff);
116        if (cf == NULL)
117        {
118                syserr("cannot open");
119                finis(false, true, EX_OSFILE);
120        }
121
122        if (fstat(sm_io_getinfo(cf, SM_IO_WHAT_FD, NULL), &statb) < 0)
123        {
124                syserr("cannot fstat");
125                finis(false, true, EX_OSFILE);
126        }
127
128        if (!S_ISREG(statb.st_mode))
129        {
130                syserr("not a plain file");
131                finis(false, true, EX_OSFILE);
132        }
133
134        if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
135        {
136                if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS)
137                        (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
138                                             "%s: WARNING: dangerous write permissions\n",
139                                             FileName);
140                if (LogLevel > 0)
141                        sm_syslog(LOG_CRIT, NOQID,
142                                  "%s: WARNING: dangerous write permissions",
143                                  FileName);
144        }
145
146#if XLA
147        xla_zero();
148#endif /* XLA */
149
150        while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
151        {
152                if (bp[0] == '#')
153                {
154                        if (bp != buf)
155                                sm_free(bp); /* XXX */
156                        continue;
157                }
158
159                /* do macro expansion mappings */
160                translate_dollars(bp);
161
162                /* interpret this line */
163                errno = 0;
164                switch (bp[0])
165                {
166                  case '\0':
167                  case '#':             /* comment */
168                        break;
169
170                  case 'R':             /* rewriting rule */
171                        if (ruleset < 0)
172                        {
173                                syserr("missing valid ruleset for \"%s\"", bp);
174                                break;
175                        }
176                        for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
177                                continue;
178
179                        if (*p == '\0')
180                        {
181                                syserr("invalid rewrite line \"%s\" (tab expected)", bp);
182                                break;
183                        }
184
185                        /* allocate space for the rule header */
186                        if (rwp == NULL)
187                        {
188                                RewriteRules[ruleset] = rwp =
189                                        (struct rewrite *) xalloc(sizeof *rwp);
190                        }
191                        else
192                        {
193                                rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
194                                rwp = rwp->r_next;
195                        }
196                        rwp->r_next = NULL;
197
198                        /* expand and save the LHS */
199                        *p = '\0';
200                        expand(&bp[1], exbuf, sizeof exbuf, e);
201                        rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
202                                             sizeof pvpbuf, NULL,
203                                             ConfigLevel >= 9 ? TokTypeNoC : NULL);
204                        nfuzzy = 0;
205                        if (rwp->r_lhs != NULL)
206                        {
207                                register char **ap;
208
209                                rwp->r_lhs = copyplist(rwp->r_lhs, true, NULL);
210
211                                /* count the number of fuzzy matches in LHS */
212                                for (ap = rwp->r_lhs; *ap != NULL; ap++)
213                                {
214                                        char *botch;
215
216                                        botch = NULL;
217                                        switch (**ap & 0377)
218                                        {
219                                          case MATCHZANY:
220                                          case MATCHANY:
221                                          case MATCHONE:
222                                          case MATCHCLASS:
223                                          case MATCHNCLASS:
224                                                nfuzzy++;
225                                                break;
226
227                                          case MATCHREPL:
228                                                botch = "$0-$9";
229                                                break;
230
231                                          case CANONUSER:
232                                                botch = "$:";
233                                                break;
234
235                                          case CALLSUBR:
236                                                botch = "$>";
237                                                break;
238
239                                          case CONDIF:
240                                                botch = "$?";
241                                                break;
242
243                                          case CONDFI:
244                                                botch = "$.";
245                                                break;
246
247                                          case HOSTBEGIN:
248                                                botch = "$[";
249                                                break;
250
251                                          case HOSTEND:
252                                                botch = "$]";
253                                                break;
254
255                                          case LOOKUPBEGIN:
256                                                botch = "$(";
257                                                break;
258
259                                          case LOOKUPEND:
260                                                botch = "$)";
261                                                break;
262                                        }
263                                        if (botch != NULL)
264                                                syserr("Inappropriate use of %s on LHS",
265                                                        botch);
266                                }
267                                rwp->r_line = LineNumber;
268                        }
269                        else
270                        {
271                                syserr("R line: null LHS");
272                                rwp->r_lhs = null_list;
273                        }
274                        if (nfuzzy > MAXMATCH)
275                        {
276                                syserr("R line: too many wildcards");
277                                rwp->r_lhs = null_list;
278                        }
279
280                        /* expand and save the RHS */
281                        while (*++p == '\t')
282                                continue;
283                        q = p;
284                        while (*p != '\0' && *p != '\t')
285                                p++;
286                        *p = '\0';
287                        expand(q, exbuf, sizeof exbuf, e);
288                        rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
289                                             sizeof pvpbuf, NULL,
290                                             ConfigLevel >= 9 ? TokTypeNoC : NULL);
291                        if (rwp->r_rhs != NULL)
292                        {
293                                register char **ap;
294
295                                rwp->r_rhs = copyplist(rwp->r_rhs, true, NULL);
296
297                                /* check no out-of-bounds replacements */
298                                nfuzzy += '0';
299                                for (ap = rwp->r_rhs; *ap != NULL; ap++)
300                                {
301                                        char *botch;
302
303                                        botch = NULL;
304                                        switch (**ap & 0377)
305                                        {
306                                          case MATCHREPL:
307                                                if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
308                                                {
309                                                        syserr("replacement $%c out of bounds",
310                                                                (*ap)[1]);
311                                                }
312                                                break;
313
314                                          case MATCHZANY:
315                                                botch = "$*";
316                                                break;
317
318                                          case MATCHANY:
319                                                botch = "$+";
320                                                break;
321
322                                          case MATCHONE:
323                                                botch = "$-";
324                                                break;
325
326                                          case MATCHCLASS:
327                                                botch = "$=";
328                                                break;
329
330                                          case MATCHNCLASS:
331                                                botch = "$~";
332                                                break;
333
334#if 0
335/*
336**  This doesn't work yet as there are maps defined *after* the cf
337**  is read such as host, user, and alias.  So for now, it's removed.
338**  When it comes back, the RELEASE_NOTES entry will be:
339**      Emit warnings for unknown maps when reading the .cf file.  Based on
340**              patch from Robert Harker of Harker Systems.
341*/
342
343                                          case LOOKUPBEGIN:
344                                                /*
345                                                **  Got a database lookup,
346                                                **  check if map is defined.
347                                                */
348
349                                                ep = *(ap + 1);
350                                                if ((*ep & 0377) != MACRODEXPAND &&
351                                                    stab(ep, ST_MAP,
352                                                         ST_FIND) == NULL)
353                                                {
354                                                        (void) sm_io_fprintf(smioout,
355                                                                             SM_TIME_DEFAULT,
356                                                                             "Warning: %s: line %d: map %s not found\n",
357                                                                             FileName,
358                                                                             LineNumber,
359                                                                             ep);
360                                                }
361                                                break;
362#endif /* 0 */
363                                        }
364                                        if (botch != NULL)
365                                                syserr("Inappropriate use of %s on RHS",
366                                                        botch);
367                                }
368                        }
369                        else
370                        {
371                                syserr("R line: null RHS");
372                                rwp->r_rhs = null_list;
373                        }
374                        break;
375
376                  case 'S':             /* select rewriting set */
377                        expand(&bp[1], exbuf, sizeof exbuf, e);
378                        ruleset = strtorwset(exbuf, NULL, ST_ENTER);
379                        if (ruleset < 0)
380                                break;
381
382                        rwp = RewriteRules[ruleset];
383                        if (rwp != NULL)
384                        {
385                                if (OpMode == MD_TEST)
386                                        (void) sm_io_fprintf(smioout,
387                                                             SM_TIME_DEFAULT,
388                                                             "WARNING: Ruleset %s has multiple definitions\n",
389                                                            &bp[1]);
390                                if (tTd(37, 1))
391                                        sm_dprintf("WARNING: Ruleset %s has multiple definitions\n",
392                                                   &bp[1]);
393                                while (rwp->r_next != NULL)
394                                        rwp = rwp->r_next;
395                        }
396                        break;
397
398                  case 'D':             /* macro definition */
399                        mid = macid_parse(&bp[1], &ep);
400                        if (mid == 0)
401                                break;
402                        p = munchstring(ep, NULL, '\0');
403                        macdefine(&e->e_macro, A_TEMP, mid, p);
404                        break;
405
406                  case 'H':             /* required header line */
407                        (void) chompheader(&bp[1], CHHDR_DEF, NULL, e);
408                        break;
409
410                  case 'C':             /* word class */
411                  case 'T':             /* trusted user (set class `t') */
412                        if (bp[0] == 'C')
413                        {
414                                mid = macid_parse(&bp[1], &ep);
415                                if (mid == 0)
416                                        break;
417                                expand(ep, exbuf, sizeof exbuf, e);
418                                p = exbuf;
419                        }
420                        else
421                        {
422                                mid = 't';
423                                p = &bp[1];
424                        }
425                        while (*p != '\0')
426                        {
427                                register char *wd;
428                                char delim;
429
430                                while (*p != '\0' && isascii(*p) && isspace(*p))
431                                        p++;
432                                wd = p;
433                                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
434                                        p++;
435                                delim = *p;
436                                *p = '\0';
437                                if (wd[0] != '\0')
438                                        setclass(mid, wd);
439                                *p = delim;
440                        }
441                        break;
442
443                  case 'F':             /* word class from file */
444                        mid = macid_parse(&bp[1], &ep);
445                        if (mid == 0)
446                                break;
447                        for (p = ep; isascii(*p) && isspace(*p); )
448                                p++;
449                        if (p[0] == '-' && p[1] == 'o')
450                        {
451                                optional = true;
452                                while (*p != '\0' &&
453                                       !(isascii(*p) && isspace(*p)))
454                                        p++;
455                                while (isascii(*p) && isspace(*p))
456                                        p++;
457                                file = p;
458                        }
459                        else
460                                optional = false;
461
462                        /* check if [key]@map:spec */
463                        ismap = false;
464                        if (!SM_IS_DIR_DELIM(*p) &&
465                            *p != '|' &&
466                            (q = strchr(p, '@')) != NULL)
467                        {
468                                q++;
469
470                                /* look for @LDAP or @map: in string */
471                                if (strcmp(q, "LDAP") == 0 ||
472                                    (*q != ':' &&
473                                     strchr(q, ':') != NULL))
474                                        ismap = true;
475                        }
476
477                        if (ismap)
478                        {
479                                /* use entire spec */
480                                file = p;
481                        }
482                        else
483                        {
484                                file = extrquotstr(p, &q, " ", &ok);
485                                if (!ok)
486                                {
487                                        syserr("illegal filename '%s'", p);
488                                        break;
489                                }
490                        }
491
492                        if (*file == '|' || ismap)
493                                p = "%s";
494                        else
495                        {
496                                p = q;
497                                if (*p == '\0')
498                                        p = "%s";
499                                else
500                                {
501                                        *p = '\0';
502                                        while (isascii(*++p) && isspace(*p))
503                                                continue;
504                                }
505                        }
506                        fileclass(mid, file, p, ismap, safe, optional);
507                        break;
508
509#if XLA
510                  case 'L':             /* extended load average description */
511                        xla_init(&bp[1]);
512                        break;
513#endif /* XLA */
514
515#if defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO)
516                  case 'L':             /* lookup macro */
517                  case 'G':             /* lookup class */
518                        /* reserved for Sun -- NIS+ database lookup */
519                        if (VendorCode != VENDOR_SUN)
520                                goto badline;
521                        sun_lg_config_line(bp, e);
522                        break;
523#endif /* defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO) */
524
525                  case 'M':             /* define mailer */
526                        makemailer(&bp[1]);
527                        break;
528
529                  case 'O':             /* set option */
530                        setoption(bp[1], &bp[2], safe, false, e);
531                        break;
532
533                  case 'P':             /* set precedence */
534                        if (NumPriorities >= MAXPRIORITIES)
535                        {
536                                toomany('P', MAXPRIORITIES);
537                                break;
538                        }
539                        for (p = &bp[1]; *p != '\0' && *p != '='; p++)
540                                continue;
541                        if (*p == '\0')
542                                goto badline;
543                        *p = '\0';
544                        Priorities[NumPriorities].pri_name = newstr(&bp[1]);
545                        Priorities[NumPriorities].pri_val = atoi(++p);
546                        NumPriorities++;
547                        break;
548
549                  case 'Q':             /* define queue */
550                        makequeue(&bp[1], true);
551                        break;
552
553                  case 'V':             /* configuration syntax version */
554                        for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
555                                continue;
556                        if (!isascii(*p) || !isdigit(*p))
557                        {
558                                syserr("invalid argument to V line: \"%.20s\"",
559                                        &bp[1]);
560                                break;
561                        }
562                        ConfigLevel = strtol(p, &ep, 10);
563
564                        /*
565                        **  Do heuristic tweaking for back compatibility.
566                        */
567
568                        if (ConfigLevel >= 5)
569                        {
570                                /* level 5 configs have short name in $w */
571                                p = macvalue('w', e);
572                                if (p != NULL && (p = strchr(p, '.')) != NULL)
573                                {
574                                        *p = '\0';
575                                        macdefine(&e->e_macro, A_TEMP, 'w',
576                                                  macvalue('w', e));
577                                }
578                        }
579                        if (ConfigLevel >= 6)
580                        {
581                                ColonOkInAddr = false;
582                        }
583
584                        /*
585                        **  Look for vendor code.
586                        */
587
588                        if (*ep++ == '/')
589                        {
590                                /* extract vendor code */
591                                for (p = ep; isascii(*p) && isalpha(*p); )
592                                        p++;
593                                *p = '\0';
594
595                                if (!setvendor(ep))
596                                        syserr("invalid V line vendor code: \"%s\"",
597                                                ep);
598                        }
599                        break;
600
601                  case 'K':
602                        expand(&bp[1], exbuf, sizeof exbuf, e);
603                        (void) makemapentry(exbuf);
604                        break;
605
606                  case 'E':
607                        p = strchr(bp, '=');
608                        if (p != NULL)
609                                *p++ = '\0';
610                        setuserenv(&bp[1], p);
611                        break;
612
613                  case 'X':             /* mail filter */
614#if MILTER
615                        milter_setup(&bp[1]);
616#else /* MILTER */
617                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
618                                             "Warning: Filter usage ('X') requires Milter support (-DMILTER)\n");
619#endif /* MILTER */
620                        break;
621
622                  default:
623                  badline:
624                        syserr("unknown configuration line \"%s\"", bp);
625                }
626                if (bp != buf)
627                        sm_free(bp); /* XXX */
628        }
629        if (sm_io_error(cf))
630        {
631                syserr("I/O read error");
632                finis(false, true, EX_OSFILE);
633        }
634        (void) sm_io_close(cf, SM_TIME_DEFAULT);
635        FileName = NULL;
636
637        /* initialize host maps from local service tables */
638        inithostmaps();
639
640        /* initialize daemon (if not defined yet) */
641        initdaemon();
642
643        /* determine if we need to do special name-server frotz */
644        {
645                int nmaps;
646                char *maptype[MAXMAPSTACK];
647                short mapreturn[MAXMAPACTIONS];
648
649                nmaps = switch_map_find("hosts", maptype, mapreturn);
650                UseNameServer = false;
651                if (nmaps > 0 && nmaps <= MAXMAPSTACK)
652                {
653                        register int mapno;
654
655                        for (mapno = 0; mapno < nmaps && !UseNameServer;
656                             mapno++)
657                        {
658                                if (strcmp(maptype[mapno], "dns") == 0)
659                                        UseNameServer = true;
660                        }
661                }
662        }
663}
664/*
665**  TRANSLATE_DOLLARS -- convert $x into internal form
666**
667**      Actually does all appropriate pre-processing of a config line
668**      to turn it into internal form.
669**
670**      Parameters:
671**              bp -- the buffer to translate.
672**
673**      Returns:
674**              None.  The buffer is translated in place.  Since the
675**              translations always make the buffer shorter, this is
676**              safe without a size parameter.
677*/
678
679void
680translate_dollars(bp)
681        char *bp;
682{
683        register char *p;
684        auto char *ep;
685
686        for (p = bp; *p != '\0'; p++)
687        {
688                if (*p == '#' && p > bp && ConfigLevel >= 3)
689                {
690                        register char *e;
691
692                        switch (*--p & 0377)
693                        {
694                          case MACROEXPAND:
695                                /* it's from $# -- let it go through */
696                                p++;
697                                break;
698
699                          case '\\':
700                                /* it's backslash escaped */
701                                (void) sm_strlcpy(p, p + 1, strlen(p));
702                                break;
703
704                          default:
705                                /* delete leading white space */
706                                while (isascii(*p) && isspace(*p) &&
707                                       *p != '\n' && p > bp)
708                                        p--;
709                                if ((e = strchr(++p, '\n')) != NULL)
710                                        (void) sm_strlcpy(p, e, strlen(p));
711                                else
712                                        *p-- = '\0';
713                                break;
714                        }
715                        continue;
716                }
717
718                if (*p != '$' || p[1] == '\0')
719                        continue;
720
721                if (p[1] == '$')
722                {
723                        /* actual dollar sign.... */
724                        (void) sm_strlcpy(p, p + 1, strlen(p));
725                        continue;
726                }
727
728                /* convert to macro expansion character */
729                *p++ = MACROEXPAND;
730
731                /* special handling for $=, $~, $&, and $? */
732                if (*p == '=' || *p == '~' || *p == '&' || *p == '?')
733                        p++;
734
735                /* convert macro name to code */
736                *p = macid_parse(p, &ep);
737                if (ep != p + 1)
738                        (void) sm_strlcpy(p + 1, ep, strlen(p + 1));
739        }
740
741        /* strip trailing white space from the line */
742        while (--p > bp && isascii(*p) && isspace(*p))
743                *p = '\0';
744}
745/*
746**  TOOMANY -- signal too many of some option
747**
748**      Parameters:
749**              id -- the id of the error line
750**              maxcnt -- the maximum possible values
751**
752**      Returns:
753**              none.
754**
755**      Side Effects:
756**              gives a syserr.
757*/
758
759static void
760toomany(id, maxcnt)
761        int id;
762        int maxcnt;
763{
764        syserr("too many %c lines, %d max", id, maxcnt);
765}
766/*
767**  FILECLASS -- read members of a class from a file
768**
769**      Parameters:
770**              class -- class to define.
771**              filename -- name of file to read.
772**              fmt -- scanf string to use for match.
773**              ismap -- if set, this is a map lookup.
774**              safe -- if set, this is a safe read.
775**              optional -- if set, it is not an error for the file to
776**                      not exist.
777**
778**      Returns:
779**              none
780**
781**      Side Effects:
782**              puts all lines in filename that match a scanf into
783**                      the named class.
784*/
785
786/*
787**  Break up the match into words and add to class.
788*/
789
790static void
791parse_class_words(class, line)
792        int class;
793        char *line;
794{
795        while (line != NULL && *line != '\0')
796        {
797                register char *q;
798
799                /* strip leading spaces */
800                while (isascii(*line) && isspace(*line))
801                        line++;
802                if (*line == '\0')
803                        break;
804
805                /* find the end of the word */
806                q = line;
807                while (*line != '\0' && !(isascii(*line) && isspace(*line)))
808                        line++;
809                if (*line != '\0')
810                        *line++ = '\0';
811
812                /* enter the word in the symbol table */
813                setclass(class, q);
814        }
815}
816
817static void
818fileclass(class, filename, fmt, ismap, safe, optional)
819        int class;
820        char *filename;
821        char *fmt;
822        bool ismap;
823        bool safe;
824        bool optional;
825{
826        SM_FILE_T *f;
827        long sff;
828        pid_t pid;
829        register char *p;
830        char buf[MAXLINE];
831
832        if (tTd(37, 2))
833                sm_dprintf("fileclass(%s, fmt=%s)\n", filename, fmt);
834
835        if (*filename == '\0')
836        {
837                syserr("fileclass: missing file name");
838                return;
839        }
840        else if (ismap)
841        {
842                int status = 0;
843                char *key;
844                char *mn;
845                char *cl, *spec;
846                STAB *mapclass;
847                MAP map;
848
849                mn = newstr(macname(class));
850
851                key = filename;
852
853                /* skip past key */
854                if ((p = strchr(filename, '@')) == NULL)
855                {
856                        /* should not happen */
857                        syserr("fileclass: bogus map specification");
858                        sm_free(mn);
859                        return;
860                }
861
862                /* skip past '@' */
863                *p++ = '\0';
864                cl = p;
865
866                if (strcmp(cl, "LDAP") == 0)
867                {
868                        int n;
869                        char *lc;
870                        char jbuf[MAXHOSTNAMELEN];
871                        char lcbuf[MAXLINE];
872
873                        /* Get $j */
874                        expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
875                        if (jbuf[0] == '\0')
876                        {
877                                (void) sm_strlcpy(jbuf, "localhost",
878                                                  sizeof jbuf);
879                        }
880
881                        /* impose the default schema */
882                        lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
883                        if (lc == NULL)
884                                lc = "";
885                        else
886                        {
887                                expand(lc, lcbuf, sizeof lcbuf, CurEnv);
888                                lc = lcbuf;
889                        }
890
891                        cl = "ldap";
892                        n = sm_snprintf(buf, sizeof buf,
893                                        "-k (&(objectClass=sendmailMTAClass)(sendmailMTAClassName=%s)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))) -v sendmailMTAClassValue",
894                                        mn, lc, jbuf);
895                        if (n >= sizeof buf)
896                        {
897                                syserr("fileclass: F{%s}: Default LDAP string too long",
898                                       mn);
899                                sm_free(mn);
900                                return;
901                        }
902                        spec = buf;
903                }
904                else
905                {
906                        if ((spec = strchr(cl, ':')) == NULL)
907                        {
908                                syserr("fileclass: F{%s}: missing map class",
909                                       mn);
910                                sm_free(mn);
911                                return;
912                        }
913                        *spec++ ='\0';
914                }
915
916                /* set up map structure */
917                mapclass = stab(cl, ST_MAPCLASS, ST_FIND);
918                if (mapclass == NULL)
919                {
920                        syserr("fileclass: F{%s}: class %s not available",
921                               mn, cl);
922                        sm_free(mn);
923                        return;
924                }
925                memset(&map, '\0', sizeof map);
926                map.map_class = &mapclass->s_mapclass;
927                map.map_mname = mn;
928                map.map_mflags |= MF_FILECLASS;
929
930                if (tTd(37, 5))
931                        sm_dprintf("fileclass: F{%s}: map class %s, key %s, spec %s\n",
932                                   mn, cl, key, spec);
933
934
935                /* parse map spec */
936                if (!map.map_class->map_parse(&map, spec))
937                {
938                        /* map_parse() showed the error already */
939                        sm_free(mn);
940                        return;
941                }
942                map.map_mflags |= MF_VALID;
943
944                /* open map */
945                if (map.map_class->map_open(&map, O_RDONLY))
946                {
947                        map.map_mflags |= MF_OPEN;
948                        map.map_pid = getpid();
949                }
950                else
951                {
952                        if (!optional &&
953                            !bitset(MF_OPTIONAL, map.map_mflags))
954                                syserr("fileclass: F{%s}: map open failed",
955                                       mn);
956                        sm_free(mn);
957                        return;
958                }
959
960                /* lookup */
961                p = (*map.map_class->map_lookup)(&map, key, NULL, &status);
962                if (status != EX_OK && status != EX_NOTFOUND)
963                {
964                        if (!optional)
965                                syserr("fileclass: F{%s}: map lookup failed",
966                                       mn);
967                        p = NULL;
968                }
969
970                /* use the results */
971                if (p != NULL)
972                        parse_class_words(class, p);
973
974                /* close map */
975                map.map_mflags |= MF_CLOSING;
976                map.map_class->map_close(&map);
977                map.map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
978                sm_free(mn);
979                return;
980        }
981        else if (filename[0] == '|')
982        {
983                auto int fd;
984                int i;
985                char *argv[MAXPV + 1];
986
987                i = 0;
988                for (p = strtok(&filename[1], " \t");
989                     p != NULL && i < MAXPV;
990                     p = strtok(NULL, " \t"))
991                        argv[i++] = p;
992                argv[i] = NULL;
993                pid = prog_open(argv, &fd, CurEnv);
994                if (pid < 0)
995                        f = NULL;
996                else
997                        f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
998                                       (void *) &fd, SM_IO_RDONLY, NULL);
999        }
1000        else
1001        {
1002                pid = -1;
1003                sff = SFF_REGONLY;
1004                if (!bitnset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail))
1005                        sff |= SFF_SAFEDIRPATH;
1006                if (!bitnset(DBS_LINKEDCLASSFILEINWRITABLEDIR,
1007                             DontBlameSendmail))
1008                        sff |= SFF_NOWLINK;
1009                if (safe)
1010                        sff |= SFF_OPENASROOT;
1011                else if (RealUid == 0)
1012                        sff |= SFF_ROOTOK;
1013                if (DontLockReadFiles)
1014                        sff |= SFF_NOLOCK;
1015                f = safefopen(filename, O_RDONLY, 0, sff);
1016        }
1017        if (f == NULL)
1018        {
1019                if (!optional)
1020                        syserr("fileclass: cannot open '%s'", filename);
1021                return;
1022        }
1023
1024        while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
1025        {
1026#if SCANF
1027                char wordbuf[MAXLINE + 1];
1028#endif /* SCANF */
1029
1030                if (buf[0] == '#')
1031                        continue;
1032#if SCANF
1033                if (sm_io_sscanf(buf, fmt, wordbuf) != 1)
1034                        continue;
1035                p = wordbuf;
1036#else /* SCANF */
1037                p = buf;
1038#endif /* SCANF */
1039
1040                parse_class_words(class, p);
1041
1042                /*
1043                **  If anything else is added here,
1044                **  check if the '@' map case above
1045                **  needs the code as well.
1046                */
1047        }
1048
1049        (void) sm_io_close(f, SM_TIME_DEFAULT);
1050        if (pid > 0)
1051                (void) waitfor(pid);
1052}
1053/*
1054**  MAKEMAILER -- define a new mailer.
1055**
1056**      Parameters:
1057**              line -- description of mailer.  This is in labeled
1058**                      fields.  The fields are:
1059**                         A -- the argv for this mailer
1060**                         C -- the character set for MIME conversions
1061**                         D -- the directory to run in
1062**                         E -- the eol string
1063**                         F -- the flags associated with the mailer
1064**                         L -- the maximum line length
1065**                         M -- the maximum message size
1066**                         N -- the niceness at which to run
1067**                         P -- the path to the mailer
1068**                         Q -- the queue group for the mailer
1069**                         R -- the recipient rewriting set
1070**                         S -- the sender rewriting set
1071**                         T -- the mailer type (for DSNs)
1072**                         U -- the uid to run as
1073**                         W -- the time to wait at the end
1074**                         m -- maximum messages per connection
1075**                         r -- maximum number of recipients per message
1076**                         / -- new root directory
1077**                      The first word is the canonical name of the mailer.
1078**
1079**      Returns:
1080**              none.
1081**
1082**      Side Effects:
1083**              enters the mailer into the mailer table.
1084*/
1085
1086void
1087makemailer(line)
1088        char *line;
1089{
1090        register char *p;
1091        register struct mailer *m;
1092        register STAB *s;
1093        int i;
1094        char fcode;
1095        auto char *endp;
1096        static int nextmailer = 0;      /* "free" index into Mailer struct */
1097
1098        /* allocate a mailer and set up defaults */
1099        m = (struct mailer *) xalloc(sizeof *m);
1100        memset((char *) m, '\0', sizeof *m);
1101        errno = 0; /* avoid bogus error text */
1102
1103        /* collect the mailer name */
1104        for (p = line;
1105             *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
1106             p++)
1107                continue;
1108        if (*p != '\0')
1109                *p++ = '\0';
1110        if (line[0] == '\0')
1111        {
1112                syserr("name required for mailer");
1113                return;
1114        }
1115        m->m_name = newstr(line);
1116        m->m_qgrp = NOQGRP;
1117
1118        /* now scan through and assign info from the fields */
1119        while (*p != '\0')
1120        {
1121                auto char *delimptr;
1122
1123                while (*p != '\0' &&
1124                       (*p == ',' || (isascii(*p) && isspace(*p))))
1125                        p++;
1126
1127                /* p now points to field code */
1128                fcode = *p;
1129                while (*p != '\0' && *p != '=' && *p != ',')
1130                        p++;
1131                if (*p++ != '=')
1132                {
1133                        syserr("mailer %s: `=' expected", m->m_name);
1134                        return;
1135                }
1136                while (isascii(*p) && isspace(*p))
1137                        p++;
1138
1139                /* p now points to the field body */
1140                p = munchstring(p, &delimptr, ',');
1141
1142                /* install the field into the mailer struct */
1143                switch (fcode)
1144                {
1145                  case 'P':             /* pathname */
1146                        if (*p != '\0') /* error is issued below */
1147                                m->m_mailer = newstr(p);
1148                        break;
1149
1150                  case 'F':             /* flags */
1151                        for (; *p != '\0'; p++)
1152                        {
1153                                if (!(isascii(*p) && isspace(*p)))
1154                                {
1155#if _FFR_DEPRECATE_MAILER_FLAG_I
1156                                        if (*p == M_INTERNAL)
1157                                                sm_syslog(LOG_WARNING, NOQID,
1158                                                          "WARNING: mailer=%s, flag=%c deprecated",
1159                                                          m->m_name, *p);
1160#endif /* _FFR_DEPRECATE_MAILER_FLAG_I */
1161                                        setbitn(bitidx(*p), m->m_flags);
1162                                }
1163                        }
1164                        break;
1165
1166                  case 'S':             /* sender rewriting ruleset */
1167                  case 'R':             /* recipient rewriting ruleset */
1168                        i = strtorwset(p, &endp, ST_ENTER);
1169                        if (i < 0)
1170                                return;
1171                        if (fcode == 'S')
1172                                m->m_sh_rwset = m->m_se_rwset = i;
1173                        else
1174                                m->m_rh_rwset = m->m_re_rwset = i;
1175
1176                        p = endp;
1177                        if (*p++ == '/')
1178                        {
1179                                i = strtorwset(p, NULL, ST_ENTER);
1180                                if (i < 0)
1181                                        return;
1182                                if (fcode == 'S')
1183                                        m->m_sh_rwset = i;
1184                                else
1185                                        m->m_rh_rwset = i;
1186                        }
1187                        break;
1188
1189                  case 'E':             /* end of line string */
1190                        if (*p == '\0')
1191                                syserr("mailer %s: null end-of-line string",
1192                                        m->m_name);
1193                        else
1194                                m->m_eol = newstr(p);
1195                        break;
1196
1197                  case 'A':             /* argument vector */
1198                        if (*p != '\0') /* error is issued below */
1199                                m->m_argv = makeargv(p);
1200                        break;
1201
1202                  case 'M':             /* maximum message size */
1203                        m->m_maxsize = atol(p);
1204                        break;
1205
1206                  case 'm':             /* maximum messages per connection */
1207                        m->m_maxdeliveries = atoi(p);
1208                        break;
1209
1210                  case 'r':             /* max recipient per envelope */
1211                        m->m_maxrcpt = atoi(p);
1212                        break;
1213
1214                  case 'L':             /* maximum line length */
1215                        m->m_linelimit = atoi(p);
1216                        if (m->m_linelimit < 0)
1217                                m->m_linelimit = 0;
1218                        break;
1219
1220                  case 'N':             /* run niceness */
1221                        m->m_nice = atoi(p);
1222                        break;
1223
1224                  case 'D':             /* working directory */
1225                        if (*p == '\0')
1226                                syserr("mailer %s: null working directory",
1227                                        m->m_name);
1228                        else
1229                                m->m_execdir = newstr(p);
1230                        break;
1231
1232                  case 'C':             /* default charset */
1233                        if (*p == '\0')
1234                                syserr("mailer %s: null charset", m->m_name);
1235                        else
1236                                m->m_defcharset = newstr(p);
1237                        break;
1238
1239                  case 'Q':             /* queue for this mailer */
1240                        if (*p == '\0')
1241                        {
1242                                syserr("mailer %s: null queue", m->m_name);
1243                                break;
1244                        }
1245                        s = stab(p, ST_QUEUE, ST_FIND);
1246                        if (s == NULL)
1247                                syserr("mailer %s: unknown queue %s",
1248                                        m->m_name, p);
1249                        else
1250                                m->m_qgrp = s->s_quegrp->qg_index;
1251                        break;
1252
1253                  case 'T':             /* MTA-Name/Address/Diagnostic types */
1254                        /* extract MTA name type; default to "dns" */
1255                        m->m_mtatype = newstr(p);
1256                        p = strchr(m->m_mtatype, '/');
1257                        if (p != NULL)
1258                        {
1259                                *p++ = '\0';
1260                                if (*p == '\0')
1261                                        p = NULL;
1262                        }
1263                        if (*m->m_mtatype == '\0')
1264                                m->m_mtatype = "dns";
1265
1266                        /* extract address type; default to "rfc822" */
1267                        m->m_addrtype = p;
1268                        if (p != NULL)
1269                                p = strchr(p, '/');
1270                        if (p != NULL)
1271                        {
1272                                *p++ = '\0';
1273                                if (*p == '\0')
1274                                        p = NULL;
1275                        }
1276                        if (m->m_addrtype == NULL || *m->m_addrtype == '\0')
1277                                m->m_addrtype = "rfc822";
1278
1279                        /* extract diagnostic type; default to "smtp" */
1280                        m->m_diagtype = p;
1281                        if (m->m_diagtype == NULL || *m->m_diagtype == '\0')
1282                                m->m_diagtype = "smtp";
1283                        break;
1284
1285                  case 'U':             /* user id */
1286                        if (isascii(*p) && !isdigit(*p))
1287                        {
1288                                char *q = p;
1289                                struct passwd *pw;
1290
1291                                while (*p != '\0' && isascii(*p) &&
1292                                       (isalnum(*p) || strchr("-_", *p) != NULL))
1293                                        p++;
1294                                while (isascii(*p) && isspace(*p))
1295                                        *p++ = '\0';
1296                                if (*p != '\0')
1297                                        *p++ = '\0';
1298                                if (*q == '\0')
1299                                {
1300                                        syserr("mailer %s: null user name",
1301                                                m->m_name);
1302                                        break;
1303                                }
1304                                pw = sm_getpwnam(q);
1305                                if (pw == NULL)
1306                                {
1307                                        syserr("readcf: mailer U= flag: unknown user %s", q);
1308                                        break;
1309                                }
1310                                else
1311                                {
1312                                        m->m_uid = pw->pw_uid;
1313                                        m->m_gid = pw->pw_gid;
1314                                }
1315                        }
1316                        else
1317                        {
1318                                auto char *q;
1319
1320                                m->m_uid = strtol(p, &q, 0);
1321                                p = q;
1322                                while (isascii(*p) && isspace(*p))
1323                                        p++;
1324                                if (*p != '\0')
1325                                        p++;
1326                        }
1327                        while (isascii(*p) && isspace(*p))
1328                                p++;
1329                        if (*p == '\0')
1330                                break;
1331                        if (isascii(*p) && !isdigit(*p))
1332                        {
1333                                char *q = p;
1334                                struct group *gr;
1335
1336                                while (isascii(*p) && isalnum(*p))
1337                                        p++;
1338                                *p++ = '\0';
1339                                if (*q == '\0')
1340                                {
1341                                        syserr("mailer %s: null group name",
1342                                                m->m_name);
1343                                        break;
1344                                }
1345                                gr = getgrnam(q);
1346                                if (gr == NULL)
1347                                {
1348                                        syserr("readcf: mailer U= flag: unknown group %s", q);
1349                                        break;
1350                                }
1351                                else
1352                                        m->m_gid = gr->gr_gid;
1353                        }
1354                        else
1355                        {
1356                                m->m_gid = strtol(p, NULL, 0);
1357                        }
1358                        break;
1359
1360                  case 'W':             /* wait timeout */
1361                        m->m_wait = convtime(p, 's');
1362                        break;
1363
1364                  case '/':             /* new root directory */
1365                        if (*p == '\0')
1366                                syserr("mailer %s: null root directory",
1367                                        m->m_name);
1368                        else
1369                                m->m_rootdir = newstr(p);
1370                        break;
1371
1372                  default:
1373                        syserr("M%s: unknown mailer equate %c=",
1374                               m->m_name, fcode);
1375                        break;
1376                }
1377
1378                p = delimptr;
1379        }
1380
1381#if !HASRRESVPORT
1382        if (bitnset(M_SECURE_PORT, m->m_flags))
1383        {
1384                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1385                                     "M%s: Warning: F=%c set on system that doesn't support rresvport()\n",
1386                                     m->m_name, M_SECURE_PORT);
1387        }
1388#endif /* !HASRRESVPORT */
1389
1390#if !HASNICE
1391        if (m->m_nice != 0)
1392        {
1393                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1394                                     "M%s: Warning: N= set on system that doesn't support nice()\n",
1395                                     m->m_name);
1396        }
1397#endif /* !HASNICE */
1398
1399        /* do some rationality checking */
1400        if (m->m_argv == NULL)
1401        {
1402                syserr("M%s: A= argument required", m->m_name);
1403                return;
1404        }
1405        if (m->m_mailer == NULL)
1406        {
1407                syserr("M%s: P= argument required", m->m_name);
1408                return;
1409        }
1410
1411        if (nextmailer >= MAXMAILERS)
1412        {
1413                syserr("too many mailers defined (%d max)", MAXMAILERS);
1414                return;
1415        }
1416
1417        if (m->m_maxrcpt <= 0)
1418                m->m_maxrcpt = DEFAULT_MAX_RCPT;
1419
1420        /* do some heuristic cleanup for back compatibility */
1421        if (bitnset(M_LIMITS, m->m_flags))
1422        {
1423                if (m->m_linelimit == 0)
1424                        m->m_linelimit = SMTPLINELIM;
1425                if (ConfigLevel < 2)
1426                        setbitn(M_7BITS, m->m_flags);
1427        }
1428
1429        if (strcmp(m->m_mailer, "[TCP]") == 0)
1430        {
1431                syserr("M%s: P=[TCP] must be replaced by P=[IPC]", m->m_name);
1432                return;
1433        }
1434
1435        if (strcmp(m->m_mailer, "[IPC]") == 0)
1436        {
1437                /* Use the second argument for host or path to socket */
1438                if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
1439                    m->m_argv[1][0] == '\0')
1440                {
1441                        syserr("M%s: too few parameters for %s mailer",
1442                               m->m_name, m->m_mailer);
1443                        return;
1444                }
1445                if (strcmp(m->m_argv[0], "TCP") != 0
1446#if NETUNIX
1447                    && strcmp(m->m_argv[0], "FILE") != 0
1448#endif /* NETUNIX */
1449                    )
1450                {
1451                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1452                                             "M%s: Warning: first argument in %s mailer must be %s\n",
1453                                             m->m_name, m->m_mailer,
1454#if NETUNIX
1455                                             "TCP or FILE"
1456#else /* NETUNIX */
1457                                             "TCP"
1458#endif /* NETUNIX */
1459                                     );
1460                }
1461                if (m->m_mtatype == NULL)
1462                        m->m_mtatype = "dns";
1463                if (m->m_addrtype == NULL)
1464                        m->m_addrtype = "rfc822";
1465                if (m->m_diagtype == NULL)
1466                {
1467                        if (m->m_argv[0] != NULL &&
1468                            strcmp(m->m_argv[0], "FILE") == 0)
1469                                m->m_diagtype = "x-unix";
1470                        else
1471                                m->m_diagtype = "smtp";
1472                }
1473        }
1474        else if (strcmp(m->m_mailer, "[FILE]") == 0)
1475        {
1476                /* Use the second argument for filename */
1477                if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
1478                    m->m_argv[2] != NULL)
1479                {
1480                        syserr("M%s: too %s parameters for [FILE] mailer",
1481                               m->m_name,
1482                               (m->m_argv[0] == NULL ||
1483                                m->m_argv[1] == NULL) ? "few" : "many");
1484                        return;
1485                }
1486                else if (strcmp(m->m_argv[0], "FILE") != 0)
1487                {
1488                        syserr("M%s: first argument in [FILE] mailer must be FILE",
1489                               m->m_name);
1490                        return;
1491                }
1492        }
1493
1494        if (m->m_eol == NULL)
1495        {
1496                char **pp;
1497
1498                /* default for SMTP is \r\n; use \n for local delivery */
1499                for (pp = m->m_argv; *pp != NULL; pp++)
1500                {
1501                        for (p = *pp; *p != '\0'; )
1502                        {
1503                                if ((*p++ & 0377) == MACROEXPAND && *p == 'u')
1504                                        break;
1505                        }
1506                        if (*p != '\0')
1507                                break;
1508                }
1509                if (*pp == NULL)
1510                        m->m_eol = "\r\n";
1511                else
1512                        m->m_eol = "\n";
1513        }
1514
1515        /* enter the mailer into the symbol table */
1516        s = stab(m->m_name, ST_MAILER, ST_ENTER);
1517        if (s->s_mailer != NULL)
1518        {
1519                i = s->s_mailer->m_mno;
1520                sm_free(s->s_mailer); /* XXX */
1521        }
1522        else
1523        {
1524                i = nextmailer++;
1525        }
1526        Mailer[i] = s->s_mailer = m;
1527        m->m_mno = i;
1528}
1529/*
1530**  MUNCHSTRING -- translate a string into internal form.
1531**
1532**      Parameters:
1533**              p -- the string to munch.
1534**              delimptr -- if non-NULL, set to the pointer of the
1535**                      field delimiter character.
1536**              delim -- the delimiter for the field.
1537**
1538**      Returns:
1539**              the munched string.
1540**
1541**      Side Effects:
1542**              the munched string is a local static buffer.
1543**              it must be copied before the function is called again.
1544*/
1545
1546char *
1547munchstring(p, delimptr, delim)
1548        register char *p;
1549        char **delimptr;
1550        int delim;
1551{
1552        register char *q;
1553        bool backslash = false;
1554        bool quotemode = false;
1555        static char buf[MAXLINE];
1556
1557        for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++)
1558        {
1559                if (backslash)
1560                {
1561                        /* everything is roughly literal */
1562                        backslash = false;
1563                        switch (*p)
1564                        {
1565                          case 'r':             /* carriage return */
1566                                *q++ = '\r';
1567                                continue;
1568
1569                          case 'n':             /* newline */
1570                                *q++ = '\n';
1571                                continue;
1572
1573                          case 'f':             /* form feed */
1574                                *q++ = '\f';
1575                                continue;
1576
1577                          case 'b':             /* backspace */
1578                                *q++ = '\b';
1579                                continue;
1580                        }
1581                        *q++ = *p;
1582                }
1583                else
1584                {
1585                        if (*p == '\\')
1586                                backslash = true;
1587                        else if (*p == '"')
1588                                quotemode = !quotemode;
1589                        else if (quotemode || *p != delim)
1590                                *q++ = *p;
1591                        else
1592                                break;
1593                }
1594        }
1595
1596        if (delimptr != NULL)
1597                *delimptr = p;
1598        *q++ = '\0';
1599        return buf;
1600}
1601/*
1602**  EXTRQUOTSTR -- extract a (quoted) string.
1603**
1604**      This routine deals with quoted (") strings and escaped
1605**      spaces (\\ ).
1606**
1607**      Parameters:
1608**              p -- source string.
1609**              delimptr -- if non-NULL, set to the pointer of the
1610**                      field delimiter character.
1611**              delimbuf -- delimiters for the field.
1612**              st -- if non-NULL, store the return value (whether the
1613**                      string was correctly quoted) here.
1614**
1615**      Returns:
1616**              the extracted string.
1617**
1618**      Side Effects:
1619**              the returned string is a local static buffer.
1620**              it must be copied before the function is called again.
1621*/
1622
1623static char *
1624extrquotstr(p, delimptr, delimbuf, st)
1625        register char *p;
1626        char **delimptr;
1627        char *delimbuf;
1628        bool *st;
1629{
1630        register char *q;
1631        bool backslash = false;
1632        bool quotemode = false;
1633        static char buf[MAXLINE];
1634
1635        for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++)
1636        {
1637                if (backslash)
1638                {
1639                        backslash = false;
1640                        if (*p != ' ')
1641                                *q++ = '\\';
1642                }
1643                if (*p == '\\')
1644                        backslash = true;
1645                else if (*p == '"')
1646                        quotemode = !quotemode;
1647                else if (quotemode ||
1648                         strchr(delimbuf, (int) *p) == NULL)
1649                        *q++ = *p;
1650                else
1651                        break;
1652        }
1653
1654        if (delimptr != NULL)
1655                *delimptr = p;
1656        *q++ = '\0';
1657        if (st != NULL)
1658                *st = !(quotemode || backslash);
1659        return buf;
1660}
1661/*
1662**  MAKEARGV -- break up a string into words
1663**
1664**      Parameters:
1665**              p -- the string to break up.
1666**
1667**      Returns:
1668**              a char **argv (dynamically allocated)
1669**
1670**      Side Effects:
1671**              munges p.
1672*/
1673
1674static char **
1675makeargv(p)
1676        register char *p;
1677{
1678        char *q;
1679        int i;
1680        char **avp;
1681        char *argv[MAXPV + 1];
1682
1683        /* take apart the words */
1684        i = 0;
1685        while (*p != '\0' && i < MAXPV)
1686        {
1687                q = p;
1688                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1689                        p++;
1690                while (isascii(*p) && isspace(*p))
1691                        *p++ = '\0';
1692                argv[i++] = newstr(q);
1693        }
1694        argv[i++] = NULL;
1695
1696        /* now make a copy of the argv */
1697        avp = (char **) xalloc(sizeof *avp * i);
1698        memmove((char *) avp, (char *) argv, sizeof *avp * i);
1699
1700        return avp;
1701}
1702/*
1703**  PRINTRULES -- print rewrite rules (for debugging)
1704**
1705**      Parameters:
1706**              none.
1707**
1708**      Returns:
1709**              none.
1710**
1711**      Side Effects:
1712**              prints rewrite rules.
1713*/
1714
1715void
1716printrules()
1717{
1718        register struct rewrite *rwp;
1719        register int ruleset;
1720
1721        for (ruleset = 0; ruleset < 10; ruleset++)
1722        {
1723                if (RewriteRules[ruleset] == NULL)
1724                        continue;
1725                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1726                                     "\n----Rule Set %d:", ruleset);
1727
1728                for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
1729                {
1730                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1731                                             "\nLHS:");
1732                        printav(rwp->r_lhs);
1733                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1734                                             "RHS:");
1735                        printav(rwp->r_rhs);
1736                }
1737        }
1738}
1739/*
1740**  PRINTMAILER -- print mailer structure (for debugging)
1741**
1742**      Parameters:
1743**              m -- the mailer to print
1744**
1745**      Returns:
1746**              none.
1747*/
1748
1749void
1750printmailer(m)
1751        register MAILER *m;
1752{
1753        int j;
1754
1755        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1756                             "mailer %d (%s): P=%s S=", m->m_mno, m->m_name,
1757                             m->m_mailer);
1758        if (RuleSetNames[m->m_se_rwset] == NULL)
1759                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%d/",
1760                                     m->m_se_rwset);
1761        else
1762                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s/",
1763                                     RuleSetNames[m->m_se_rwset]);
1764        if (RuleSetNames[m->m_sh_rwset] == NULL)
1765                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%d R=",
1766                                     m->m_sh_rwset);
1767        else
1768                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s R=",
1769                                     RuleSetNames[m->m_sh_rwset]);
1770        if (RuleSetNames[m->m_re_rwset] == NULL)
1771                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%d/",
1772                                     m->m_re_rwset);
1773        else
1774                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s/",
1775                                     RuleSetNames[m->m_re_rwset]);
1776        if (RuleSetNames[m->m_rh_rwset] == NULL)
1777                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%d ",
1778                                     m->m_rh_rwset);
1779        else
1780                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s ",
1781                                     RuleSetNames[m->m_rh_rwset]);
1782        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "M=%ld U=%d:%d F=",
1783                             m->m_maxsize, (int) m->m_uid, (int) m->m_gid);
1784        for (j = '\0'; j <= '\177'; j++)
1785                if (bitnset(j, m->m_flags))
1786                        (void) sm_io_putc(smioout, SM_TIME_DEFAULT, j);
1787        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " L=%d E=",
1788                             m->m_linelimit);
1789        xputs(m->m_eol);
1790        if (m->m_defcharset != NULL)
1791                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " C=%s",
1792                                     m->m_defcharset);
1793        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " T=%s/%s/%s",
1794                             m->m_mtatype == NULL
1795                                ? "<undefined>" : m->m_mtatype,
1796                             m->m_addrtype == NULL
1797                                ? "<undefined>" : m->m_addrtype,
1798                             m->m_diagtype == NULL
1799                                ? "<undefined>" : m->m_diagtype);
1800        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " r=%d", m->m_maxrcpt);
1801        if (m->m_argv != NULL)
1802        {
1803                char **a = m->m_argv;
1804
1805                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " A=");
1806                while (*a != NULL)
1807                {
1808                        if (a != m->m_argv)
1809                                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1810                                                     " ");
1811                        xputs(*a++);
1812                }
1813        }
1814        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n");
1815}
1816/*
1817**  SETOPTION -- set global processing option
1818**
1819**      Parameters:
1820**              opt -- option name.
1821**              val -- option value (as a text string).
1822**              safe -- set if this came from a configuration file.
1823**                      Some options (if set from the command line) will
1824**                      reset the user id to avoid security problems.
1825**              sticky -- if set, don't let other setoptions override
1826**                      this value.
1827**              e -- the main envelope.
1828**
1829**      Returns:
1830**              none.
1831**
1832**      Side Effects:
1833**              Sets options as implied by the arguments.
1834*/
1835
1836static BITMAP256        StickyOpt;              /* set if option is stuck */
1837
1838#if NAMED_BIND
1839
1840static struct resolverflags
1841{
1842        char    *rf_name;       /* name of the flag */
1843        long    rf_bits;        /* bits to set/clear */
1844} ResolverFlags[] =
1845{
1846        { "debug",      RES_DEBUG       },
1847        { "aaonly",     RES_AAONLY      },
1848        { "usevc",      RES_USEVC       },
1849        { "primary",    RES_PRIMARY     },
1850        { "igntc",      RES_IGNTC       },
1851        { "recurse",    RES_RECURSE     },
1852        { "defnames",   RES_DEFNAMES    },
1853        { "stayopen",   RES_STAYOPEN    },
1854        { "dnsrch",     RES_DNSRCH      },
1855# ifdef RES_USE_INET6
1856        { "use_inet6",  RES_USE_INET6   },
1857# endif /* RES_USE_INET6 */
1858        { "true",       0               },      /* avoid error on old syntax */
1859        { NULL,         0               }
1860};
1861
1862#endif /* NAMED_BIND */
1863
1864#define OI_NONE         0       /* no special treatment */
1865#define OI_SAFE         0x0001  /* safe for random people to use */
1866#define OI_SUBOPT       0x0002  /* option has suboptions */
1867
1868static struct optioninfo
1869{
1870        char            *o_name;        /* long name of option */
1871        unsigned char   o_code;         /* short name of option */
1872        unsigned short  o_flags;        /* option flags */
1873} OptionTab[] =
1874{
1875#if defined(SUN_EXTENSIONS) && defined(REMOTE_MODE)
1876        { "RemoteMode",                 '>',            OI_NONE },
1877#endif /* defined(SUN_EXTENSIONS) && defined(REMOTE_MODE) */
1878        { "SevenBitInput",              '7',            OI_SAFE },
1879        { "EightBitMode",               '8',            OI_SAFE },
1880        { "AliasFile",                  'A',            OI_NONE },
1881        { "AliasWait",                  'a',            OI_NONE },
1882        { "BlankSub",                   'B',            OI_NONE },
1883        { "MinFreeBlocks",              'b',            OI_SAFE },
1884        { "CheckpointInterval",         'C',            OI_SAFE },
1885        { "HoldExpensive",              'c',            OI_NONE },
1886        { "DeliveryMode",               'd',            OI_SAFE },
1887        { "ErrorHeader",                'E',            OI_NONE },
1888        { "ErrorMode",                  'e',            OI_SAFE },
1889        { "TempFileMode",               'F',            OI_NONE },
1890        { "SaveFromLine",               'f',            OI_NONE },
1891        { "MatchGECOS",                 'G',            OI_NONE },
1892
1893        /* no long name, just here to avoid problems in setoption */
1894        { "",                           'g',            OI_NONE },
1895        { "HelpFile",                   'H',            OI_NONE },
1896        { "MaxHopCount",                'h',            OI_NONE },
1897        { "ResolverOptions",            'I',            OI_NONE },
1898        { "IgnoreDots",                 'i',            OI_SAFE },
1899        { "ForwardPath",                'J',            OI_NONE },
1900        { "SendMimeErrors",             'j',            OI_SAFE },
1901        { "ConnectionCacheSize",        'k',            OI_NONE },
1902        { "ConnectionCacheTimeout",     'K',            OI_NONE },
1903        { "UseErrorsTo",                'l',            OI_NONE },
1904        { "LogLevel",                   'L',            OI_SAFE },
1905        { "MeToo",                      'm',            OI_SAFE },
1906
1907        /* no long name, just here to avoid problems in setoption */
1908        { "",                           'M',            OI_NONE },
1909        { "CheckAliases",               'n',            OI_NONE },
1910        { "OldStyleHeaders",            'o',            OI_SAFE },
1911        { "DaemonPortOptions",          'O',            OI_NONE },
1912        { "PrivacyOptions",             'p',            OI_SAFE },
1913        { "PostmasterCopy",             'P',            OI_NONE },
1914        { "QueueFactor",                'q',            OI_NONE },
1915        { "QueueDirectory",             'Q',            OI_NONE },
1916        { "DontPruneRoutes",            'R',            OI_NONE },
1917        { "Timeout",                    'r',            OI_SUBOPT },
1918        { "StatusFile",                 'S',            OI_NONE },
1919        { "SuperSafe",                  's',            OI_SAFE },
1920        { "QueueTimeout",               'T',            OI_NONE },
1921        { "TimeZoneSpec",               't',            OI_NONE },
1922        { "UserDatabaseSpec",           'U',            OI_NONE },
1923        { "DefaultUser",                'u',            OI_NONE },
1924        { "FallbackMXhost",             'V',            OI_NONE },
1925        { "Verbose",                    'v',            OI_SAFE },
1926        { "TryNullMXList",              'w',            OI_NONE },
1927        { "QueueLA",                    'x',            OI_NONE },
1928        { "RefuseLA",                   'X',            OI_NONE },
1929        { "RecipientFactor",            'y',            OI_NONE },
1930        { "ForkEachJob",                'Y',            OI_NONE },
1931        { "ClassFactor",                'z',            OI_NONE },
1932        { "RetryFactor",                'Z',            OI_NONE },
1933#define O_QUEUESORTORD  0x81
1934        { "QueueSortOrder",             O_QUEUESORTORD, OI_SAFE },
1935#define O_HOSTSFILE     0x82
1936        { "HostsFile",                  O_HOSTSFILE,    OI_NONE },
1937#define O_MQA           0x83
1938        { "MinQueueAge",                O_MQA,          OI_SAFE },
1939#define O_DEFCHARSET    0x85
1940        { "DefaultCharSet",             O_DEFCHARSET,   OI_SAFE },
1941#define O_SSFILE        0x86
1942        { "ServiceSwitchFile",          O_SSFILE,       OI_NONE },
1943#define O_DIALDELAY     0x87
1944        { "DialDelay",                  O_DIALDELAY,    OI_SAFE },
1945#define O_NORCPTACTION  0x88
1946        { "NoRecipientAction",          O_NORCPTACTION, OI_SAFE },
1947#define O_SAFEFILEENV   0x89
1948        { "SafeFileEnvironment",        O_SAFEFILEENV,  OI_NONE },
1949#define O_MAXMSGSIZE    0x8a
1950        { "MaxMessageSize",             O_MAXMSGSIZE,   OI_NONE },
1951#define O_COLONOKINADDR 0x8b
1952        { "ColonOkInAddr",              O_COLONOKINADDR, OI_SAFE },
1953#define O_MAXQUEUERUN   0x8c
1954        { "MaxQueueRunSize",            O_MAXQUEUERUN,  OI_SAFE },
1955#define O_MAXCHILDREN   0x8d
1956        { "MaxDaemonChildren",          O_MAXCHILDREN,  OI_NONE },
1957#define O_KEEPCNAMES    0x8e
1958        { "DontExpandCnames",           O_KEEPCNAMES,   OI_NONE },
1959#define O_MUSTQUOTE     0x8f
1960        { "MustQuoteChars",             O_MUSTQUOTE,    OI_NONE },
1961#define O_SMTPGREETING  0x90
1962        { "SmtpGreetingMessage",        O_SMTPGREETING, OI_NONE },
1963#define O_UNIXFROM      0x91
1964        { "UnixFromLine",               O_UNIXFROM,     OI_NONE },
1965#define O_OPCHARS       0x92
1966        { "OperatorChars",              O_OPCHARS,      OI_NONE },
1967#define O_DONTINITGRPS  0x93
1968        { "DontInitGroups",             O_DONTINITGRPS, OI_NONE },
1969#define O_SLFH          0x94
1970        { "SingleLineFromHeader",       O_SLFH,         OI_SAFE },
1971#define O_ABH           0x95
1972        { "AllowBogusHELO",             O_ABH,          OI_SAFE },
1973#define O_CONNTHROT     0x97
1974        { "ConnectionRateThrottle",     O_CONNTHROT,    OI_NONE },
1975#define O_UGW           0x99
1976        { "UnsafeGroupWrites",          O_UGW,          OI_NONE },
1977#define O_DBLBOUNCE     0x9a
1978        { "DoubleBounceAddress",        O_DBLBOUNCE,    OI_NONE },
1979#define O_HSDIR         0x9b
1980        { "HostStatusDirectory",        O_HSDIR,        OI_NONE },
1981#define O_SINGTHREAD    0x9c
1982        { "SingleThreadDelivery",       O_SINGTHREAD,   OI_NONE },
1983#define O_RUNASUSER     0x9d
1984        { "RunAsUser",                  O_RUNASUSER,    OI_NONE },
1985#define O_DSN_RRT       0x9e
1986        { "RrtImpliesDsn",              O_DSN_RRT,      OI_NONE },
1987#define O_PIDFILE       0x9f
1988        { "PidFile",                    O_PIDFILE,      OI_NONE },
1989#define O_DONTBLAMESENDMAIL     0xa0
1990        { "DontBlameSendmail",          O_DONTBLAMESENDMAIL,    OI_NONE },
1991#define O_DPI           0xa1
1992        { "DontProbeInterfaces",        O_DPI,          OI_NONE },
1993#define O_MAXRCPT       0xa2
1994        { "MaxRecipientsPerMessage",    O_MAXRCPT,      OI_SAFE },
1995#define O_DEADLETTER    0xa3
1996        { "DeadLetterDrop",             O_DEADLETTER,   OI_NONE },
1997#if _FFR_DONTLOCKFILESFORREAD_OPTION
1998# define O_DONTLOCK     0xa4
1999        { "DontLockFilesForRead",       O_DONTLOCK,     OI_NONE },
2000#endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */
2001#define O_MAXALIASRCSN  0xa5
2002        { "MaxAliasRecursion",          O_MAXALIASRCSN, OI_NONE },
2003#define O_CNCTONLYTO    0xa6
2004        { "ConnectOnlyTo",              O_CNCTONLYTO,   OI_NONE },
2005#define O_TRUSTUSER     0xa7
2006        { "TrustedUser",                O_TRUSTUSER,    OI_NONE },
2007#define O_MAXMIMEHDRLEN 0xa8
2008        { "MaxMimeHeaderLength",        O_MAXMIMEHDRLEN,        OI_NONE },
2009#define O_CONTROLSOCKET 0xa9
2010        { "ControlSocketName",          O_CONTROLSOCKET,        OI_NONE },
2011#define O_MAXHDRSLEN    0xaa
2012        { "MaxHeadersLength",           O_MAXHDRSLEN,   OI_NONE },
2013#if _FFR_MAX_FORWARD_ENTRIES
2014# define O_MAXFORWARD   0xab
2015        { "MaxForwardEntries",          O_MAXFORWARD,   OI_NONE },
2016#endif /* _FFR_MAX_FORWARD_ENTRIES */
2017#define O_PROCTITLEPREFIX       0xac
2018        { "ProcessTitlePrefix",         O_PROCTITLEPREFIX,      OI_NONE },
2019#define O_SASLINFO      0xad
2020#if _FFR_ALLOW_SASLINFO
2021        { "DefaultAuthInfo",            O_SASLINFO,     OI_SAFE },
2022#else /* _FFR_ALLOW_SASLINFO */
2023        { "DefaultAuthInfo",            O_SASLINFO,     OI_NONE },
2024#endif /* _FFR_ALLOW_SASLINFO */
2025#define O_SASLMECH      0xae
2026        { "AuthMechanisms",             O_SASLMECH,     OI_NONE },
2027#define O_CLIENTPORT    0xaf
2028        { "ClientPortOptions",          O_CLIENTPORT,   OI_NONE },
2029#define O_DF_BUFSIZE    0xb0
2030        { "DataFileBufferSize",         O_DF_BUFSIZE,   OI_NONE },
2031#define O_XF_BUFSIZE    0xb1
2032        { "XscriptFileBufferSize",      O_XF_BUFSIZE,   OI_NONE },
2033#define O_LDAPDEFAULTSPEC       0xb2
2034        { "LDAPDefaultSpec",            O_LDAPDEFAULTSPEC,      OI_NONE },
2035#if _FFR_QUEUEDELAY
2036# define O_QUEUEDELAY   0xb3
2037        { "QueueDelay",                 O_QUEUEDELAY,   OI_NONE },
2038#endif /* _FFR_QUEUEDELAY */
2039#define O_SRVCERTFILE   0xb4
2040        { "ServerCertFile",             O_SRVCERTFILE,  OI_NONE },
2041#define O_SRVKEYFILE    0xb5
2042        { "ServerKeyFile",              O_SRVKEYFILE,   OI_NONE },
2043#define O_CLTCERTFILE   0xb6
2044        { "ClientCertFile",             O_CLTCERTFILE,  OI_NONE },
2045#define O_CLTKEYFILE    0xb7
2046        { "ClientKeyFile",              O_CLTKEYFILE,   OI_NONE },
2047#define O_CACERTFILE    0xb8
2048        { "CACertFile",                 O_CACERTFILE,   OI_NONE },
2049#define O_CACERTPATH    0xb9
2050        { "CACertPath",                 O_CACERTPATH,   OI_NONE },
2051#define O_DHPARAMS      0xba
2052        { "DHParameters",               O_DHPARAMS,     OI_NONE },
2053#define O_INPUTMILTER   0xbb
2054        { "InputMailFilters",           O_INPUTMILTER,  OI_NONE },
2055#define O_MILTER        0xbc
2056        { "Milter",                     O_MILTER,       OI_SUBOPT       },
2057#define O_SASLOPTS      0xbd
2058        { "AuthOptions",                O_SASLOPTS,     OI_NONE },
2059#define O_QUEUE_FILE_MODE       0xbe
2060        { "QueueFileMode",              O_QUEUE_FILE_MODE, OI_NONE      },
2061#if _FFR_TLS_1
2062# define O_DHPARAMS5    0xbf
2063        { "DHParameters512",            O_DHPARAMS5,    OI_NONE },
2064# define O_CIPHERLIST   0xc0
2065        { "CipherList",                 O_CIPHERLIST,   OI_NONE },
2066#endif /* _FFR_TLS_1 */
2067#define O_RANDFILE      0xc1
2068        { "RandFile",                   O_RANDFILE,     OI_NONE },
2069#define O_TLS_SRV_OPTS  0xc2
2070        { "TLSSrvOptions",              O_TLS_SRV_OPTS, OI_NONE },
2071#define O_RCPTTHROT     0xc3
2072        { "BadRcptThrottle",            O_RCPTTHROT,    OI_SAFE },
2073#define O_DLVR_MIN      0xc4
2074        { "DeliverByMin",               O_DLVR_MIN,     OI_NONE },
2075#define O_MAXQUEUECHILDREN      0xc5
2076        { "MaxQueueChildren",           O_MAXQUEUECHILDREN,     OI_NONE },
2077#define O_MAXRUNNERSPERQUEUE    0xc6
2078        { "MaxRunnersPerQueue",         O_MAXRUNNERSPERQUEUE,   OI_NONE },
2079#define O_DIRECTSUBMODIFIERS    0xc7
2080        { "DirectSubmissionModifiers",  O_DIRECTSUBMODIFIERS,   OI_NONE },
2081#define O_NICEQUEUERUN  0xc8
2082        { "NiceQueueRun",               O_NICEQUEUERUN, OI_NONE },
2083#define O_SHMKEY        0xc9
2084        { "SharedMemoryKey",            O_SHMKEY,       OI_NONE },
2085#define O_SASLBITS      0xca
2086        { "AuthMaxBits",                O_SASLBITS,     OI_NONE },
2087#define O_MBDB          0xcb
2088        { "MailboxDatabase",            O_MBDB,         OI_NONE },
2089#define O_MSQ           0xcc
2090        { "UseMSP",     O_MSQ,          OI_NONE },
2091#define O_DELAY_LA      0xcd
2092        { "DelayLA",    O_DELAY_LA,     OI_NONE },
2093#define O_FASTSPLIT     0xce
2094        { "FastSplit",  O_FASTSPLIT,    OI_NONE },
2095#if _FFR_SOFT_BOUNCE
2096# define O_SOFTBOUNCE   0xcf
2097        { "SoftBounce", O_SOFTBOUNCE,   OI_NONE },
2098#endif /* _FFR_SOFT_BOUNCE */
2099#if _FFR_SELECT_SHM
2100# define O_SHMKEYFILE   0xd0
2101        { "SharedMemoryKeyFile",        O_SHMKEYFILE,   OI_NONE },
2102#endif /* _FFR_SELECT_SHM */
2103#if _FFR_REJECT_LOG
2104# define O_REJECTLOGINTERVAL    0xd1
2105        { "RejectLogInterval",  O_REJECTLOGINTERVAL,    OI_NONE },
2106#endif /* _FFR_REJECT_LOG */
2107#if _FFR_REQ_DIR_FSYNC_OPT
2108# define O_REQUIRES_DIR_FSYNC   0xd2
2109        { "RequiresDirfsync",   O_REQUIRES_DIR_FSYNC,   OI_NONE },
2110#endif /* _FFR_REQ_DIR_FSYNC_OPT */
2111        { NULL,                         '\0',           OI_NONE }
2112};
2113
2114# define CANONIFY(val)
2115
2116# define SET_OPT_DEFAULT(opt, val)      opt = val
2117
2118/* set a string option by expanding the value and assigning it */
2119/* WARNING this belongs ONLY into a case statement! */
2120#define SET_STRING_EXP(str)     \
2121                expand(val, exbuf, sizeof exbuf, e);    \
2122                newval = sm_pstrdup_x(exbuf);           \
2123                if (str != NULL)        \
2124                        sm_free(str);   \
2125                CANONIFY(newval);       \
2126                str = newval;           \
2127                break
2128
2129#define OPTNAME o->o_name == NULL ? "<unknown>" : o->o_name
2130
2131void
2132setoption(opt, val, safe, sticky, e)
2133        int opt;
2134        char *val;
2135        bool safe;
2136        bool sticky;
2137        register ENVELOPE *e;
2138{
2139        register char *p;
2140        register struct optioninfo *o;
2141        char *subopt;
2142        int mid;
2143        bool can_setuid = RunAsUid == 0;
2144        auto char *ep;
2145        char buf[50];
2146        extern bool Warn_Q_option;
2147#if _FFR_ALLOW_SASLINFO
2148        extern unsigned int SubmitMode;
2149#endif /* _FFR_ALLOW_SASLINFO */
2150#if STARTTLS
2151        char *newval;
2152        char exbuf[MAXLINE];
2153#endif /* STARTTLS */
2154
2155        errno = 0;
2156        if (opt == ' ')
2157        {
2158                /* full word options */
2159                struct optioninfo *sel;
2160
2161                p = strchr(val, '=');
2162                if (p == NULL)
2163                        p = &val[strlen(val)];
2164                while (*--p == ' ')
2165                        continue;
2166                while (*++p == ' ')
2167                        *p = '\0';
2168                if (p == val)
2169                {
2170                        syserr("readcf: null option name");
2171                        return;
2172                }
2173                if (*p == '=')
2174                        *p++ = '\0';
2175                while (*p == ' ')
2176                        p++;
2177                subopt = strchr(val, '.');
2178                if (subopt != NULL)
2179                        *subopt++ = '\0';
2180                sel = NULL;
2181                for (o = OptionTab; o->o_name != NULL; o++)
2182                {
2183                        if (sm_strncasecmp(o->o_name, val, strlen(val)) != 0)
2184                                continue;
2185                        if (strlen(o->o_name) == strlen(val))
2186                        {
2187                                /* completely specified -- this must be it */
2188                                sel = NULL;
2189                                break;
2190                        }
2191                        if (sel != NULL)
2192                                break;
2193                        sel = o;
2194                }
2195                if (sel != NULL && o->o_name == NULL)
2196                        o = sel;
2197                else if (o->o_name == NULL)
2198                {
2199                        syserr("readcf: unknown option name %s", val);
2200                        return;
2201                }
2202                else if (sel != NULL)
2203                {
2204                        syserr("readcf: ambiguous option name %s (matches %s and %s)",
2205                                val, sel->o_name, o->o_name);
2206                        return;
2207                }
2208                if (strlen(val) != strlen(o->o_name))
2209                {
2210                        int oldVerbose = Verbose;
2211
2212                        Verbose = 1;
2213                        message("Option %s used as abbreviation for %s",
2214                                val, o->o_name);
2215                        Verbose = oldVerbose;
2216                }
2217                opt = o->o_code;
2218                val = p;
2219        }
2220        else
2221        {
2222                for (o = OptionTab; o->o_name != NULL; o++)
2223                {
2224                        if (o->o_code == opt)
2225                                break;
2226                }
2227                if (o->o_name == NULL)
2228                {
2229                        syserr("readcf: unknown option name 0x%x", opt & 0xff);
2230                        return;
2231                }
2232                subopt = NULL;
2233        }
2234
2235        if (subopt != NULL && !bitset(OI_SUBOPT, o->o_flags))
2236        {
2237                if (tTd(37, 1))
2238                        sm_dprintf("setoption: %s does not support suboptions, ignoring .%s\n",
2239                                   OPTNAME, subopt);
2240                subopt = NULL;
2241        }
2242
2243        if (tTd(37, 1))
2244        {
2245                sm_dprintf(isascii(opt) && isprint(opt) ?
2246                           "setoption %s (%c)%s%s=" :
2247                           "setoption %s (0x%x)%s%s=",
2248                           OPTNAME, opt, subopt == NULL ? "" : ".",
2249                           subopt == NULL ? "" : subopt);
2250                xputs(val);
2251        }
2252
2253        /*
2254        **  See if this option is preset for us.
2255        */
2256
2257        if (!sticky && bitnset(opt, StickyOpt))
2258        {
2259                if (tTd(37, 1))
2260                        sm_dprintf(" (ignored)\n");
2261                return;
2262        }
2263
2264        /*
2265        **  Check to see if this option can be specified by this user.
2266        */
2267
2268        if (!safe && RealUid == 0)
2269                safe = true;
2270        if (!safe && !bitset(OI_SAFE, o->o_flags))
2271        {
2272                if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
2273                {
2274                        int dp;
2275
2276                        if (tTd(37, 1))
2277                                sm_dprintf(" (unsafe)");
2278                        dp = drop_privileges(true);
2279                        setstat(dp);
2280                }
2281        }
2282        if (tTd(37, 1))
2283                sm_dprintf("\n");
2284
2285        switch (opt & 0xff)
2286        {
2287          case '7':             /* force seven-bit input */
2288                SevenBitInput = atobool(val);
2289                break;
2290
2291          case '8':             /* handling of 8-bit input */
2292#if MIME8TO7
2293                switch (*val)
2294                {
2295                  case 'p':             /* pass 8 bit, convert MIME */
2296                        MimeMode = MM_CVTMIME|MM_PASS8BIT;
2297                        break;
2298
2299                  case 'm':             /* convert 8-bit, convert MIME */
2300                        MimeMode = MM_CVTMIME|MM_MIME8BIT;
2301                        break;
2302
2303                  case 's':             /* strict adherence */
2304                        MimeMode = MM_CVTMIME;
2305                        break;
2306
2307# if 0
2308                  case 'r':             /* reject 8-bit, don't convert MIME */
2309                        MimeMode = 0;
2310                        break;
2311
2312                  case 'j':             /* "just send 8" */
2313                        MimeMode = MM_PASS8BIT;
2314                        break;
2315
2316                  case 'a':             /* encode 8 bit if available */
2317                        MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
2318                        break;
2319
2320                  case 'c':             /* convert 8 bit to MIME, never 7 bit */
2321                        MimeMode = MM_MIME8BIT;
2322                        break;
2323# endif /* 0 */
2324
2325                  default:
2326                        syserr("Unknown 8-bit mode %c", *val);
2327                        finis(false, true, EX_USAGE);
2328                }
2329#else /* MIME8TO7 */
2330                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2331                                     "Warning: Option: %s requires MIME8TO7 support\n",
2332                                     OPTNAME);
2333#endif /* MIME8TO7 */
2334                break;
2335
2336          case 'A':             /* set default alias file */
2337                if (val[0] == '\0')
2338                {
2339                        char *al;
2340
2341                        SET_OPT_DEFAULT(al, "aliases");
2342                        setalias(al);
2343                }
2344                else
2345                        setalias(val);
2346                break;
2347
2348          case 'a':             /* look N minutes for "@:@" in alias file */
2349                if (val[0] == '\0')
2350                        SafeAlias = 5 MINUTES;
2351                else
2352                        SafeAlias = convtime(val, 'm');
2353                break;
2354
2355          case 'B':             /* substitution for blank character */
2356                SpaceSub = val[0];
2357                if (SpaceSub == '\0')
2358                        SpaceSub = ' ';
2359                break;
2360
2361          case 'b':             /* min blocks free on queue fs/max msg size */
2362                p = strchr(val, '/');
2363                if (p != NULL)
2364                {
2365                        *p++ = '\0';
2366                        MaxMessageSize = atol(p);
2367                }
2368                MinBlocksFree = atol(val);
2369                break;
2370
2371          case 'c':             /* don't connect to "expensive" mailers */
2372                NoConnect = atobool(val);
2373                break;
2374
2375          case 'C':             /* checkpoint every N addresses */
2376                CheckpointInterval = atoi(val);
2377                break;
2378
2379          case 'd':             /* delivery mode */
2380                switch (*val)
2381                {
2382                  case '\0':
2383                        set_delivery_mode(SM_DELIVER, e);
2384                        break;
2385
2386                  case SM_QUEUE:        /* queue only */
2387                  case SM_DEFER:        /* queue only and defer map lookups */
2388                  case SM_DELIVER:      /* do everything */
2389                  case SM_FORK:         /* fork after verification */
2390                        set_delivery_mode(*val, e);
2391                        break;
2392
2393                  default:
2394                        syserr("Unknown delivery mode %c", *val);
2395                        finis(false, true, EX_USAGE);
2396                }
2397                break;
2398
2399          case 'E':             /* error message header/header file */
2400                if (*val != '\0')
2401                        ErrMsgFile = newstr(val);
2402                break;
2403
2404          case 'e':             /* set error processing mode */
2405                switch (*val)
2406                {
2407                  case EM_QUIET:        /* be silent about it */
2408                  case EM_MAIL:         /* mail back */
2409                  case EM_BERKNET:      /* do berknet error processing */
2410                  case EM_WRITE:        /* write back (or mail) */
2411                  case EM_PRINT:        /* print errors normally (default) */
2412                        e->e_errormode = *val;
2413                        break;
2414                }
2415                break;
2416
2417          case 'F':             /* file mode */
2418                FileMode = atooct(val) & 0777;
2419                break;
2420
2421          case 'f':             /* save Unix-style From lines on front */
2422                SaveFrom = atobool(val);
2423                break;
2424
2425          case 'G':             /* match recipients against GECOS field */
2426                MatchGecos = atobool(val);
2427                break;
2428
2429          case 'g':             /* default gid */
2430  g_opt:
2431                if (isascii(*val) && isdigit(*val))
2432                        DefGid = atoi(val);
2433                else
2434                {
2435                        register struct group *gr;
2436
2437                        DefGid = -1;
2438                        gr = getgrnam(val);
2439                        if (gr == NULL)
2440                                syserr("readcf: option %c: unknown group %s",
2441                                        opt, val);
2442                        else
2443                                DefGid = gr->gr_gid;
2444                }
2445                break;
2446
2447          case 'H':             /* help file */
2448                if (val[0] == '\0')
2449                {
2450                        SET_OPT_DEFAULT(HelpFile, "helpfile");
2451                }
2452                else
2453                {
2454                        CANONIFY(val);
2455                        HelpFile = newstr(val);
2456                }
2457                break;
2458
2459          case 'h':             /* maximum hop count */
2460                MaxHopCount = atoi(val);
2461                break;
2462
2463          case 'I':             /* use internet domain name server */
2464#if NAMED_BIND
2465                for (p = val; *p != 0; )
2466                {
2467                        bool clearmode;
2468                        char *q;
2469                        struct resolverflags *rfp;
2470
2471                        while (*p == ' ')
2472                                p++;
2473                        if (*p == '\0')
2474                                break;
2475                        clearmode = false;
2476                        if (*p == '-')
2477                                clearmode = true;
2478                        else if (*p != '+')
2479                                p--;
2480                        p++;
2481                        q = p;
2482                        while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2483                                p++;
2484                        if (*p != '\0')
2485                                *p++ = '\0';
2486                        if (sm_strcasecmp(q, "HasWildcardMX") == 0)
2487                        {
2488                                HasWildcardMX = !clearmode;
2489                                continue;
2490                        }
2491                        if (sm_strcasecmp(q, "WorkAroundBrokenAAAA") == 0)
2492                        {
2493                                WorkAroundBrokenAAAA = !clearmode;
2494                                continue;
2495                        }
2496                        for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
2497                        {
2498                                if (sm_strcasecmp(q, rfp->rf_name) == 0)
2499                                        break;
2500                        }
2501                        if (rfp->rf_name == NULL)
2502                                syserr("readcf: I option value %s unrecognized", q);
2503                        else if (clearmode)
2504                                _res.options &= ~rfp->rf_bits;
2505                        else
2506                                _res.options |= rfp->rf_bits;
2507                }
2508                if (tTd(8, 2))
2509                        sm_dprintf("_res.options = %x, HasWildcardMX = %d\n",
2510                                   (unsigned int) _res.options, HasWildcardMX);
2511#else /* NAMED_BIND */
2512                usrerr("name server (I option) specified but BIND not compiled in");
2513#endif /* NAMED_BIND */
2514                break;
2515
2516          case 'i':             /* ignore dot lines in message */
2517                IgnrDot = atobool(val);
2518                break;
2519
2520          case 'j':             /* send errors in MIME (RFC 1341) format */
2521                SendMIMEErrors = atobool(val);
2522                break;
2523
2524          case 'J':             /* .forward search path */
2525                CANONIFY(val);
2526                ForwardPath = newstr(val);
2527                break;
2528
2529          case 'k':             /* connection cache size */
2530                MaxMciCache = atoi(val);
2531                if (MaxMciCache < 0)
2532                        MaxMciCache = 0;
2533                break;
2534
2535          case 'K':             /* connection cache timeout */
2536                MciCacheTimeout = convtime(val, 'm');
2537                break;
2538
2539          case 'l':             /* use Errors-To: header */
2540                UseErrorsTo = atobool(val);
2541                break;
2542
2543          case 'L':             /* log level */
2544                if (safe || LogLevel < atoi(val))
2545                        LogLevel = atoi(val);
2546                break;
2547
2548          case 'M':             /* define macro */
2549                sticky = false;
2550                mid = macid_parse(val, &ep);
2551                if (mid == 0)
2552                        break;
2553                p = newstr(ep);
2554                if (!safe)
2555                        cleanstrcpy(p, p, MAXNAME);
2556                macdefine(&CurEnv->e_macro, A_TEMP, mid, p);
2557                break;
2558
2559          case 'm':             /* send to me too */
2560                MeToo = atobool(val);
2561                break;
2562
2563          case 'n':             /* validate RHS in newaliases */
2564                CheckAliases = atobool(val);
2565                break;
2566
2567            /* 'N' available -- was "net name" */
2568
2569          case 'O':             /* daemon options */
2570                if (!setdaemonoptions(val))
2571                        syserr("too many daemons defined (%d max)", MAXDAEMONS);
2572                break;
2573
2574          case 'o':             /* assume old style headers */
2575                if (atobool(val))
2576                        CurEnv->e_flags |= EF_OLDSTYLE;
2577                else
2578                        CurEnv->e_flags &= ~EF_OLDSTYLE;
2579                break;
2580
2581          case 'p':             /* select privacy level */
2582                p = val;
2583                for (;;)
2584                {
2585                        register struct prival *pv;
2586                        extern struct prival PrivacyValues[];
2587
2588                        while (isascii(*p) && (isspace(*p) || ispunct(*p)))
2589                                p++;
2590                        if (*p == '\0')
2591                                break;
2592                        val = p;
2593                        while (isascii(*p) && isalnum(*p))
2594                                p++;
2595                        if (*p != '\0')
2596                                *p++ = '\0';
2597
2598                        for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
2599                        {
2600                                if (sm_strcasecmp(val, pv->pv_name) == 0)
2601                                        break;
2602                        }
2603                        if (pv->pv_name == NULL)
2604                                syserr("readcf: Op line: %s unrecognized", val);
2605                        else
2606                                PrivacyFlags |= pv->pv_flag;
2607                }
2608                sticky = false;
2609                break;
2610
2611          case 'P':             /* postmaster copy address for returned mail */
2612                PostMasterCopy = newstr(val);
2613                break;
2614
2615          case 'q':             /* slope of queue only function */
2616                QueueFactor = atoi(val);
2617                break;
2618
2619          case 'Q':             /* queue directory */
2620                if (val[0] == '\0')
2621                {
2622                        QueueDir = "mqueue";
2623                }
2624                else
2625                {
2626                        QueueDir = newstr(val);
2627                }
2628                if (RealUid != 0 && !safe)
2629                        Warn_Q_option = true;
2630                break;
2631
2632          case 'R':             /* don't prune routes */
2633                DontPruneRoutes = atobool(val);
2634                break;
2635
2636          case 'r':             /* read timeout */
2637                if (subopt == NULL)
2638                        inittimeouts(val, sticky);
2639                else
2640                        settimeout(subopt, val, sticky);
2641                break;
2642
2643          case 'S':             /* status file */
2644                if (val[0] == '\0')
2645                {
2646                        SET_OPT_DEFAULT(StatFile, "statistics");
2647                }
2648                else
2649                {
2650                        CANONIFY(val);
2651                        StatFile = newstr(val);
2652                }
2653                break;
2654
2655          case 's':             /* be super safe, even if expensive */
2656                if (tolower(*val) == 'i')
2657                        SuperSafe = SAFE_INTERACTIVE;
2658                else
2659                        SuperSafe = atobool(val) ? SAFE_REALLY : SAFE_NO;
2660                break;
2661
2662          case 'T':             /* queue timeout */
2663                p = strchr(val, '/');
2664                if (p != NULL)
2665                {
2666                        *p++ = '\0';
2667                        settimeout("queuewarn", p, sticky);
2668                }
2669                settimeout("queuereturn", val, sticky);
2670                break;
2671
2672          case 't':             /* time zone name */
2673                TimeZoneSpec = newstr(val);
2674                break;
2675
2676          case 'U':             /* location of user database */
2677                UdbSpec = newstr(val);
2678                break;
2679
2680          case 'u':             /* set default uid */
2681                for (p = val; *p != '\0'; p++)
2682                {
2683# if _FFR_DOTTED_USERNAMES
2684                        if (*p == '/' || *p == ':')
2685# else /* _FFR_DOTTED_USERNAMES */
2686                        if (*p == '.' || *p == '/' || *p == ':')
2687# endif /* _FFR_DOTTED_USERNAMES */
2688                        {
2689                                *p++ = '\0';
2690                                break;
2691                        }
2692                }
2693                if (isascii(*val) && isdigit(*val))
2694                {
2695                        DefUid = atoi(val);
2696                        setdefuser();
2697                }
2698                else
2699                {
2700                        register struct passwd *pw;
2701
2702                        DefUid = -1;
2703                        pw = sm_getpwnam(val);
2704                        if (pw == NULL)
2705                        {
2706                                syserr("readcf: option u: unknown user %s", val);
2707                                break;
2708                        }
2709                        else
2710                        {
2711                                DefUid = pw->pw_uid;
2712                                DefGid = pw->pw_gid;
2713                                DefUser = newstr(pw->pw_name);
2714                        }
2715                }
2716
2717# ifdef UID_MAX
2718                if (DefUid > UID_MAX)
2719                {
2720                        syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored",
2721                                (long)DefUid, (long)UID_MAX);
2722                        break;
2723                }
2724# endif /* UID_MAX */
2725
2726                /* handle the group if it is there */
2727                if (*p == '\0')
2728                        break;
2729                val = p;
2730                goto g_opt;
2731
2732          case 'V':             /* fallback MX host */
2733                if (val[0] != '\0')
2734                        FallBackMX = newstr(val);
2735                break;
2736
2737          case 'v':             /* run in verbose mode */
2738                Verbose = atobool(val) ? 1 : 0;
2739                break;
2740
2741          case 'w':             /* if we are best MX, try host directly */
2742                TryNullMXList = atobool(val);
2743                break;
2744
2745            /* 'W' available -- was wizard password */
2746
2747          case 'x':             /* load avg at which to auto-queue msgs */
2748                QueueLA = atoi(val);
2749                break;
2750
2751          case 'X':     /* load avg at which to auto-reject connections */
2752                RefuseLA = atoi(val);
2753                break;
2754
2755          case O_DELAY_LA:      /* load avg at which to delay connections */
2756                DelayLA = atoi(val);
2757                break;
2758
2759          case 'y':             /* work recipient factor */
2760                WkRecipFact = atoi(val);
2761                break;
2762
2763          case 'Y':             /* fork jobs during queue runs */
2764                ForkQueueRuns = atobool(val);
2765                break;
2766
2767          case 'z':             /* work message class factor */
2768                WkClassFact = atoi(val);
2769                break;
2770
2771          case 'Z':             /* work time factor */
2772                WkTimeFact = atoi(val);
2773                break;
2774
2775
2776#if _FFR_QUEUE_GROUP_SORTORDER
2777        /* coordinate this with makequeue() */
2778#endif /* _FFR_QUEUE_GROUP_SORTORDER */
2779          case O_QUEUESORTORD:  /* queue sorting order */
2780                switch (*val)
2781                {
2782                  case 'f':     /* File Name */
2783                  case 'F':
2784                        QueueSortOrder = QSO_BYFILENAME;
2785                        break;
2786
2787                  case 'h':     /* Host first */
2788                  case 'H':
2789                        QueueSortOrder = QSO_BYHOST;
2790                        break;
2791
2792                  case 'm':     /* Modification time */
2793                  case 'M':
2794                        QueueSortOrder = QSO_BYMODTIME;
2795                        break;
2796
2797                  case 'p':     /* Priority order */
2798                  case 'P':
2799                        QueueSortOrder = QSO_BYPRIORITY;
2800                        break;
2801
2802                  case 't':     /* Submission time */
2803                  case 'T':
2804                        QueueSortOrder = QSO_BYTIME;
2805                        break;
2806
2807                  case 'r':     /* Random */
2808                  case 'R':
2809                        QueueSortOrder = QSO_RANDOM;
2810                        break;
2811
2812#if _FFR_RHS
2813                  case 's':     /* Shuffled host name */
2814                  case 'S':
2815                        QueueSortOrder = QSO_BYSHUFFLE;
2816                        break;
2817#endif /* _FFR_RHS */
2818
2819                  default:
2820                        syserr("Invalid queue sort order \"%s\"", val);
2821                }
2822                break;
2823
2824#if _FFR_QUEUEDELAY
2825          case O_QUEUEDELAY:    /* queue delay algorithm */
2826                switch (*val)
2827                {
2828                  case 'e':     /* exponential */
2829                  case 'E':
2830                        QueueAlg = QD_EXP;
2831                        QueueInitDelay = 10 MINUTES;
2832                        QueueMaxDelay = 2 HOURS;
2833                        p = strchr(val, '/');
2834                        if (p != NULL)
2835                        {
2836                                char *q;
2837
2838                                *p++ = '\0';
2839                                q = strchr(p, '/');
2840                                if (q != NULL)
2841                                        *q++ = '\0';
2842                                QueueInitDelay = convtime(p, 's');
2843                                if (q != NULL)
2844                                {
2845                                        QueueMaxDelay = convtime(q, 's');
2846                                }
2847                        }
2848                        break;
2849
2850                  case 'l':     /* linear */
2851                  case 'L':
2852                        QueueAlg = QD_LINEAR;
2853                        break;
2854
2855                  default:
2856                        syserr("Invalid queue delay algorithm \"%s\"", val);
2857                }
2858                break;
2859#endif /* _FFR_QUEUEDELAY */
2860
2861          case O_HOSTSFILE:     /* pathname of /etc/hosts file */
2862                CANONIFY(val);
2863                HostsFile = newstr(val);
2864                break;
2865
2866          case O_MQA:           /* minimum queue age between deliveries */
2867                MinQueueAge = convtime(val, 'm');
2868                break;
2869
2870          case O_DEFCHARSET:    /* default character set for mimefying */
2871                DefaultCharSet = newstr(denlstring(val, true, true));
2872                break;
2873
2874          case O_SSFILE:        /* service switch file */
2875                CANONIFY(val);
2876                ServiceSwitchFile = newstr(val);
2877                break;
2878
2879          case O_DIALDELAY:     /* delay for dial-on-demand operation */
2880                DialDelay = convtime(val, 's');
2881                break;
2882
2883          case O_NORCPTACTION:  /* what to do if no recipient */
2884                if (sm_strcasecmp(val, "none") == 0)
2885                        NoRecipientAction = NRA_NO_ACTION;
2886                else if (sm_strcasecmp(val, "add-to") == 0)
2887                        NoRecipientAction = NRA_ADD_TO;
2888                else if (sm_strcasecmp(val, "add-apparently-to") == 0)
2889                        NoRecipientAction = NRA_ADD_APPARENTLY_TO;
2890                else if (sm_strcasecmp(val, "add-bcc") == 0)
2891                        NoRecipientAction = NRA_ADD_BCC;
2892                else if (sm_strcasecmp(val, "add-to-undisclosed") == 0)
2893                        NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
2894                else
2895                        syserr("Invalid NoRecipientAction: %s", val);
2896                break;
2897
2898          case O_SAFEFILEENV:   /* chroot() environ for writing to files */
2899                if (*val == '\0')
2900                        break;
2901
2902                /* strip trailing slashes */
2903                p = val + strlen(val) - 1;
2904                while (p >= val && *p == '/')
2905                        *p-- = '\0';
2906
2907                if (*val == '\0')
2908                        break;
2909
2910                SafeFileEnv = newstr(val);
2911                break;
2912
2913          case O_MAXMSGSIZE:    /* maximum message size */
2914                MaxMessageSize = atol(val);
2915                break;
2916
2917          case O_COLONOKINADDR: /* old style handling of colon addresses */
2918                ColonOkInAddr = atobool(val);
2919                break;
2920
2921          case O_MAXQUEUERUN:   /* max # of jobs in a single queue run */
2922                MaxQueueRun = atoi(val);
2923                break;
2924
2925          case O_MAXCHILDREN:   /* max # of children of daemon */
2926                MaxChildren = atoi(val);
2927                break;
2928
2929          case O_MAXQUEUECHILDREN: /* max # of children of daemon */
2930                MaxQueueChildren = atoi(val);
2931                break;
2932
2933          case O_MAXRUNNERSPERQUEUE: /* max # runners in a queue group */
2934                MaxRunnersPerQueue = atoi(val);
2935                break;
2936
2937          case O_NICEQUEUERUN:          /* nice queue runs */
2938#if !HASNICE
2939                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2940                                     "Warning: NiceQueueRun set on system that doesn't support nice()\n");
2941#endif /* !HASNICE */
2942
2943                /* XXX do we want to check the range? > 0 ? */
2944                NiceQueueRun = atoi(val);
2945                break;
2946
2947          case O_SHMKEY:                /* shared memory key */
2948#if SM_CONF_SHM
2949                ShmKey = atol(val);
2950#else /* SM_CONF_SHM */
2951                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2952                                     "Warning: Option: %s requires shared memory support (-DSM_CONF_SHM)\n",
2953                                     OPTNAME);
2954#endif /* SM_CONF_SHM */
2955                break;
2956
2957#if _FFR_SELECT_SHM
2958          case O_SHMKEYFILE:            /* shared memory key file */
2959# if SM_CONF_SHM
2960                SET_STRING_EXP(ShmKeyFile);
2961# else /* SM_CONF_SHM */
2962                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2963                                     "Warning: Option: %s requires shared memory support (-DSM_CONF_SHM)\n",
2964                                     OPTNAME);
2965                break;
2966# endif /* SM_CONF_SHM */
2967#endif /* _FFR_SELECT_SHM */
2968
2969#if _FFR_MAX_FORWARD_ENTRIES
2970          case O_MAXFORWARD:    /* max # of forward entries */
2971                MaxForwardEntries = atoi(val);
2972                break;
2973#endif /* _FFR_MAX_FORWARD_ENTRIES */
2974
2975          case O_KEEPCNAMES:    /* don't expand CNAME records */
2976                DontExpandCnames = atobool(val);
2977                break;
2978
2979          case O_MUSTQUOTE:     /* must quote these characters in phrases */
2980                (void) sm_strlcpy(buf, "@,;:\\()[]", sizeof buf);
2981                if (strlen(val) < sizeof buf - 10)
2982                        (void) sm_strlcat(buf, val, sizeof buf);
2983                else
2984                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2985                                             "Warning: MustQuoteChars too long, ignored.\n");
2986                MustQuoteChars = newstr(buf);
2987                break;
2988
2989          case O_SMTPGREETING:  /* SMTP greeting message (old $e macro) */
2990                SmtpGreeting = newstr(munchstring(val, NULL, '\0'));
2991                break;
2992
2993          case O_UNIXFROM:      /* UNIX From_ line (old $l macro) */
2994                UnixFromLine = newstr(munchstring(val, NULL, '\0'));
2995                break;
2996
2997          case O_OPCHARS:       /* operator characters (old $o macro) */
2998                if (OperatorChars != NULL)
2999                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3000                                             "Warning: OperatorChars is being redefined.\n         It should only be set before ruleset definitions.\n");
3001                OperatorChars = newstr(munchstring(val, NULL, '\0'));
3002                break;
3003
3004          case O_DONTINITGRPS:  /* don't call initgroups(3) */
3005                DontInitGroups = atobool(val);
3006                break;
3007
3008          case O_SLFH:          /* make sure from fits on one line */
3009                SingleLineFromHeader = atobool(val);
3010                break;
3011
3012          case O_ABH:           /* allow HELO commands with syntax errors */
3013                AllowBogusHELO = atobool(val);
3014                break;
3015
3016          case O_CONNTHROT:     /* connection rate throttle */
3017                ConnRateThrottle = atoi(val);
3018                break;
3019
3020          case O_UGW:           /* group writable files are unsafe */
3021                if (!atobool(val))
3022                {
3023                        setbitn(DBS_GROUPWRITABLEFORWARDFILESAFE,
3024                                DontBlameSendmail);
3025                        setbitn(DBS_GROUPWRITABLEINCLUDEFILESAFE,
3026                                DontBlameSendmail);
3027                }
3028                break;
3029
3030          case O_DBLBOUNCE:     /* address to which to send double bounces */
3031                DoubleBounceAddr = newstr(val);
3032                break;
3033
3034          case O_HSDIR:         /* persistent host status directory */
3035                if (val[0] != '\0')
3036                {
3037                        CANONIFY(val);
3038                        HostStatDir = newstr(val);
3039                }
3040                break;
3041
3042          case O_SINGTHREAD:    /* single thread deliveries (requires hsdir) */
3043                SingleThreadDelivery = atobool(val);
3044                break;
3045
3046          case O_RUNASUSER:     /* run bulk of code as this user */
3047                for (p = val; *p != '\0'; p++)
3048                {
3049# if _FFR_DOTTED_USERNAMES
3050                        if (*p == '/' || *p == ':')
3051# else /* _FFR_DOTTED_USERNAMES */
3052                        if (*p == '.' || *p == '/' || *p == ':')
3053# endif /* _FFR_DOTTED_USERNAMES */
3054                        {
3055                                *p++ = '\0';
3056                                break;
3057                        }
3058                }
3059                if (isascii(*val) && isdigit(*val))
3060                {
3061                        if (can_setuid)
3062                                RunAsUid = atoi(val);
3063                }
3064                else
3065                {
3066                        register struct passwd *pw;
3067
3068                        pw = sm_getpwnam(val);
3069                        if (pw == NULL)
3070                        {
3071                                syserr("readcf: option RunAsUser: unknown user %s", val);
3072                                break;
3073                        }
3074                        else if (can_setuid)
3075                        {
3076                                if (*p == '\0')
3077                                        RunAsUserName = newstr(val);
3078                                RunAsUid = pw->pw_uid;
3079                                RunAsGid = pw->pw_gid;
3080                        }
3081                        else if (EffGid == pw->pw_gid)
3082                                RunAsGid = pw->pw_gid;
3083                        else if (UseMSP && *p == '\0')
3084                                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3085                                                     "WARNING: RunAsGid for MSP ignored, check group ids (egid=%d, want=%d)\n",
3086                                                     (int) EffGid,
3087                                                     (int) pw->pw_gid);
3088                }
3089# ifdef UID_MAX
3090                if (RunAsUid > UID_MAX)
3091                {
3092                        syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored",
3093                                (long) RunAsUid, (long) UID_MAX);
3094                        break;
3095                }
3096# endif /* UID_MAX */
3097                if (*p != '\0')
3098                {
3099                        if (isascii(*p) && isdigit(*p))
3100                        {
3101                                gid_t runasgid;
3102
3103                                runasgid = (gid_t) atoi(p);
3104                                if (can_setuid || EffGid == runasgid)
3105                                        RunAsGid = runasgid;
3106                                else if (UseMSP)
3107                                        (void) sm_io_fprintf(smioout,
3108                                                             SM_TIME_DEFAULT,
3109                                                             "WARNING: RunAsGid for MSP ignored, check group ids (egid=%d, want=%d)\n",
3110                                                             (int) EffGid,
3111                                                             (int) runasgid);
3112                        }
3113                        else
3114                        {
3115                                register struct group *gr;
3116
3117                                gr = getgrnam(p);
3118                                if (gr == NULL)
3119                                        syserr("readcf: option RunAsUser: unknown group %s",
3120                                                p);
3121                                else if (can_setuid || EffGid == gr->gr_gid)
3122                                        RunAsGid = gr->gr_gid;
3123                                else if (UseMSP)
3124                                        (void) sm_io_fprintf(smioout,
3125                                                             SM_TIME_DEFAULT,
3126                                                             "WARNING: RunAsGid for MSP ignored, check group ids (egid=%d, want=%d)\n",
3127                                                             (int) EffGid,
3128                                                             (int) gr->gr_gid);
3129                        }
3130                }
3131                if (tTd(47, 5))
3132                        sm_dprintf("readcf: RunAsUser = %d:%d\n",
3133                                   (int) RunAsUid, (int) RunAsGid);
3134                break;
3135
3136          case O_DSN_RRT:
3137                RrtImpliesDsn = atobool(val);
3138                break;
3139
3140          case O_PIDFILE:
3141                PSTRSET(PidFile, val);
3142                break;
3143
3144          case O_DONTBLAMESENDMAIL:
3145                p = val;
3146                for (;;)
3147                {
3148                        register struct dbsval *dbs;
3149                        extern struct dbsval DontBlameSendmailValues[];
3150
3151                        while (isascii(*p) && (isspace(*p) || ispunct(*p)))
3152                                p++;
3153                        if (*p == '\0')
3154                                break;
3155                        val = p;
3156                        while (isascii(*p) && isalnum(*p))
3157                                p++;
3158                        if (*p != '\0')
3159                                *p++ = '\0';
3160
3161                        for (dbs = DontBlameSendmailValues;
3162                             dbs->dbs_name != NULL; dbs++)
3163                        {
3164                                if (sm_strcasecmp(val, dbs->dbs_name) == 0)
3165                                        break;
3166                        }
3167                        if (dbs->dbs_name == NULL)
3168                                syserr("readcf: DontBlameSendmail option: %s unrecognized", val);
3169                        else if (dbs->dbs_flag == DBS_SAFE)
3170                                clrbitmap(DontBlameSendmail);
3171                        else
3172                                setbitn(dbs->dbs_flag, DontBlameSendmail);
3173                }
3174                sticky = false;
3175                break;
3176
3177          case O_DPI:
3178                if (sm_strcasecmp(val, "loopback") == 0)
3179                        DontProbeInterfaces = DPI_SKIPLOOPBACK;
3180                else if (atobool(val))
3181                        DontProbeInterfaces = DPI_PROBENONE;
3182                else
3183                        DontProbeInterfaces = DPI_PROBEALL;
3184                break;
3185
3186          case O_MAXRCPT:
3187                MaxRcptPerMsg = atoi(val);
3188                break;
3189
3190          case O_RCPTTHROT:
3191                BadRcptThrottle = atoi(val);
3192                break;
3193
3194          case O_DEADLETTER:
3195                CANONIFY(val);
3196                PSTRSET(DeadLetterDrop, val);
3197                break;
3198
3199#if _FFR_DONTLOCKFILESFORREAD_OPTION
3200          case O_DONTLOCK:
3201                DontLockReadFiles = atobool(val);
3202                break;
3203#endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */
3204
3205          case O_MAXALIASRCSN:
3206                MaxAliasRecursion = atoi(val);
3207                break;
3208
3209          case O_CNCTONLYTO:
3210                /* XXX should probably use gethostbyname */
3211#if NETINET || NETINET6
3212                ConnectOnlyTo.sa.sa_family = AF_UNSPEC;
3213# if NETINET6
3214                if (anynet_pton(AF_INET6, val,
3215                                &ConnectOnlyTo.sin6.sin6_addr) != 1)
3216                        ConnectOnlyTo.sa.sa_family = AF_INET6;
3217                else
3218# endif /* NETINET6 */
3219# if NETINET
3220                {
3221                        ConnectOnlyTo.sin.sin_addr.s_addr = inet_addr(val);
3222                        if (ConnectOnlyTo.sin.sin_addr.s_addr != INADDR_NONE)
3223                                ConnectOnlyTo.sa.sa_family = AF_INET;
3224                }
3225
3226# endif /* NETINET */
3227                if (ConnectOnlyTo.sa.sa_family == AF_UNSPEC)
3228                {
3229                        syserr("readcf: option ConnectOnlyTo: invalid IP address %s",
3230                               val);
3231                        break;
3232                }
3233#endif /* NETINET || NETINET6 */
3234                break;
3235
3236          case O_TRUSTUSER:
3237# if !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING)
3238                if (!UseMSP)
3239                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3240                                             "readcf: option TrustedUser may cause problems on systems\n        which do not support fchown() if UseMSP is not set.\n");
3241# endif /* !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING) */
3242                if (isascii(*val) && isdigit(*val))
3243                        TrustedUid = atoi(val);
3244                else
3245                {
3246                        register struct passwd *pw;
3247
3248                        TrustedUid = 0;
3249                        pw = sm_getpwnam(val);
3250                        if (pw == NULL)
3251                        {
3252                                syserr("readcf: option TrustedUser: unknown user %s", val);
3253                                break;
3254                        }
3255                        else
3256                                TrustedUid = pw->pw_uid;
3257                }
3258
3259# ifdef UID_MAX
3260                if (TrustedUid > UID_MAX)
3261                {
3262                        syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)",
3263                                (long) TrustedUid, (long) UID_MAX);
3264                        TrustedUid = 0;
3265                }
3266# endif /* UID_MAX */
3267                break;
3268
3269          case O_MAXMIMEHDRLEN:
3270                p = strchr(val, '/');
3271                if (p != NULL)
3272                        *p++ = '\0';
3273                MaxMimeHeaderLength = atoi(val);
3274                if (p != NULL && *p != '\0')
3275                        MaxMimeFieldLength = atoi(p);
3276                else
3277                        MaxMimeFieldLength = MaxMimeHeaderLength / 2;
3278
3279                if (MaxMimeHeaderLength < 0)
3280                        MaxMimeHeaderLength = 0;
3281                else if (MaxMimeHeaderLength < 128)
3282                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3283                                             "Warning: MaxMimeHeaderLength: header length limit set lower than 128\n");
3284
3285                if (MaxMimeFieldLength < 0)
3286                        MaxMimeFieldLength = 0;
3287                else if (MaxMimeFieldLength < 40)
3288                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3289                                             "Warning: MaxMimeHeaderLength: field length limit set lower than 40\n");
3290                break;
3291
3292          case O_CONTROLSOCKET:
3293                PSTRSET(ControlSocketName, val);
3294                break;
3295
3296          case O_MAXHDRSLEN:
3297                MaxHeadersLength = atoi(val);
3298
3299                if (MaxHeadersLength > 0 &&
3300                    MaxHeadersLength < (MAXHDRSLEN / 2))
3301                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3302                                             "Warning: MaxHeadersLength: headers length limit set lower than %d\n",
3303                                             (MAXHDRSLEN / 2));
3304                break;
3305
3306          case O_PROCTITLEPREFIX:
3307                PSTRSET(ProcTitlePrefix, val);
3308                break;
3309
3310#if SASL
3311          case O_SASLINFO:
3312# if _FFR_ALLOW_SASLINFO
3313                /*
3314                **  Allow users to select their own authinfo file
3315                **  under certain circumstances, otherwise just ignore
3316                **  the option.  If the option isn't ignored, several
3317                **  commands don't work very well, e.g., mailq.
3318                **  However, this is not a "perfect" solution.
3319                **  If mail is queued, the authentication info
3320                **  will not be used in subsequent delivery attempts.
3321                **  If we really want to support this, then it has
3322                **  to be stored in the queue file.
3323                */
3324                if (!bitset(SUBMIT_MSA, SubmitMode) && RealUid != 0 &&
3325                    RunAsUid != RealUid)
3326                        break;
3327# endif /* _FFR_ALLOW_SASLINFO */
3328                PSTRSET(SASLInfo, val);
3329                break;
3330
3331          case O_SASLMECH:
3332                if (AuthMechanisms != NULL)
3333                        sm_free(AuthMechanisms); /* XXX */
3334                if (*val != '\0')
3335                        AuthMechanisms = newstr(val);
3336                else
3337                        AuthMechanisms = NULL;
3338                break;
3339
3340          case O_SASLOPTS:
3341                while (val != NULL && *val != '\0')
3342                {
3343                        switch (*val)
3344                        {
3345                          case 'A':
3346                                SASLOpts |= SASL_AUTH_AUTH;
3347                                break;
3348
3349                          case 'a':
3350                                SASLOpts |= SASL_SEC_NOACTIVE;
3351                                break;
3352
3353                          case 'c':
3354                                SASLOpts |= SASL_SEC_PASS_CREDENTIALS;
3355                                break;
3356
3357                          case 'd':
3358                                SASLOpts |= SASL_SEC_NODICTIONARY;
3359                                break;
3360
3361                          case 'f':
3362                                SASLOpts |= SASL_SEC_FORWARD_SECRECY;
3363                                break;
3364
3365# if _FFR_SASL_OPT_M
3366/* to be activated in 8.13 */
3367#  if SASL >= 20101
3368                          case 'm':
3369                                SASLOpts |= SASL_SEC_MUTUAL_AUTH;
3370                                break;
3371#  endif /* SASL >= 20101 */
3372# endif /* _FFR_SASL_OPT_M */
3373
3374                          case 'p':
3375                                SASLOpts |= SASL_SEC_NOPLAINTEXT;
3376                                break;
3377
3378                          case 'y':
3379                                SASLOpts |= SASL_SEC_NOANONYMOUS;
3380                                break;
3381
3382                          case ' ':     /* ignore */
3383                          case '\t':    /* ignore */
3384                          case ',':     /* ignore */
3385                                break;
3386
3387                          default:
3388                                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3389                                                     "Warning: Option: %s unknown parameter '%c'\n",
3390                                                     OPTNAME,
3391                                                     (isascii(*val) &&
3392                                                        isprint(*val))
3393                                                        ? *val : '?');
3394                                break;
3395                        }
3396                        ++val;
3397                        val = strpbrk(val, ", \t");
3398                        if (val != NULL)
3399                                ++val;
3400                }
3401                break;
3402
3403          case O_SASLBITS:
3404                MaxSLBits = atoi(val);
3405                break;
3406
3407#else /* SASL */
3408          case O_SASLINFO:
3409          case O_SASLMECH:
3410          case O_SASLOPTS:
3411          case O_SASLBITS:
3412                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3413                                     "Warning: Option: %s requires SASL support (-DSASL)\n",
3414                                     OPTNAME);
3415                break;
3416#endif /* SASL */
3417
3418#if STARTTLS
3419          case O_SRVCERTFILE:
3420                SET_STRING_EXP(SrvCertFile);
3421          case O_SRVKEYFILE:
3422                SET_STRING_EXP(SrvKeyFile);
3423          case O_CLTCERTFILE:
3424                SET_STRING_EXP(CltCertFile);
3425          case O_CLTKEYFILE:
3426                SET_STRING_EXP(CltKeyFile);
3427          case O_CACERTFILE:
3428                SET_STRING_EXP(CACertFile);
3429          case O_CACERTPATH:
3430                SET_STRING_EXP(CACertPath);
3431          case O_DHPARAMS:
3432                SET_STRING_EXP(DHParams);
3433# if _FFR_TLS_1
3434          case O_DHPARAMS5:
3435                SET_STRING_EXP(DHParams5);
3436          case O_CIPHERLIST:
3437                SET_STRING_EXP(CipherList);
3438# endif /* _FFR_TLS_1 */
3439
3440        /*
3441        **  XXX How about options per daemon/client instead of globally?
3442        **  This doesn't work well for some options, e.g., no server cert,
3443        **  but fine for others.
3444        **
3445        **  XXX Some people may want different certs per server.
3446        **
3447        **  See also srvfeatures()
3448        */
3449
3450          case O_TLS_SRV_OPTS:
3451                while (val != NULL && *val != '\0')
3452                {
3453                        switch (*val)
3454                        {
3455                          case 'V':
3456                                TLS_Srv_Opts |= TLS_I_NO_VRFY;
3457                                break;
3458# if _FFR_TLS_1
3459                        /*
3460                        **  Server without a cert? That works only if
3461                        **  AnonDH is enabled as cipher, which is not in the
3462                        **  default list. Hence the CipherList option must
3463                        **  be available. Moreover: which clients support this
3464                        **  besides sendmail with this setting?
3465                        */
3466
3467                          case 'C':
3468                                TLS_Srv_Opts &= ~TLS_I_SRV_CERT;
3469                                break;
3470# endif /* _FFR_TLS_1 */
3471                          case ' ':     /* ignore */
3472                          case '\t':    /* ignore */
3473                          case ',':     /* ignore */
3474                                break;
3475                          default:
3476                                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3477                                                     "Warning: Option: %s unknown parameter '%c'\n",
3478                                                     OPTNAME,
3479                                                     (isascii(*val) &&
3480                                                        isprint(*val))
3481                                                        ? *val : '?');
3482                                break;
3483                        }
3484                        ++val;
3485                        val = strpbrk(val, ", \t");
3486                        if (val != NULL)
3487                                ++val;
3488                }
3489                break;
3490
3491          case O_RANDFILE:
3492                PSTRSET(RandFile, val);
3493                break;
3494
3495#else /* STARTTLS */
3496          case O_SRVCERTFILE:
3497          case O_SRVKEYFILE:
3498          case O_CLTCERTFILE:
3499          case O_CLTKEYFILE:
3500          case O_CACERTFILE:
3501          case O_CACERTPATH:
3502          case O_DHPARAMS:
3503# if _FFR_TLS_1
3504          case O_DHPARAMS5:
3505          case O_CIPHERLIST:
3506# endif /* _FFR_TLS_1 */
3507          case O_RANDFILE:
3508                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3509                                     "Warning: Option: %s requires TLS support\n",
3510                                     OPTNAME);
3511                break;
3512
3513#endif /* STARTTLS */
3514
3515          case O_CLIENTPORT:
3516                setclientoptions(val);
3517                break;
3518
3519          case O_DF_BUFSIZE:
3520                DataFileBufferSize = atoi(val);
3521                break;
3522
3523          case O_XF_BUFSIZE:
3524                XscriptFileBufferSize = atoi(val);
3525                break;
3526
3527          case O_LDAPDEFAULTSPEC:
3528#if LDAPMAP
3529                ldapmap_set_defaults(val);
3530#else /* LDAPMAP */
3531                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3532                                     "Warning: Option: %s requires LDAP support (-DLDAPMAP)\n",
3533                                     OPTNAME);
3534#endif /* LDAPMAP */
3535                break;
3536
3537          case O_INPUTMILTER:
3538#if MILTER
3539                InputFilterList = newstr(val);
3540#else /* MILTER */
3541                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3542                                     "Warning: Option: %s requires Milter support (-DMILTER)\n",
3543                                     OPTNAME);
3544#endif /* MILTER */
3545                break;
3546
3547          case O_MILTER:
3548#if MILTER
3549                milter_set_option(subopt, val, sticky);
3550#else /* MILTER */
3551                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3552                                     "Warning: Option: %s requires Milter support (-DMILTER)\n",
3553                                     OPTNAME);
3554#endif /* MILTER */
3555                break;
3556
3557          case O_QUEUE_FILE_MODE:       /* queue file mode */
3558                QueueFileMode = atooct(val) & 0777;
3559                break;
3560
3561          case O_DLVR_MIN:      /* deliver by minimum time */
3562                DeliverByMin = convtime(val, 's');
3563                break;
3564
3565          /* modifiers {daemon_flags} for direct submissions */
3566          case O_DIRECTSUBMODIFIERS:
3567                {
3568                        BITMAP256 m;    /* ignored */
3569                        extern ENVELOPE BlankEnvelope;
3570
3571                        macdefine(&BlankEnvelope.e_macro, A_PERM,
3572                                  macid("{daemon_flags}"),
3573                                  getmodifiers(val, m));
3574                }
3575                break;
3576
3577          case O_FASTSPLIT:
3578                FastSplit = atoi(val);
3579                break;
3580
3581          case O_MBDB:
3582                Mbdb = newstr(val);
3583                break;
3584
3585          case O_MSQ:
3586                UseMSP = atobool(val);
3587                break;
3588
3589#if _FFR_SOFT_BOUNCE
3590          case O_SOFTBOUNCE:
3591                SoftBounce = atobool(val);
3592                break;
3593#endif /* _FFR_SOFT_BOUNCE */
3594
3595#if _FFR_REJECT_LOG
3596          case O_REJECTLOGINTERVAL:     /* time btwn log msgs while refusing */
3597                RejectLogInterval = convtime(val, 'h');
3598                break;
3599#endif /* _FFR_REJECT_LOG */
3600
3601#if _FFR_REQ_DIR_FSYNC_OPT
3602          case O_REQUIRES_DIR_FSYNC:
3603# if REQUIRES_DIR_FSYNC
3604                RequiresDirfsync = atobool(val);
3605# else /* REQUIRES_DIR_FSYNC */
3606                /* silently ignored... required for cf file option */
3607# endif /* REQUIRES_DIR_FSYNC */
3608                break;
3609#endif /* _FFR_REQ_DIR_FSYNC_OPT */
3610
3611          default:
3612                if (tTd(37, 1))
3613                {
3614                        if (isascii(opt) && isprint(opt))
3615                                sm_dprintf("Warning: option %c unknown\n", opt);
3616                        else
3617                                sm_dprintf("Warning: option 0x%x unknown\n", opt);
3618                }
3619                break;
3620        }
3621
3622        /*
3623        **  Options with suboptions are responsible for taking care
3624        **  of sticky-ness (e.g., that a command line setting is kept
3625        **  when reading in the sendmail.cf file).  This has to be done
3626        **  when the suboptions are parsed since each suboption must be
3627        **  sticky, not the root option.
3628        */
3629
3630        if (sticky && !bitset(OI_SUBOPT, o->o_flags))
3631                setbitn(opt, StickyOpt);
3632}
3633/*
3634**  SETCLASS -- set a string into a class
3635**
3636**      Parameters:
3637**              class -- the class to put the string in.
3638**              str -- the string to enter
3639**
3640**      Returns:
3641**              none.
3642**
3643**      Side Effects:
3644**              puts the word into the symbol table.
3645*/
3646
3647void
3648setclass(class, str)
3649        int class;
3650        char *str;
3651{
3652        register STAB *s;
3653
3654        if ((*str & 0377) == MATCHCLASS)
3655        {
3656                int mid;
3657
3658                str++;
3659                mid = macid(str);
3660                if (mid == 0)
3661                        return;
3662
3663                if (tTd(37, 8))
3664                        sm_dprintf("setclass(%s, $=%s)\n",
3665                                   macname(class), macname(mid));
3666                copy_class(mid, class);
3667        }
3668        else
3669        {
3670                if (tTd(37, 8))
3671                        sm_dprintf("setclass(%s, %s)\n", macname(class), str);
3672
3673                s = stab(str, ST_CLASS, ST_ENTER);
3674                setbitn(bitidx(class), s->s_class);
3675        }
3676}
3677/*
3678**  MAKEMAPENTRY -- create a map entry
3679**
3680**      Parameters:
3681**              line -- the config file line
3682**
3683**      Returns:
3684**              A pointer to the map that has been created.
3685**              NULL if there was a syntax error.
3686**
3687**      Side Effects:
3688**              Enters the map into the dictionary.
3689*/
3690
3691MAP *
3692makemapentry(line)
3693        char *line;
3694{
3695        register char *p;
3696        char *mapname;
3697        char *classname;
3698        register STAB *s;
3699        STAB *class;
3700
3701        for (p = line; isascii(*p) && isspace(*p); p++)
3702                continue;
3703        if (!(isascii(*p) && isalnum(*p)))
3704        {
3705                syserr("readcf: config K line: no map name");
3706                return NULL;
3707        }
3708
3709        mapname = p;
3710        while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.')
3711                continue;
3712        if (*p != '\0')
3713                *p++ = '\0';
3714        while (isascii(*p) && isspace(*p))
3715                p++;
3716        if (!(isascii(*p) && isalnum(*p)))
3717        {
3718                syserr("readcf: config K line, map %s: no map class", mapname);
3719                return NULL;
3720        }
3721        classname = p;
3722        while (isascii(*++p) && isalnum(*p))
3723                continue;
3724        if (*p != '\0')
3725                *p++ = '\0';
3726        while (isascii(*p) && isspace(*p))
3727                p++;
3728
3729        /* look up the class */
3730        class = stab(classname, ST_MAPCLASS, ST_FIND);
3731        if (class == NULL)
3732        {
3733                syserr("readcf: map %s: class %s not available", mapname,
3734                        classname);
3735                return NULL;
3736        }
3737
3738        /* enter the map */
3739        s = stab(mapname, ST_MAP, ST_ENTER);
3740        s->s_map.map_class = &class->s_mapclass;
3741        s->s_map.map_mname = newstr(mapname);
3742
3743        if (class->s_mapclass.map_parse(&s->s_map, p))
3744                s->s_map.map_mflags |= MF_VALID;
3745
3746        if (tTd(37, 5))
3747        {
3748                sm_dprintf("map %s, class %s, flags %lx, file %s,\n",
3749                           s->s_map.map_mname, s->s_map.map_class->map_cname,
3750                           s->s_map.map_mflags, s->s_map.map_file);
3751                sm_dprintf("\tapp %s, domain %s, rebuild %s\n",
3752                           s->s_map.map_app, s->s_map.map_domain,
3753                           s->s_map.map_rebuild);
3754        }
3755        return &s->s_map;
3756}
3757/*
3758**  STRTORWSET -- convert string to rewriting set number
3759**
3760**      Parameters:
3761**              p -- the pointer to the string to decode.
3762**              endp -- if set, store the trailing delimiter here.
3763**              stabmode -- ST_ENTER to create this entry, ST_FIND if
3764**                      it must already exist.
3765**
3766**      Returns:
3767**              The appropriate ruleset number.
3768**              -1 if it is not valid (error already printed)
3769*/
3770
3771int
3772strtorwset(p, endp, stabmode)
3773        char *p;
3774        char **endp;
3775        int stabmode;
3776{
3777        int ruleset;
3778        static int nextruleset = MAXRWSETS;
3779
3780        while (isascii(*p) && isspace(*p))
3781                p++;
3782        if (!isascii(*p))
3783        {
3784                syserr("invalid ruleset name: \"%.20s\"", p);
3785                return -1;
3786        }
3787        if (isdigit(*p))
3788        {
3789                ruleset = strtol(p, endp, 10);
3790                if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
3791                {
3792                        syserr("bad ruleset %d (%d max)",
3793                                ruleset, MAXRWSETS / 2);
3794                        ruleset = -1;
3795                }
3796        }
3797        else
3798        {
3799                STAB *s;
3800                char delim;
3801                char *q = NULL;
3802
3803                q = p;
3804                while (*p != '\0' && isascii(*p) &&
3805                       (isalnum(*p) || *p == '_'))
3806                        p++;
3807                if (q == p || !(isascii(*q) && isalpha(*q)))
3808                {
3809                        /* no valid characters */
3810                        syserr("invalid ruleset name: \"%.20s\"", q);
3811                        return -1;
3812                }
3813                while (isascii(*p) && isspace(*p))
3814                        *p++ = '\0';
3815                delim = *p;
3816                if (delim != '\0')
3817                        *p = '\0';
3818                s = stab(q, ST_RULESET, stabmode);
3819                if (delim != '\0')
3820                        *p = delim;
3821
3822                if (s == NULL)
3823                        return -1;
3824
3825                if (stabmode == ST_ENTER && delim == '=')
3826                {
3827                        while (isascii(*++p) && isspace(*p))
3828                                continue;
3829                        if (!(isascii(*p) && isdigit(*p)))
3830                        {
3831                                syserr("bad ruleset definition \"%s\" (number required after `=')", q);
3832                                ruleset = -1;
3833                        }
3834                        else
3835                        {
3836                                ruleset = strtol(p, endp, 10);
3837                                if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
3838                                {
3839                                        syserr("bad ruleset number %d in \"%s\" (%d max)",
3840                                                ruleset, q, MAXRWSETS / 2);
3841                                        ruleset = -1;
3842                                }
3843                        }
3844                }
3845                else
3846                {
3847                        if (endp != NULL)
3848                                *endp = p;
3849                        if (s->s_ruleset >= 0)
3850                                ruleset = s->s_ruleset;
3851                        else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
3852                        {
3853                                syserr("%s: too many named rulesets (%d max)",
3854                                        q, MAXRWSETS / 2);
3855                                ruleset = -1;
3856                        }
3857                }
3858                if (s->s_ruleset >= 0 &&
3859                    ruleset >= 0 &&
3860                    ruleset != s->s_ruleset)
3861                {
3862                        syserr("%s: ruleset changed value (old %d, new %d)",
3863                                q, s->s_ruleset, ruleset);
3864                        ruleset = s->s_ruleset;
3865                }
3866                else if (ruleset >= 0)
3867                {
3868                        s->s_ruleset = ruleset;
3869                }
3870                if (stabmode == ST_ENTER && ruleset >= 0)
3871                {
3872                        char *h = NULL;
3873
3874                        if (RuleSetNames[ruleset] != NULL)
3875                                sm_free(RuleSetNames[ruleset]); /* XXX */
3876                        if (delim != '\0' && (h = strchr(q, delim)) != NULL)
3877                                *h = '\0';
3878                        RuleSetNames[ruleset] = newstr(q);
3879                        if (delim == '/' && h != NULL)
3880                                *h = delim;     /* put back delim */
3881                }
3882        }
3883        return ruleset;
3884}
3885/*
3886**  SETTIMEOUT -- set an individual timeout
3887**
3888**      Parameters:
3889**              name -- the name of the timeout.
3890**              val -- the value of the timeout.
3891**              sticky -- if set, don't let other setoptions override
3892**                      this value.
3893**
3894**      Returns:
3895**              none.
3896*/
3897
3898/* set if Timeout sub-option is stuck */
3899static BITMAP256        StickyTimeoutOpt;
3900
3901static struct timeoutinfo
3902{
3903        char            *to_name;       /* long name of timeout */
3904        unsigned char   to_code;        /* code for option */
3905} TimeOutTab[] =
3906{
3907#define TO_INITIAL                      0x01
3908        { "initial",                    TO_INITIAL                      },
3909#define TO_MAIL                         0x02
3910        { "mail",                       TO_MAIL                         },
3911#define TO_RCPT                         0x03
3912        { "rcpt",                       TO_RCPT                         },
3913#define TO_DATAINIT                     0x04
3914        { "datainit",                   TO_DATAINIT                     },
3915#define TO_DATABLOCK                    0x05
3916        { "datablock",                  TO_DATABLOCK                    },
3917#define TO_DATAFINAL                    0x06
3918        { "datafinal",                  TO_DATAFINAL                    },
3919#define TO_COMMAND                      0x07
3920        { "command",                    TO_COMMAND                      },
3921#define TO_RSET                         0x08
3922        { "rset",                       TO_RSET                         },
3923#define TO_HELO                         0x09
3924        { "helo",                       TO_HELO                         },
3925#define TO_QUIT                         0x0A
3926        { "quit",                       TO_QUIT                         },
3927#define TO_MISC                         0x0B
3928        { "misc",                       TO_MISC                         },
3929#define TO_IDENT                        0x0C
3930        { "ident",                      TO_IDENT                        },
3931#define TO_FILEOPEN                     0x0D
3932        { "fileopen",                   TO_FILEOPEN                     },
3933#define TO_CONNECT                      0x0E
3934        { "connect",                    TO_CONNECT                      },
3935#define TO_ICONNECT                     0x0F
3936        { "iconnect",                   TO_ICONNECT                     },
3937#define TO_QUEUEWARN                    0x10
3938        { "queuewarn",                  TO_QUEUEWARN                    },
3939        { "queuewarn.*",                TO_QUEUEWARN                    },
3940#define TO_QUEUEWARN_NORMAL             0x11
3941        { "queuewarn.normal",           TO_QUEUEWARN_NORMAL             },
3942#define TO_QUEUEWARN_URGENT             0x12
3943        { "queuewarn.urgent",           TO_QUEUEWARN_URGENT             },
3944#define TO_QUEUEWARN_NON_URGENT         0x13
3945        { "queuewarn.non-urgent",       TO_QUEUEWARN_NON_URGENT         },
3946#define TO_QUEUERETURN                  0x14
3947        { "queuereturn",                TO_QUEUERETURN                  },
3948        { "queuereturn.*",              TO_QUEUERETURN                  },
3949#define TO_QUEUERETURN_NORMAL           0x15
3950        { "queuereturn.normal",         TO_QUEUERETURN_NORMAL           },
3951#define TO_QUEUERETURN_URGENT           0x16
3952        { "queuereturn.urgent",         TO_QUEUERETURN_URGENT           },
3953#define TO_QUEUERETURN_NON_URGENT       0x17
3954        { "queuereturn.non-urgent",     TO_QUEUERETURN_NON_URGENT       },
3955#define TO_HOSTSTATUS                   0x18
3956        { "hoststatus",                 TO_HOSTSTATUS                   },
3957#define TO_RESOLVER_RETRANS             0x19
3958        { "resolver.retrans",           TO_RESOLVER_RETRANS             },
3959#define TO_RESOLVER_RETRANS_NORMAL      0x1A
3960        { "resolver.retrans.normal",    TO_RESOLVER_RETRANS_NORMAL      },
3961#define TO_RESOLVER_RETRANS_FIRST       0x1B
3962        { "resolver.retrans.first",     TO_RESOLVER_RETRANS_FIRST       },
3963#define TO_RESOLVER_RETRY               0x1C
3964        { "resolver.retry",             TO_RESOLVER_RETRY               },
3965#define TO_RESOLVER_RETRY_NORMAL        0x1D
3966        { "resolver.retry.normal",      TO_RESOLVER_RETRY_NORMAL        },
3967#define TO_RESOLVER_RETRY_FIRST         0x1E
3968        { "resolver.retry.first",       TO_RESOLVER_RETRY_FIRST         },
3969#define TO_CONTROL                      0x1F
3970        { "control",                    TO_CONTROL                      },
3971#define TO_LHLO                         0x20
3972        { "lhlo",                       TO_LHLO                         },
3973#define TO_AUTH                         0x21
3974        { "auth",                       TO_AUTH                         },
3975#define TO_STARTTLS                     0x22
3976        { "starttls",                   TO_STARTTLS                     },
3977#define TO_ACONNECT                     0x23
3978        { "aconnect",                   TO_ACONNECT                     },
3979#if _FFR_QUEUERETURN_DSN
3980# define TO_QUEUEWARN_DSN               0x24
3981        { "queuewarn.dsn",              TO_QUEUEWARN_DSN                },
3982# define TO_QUEUERETURN_DSN             0x25
3983        { "queuereturn.dsn",            TO_QUEUERETURN_DSN              },
3984#endif /* _FFR_QUEUERETURN_DSN */
3985        { NULL,                         0                               },
3986};
3987
3988
3989static void
3990settimeout(name, val, sticky)
3991        char *name;
3992        char *val;
3993        bool sticky;
3994{
3995        register struct timeoutinfo *to;
3996        int i, addopts;
3997        time_t toval;
3998
3999        if (tTd(37, 2))
4000                sm_dprintf("settimeout(%s = %s)", name, val);
4001
4002        for (to = TimeOutTab; to->to_name != NULL; to++)
4003        {
4004                if (sm_strcasecmp(to->to_name, name) == 0)
4005                        break;
4006        }
4007
4008        if (to->to_name == NULL)
4009        {
4010                errno = 0; /* avoid bogus error text */
4011                syserr("settimeout: invalid timeout %s", name);
4012                return;
4013        }
4014
4015        /*
4016        **  See if this option is preset for us.
4017        */
4018
4019        if (!sticky && bitnset(to->to_code, StickyTimeoutOpt))
4020        {
4021                if (tTd(37, 2))
4022                        sm_dprintf(" (ignored)\n");
4023                return;
4024        }
4025
4026        if (tTd(37, 2))
4027                sm_dprintf("\n");
4028
4029        toval = convtime(val, 'm');
4030        addopts = 0;
4031
4032        switch (to->to_code)
4033        {
4034          case TO_INITIAL:
4035                TimeOuts.to_initial = toval;
4036                break;
4037
4038          case TO_MAIL:
4039                TimeOuts.to_mail = toval;
4040                break;
4041
4042          case TO_RCPT:
4043                TimeOuts.to_rcpt = toval;
4044                break;
4045
4046          case TO_DATAINIT:
4047                TimeOuts.to_datainit = toval;
4048                break;
4049
4050          case TO_DATABLOCK:
4051                TimeOuts.to_datablock = toval;
4052                break;
4053
4054          case TO_DATAFINAL:
4055                TimeOuts.to_datafinal = toval;
4056                break;
4057
4058          case TO_COMMAND:
4059                TimeOuts.to_nextcommand = toval;
4060                break;
4061
4062          case TO_RSET:
4063                TimeOuts.to_rset = toval;
4064                break;
4065
4066          case TO_HELO:
4067                TimeOuts.to_helo = toval;
4068                break;
4069
4070          case TO_QUIT:
4071                TimeOuts.to_quit = toval;
4072                break;
4073
4074          case TO_MISC:
4075                TimeOuts.to_miscshort = toval;
4076                break;
4077
4078          case TO_IDENT:
4079                TimeOuts.to_ident = toval;
4080                break;
4081
4082          case TO_FILEOPEN:
4083                TimeOuts.to_fileopen = toval;
4084                break;
4085
4086          case TO_CONNECT:
4087                TimeOuts.to_connect = toval;
4088                break;
4089
4090          case TO_ICONNECT:
4091                TimeOuts.to_iconnect = toval;
4092                break;
4093
4094          case TO_ACONNECT:
4095                TimeOuts.to_aconnect = toval;
4096                break;
4097
4098          case TO_QUEUEWARN:
4099                toval = convtime(val, 'h');
4100                TimeOuts.to_q_warning[TOC_NORMAL] = toval;
4101                TimeOuts.to_q_warning[TOC_URGENT] = toval;
4102                TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
4103#if _FFR_QUEUERETURN_DSN
4104                TimeOuts.to_q_warning[TOC_DSN] = toval;
4105#endif /* _FFR_QUEUERETURN_DSN */
4106                addopts = 2;
4107                break;
4108
4109          case TO_QUEUEWARN_NORMAL:
4110                toval = convtime(val, 'h');
4111                TimeOuts.to_q_warning[TOC_NORMAL] = toval;
4112                break;
4113
4114          case TO_QUEUEWARN_URGENT:
4115                toval = convtime(val, 'h');
4116                TimeOuts.to_q_warning[TOC_URGENT] = toval;
4117                break;
4118
4119          case TO_QUEUEWARN_NON_URGENT:
4120                toval = convtime(val, 'h');
4121                TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
4122                break;
4123
4124#if _FFR_QUEUERETURN_DSN
4125          case TO_QUEUEWARN_DSN:
4126                toval = convtime(val, 'h');
4127                TimeOuts.to_q_warning[TOC_DSN] = toval;
4128                break;
4129#endif /* _FFR_QUEUERETURN_DSN */
4130
4131          case TO_QUEUERETURN:
4132                toval = convtime(val, 'd');
4133                TimeOuts.to_q_return[TOC_NORMAL] = toval;
4134                TimeOuts.to_q_return[TOC_URGENT] = toval;
4135                TimeOuts.to_q_return[TOC_NONURGENT] = toval;
4136#if _FFR_QUEUERETURN_DSN
4137                TimeOuts.to_q_return[TOC_DSN] = toval;
4138#endif /* _FFR_QUEUERETURN_DSN */
4139                addopts = 2;
4140                break;
4141
4142          case TO_QUEUERETURN_NORMAL:
4143                toval = convtime(val, 'd');
4144                TimeOuts.to_q_return[TOC_NORMAL] = toval;
4145                break;
4146
4147          case TO_QUEUERETURN_URGENT:
4148                toval = convtime(val, 'd');
4149                TimeOuts.to_q_return[TOC_URGENT] = toval;
4150                break;
4151
4152          case TO_QUEUERETURN_NON_URGENT:
4153                toval = convtime(val, 'd');
4154                TimeOuts.to_q_return[TOC_NONURGENT] = toval;
4155                break;
4156
4157#if _FFR_QUEUERETURN_DSN
4158          case TO_QUEUERETURN_DSN:
4159                toval = convtime(val, 'd');
4160                TimeOuts.to_q_return[TOC_DSN] = toval;
4161                break;
4162#endif /* _FFR_QUEUERETURN_DSN */
4163
4164          case TO_HOSTSTATUS:
4165                MciInfoTimeout = toval;
4166                break;
4167
4168          case TO_RESOLVER_RETRANS:
4169                toval = convtime(val, 's');
4170                TimeOuts.res_retrans[RES_TO_DEFAULT] = toval;
4171                TimeOuts.res_retrans[RES_TO_FIRST] = toval;
4172                TimeOuts.res_retrans[RES_TO_NORMAL] = toval;
4173                addopts = 2;
4174                break;
4175
4176          case TO_RESOLVER_RETRY:
4177                i = atoi(val);
4178                TimeOuts.res_retry[RES_TO_DEFAULT] = i;
4179                TimeOuts.res_retry[RES_TO_FIRST] = i;
4180                TimeOuts.res_retry[RES_TO_NORMAL] = i;
4181                addopts = 2;
4182                break;
4183
4184          case TO_RESOLVER_RETRANS_NORMAL:
4185                TimeOuts.res_retrans[RES_TO_NORMAL] = convtime(val, 's');
4186                break;
4187
4188          case TO_RESOLVER_RETRY_NORMAL:
4189                TimeOuts.res_retry[RES_TO_NORMAL] = atoi(val);
4190                break;
4191
4192          case TO_RESOLVER_RETRANS_FIRST:
4193                TimeOuts.res_retrans[RES_TO_FIRST] = convtime(val, 's');
4194                break;
4195
4196          case TO_RESOLVER_RETRY_FIRST:
4197                TimeOuts.res_retry[RES_TO_FIRST] = atoi(val);
4198                break;
4199
4200          case TO_CONTROL:
4201                TimeOuts.to_control = toval;
4202                break;
4203
4204          case TO_LHLO:
4205                TimeOuts.to_lhlo = toval;
4206                break;
4207
4208#if SASL
4209          case TO_AUTH:
4210                TimeOuts.to_auth = toval;
4211                break;
4212#endif /* SASL */
4213
4214#if STARTTLS
4215          case TO_STARTTLS:
4216                TimeOuts.to_starttls = toval;
4217                break;
4218#endif /* STARTTLS */
4219
4220          default:
4221                syserr("settimeout: invalid timeout %s", name);
4222                break;
4223        }
4224
4225        if (sticky)
4226        {
4227                for (i = 0; i <= addopts; i++)
4228                        setbitn(to->to_code + i, StickyTimeoutOpt);
4229        }
4230}
4231/*
4232**  INITTIMEOUTS -- parse and set timeout values
4233**
4234**      Parameters:
4235**              val -- a pointer to the values.  If NULL, do initial
4236**                      settings.
4237**              sticky -- if set, don't let other setoptions override
4238**                      this suboption value.
4239**
4240**      Returns:
4241**              none.
4242**
4243**      Side Effects:
4244**              Initializes the TimeOuts structure
4245*/
4246
4247void
4248inittimeouts(val, sticky)
4249        register char *val;
4250        bool sticky;
4251{
4252        register char *p;
4253
4254        if (tTd(37, 2))
4255                sm_dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val);
4256        if (val == NULL)
4257        {
4258                TimeOuts.to_connect = (time_t) 0 SECONDS;
4259                TimeOuts.to_aconnect = (time_t) 0 SECONDS;
4260                TimeOuts.to_iconnect = (time_t) 0 SECONDS;
4261                TimeOuts.to_initial = (time_t) 5 MINUTES;
4262                TimeOuts.to_helo = (time_t) 5 MINUTES;
4263                TimeOuts.to_mail = (time_t) 10 MINUTES;
4264                TimeOuts.to_rcpt = (time_t) 1 HOUR;
4265                TimeOuts.to_datainit = (time_t) 5 MINUTES;
4266                TimeOuts.to_datablock = (time_t) 1 HOUR;
4267                TimeOuts.to_datafinal = (time_t) 1 HOUR;
4268                TimeOuts.to_rset = (time_t) 5 MINUTES;
4269                TimeOuts.to_quit = (time_t) 2 MINUTES;
4270                TimeOuts.to_nextcommand = (time_t) 1 HOUR;
4271                TimeOuts.to_miscshort = (time_t) 2 MINUTES;
4272#if IDENTPROTO
4273                TimeOuts.to_ident = (time_t) 5 SECONDS;
4274#else /* IDENTPROTO */
4275                TimeOuts.to_ident = (time_t) 0 SECONDS;
4276#endif /* IDENTPROTO */
4277                TimeOuts.to_fileopen = (time_t) 60 SECONDS;
4278                TimeOuts.to_control = (time_t) 2 MINUTES;
4279                TimeOuts.to_lhlo = (time_t) 2 MINUTES;
4280#if SASL
4281                TimeOuts.to_auth = (time_t) 10 MINUTES;
4282#endif /* SASL */
4283#if STARTTLS
4284                TimeOuts.to_starttls = (time_t) 1 HOUR;
4285#endif /* STARTTLS */
4286                if (tTd(37, 5))
4287                {
4288                        sm_dprintf("Timeouts:\n");
4289                        sm_dprintf("  connect = %ld\n",
4290                                   (long) TimeOuts.to_connect);
4291                        sm_dprintf("  aconnect = %ld\n",
4292                                   (long) TimeOuts.to_aconnect);
4293                        sm_dprintf("  initial = %ld\n",
4294                                   (long) TimeOuts.to_initial);
4295                        sm_dprintf("  helo = %ld\n", (long) TimeOuts.to_helo);
4296                        sm_dprintf("  mail = %ld\n", (long) TimeOuts.to_mail);
4297                        sm_dprintf("  rcpt = %ld\n", (long) TimeOuts.to_rcpt);
4298                        sm_dprintf("  datainit = %ld\n",
4299                                   (long) TimeOuts.to_datainit);
4300                        sm_dprintf("  datablock = %ld\n",
4301                                   (long) TimeOuts.to_datablock);
4302                        sm_dprintf("  datafinal = %ld\n",
4303                                   (long) TimeOuts.to_datafinal);
4304                        sm_dprintf("  rset = %ld\n", (long) TimeOuts.to_rset);
4305                        sm_dprintf("  quit = %ld\n", (long) TimeOuts.to_quit);
4306                        sm_dprintf("  nextcommand = %ld\n",
4307                                   (long) TimeOuts.to_nextcommand);
4308                        sm_dprintf("  miscshort = %ld\n",
4309                                   (long) TimeOuts.to_miscshort);
4310                        sm_dprintf("  ident = %ld\n", (long) TimeOuts.to_ident);
4311                        sm_dprintf("  fileopen = %ld\n",
4312                                   (long) TimeOuts.to_fileopen);
4313                        sm_dprintf("  lhlo = %ld\n",
4314                                   (long) TimeOuts.to_lhlo);
4315                        sm_dprintf("  control = %ld\n",
4316                                   (long) TimeOuts.to_control);
4317                }
4318                return;
4319        }
4320
4321        for (;; val = p)
4322        {
4323                while (isascii(*val) && isspace(*val))
4324                        val++;
4325                if (*val == '\0')
4326                        break;
4327                for (p = val; *p != '\0' && *p != ','; p++)
4328                        continue;
4329                if (*p != '\0')
4330                        *p++ = '\0';
4331
4332                if (isascii(*val) && isdigit(*val))
4333                {
4334                        /* old syntax -- set everything */
4335                        TimeOuts.to_mail = convtime(val, 'm');
4336                        TimeOuts.to_rcpt = TimeOuts.to_mail;
4337                        TimeOuts.to_datainit = TimeOuts.to_mail;
4338                        TimeOuts.to_datablock = TimeOuts.to_mail;
4339                        TimeOuts.to_datafinal = TimeOuts.to_mail;
4340                        TimeOuts.to_nextcommand = TimeOuts.to_mail;
4341                        if (sticky)
4342                        {
4343                                setbitn(TO_MAIL, StickyTimeoutOpt);
4344                                setbitn(TO_RCPT, StickyTimeoutOpt);
4345                                setbitn(TO_DATAINIT, StickyTimeoutOpt);
4346                                setbitn(TO_DATABLOCK, StickyTimeoutOpt);
4347                                setbitn(TO_DATAFINAL, StickyTimeoutOpt);
4348                                setbitn(TO_COMMAND, StickyTimeoutOpt);
4349                        }
4350                        continue;
4351                }
4352                else
4353                {
4354                        register char *q = strchr(val, ':');
4355
4356                        if (q == NULL && (q = strchr(val, '=')) == NULL)
4357                        {
4358                                /* syntax error */
4359                                continue;
4360                        }
4361                        *q++ = '\0';
4362                        settimeout(val, q, sticky);
4363                }
4364        }
4365}
Note: See TracBrowser for help on using the repository browser.