implement arpatounix; log more info; allow nested $?...$|...$.;
[unix-history] / usr / src / usr.sbin / sendmail / src / readcf.c
index cdee36c..6e1c56d 100644 (file)
@@ -1,6 +1,22 @@
-# include "sendmail.h"
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * %sccs.include.redist.c%
+ */
 
 
-SCCSID(@(#)readcf.c    4.2             %G%);
+#ifndef lint
+static char sccsid[] = "@(#)readcf.c   6.4 (Berkeley) %G%";
+#endif /* not lint */
+
+# include "sendmail.h"
+# include <sys/stat.h>
+# include <unistd.h>
+#ifdef NAMED_BIND
+# include <arpa/nameser.h>
+# include <resolv.h>
+#endif
 
 /*
 **  READCF -- read control file.
 
 /*
 **  READCF -- read control file.
@@ -24,19 +40,20 @@ SCCSID(@(#)readcf.c 4.2             %G%);
 **             Sn              Use rewriting set n.
 **             Rlhs rhs        Rewrite addresses that match lhs to
 **                             be rhs.
 **             Sn              Use rewriting set n.
 **             Rlhs rhs        Rewrite addresses that match lhs to
 **                             be rhs.
-**             Mn p f s r a    Define mailer.  n - internal name,
-**                             p - pathname, f - flags, s - rewriting
-**                             ruleset for sender, s - rewriting ruleset
-**                             for recipients, a - argument vector.
+**             Mn arg=val...   Define mailer.  n is the internal name.
+**                             Args specify mailer parameters.
 **             Oxvalue         Set option x to value.
 **             Pname=value     Set precedence name to value.
 **             Oxvalue         Set option x to value.
 **             Pname=value     Set precedence name to value.
+**             Vversioncode    Version level of configuration syntax.
+**             Kmapname mapclass arguments....
+**                             Define keyed lookup of a given class.
+**                             Arguments are class dependent.
 **
 **     Parameters:
 **             cfname -- control file name.
 **
 **     Parameters:
 **             cfname -- control file name.
-**             safe -- set if this is a system configuration file.
-**                     Non-system configuration files can not do
-**                     certain things (e.g., leave the SUID bit on
-**                     when executing mailers).
+**             safe -- TRUE if this is the system config file;
+**                     FALSE otherwise.
+**             e -- the main envelope.
 **
 **     Returns:
 **             none.
 **
 **     Returns:
 **             none.
@@ -45,47 +62,133 @@ SCCSID(@(#)readcf.c        4.2             %G%);
 **             Builds several internal tables.
 */
 
 **             Builds several internal tables.
 */
 
-readcf(cfname, safe)
+readcf(cfname)
        char *cfname;
        bool safe;
        char *cfname;
        bool safe;
+       register ENVELOPE *e;
 {
        FILE *cf;
        int ruleset = 0;
        char *q;
        char **pv;
        struct rewrite *rwp = NULL;
 {
        FILE *cf;
        int ruleset = 0;
        char *q;
        char **pv;
        struct rewrite *rwp = NULL;
+       char *bp;
+       int nfuzzy;
        char buf[MAXLINE];
        register char *p;
        extern char **prescan();
        extern char **copyplist();
        char buf[MAXLINE];
        register char *p;
        extern char **prescan();
        extern char **copyplist();
+       struct stat statb;
        char exbuf[MAXLINE];
        char exbuf[MAXLINE];
+       char pvpbuf[PSBUFSIZE];
        extern char *fgetfolded();
        extern char *munchstring();
        extern char *fgetfolded();
        extern char *munchstring();
+       extern void makemapentry();
+
+       FileName = cfname;
+       LineNumber = 0;
 
        cf = fopen(cfname, "r");
        if (cf == NULL)
        {
 
        cf = fopen(cfname, "r");
        if (cf == NULL)
        {
-               syserr("cannot open %s", cfname);
+               syserr("cannot open");
                exit(EX_OSFILE);
        }
 
                exit(EX_OSFILE);
        }
 
-       FileName = cfname;
-       LineNumber = 0;
-       while (fgetfolded(buf, sizeof buf, cf) != NULL)
+       if (fstat(fileno(cf), &statb) < 0)
+       {
+               syserr("cannot fstat");
+               exit(EX_OSFILE);
+       }
+
+       if (!S_ISREG(statb.st_mode))
        {
        {
-               switch (buf[0])
+               syserr("not a plain file");
+               exit(EX_OSFILE);
+       }
+
+       if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
+       {
+               if (OpMode == MD_DAEMON || OpMode == MD_FREEZE)
+                       fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
+                               FileName);
+#ifdef LOG
+               if (LogLevel > 0)
+                       syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
+                               FileName);
+#endif
+       }
+
+       while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
+       {
+               if (bp[0] == '#')
+               {
+                       if (bp != buf)
+                               free(bp);
+                       continue;
+               }
+
+               /* map $ into \001 (ASCII SOH) for macro expansion */
+               for (p = bp; *p != '\0'; p++)
+               {
+                       if (*p == '#' && p > bp && ConfigLevel >= 3)
+                       {
+                               /* this is an on-line comment */
+                               register char *e;
+
+                               switch (*--p)
+                               {
+                                 case '\001':
+                                       /* it's from $# -- let it go through */
+                                       p++;
+                                       break;
+
+                                 case '\\':
+                                       /* it's backslash escaped */
+                                       (void) strcpy(p, p + 1);
+                                       break;
+
+                                 default:
+                                       /* delete preceeding white space */
+                                       while (isspace(*p) && p > bp)
+                                               p--;
+                                       if ((e = strchr(++p, '\n')) != NULL)
+                                               (void) strcpy(p, e);
+                                       else
+                                               p[0] = p[1] = '\0';
+                                       break;
+                               }
+                               continue;
+                       }
+
+                       if (*p != '$')
+                               continue;
+
+                       if (p[1] == '$')
+                       {
+                               /* actual dollar sign.... */
+                               (void) strcpy(p, p + 1);
+                               continue;
+                       }
+
+                       /* convert to macro expansion character */
+                       *p = '\001';
+               }
+
+               /* interpret this line */
+               switch (bp[0])
                {
                  case '\0':
                  case '#':             /* comment */
                        break;
 
                  case 'R':             /* rewriting rule */
                {
                  case '\0':
                  case '#':             /* comment */
                        break;
 
                  case 'R':             /* rewriting rule */
-                       for (p = &buf[1]; *p != '\0' && *p != '\t'; p++)
+                       for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
                                continue;
 
                        if (*p == '\0')
                        {
                                continue;
 
                        if (*p == '\0')
                        {
-                               syserr("invalid rewrite line \"%s\"", buf);
+                               syserr("invalid rewrite line \"%s\"", bp);
                                break;
                        }
 
                                break;
                        }
 
@@ -104,11 +207,32 @@ readcf(cfname, safe)
 
                        /* expand and save the LHS */
                        *p = '\0';
 
                        /* expand and save the LHS */
                        *p = '\0';
-                       expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv);
-                       rwp->r_lhs = prescan(exbuf, '\t');
+                       expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e);
+                       rwp->r_lhs = prescan(exbuf, '\t', pvpbuf);
+                       nfuzzy = 0;
                        if (rwp->r_lhs != NULL)
                        if (rwp->r_lhs != NULL)
+                       {
+                               register char **ap;
+
                                rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
 
                                rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
 
+                               /* count the number of fuzzy matches in LHS */
+                               for (ap = rwp->r_lhs; *ap != NULL; ap++)
+                               {
+                                       switch (**ap)
+                                       {
+                                         case MATCHZANY:
+                                         case MATCHANY:
+                                         case MATCHONE:
+                                         case MATCHCLASS:
+                                         case MATCHNCLASS:
+                                               nfuzzy++;
+                                       }
+                               }
+                       }
+                       else
+                               syserr("R line: null LHS");
+
                        /* expand and save the RHS */
                        while (*++p == '\t')
                                continue;
                        /* expand and save the RHS */
                        while (*++p == '\t')
                                continue;
@@ -116,14 +240,33 @@ readcf(cfname, safe)
                        while (*p != '\0' && *p != '\t')
                                p++;
                        *p = '\0';
                        while (*p != '\0' && *p != '\t')
                                p++;
                        *p = '\0';
-                       expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv);
-                       rwp->r_rhs = prescan(exbuf, '\t');
+                       expand(q, exbuf, &exbuf[sizeof exbuf], e);
+                       rwp->r_rhs = prescan(exbuf, '\t', pvpbuf);
                        if (rwp->r_rhs != NULL)
                        if (rwp->r_rhs != NULL)
+                       {
+                               register char **ap;
+
                                rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
                                rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
+
+                               /* check no out-of-bounds replacements */
+                               nfuzzy += '0';
+                               for (ap = rwp->r_rhs; *ap != NULL; ap++)
+                               {
+                                       if (**ap != MATCHREPL)
+                                               continue;
+                                       if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
+                                       {
+                                               syserr("replacement $%c out of bounds",
+                                                       (*ap)[1]);
+                                       }
+                               }
+                       }
+                       else
+                               syserr("R line: null RHS");
                        break;
 
                  case 'S':             /* select rewriting set */
                        break;
 
                  case 'S':             /* select rewriting set */
-                       ruleset = atoi(&buf[1]);
+                       ruleset = atoi(&bp[1]);
                        if (ruleset >= MAXRWSETS || ruleset < 0)
                        {
                                syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
                        if (ruleset >= MAXRWSETS || ruleset < 0)
                        {
                                syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
@@ -133,20 +276,20 @@ readcf(cfname, safe)
                        break;
 
                  case 'D':             /* macro definition */
                        break;
 
                  case 'D':             /* macro definition */
-                       define(buf[1], newstr(munchstring(&buf[2])), CurEnv);
+                       define(bp[1], newstr(munchstring(&bp[2])), e);
                        break;
 
                  case 'H':             /* required header line */
                        break;
 
                  case 'H':             /* required header line */
-                       (void) chompheader(&buf[1], TRUE);
+                       (void) chompheader(&bp[1], TRUE, e);
                        break;
 
                  case 'C':             /* word class */
                  case 'F':             /* word class from file */
                        /* read list of words from argument or file */
                        break;
 
                  case 'C':             /* word class */
                  case 'F':             /* word class from file */
                        /* read list of words from argument or file */
-                       if (buf[0] == 'F')
+                       if (bp[0] == 'F')
                        {
                                /* read from file */
                        {
                                /* read from file */
-                               for (p = &buf[2]; *p != '\0' && !isspace(*p); p++)
+                               for (p = &bp[2]; *p != '\0' && !isspace(*p); p++)
                                        continue;
                                if (*p == '\0')
                                        p = "%s";
                                        continue;
                                if (*p == '\0')
                                        p = "%s";
@@ -156,12 +299,12 @@ readcf(cfname, safe)
                                        while (isspace(*++p))
                                                continue;
                                }
                                        while (isspace(*++p))
                                                continue;
                                }
-                               fileclass(buf[1], &buf[2], p);
+                               fileclass(bp[1], &bp[2], p, safe);
                                break;
                        }
 
                        /* scan the list of words and set class for all */
                                break;
                        }
 
                        /* scan the list of words and set class for all */
-                       for (p = &buf[2]; *p != '\0'; )
+                       for (p = &bp[2]; *p != '\0'; )
                        {
                                register char *wd;
                                char delim;
                        {
                                register char *wd;
                                char delim;
@@ -174,17 +317,17 @@ readcf(cfname, safe)
                                delim = *p;
                                *p = '\0';
                                if (wd[0] != '\0')
                                delim = *p;
                                *p = '\0';
                                if (wd[0] != '\0')
-                                       setclass(buf[1], wd);
+                                       setclass(bp[1], wd);
                                *p = delim;
                        }
                        break;
 
                  case 'M':             /* define mailer */
                                *p = delim;
                        }
                        break;
 
                  case 'M':             /* define mailer */
-                       makemailer(&buf[1], safe);
+                       makemailer(&buf[1]);
                        break;
 
                  case 'O':             /* set option */
                        break;
 
                  case 'O':             /* set option */
-                       setoption(buf[1], &buf[2], safe, FALSE);
+                       setoption(buf[1], &buf[2], FALSE);
                        break;
 
                  case 'P':             /* set precedence */
                        break;
 
                  case 'P':             /* set precedence */
@@ -193,18 +336,18 @@ readcf(cfname, safe)
                                toomany('P', MAXPRIORITIES);
                                break;
                        }
                                toomany('P', MAXPRIORITIES);
                                break;
                        }
-                       for (p = &buf[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
+                       for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
                                continue;
                        if (*p == '\0')
                                goto badline;
                        *p = '\0';
                                continue;
                        if (*p == '\0')
                                goto badline;
                        *p = '\0';
-                       Priorities[NumPriorities].pri_name = newstr(&buf[1]);
+                       Priorities[NumPriorities].pri_name = newstr(&bp[1]);
                        Priorities[NumPriorities].pri_val = atoi(++p);
                        NumPriorities++;
                        break;
 
                  case 'T':             /* trusted user(s) */
                        Priorities[NumPriorities].pri_val = atoi(++p);
                        NumPriorities++;
                        break;
 
                  case 'T':             /* trusted user(s) */
-                       p = &buf[1];
+                       p = &bp[1];
                        while (*p != '\0')
                        {
                                while (isspace(*p))
                        while (*p != '\0')
                        {
                                while (isspace(*p))
@@ -227,12 +370,37 @@ readcf(cfname, safe)
                        }
                        break;
 
                        }
                        break;
 
+                 case 'V':             /* configuration syntax version */
+                       ConfigLevel = atoi(&bp[1]);
+                       break;
+
+                 case 'K':
+                       makemapentry(&bp[1]);
+                       break;
+
                  default:
                  badline:
                  default:
                  badline:
-                       syserr("unknown control line \"%s\"", buf);
+                       syserr("unknown control line \"%s\"", bp);
                }
                }
+               if (bp != buf)
+                       free(bp);
+       }
+       if (ferror(cf))
+       {
+               syserr("I/O read error", cfname);
+               exit(EX_OSFILE);
        }
        }
+       fclose(cf);
        FileName = NULL;
        FileName = NULL;
+
+       if (stab("host", ST_MAP, ST_FIND) == NULL)
+       {
+               /* user didn't initialize: set up host map */
+               strcpy(buf, "host host");
+               if (ConfigLevel >= 2)
+                       strcat(buf, " -a.");
+               makemapentry(buf);
+       }
 }
 \f/*
 **  TOOMANY -- signal too many of some option
 }
 \f/*
 **  TOOMANY -- signal too many of some option
@@ -271,30 +439,77 @@ toomany(id, maxcnt)
 **                     the named class.
 */
 
 **                     the named class.
 */
 
-fileclass(class, filename, fmt)
+fileclass(class, filename, fmt, safe)
        int class;
        char *filename;
        char *fmt;
        int class;
        char *filename;
        char *fmt;
+       bool safe;
 {
 {
-       register FILE *f;
+       FILE *f;
+       struct stat stbuf;
        char buf[MAXLINE];
 
        char buf[MAXLINE];
 
+       if (stat(filename, &stbuf) < 0)
+       {
+               syserr("fileclass: cannot stat %s", filename);
+               return;
+       }
+       if (!S_ISREG(stbuf.st_mode))
+       {
+               syserr("fileclass: %s not a regular file", filename);
+               return;
+       }
+       if (!safe && access(filename, R_OK) < 0)
+       {
+               syserr("fileclass: access denied on %s", filename);
+               return;
+       }
        f = fopen(filename, "r");
        if (f == NULL)
        {
        f = fopen(filename, "r");
        if (f == NULL)
        {
-               syserr("cannot open %s", filename);
+               syserr("fileclass: cannot open %s", filename);
                return;
        }
 
        while (fgets(buf, sizeof buf, f) != NULL)
        {
                register STAB *s;
                return;
        }
 
        while (fgets(buf, sizeof buf, f) != NULL)
        {
                register STAB *s;
+               register char *p;
+# ifdef SCANF
                char wordbuf[MAXNAME+1];
 
                if (sscanf(buf, fmt, wordbuf) != 1)
                        continue;
                char wordbuf[MAXNAME+1];
 
                if (sscanf(buf, fmt, wordbuf) != 1)
                        continue;
-               s = stab(wordbuf, ST_CLASS, ST_ENTER);
-               setbitn(class, s->s_class);
+               p = wordbuf;
+# else /* SCANF */
+               p = buf;
+# endif /* SCANF */
+
+               /*
+               **  Break up the match into words.
+               */
+
+               while (*p != '\0')
+               {
+                       register char *q;
+
+                       /* strip leading spaces */
+                       while (isspace(*p))
+                               p++;
+                       if (*p == '\0')
+                               break;
+
+                       /* find the end of the word */
+                       q = p;
+                       while (*p != '\0' && !isspace(*p))
+                               p++;
+                       if (*p != '\0')
+                               *p++ = '\0';
+
+                       /* enter the word in the symbol table */
+                       s = stab(q, ST_CLASS, ST_ENTER);
+                       setbitn(class, s->s_class);
+               }
        }
 
        (void) fclose(f);
        }
 
        (void) fclose(f);
@@ -312,7 +527,6 @@ fileclass(class, filename, fmt)
 **                        R -- the recipient rewriting set
 **                        E -- the eol string
 **                     The first word is the canonical name of the mailer.
 **                        R -- the recipient rewriting set
 **                        E -- the eol string
 **                     The first word is the canonical name of the mailer.
-**             safe -- set if this is a safe configuration file.
 **
 **     Returns:
 **             none.
 **
 **     Returns:
 **             none.
@@ -321,9 +535,8 @@ fileclass(class, filename, fmt)
 **             enters the mailer into the mailer table.
 */
 
 **             enters the mailer into the mailer table.
 */
 
-makemailer(line, safe)
+makemailer(line)
        char *line;
        char *line;
-       bool safe;
 {
        register char *p;
        register struct mailer *m;
 {
        register char *p;
        register struct mailer *m;
@@ -339,7 +552,6 @@ makemailer(line, safe)
        /* allocate a mailer and set up defaults */
        m = (struct mailer *) xalloc(sizeof *m);
        bzero((char *) m, sizeof *m);
        /* allocate a mailer and set up defaults */
        m = (struct mailer *) xalloc(sizeof *m);
        bzero((char *) m, sizeof *m);
-       m->m_mno = NextMailer;
        m->m_eol = "\n";
 
        /* collect the mailer name */
        m->m_eol = "\n";
 
        /* collect the mailer name */
@@ -361,7 +573,7 @@ makemailer(line, safe)
                        p++;
                if (*p++ != '=')
                {
                        p++;
                if (*p++ != '=')
                {
-                       syserr("`=' expected");
+                       syserr("mailer %s: `=' expected", m->m_name);
                        return;
                }
                while (isspace(*p))
                        return;
                }
                while (isspace(*p))
@@ -379,9 +591,8 @@ makemailer(line, safe)
 
                  case 'F':             /* flags */
                        for (; *p != '\0'; p++)
 
                  case 'F':             /* flags */
                        for (; *p != '\0'; p++)
-                               setbitn(*p, m->m_flags);
-                       if (!safe)
-                               clrbitn(M_RESTR, m->m_flags);
+                               if (!isspace(*p))
+                                       setbitn(*p, m->m_flags);
                        break;
 
                  case 'S':             /* sender rewriting ruleset */
                        break;
 
                  case 'S':             /* sender rewriting ruleset */
@@ -409,20 +620,42 @@ makemailer(line, safe)
                  case 'M':             /* maximum message size */
                        m->m_maxsize = atol(p);
                        break;
                  case 'M':             /* maximum message size */
                        m->m_maxsize = atol(p);
                        break;
+
+                 case 'L':             /* maximum line length */
+                       m->m_linelimit = atoi(p);
+                       break;
                }
 
                p = DelimChar;
        }
 
                }
 
                p = DelimChar;
        }
 
-       /* now store the mailer away */
+       /* do some heuristic cleanup for back compatibility */
+       if (bitnset(M_LIMITS, m->m_flags))
+       {
+               if (m->m_linelimit == 0)
+                       m->m_linelimit = SMTPLINELIM;
+               if (ConfigLevel < 2)
+                       setbitn(M_7BITS, m->m_flags);
+       }
+
        if (NextMailer >= MAXMAILERS)
        {
                syserr("too many mailers defined (%d max)", MAXMAILERS);
                return;
        }
        if (NextMailer >= MAXMAILERS)
        {
                syserr("too many mailers defined (%d max)", MAXMAILERS);
                return;
        }
-       Mailer[NextMailer++] = m;
+
        s = stab(m->m_name, ST_MAILER, ST_ENTER);
        s = stab(m->m_name, ST_MAILER, ST_ENTER);
-       s->s_mailer = m;
+       if (s->s_mailer != NULL)
+       {
+               i = s->s_mailer->m_mno;
+               free(s->s_mailer);
+       }
+       else
+       {
+               i = NextMailer++;
+       }
+       Mailer[i] = s->s_mailer = m;
+       m->m_mno = i;
 }
 \f/*
 **  MUNCHSTRING -- translate a string into internal form.
 }
 \f/*
 **  MUNCHSTRING -- translate a string into internal form.
@@ -528,7 +761,7 @@ makeargv(p)
 
        /* now make a copy of the argv */
        avp = (char **) xalloc(sizeof *avp * i);
 
        /* now make a copy of the argv */
        avp = (char **) xalloc(sizeof *avp * i);
-       bmove((char *) argv, (char *) avp, sizeof *avp * i);
+       bcopy((char *) argv, (char *) avp, sizeof *avp * i);
 
        return (avp);
 }
 
        return (avp);
 }
@@ -545,8 +778,6 @@ makeargv(p)
 **             prints rewrite rules.
 */
 
 **             prints rewrite rules.
 */
 
-# ifdef DEBUG
-
 printrules()
 {
        register struct rewrite *rwp;
 printrules()
 {
        register struct rewrite *rwp;
@@ -568,14 +799,12 @@ printrules()
        }
 }
 
        }
 }
 
-# endif DEBUG
 \f/*
 **  SETOPTION -- set global processing option
 **
 **     Parameters:
 **             opt -- option name.
 **             val -- option value (as a text string).
 \f/*
 **  SETOPTION -- set global processing option
 **
 **     Parameters:
 **             opt -- option name.
 **             val -- option value (as a text string).
-**             safe -- if set, this came from a system configuration file.
 **             sticky -- if set, don't let other setoptions override
 **                     this value.
 **
 **             sticky -- if set, don't let other setoptions override
 **                     this value.
 **
@@ -587,23 +816,45 @@ printrules()
 */
 
 static BITMAP  StickyOpt;              /* set if option is stuck */
 */
 
 static BITMAP  StickyOpt;              /* set if option is stuck */
-extern char    *WizWord;               /* the stored wizard password */
 
 
-setoption(opt, val, safe, sticky)
+
+#ifdef NAMED_BIND
+
+struct resolverflags
+{
+       char    *rf_name;       /* name of the flag */
+       long    rf_bits;        /* bits to set/clear */
+} ResolverFlags[] =
+{
+       "debug",        RES_DEBUG,
+       "aaonly",       RES_AAONLY,
+       "usevc",        RES_USEVC,
+       "primary",      RES_PRIMARY,
+       "igntc",        RES_IGNTC,
+       "recurse",      RES_RECURSE,
+       "defnames",     RES_DEFNAMES,
+       "stayopen",     RES_STAYOPEN,
+       "dnsrch",       RES_DNSRCH,
+       NULL,           0
+};
+
+#endif
+
+setoption(opt, val, sticky)
        char opt;
        char *val;
        char opt;
        char *val;
-       bool safe;
        bool sticky;
 {
        bool sticky;
 {
+       register char *p;
        extern bool atobool();
        extern time_t convtime();
        extern int QueueLA;
        extern int RefuseLA;
        extern bool atobool();
        extern time_t convtime();
        extern int QueueLA;
        extern int RefuseLA;
+       extern bool trusteduser();
+       extern char *username();
 
 
-# ifdef DEBUG
        if (tTd(37, 1))
                printf("setoption %c=%s", opt, val);
        if (tTd(37, 1))
                printf("setoption %c=%s", opt, val);
-# endif DEBUG
 
        /*
        **  See if this option is preset for us.
 
        /*
        **  See if this option is preset for us.
@@ -611,24 +862,20 @@ setoption(opt, val, safe, sticky)
 
        if (bitnset(opt, StickyOpt))
        {
 
        if (bitnset(opt, StickyOpt))
        {
-# ifdef DEBUG
                if (tTd(37, 1))
                        printf(" (ignored)\n");
                if (tTd(37, 1))
                        printf(" (ignored)\n");
-# endif DEBUG
                return;
        }
                return;
        }
-#ifdef DEBUG
-       else if (tTd(37, 1))
-               printf("\n");
-#endif DEBUG
-       if (sticky)
-               setbitn(opt, StickyOpt);
 
 
-       if (getruid() == 0)
-               safe = TRUE;
+       if (tTd(37, 1))
+               printf("\n");
 
        switch (opt)
        {
 
        switch (opt)
        {
+         case '8':             /* allow eight-bit input */
+               EightBit = atobool(val);
+               break;
+
          case 'A':             /* set default alias file */
                if (val[0] == '\0')
                        AliasFile = "aliases";
          case 'A':             /* set default alias file */
                if (val[0] == '\0')
                        AliasFile = "aliases";
@@ -636,14 +883,27 @@ setoption(opt, val, safe, sticky)
                        AliasFile = newstr(val);
                break;
 
                        AliasFile = newstr(val);
                break;
 
-         case 'a':             /* look for "@:@" in alias file */
-               SafeAlias = atobool(val);
+         case 'a':             /* look N minutes for "@:@" in alias file */
+               if (val[0] == '\0')
+                       SafeAlias = 5;
+               else
+                       SafeAlias = atoi(val);
+               break;
+
+         case 'B':             /* substitution for blank character */
+               SpaceSub = val[0];
+               if (SpaceSub == '\0')
+                       SpaceSub = ' ';
                break;
 
          case 'c':             /* don't connect to "expensive" mailers */
                NoConnect = atobool(val);
                break;
 
                break;
 
          case 'c':             /* don't connect to "expensive" mailers */
                NoConnect = atobool(val);
                break;
 
+         case 'C':             /* checkpoint every N addresses */
+               CheckpointInterval = atoi(val);
+               break;
+
          case 'd':             /* delivery mode */
                switch (*val)
                {
          case 'd':             /* delivery mode */
                switch (*val)
                {
@@ -654,7 +914,7 @@ setoption(opt, val, safe, sticky)
                  case SM_QUEUE:        /* queue only */
 #ifndef QUEUE
                        syserr("need QUEUE to set -odqueue");
                  case SM_QUEUE:        /* queue only */
 #ifndef QUEUE
                        syserr("need QUEUE to set -odqueue");
-#endif QUEUE
+#endif /* QUEUE */
                        /* fall through..... */
 
                  case SM_DELIVER:      /* do everything */
                        /* fall through..... */
 
                  case SM_DELIVER:      /* do everything */
@@ -672,6 +932,11 @@ setoption(opt, val, safe, sticky)
                AutoRebuild = atobool(val);
                break;
 
                AutoRebuild = atobool(val);
                break;
 
+         case 'E':             /* error message header/header file */
+               if (*val != '\0')
+                       ErrMsgFile = newstr(val);
+               break;
+
          case 'e':             /* set error processing mode */
                switch (*val)
                {
          case 'e':             /* set error processing mode */
                switch (*val)
                {
@@ -689,16 +954,19 @@ setoption(opt, val, safe, sticky)
                break;
 
          case 'F':             /* file mode */
                break;
 
          case 'F':             /* file mode */
-               FileMode = atooct(val);
+               FileMode = atooct(val) & 0777;
                break;
 
          case 'f':             /* save Unix-style From lines on front */
                SaveFrom = atobool(val);
                break;
 
                break;
 
          case 'f':             /* save Unix-style From lines on front */
                SaveFrom = atobool(val);
                break;
 
+         case 'G':             /* match recipients against GECOS field */
+               MatchGecos = atobool(val);
+               break;
+
          case 'g':             /* default gid */
          case 'g':             /* default gid */
-               if (safe)
-                       DefGid = atoi(val);
+               DefGid = atoi(val);
                break;
 
          case 'H':             /* help file */
                break;
 
          case 'H':             /* help file */
@@ -708,22 +976,86 @@ setoption(opt, val, safe, sticky)
                        HelpFile = newstr(val);
                break;
 
                        HelpFile = newstr(val);
                break;
 
+         case 'h':             /* maximum hop count */
+               MaxHopCount = atoi(val);
+               break;
+
+         case 'I':             /* use internet domain name server */
+#ifdef NAMED_BIND
+               UseNameServer = TRUE;
+               for (p = val; *p != 0; )
+               {
+                       bool clearmode;
+                       char *q;
+                       struct resolverflags *rfp;
+
+                       while (*p == ' ')
+                               p++;
+                       if (*p == '\0')
+                               break;
+                       clearmode = FALSE;
+                       if (*p == '-')
+                               clearmode = TRUE;
+                       else if (*p != '+')
+                               p--;
+                       p++;
+                       q = p;
+                       while (*p != '\0' && !isspace(*p))
+                               p++;
+                       if (*p != '\0')
+                               *p++ = '\0';
+                       for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
+                       {
+                               if (strcasecmp(q, rfp->rf_name) == 0)
+                                       break;
+                       }
+                       if (clearmode)
+                               _res.options &= ~rfp->rf_bits;
+                       else
+                               _res.options |= rfp->rf_bits;
+               }
+               if (tTd(8, 2))
+                       printf("_res.options = %x\n", _res.options);
+#else
+               usrerr("name server (I option) specified but BIND not compiled in");
+#endif
+               break;
+
          case 'i':             /* ignore dot lines in message */
                IgnrDot = atobool(val);
                break;
 
          case 'i':             /* ignore dot lines in message */
                IgnrDot = atobool(val);
                break;
 
+         case 'J':             /* .forward search path */
+               ForwardPath = newstr(val);
+               break;
+
+         case 'k':             /* connection cache size */
+               MaxMciCache = atoi(val);
+               if (MaxMciCache < 0)
+                       MaxMciCache = 0;
+               break;
+
+         case 'K':             /* connection cache timeout */
+               MciCacheTimeout = convtime(val);
+               break;
+
          case 'L':             /* log level */
                LogLevel = atoi(val);
                break;
 
          case 'M':             /* define macro */
                define(val[0], newstr(&val[1]), CurEnv);
          case 'L':             /* log level */
                LogLevel = atoi(val);
                break;
 
          case 'M':             /* define macro */
                define(val[0], newstr(&val[1]), CurEnv);
+               sticky = FALSE;
                break;
 
          case 'm':             /* send to me too */
                MeToo = atobool(val);
                break;
 
                break;
 
          case 'm':             /* send to me too */
                MeToo = atobool(val);
                break;
 
+         case 'n':             /* validate RHS in newaliases */
+               CheckAliases = atobool(val);
+               break;
+
          case 'o':             /* assume old style headers */
                if (atobool(val))
                        CurEnv->e_flags |= EF_OLDSTYLE;
          case 'o':             /* assume old style headers */
                if (atobool(val))
                        CurEnv->e_flags |= EF_OLDSTYLE;
@@ -731,6 +1063,14 @@ setoption(opt, val, safe, sticky)
                        CurEnv->e_flags &= ~EF_OLDSTYLE;
                break;
 
                        CurEnv->e_flags &= ~EF_OLDSTYLE;
                break;
 
+         case 'P':             /* postmaster copy address for returned mail */
+               PostMasterCopy = newstr(val);
+               break;
+
+         case 'q':             /* slope of queue only function */
+               QueueFactor = atoi(val);
+               break;
+
          case 'Q':             /* queue directory */
                if (val[0] == '\0')
                        QueueDir = "mqueue";
          case 'Q':             /* queue directory */
                if (val[0] == '\0')
                        QueueDir = "mqueue";
@@ -758,31 +1098,25 @@ setoption(opt, val, safe, sticky)
                break;
 
          case 't':             /* time zone name */
                break;
 
          case 't':             /* time zone name */
-# ifdef V6
-               StdTimezone = newstr(val);
-               DstTimezone = index(StdTimeZone, ',');
-               if (DstTimezone == NULL)
-                       syserr("bad time zone spec");
-               else
-                       *DstTimezone++ = '\0';
-# endif V6
+               TimeZoneSpec = newstr(val);
+               break;
+
+         case 'U':             /* location of user database */
+               UdbSpec = newstr(val);
                break;
 
          case 'u':             /* set default uid */
                break;
 
          case 'u':             /* set default uid */
-               if (safe)
-                       DefUid = atoi(val);
+               DefUid = atoi(val);
+               setdefuser();
                break;
 
          case 'v':             /* run in verbose mode */
                Verbose = atobool(val);
                break;
 
                break;
 
          case 'v':             /* run in verbose mode */
                Verbose = atobool(val);
                break;
 
-# ifdef DEBUG
-         case 'W':             /* set the wizards password */
-               if (safe)
-                       WizWord = newstr(val);
+         case 'w':             /* we don't have wildcard MX records */
+               NoWildcardMX = atobool(val);
                break;
                break;
-# endif DEBUG
 
          case 'x':             /* load avg at which to auto-queue msgs */
                QueueLA = atoi(val);
 
          case 'x':             /* load avg at which to auto-queue msgs */
                QueueLA = atoi(val);
@@ -792,9 +1126,27 @@ setoption(opt, val, safe, sticky)
                RefuseLA = atoi(val);
                break;
 
                RefuseLA = atoi(val);
                break;
 
+         case 'y':             /* work recipient factor */
+               WkRecipFact = atoi(val);
+               break;
+
+         case 'Y':             /* fork jobs during queue runs */
+               ForkQueueRuns = atobool(val);
+               break;
+
+         case 'z':             /* work message class factor */
+               WkClassFact = atoi(val);
+               break;
+
+         case 'Z':             /* work time factor */
+               WkTimeFact = atoi(val);
+               break;
+
          default:
                break;
        }
          default:
                break;
        }
+       if (sticky)
+               setbitn(opt, StickyOpt);
        return;
 }
 \f/*
        return;
 }
 \f/*
@@ -820,3 +1172,70 @@ setclass(class, word)
        s = stab(word, ST_CLASS, ST_ENTER);
        setbitn(class, s->s_class);
 }
        s = stab(word, ST_CLASS, ST_ENTER);
        setbitn(class, s->s_class);
 }
+\f/*
+**  MAKEMAPENTRY -- create a map entry
+**
+**     Parameters:
+**             line -- the config file line
+**
+**     Returns:
+**             TRUE if it successfully entered the map entry.
+**             FALSE otherwise (usually syntax error).
+**
+**     Side Effects:
+**             Enters the map into the dictionary.
+*/
+
+void
+makemapentry(line)
+       char *line;
+{
+       register char *p;
+       char *mapname;
+       char *classname;
+       register STAB *map;
+       STAB *class;
+
+       for (p = line; isspace(*p); p++)
+               continue;
+       if (!isalnum(*p))
+       {
+               syserr("readcf: config K line: no map name");
+               return;
+       }
+
+       mapname = p;
+       while (isalnum(*++p))
+               continue;
+       if (*p != '\0')
+               *p++ = '\0';
+       while (isspace(*p))
+               p++;
+       if (!isalnum(*p))
+       {
+               syserr("readcf: config K line, map %s: no map class", mapname);
+               return;
+       }
+       classname = p;
+       while (isalnum(*++p))
+               continue;
+       if (*p != '\0')
+               *p++ = '\0';
+       while (isspace(*p))
+               p++;
+
+       /* look up the class */
+       class = stab(classname, ST_MAPCLASS, ST_FIND);
+       if (class == NULL)
+       {
+               syserr("readcf: map %s: class %s not available", mapname, classname);
+               return;
+       }
+
+       /* enter the map */
+       map = stab(mapname, ST_MAP, ST_ENTER);
+       map->s_map.map_class = &class->s_mapclass;
+
+       if ((*class->s_mapclass.map_init)(&map->s_map, mapname, p))
+               map->s_map.map_flags |= MF_VALID;
+}