BSD 4_3_Net_2 release
[unix-history] / usr / src / usr.sbin / sendmail / src / readcf.c
index 0acd774..bd2bad7 100644 (file)
@@ -1,6 +1,42 @@
-# include "sendmail.h"
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)readcf.c   5.22 (Berkeley) 3/12/91";
+#endif /* not lint */
 
 
-SCCSID(@(#)readcf.c    3.30            %G%);
+# include "sendmail.h"
 
 /*
 **  READCF -- read control file.
 
 /*
 **  READCF -- read control file.
@@ -24,16 +60,13 @@ SCCSID(@(#)readcf.c 3.30            %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 r a      Define mailer.  n - internal name,
-**                             p - pathname, f - flags, r - rewriting
-**                             rule for sender, 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.
 **
 **     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).
 **
 **     Returns:
 **             none.
 **
 **     Returns:
 **             none.
@@ -42,20 +75,22 @@ SCCSID(@(#)readcf.c 3.30            %G%);
 **             Builds several internal tables.
 */
 
 **             Builds several internal tables.
 */
 
-readcf(cfname, safe)
+readcf(cfname)
        char *cfname;
        char *cfname;
-       bool safe;
 {
        FILE *cf;
 {
        FILE *cf;
+       int ruleset = 0;
+       char *q;
+       char **pv;
+       struct rewrite *rwp = NULL;
        char buf[MAXLINE];
        register char *p;
        char buf[MAXLINE];
        register char *p;
-       struct rewrite *rwp = NULL;
        extern char **prescan();
        extern char **copyplist();
        extern char **prescan();
        extern char **copyplist();
-       int class;
-       int ruleset = 0;
        char exbuf[MAXLINE];
        char exbuf[MAXLINE];
-       char *q;
+       char pvpbuf[PSBUFSIZE];
+       extern char *fgetfolded();
+       extern char *munchstring();
 
        cf = fopen(cfname, "r");
        if (cf == NULL)
 
        cf = fopen(cfname, "r");
        if (cf == NULL)
@@ -64,9 +99,28 @@ readcf(cfname, safe)
                exit(EX_OSFILE);
        }
 
                exit(EX_OSFILE);
        }
 
+       FileName = cfname;
        LineNumber = 0;
        while (fgetfolded(buf, sizeof buf, cf) != NULL)
        {
        LineNumber = 0;
        while (fgetfolded(buf, sizeof buf, cf) != NULL)
        {
+               /* map $ into \001 (ASCII SOH) for macro expansion */
+               for (p = buf; *p != '\0'; p++)
+               {
+                       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 (buf[0])
                {
                  case '\0':
                switch (buf[0])
                {
                  case '\0':
@@ -79,8 +133,7 @@ readcf(cfname, safe)
 
                        if (*p == '\0')
                        {
 
                        if (*p == '\0')
                        {
-                               syserr("line %d: invalid rewrite line \"%s\"",
-                                       LineNumber, buf);
+                               syserr("invalid rewrite line \"%s\"", buf);
                                break;
                        }
 
                                break;
                        }
 
@@ -100,7 +153,7 @@ readcf(cfname, safe)
                        /* expand and save the LHS */
                        *p = '\0';
                        expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv);
                        /* expand and save the LHS */
                        *p = '\0';
                        expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv);
-                       rwp->r_lhs = prescan(exbuf, '\t');
+                       rwp->r_lhs = prescan(exbuf, '\t', pvpbuf);
                        if (rwp->r_lhs != NULL)
                                rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
 
                        if (rwp->r_lhs != NULL)
                                rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
 
@@ -112,7 +165,7 @@ readcf(cfname, safe)
                                p++;
                        *p = '\0';
                        expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv);
                                p++;
                        *p = '\0';
                        expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv);
-                       rwp->r_rhs = prescan(exbuf, '\t');
+                       rwp->r_rhs = prescan(exbuf, '\t', pvpbuf);
                        if (rwp->r_rhs != NULL)
                                rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
                        break;
                        if (rwp->r_rhs != NULL)
                                rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
                        break;
@@ -121,15 +174,14 @@ readcf(cfname, safe)
                        ruleset = atoi(&buf[1]);
                        if (ruleset >= MAXRWSETS || ruleset < 0)
                        {
                        ruleset = atoi(&buf[1]);
                        if (ruleset >= MAXRWSETS || ruleset < 0)
                        {
-                               syserr("readcf: line %d: bad ruleset %d (%d max)",
-                                       LineNumber, ruleset, MAXRWSETS);
+                               syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
                                ruleset = 0;
                        }
                        rwp = NULL;
                        break;
 
                  case 'D':             /* macro definition */
                                ruleset = 0;
                        }
                        rwp = NULL;
                        break;
 
                  case 'D':             /* macro definition */
-                       define(buf[1], newstr(&buf[2]));
+                       define(buf[1], newstr(munchstring(&buf[2])), CurEnv);
                        break;
 
                  case 'H':             /* required header line */
                        break;
 
                  case 'H':             /* required header line */
@@ -138,14 +190,6 @@ readcf(cfname, safe)
 
                  case 'C':             /* word class */
                  case 'F':             /* word class from file */
 
                  case 'C':             /* word class */
                  case 'F':             /* word class from file */
-                       class = buf[1];
-                       if (!isalpha(class))
-                               goto badline;
-                       if (isupper(class))
-                               class -= 'A';
-                       else
-                               class -= 'a';
-                       
                        /* read list of words from argument or file */
                        if (buf[0] == 'F')
                        {
                        /* read list of words from argument or file */
                        if (buf[0] == 'F')
                        {
@@ -160,7 +204,7 @@ readcf(cfname, safe)
                                        while (isspace(*++p))
                                                continue;
                                }
                                        while (isspace(*++p))
                                                continue;
                                }
-                               fileclass(class, &buf[2], p);
+                               fileclass(buf[1], &buf[2], p);
                                break;
                        }
 
                                break;
                        }
 
@@ -169,7 +213,6 @@ readcf(cfname, safe)
                        {
                                register char *wd;
                                char delim;
                        {
                                register char *wd;
                                char delim;
-                               register STAB *s;
 
                                while (*p != '\0' && isspace(*p))
                                        p++;
 
                                while (*p != '\0' && isspace(*p))
                                        p++;
@@ -179,24 +222,85 @@ readcf(cfname, safe)
                                delim = *p;
                                *p = '\0';
                                if (wd[0] != '\0')
                                delim = *p;
                                *p = '\0';
                                if (wd[0] != '\0')
-                               {
-                                       s = stab(wd, ST_CLASS, ST_ENTER);
-                                       s->s_class |= 1L << class;
-                               }
+                                       setclass(buf[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 */
+                       setoption(buf[1], &buf[2], TRUE, FALSE);
+                       break;
+
+                 case 'P':             /* set precedence */
+                       if (NumPriorities >= MAXPRIORITIES)
+                       {
+                               toomany('P', MAXPRIORITIES);
+                               break;
+                       }
+                       for (p = &buf[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
+                               continue;
+                       if (*p == '\0')
+                               goto badline;
+                       *p = '\0';
+                       Priorities[NumPriorities].pri_name = newstr(&buf[1]);
+                       Priorities[NumPriorities].pri_val = atoi(++p);
+                       NumPriorities++;
+                       break;
+
+                 case 'T':             /* trusted user(s) */
+                       p = &buf[1];
+                       while (*p != '\0')
+                       {
+                               while (isspace(*p))
+                                       p++;
+                               q = p;
+                               while (*p != '\0' && !isspace(*p))
+                                       p++;
+                               if (*p != '\0')
+                                       *p++ = '\0';
+                               if (*q == '\0')
+                                       continue;
+                               for (pv = TrustedUsers; *pv != NULL; pv++)
+                                       continue;
+                               if (pv >= &TrustedUsers[MAXTRUST])
+                               {
+                                       toomany('T', MAXTRUST);
+                                       break;
+                               }
+                               *pv = newstr(q);
+                       }
                        break;
 
                  default:
                  badline:
                        break;
 
                  default:
                  badline:
-                       syserr("readcf: line %d: unknown control line \"%s\"",
-                               LineNumber, buf);
+                       syserr("unknown control line \"%s\"", buf);
                }
        }
                }
        }
+       FileName = NULL;
+}
+\f/*
+**  TOOMANY -- signal too many of some option
+**
+**     Parameters:
+**             id -- the id of the error line
+**             maxcnt -- the maximum possible values
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             gives a syserr.
+*/
+
+toomany(id, maxcnt)
+       char id;
+       int maxcnt;
+{
+       syserr("too many %c lines, %d max", id, maxcnt);
 }
 \f/*
 **  FILECLASS -- read members of a class from a file
 }
 \f/*
 **  FILECLASS -- read members of a class from a file
@@ -220,7 +324,7 @@ fileclass(class, filename, fmt)
        char *filename;
        char *fmt;
 {
        char *filename;
        char *fmt;
 {
-       register FILE *f;
+       FILE *f;
        char buf[MAXLINE];
 
        f = fopen(filename, "r");
        char buf[MAXLINE];
 
        f = fopen(filename, "r");
@@ -233,12 +337,42 @@ fileclass(class, filename, fmt)
        while (fgets(buf, sizeof buf, f) != NULL)
        {
                register STAB *s;
        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);
-               s->s_class |= 1L << 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);
@@ -247,18 +381,15 @@ fileclass(class, filename, fmt)
 **  MAKEMAILER -- define a new mailer.
 **
 **     Parameters:
 **  MAKEMAILER -- define a new mailer.
 **
 **     Parameters:
-**             line -- description of mailer.  This is in tokens
-**                     separated by white space.  The fields are:
-**                     * the name of the mailer, as refered to
-**                       in the rewriting rules.
-**                     * the pathname of the program to fork to
-**                       execute it.
-**                     * the options needed by this program.
-**                     * the macro string needed to translate
-**                       a local "from" name to one that can be
-**                       returned to this machine.
-**                     * the argument vector (a series of parameters).
-**             safe -- set if this is a safe configuration file.
+**             line -- description of mailer.  This is in labeled
+**                     fields.  The fields are:
+**                        P -- the path to the mailer
+**                        F -- the flags associated with the mailer
+**                        A -- the argv for this mailer
+**                        S -- the sender rewriting set
+**                        R -- the recipient rewriting set
+**                        E -- the eol string
+**                     The first word is the canonical name of the mailer.
 **
 **     Returns:
 **             none.
 **
 **     Returns:
 **             none.
@@ -267,93 +398,213 @@ fileclass(class, filename, fmt)
 **             enters the mailer into the mailer table.
 */
 
 **             enters the mailer into the mailer table.
 */
 
-# define SETWORD \
-               { \
-                       while (*p != '\0' && isspace(*p)) \
-                               p++; \
-                       q = p; \
-                       while (*p != '\0' && !isspace(*p)) \
-                               p++; \
-                       if (*p != '\0') \
-                               *p++ = '\0'; \
-               }
-
-makemailer(line, safe)
+makemailer(line)
        char *line;
        char *line;
-       bool safe;
 {
        register char *p;
 {
        register char *p;
-       register char *q;
        register struct mailer *m;
        register STAB *s;
        int i;
        register struct mailer *m;
        register STAB *s;
        int i;
-       char *mname;
-       char *mpath;
-       u_long mopts;
-       short mrset, msset;
-       char *margv[MAXPV + 1];
-       extern u_long mfencode();
+       char fcode;
        extern int NextMailer;
        extern int NextMailer;
+       extern char **makeargv();
+       extern char *munchstring();
+       extern char *DelimChar;
+       extern long atol();
 
 
-       if (NextMailer >= MAXMAILERS)
+       /* 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 */
+       for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++)
+               continue;
+       if (*p != '\0')
+               *p++ = '\0';
+       m->m_name = newstr(line);
+
+       /* now scan through and assign info from the fields */
+       while (*p != '\0')
        {
        {
-               syserr("readcf: line %d: too many mailers defined (%d max)",
-                       LineNumber, MAXMAILERS);
-               return;
+               while (*p != '\0' && (*p == ',' || isspace(*p)))
+                       p++;
+
+               /* p now points to field code */
+               fcode = *p;
+               while (*p != '\0' && *p != '=' && *p != ',')
+                       p++;
+               if (*p++ != '=')
+               {
+                       syserr("`=' expected");
+                       return;
+               }
+               while (isspace(*p))
+                       p++;
+
+               /* p now points to the field body */
+               p = munchstring(p);
+
+               /* install the field into the mailer struct */
+               switch (fcode)
+               {
+                 case 'P':             /* pathname */
+                       m->m_mailer = newstr(p);
+                       break;
+
+                 case 'F':             /* flags */
+                       for (; *p != '\0'; p++)
+                               setbitn(*p, m->m_flags);
+                       break;
+
+                 case 'S':             /* sender rewriting ruleset */
+                 case 'R':             /* recipient rewriting ruleset */
+                       i = atoi(p);
+                       if (i < 0 || i >= MAXRWSETS)
+                       {
+                               syserr("invalid rewrite set, %d max", MAXRWSETS);
+                               return;
+                       }
+                       if (fcode == 'S')
+                               m->m_s_rwset = i;
+                       else
+                               m->m_r_rwset = i;
+                       break;
+
+                 case 'E':             /* end of line string */
+                       m->m_eol = newstr(p);
+                       break;
+
+                 case 'A':             /* argument vector */
+                       m->m_argv = makeargv(p);
+                       break;
+
+                 case 'M':             /* maximum message size */
+                       m->m_maxsize = atol(p);
+                       break;
+               }
+
+               p = DelimChar;
        }
 
        }
 
-       /* collect initial information */
-       p = line;
-       SETWORD;
-       mname = q;
-       SETWORD;
-       mpath = q;
-       SETWORD;
-       mopts = mfencode(q);
-       if (!safe)
-               mopts &= ~M_RESTR;
-       SETWORD;
-       msset = atoi(q);
-       SETWORD;
-       mrset = atoi(q);
-
-       if (*p == '\0')
+       /* now store the mailer away */
+       if (NextMailer >= MAXMAILERS)
        {
        {
-               syserr("readcf: line %d: invalid M line in configuration file",
-                       LineNumber);
+               syserr("too many mailers defined (%d max)", MAXMAILERS);
                return;
        }
                return;
        }
-       if (msset >= MAXRWSETS || mrset >= MAXRWSETS)
+       Mailer[NextMailer++] = m;
+       s = stab(m->m_name, ST_MAILER, ST_ENTER);
+       s->s_mailer = m;
+}
+\f/*
+**  MUNCHSTRING -- translate a string into internal form.
+**
+**     Parameters:
+**             p -- the string to munch.
+**
+**     Returns:
+**             the munched string.
+**
+**     Side Effects:
+**             Sets "DelimChar" to point to the string that caused us
+**             to stop.
+*/
+
+char *
+munchstring(p)
+       register char *p;
+{
+       register char *q;
+       bool backslash = FALSE;
+       bool quotemode = FALSE;
+       static char buf[MAXLINE];
+       extern char *DelimChar;
+
+       for (q = buf; *p != '\0'; p++)
        {
        {
-               syserr("readcf: line %d: invalid rewrite set, %d max",
-                       LineNumber, MAXRWSETS);
-               return;
+               if (backslash)
+               {
+                       /* everything is roughly literal */
+                       backslash = FALSE;
+                       switch (*p)
+                       {
+                         case 'r':             /* carriage return */
+                               *q++ = '\r';
+                               continue;
+
+                         case 'n':             /* newline */
+                               *q++ = '\n';
+                               continue;
+
+                         case 'f':             /* form feed */
+                               *q++ = '\f';
+                               continue;
+
+                         case 'b':             /* backspace */
+                               *q++ = '\b';
+                               continue;
+                       }
+                       *q++ = *p;
+               }
+               else
+               {
+                       if (*p == '\\')
+                               backslash = TRUE;
+                       else if (*p == '"')
+                               quotemode = !quotemode;
+                       else if (quotemode || *p != ',')
+                               *q++ = *p;
+                       else
+                               break;
+               }
        }
 
        }
 
-       /* allocate a mailer */
-       m = (struct mailer *) xalloc(sizeof *m);
-       m->m_name = newstr(mname);
-       m->m_mailer = newstr(mpath);
-       m->m_flags = mopts;
-       m->m_r_rwset = mrset;
-       m->m_s_rwset = msset;
-       m->m_badstat = EX_UNAVAILABLE;
-       m->m_mno = NextMailer;
-       Mailer[NextMailer++] = m;
+       DelimChar = p;
+       *q++ = '\0';
+       return (buf);
+}
+\f/*
+**  MAKEARGV -- break up a string into words
+**
+**     Parameters:
+**             p -- the string to break up.
+**
+**     Returns:
+**             a char **argv (dynamically allocated)
+**
+**     Side Effects:
+**             munges p.
+*/
+
+char **
+makeargv(p)
+       register char *p;
+{
+       char *q;
+       int i;
+       char **avp;
+       char *argv[MAXPV + 1];
 
 
-       /* collect the argument vector */
-       for (i = 0; i < MAXPV - 1 && *p != '\0'; i++)
+       /* take apart the words */
+       i = 0;
+       while (*p != '\0' && i < MAXPV)
        {
        {
-               SETWORD;
-               margv[i] = newstr(q);
+               q = p;
+               while (*p != '\0' && !isspace(*p))
+                       p++;
+               while (isspace(*p))
+                       *p++ = '\0';
+               argv[i++] = newstr(q);
        }
        }
-       margv[i++] = NULL;
+       argv[i++] = NULL;
 
 
-       /* save the argv */
-       m->m_argv = (char **) xalloc(sizeof margv[0] * i);
-       bmove((char *) margv, (char *) m->m_argv, sizeof margv[0] * i);
-       s = stab(m->m_name, ST_MAILER, ST_ENTER);
-       s->s_mailer = m;
+       /* now make a copy of the argv */
+       avp = (char **) xalloc(sizeof *avp * i);
+       bcopy((char *) argv, (char *) avp, sizeof *avp * i);
+
+       return (avp);
 }
 \f/*
 **  PRINTRULES -- print rewrite rules (for debugging)
 }
 \f/*
 **  PRINTRULES -- print rewrite rules (for debugging)
@@ -368,8 +619,6 @@ makemailer(line, safe)
 **             prints rewrite rules.
 */
 
 **             prints rewrite rules.
 */
 
-# ifdef DEBUG
-
 printrules()
 {
        register struct rewrite *rwp;
 printrules()
 {
        register struct rewrite *rwp;
@@ -391,97 +640,310 @@ printrules()
        }
 }
 
        }
 }
 
-# endif DEBUG
 \f/*
 \f/*
-**  MFENCODE -- crack mailer options
-**
-**     These options modify the functioning of the mailer
-**     from the configuration table.
+**  SETOPTION -- set global processing option
 **
 **     Parameters:
 **
 **     Parameters:
-**             p -- pointer to vector of options.
+**             opt -- option name.
+**             val -- option value (as a text string).
+**             safe -- set if this came from a configuration file.
+**                     Some options (if set from the command line) will
+**                     reset the user id to avoid security problems.
+**             sticky -- if set, don't let other setoptions override
+**                     this value.
 **
 **     Returns:
 **
 **     Returns:
-**             option list in binary.
+**             none.
 **
 **     Side Effects:
 **
 **     Side Effects:
-**             none.
+**             Sets options as implied by the arguments.
 */
 
 */
 
-struct optlist
-{
-       char    opt_name;       /* external name of option */
-       u_long  opt_value;      /* internal name of option */
-};
-struct optlist OptList[] =
-{
-       'f',    M_FOPT,
-       'r',    M_ROPT,
-       'q',    M_QUIET,
-       'S',    M_RESTR,
-       'n',    M_NHDR,
-       'l',    M_LOCAL,
-       's',    M_STRIPQ,
-       'm',    M_MUSER,
-       'F',    M_NEEDFROM,
-       'D',    M_NEEDDATE,
-       'M',    M_MSGID,
-       'u',    M_USR_UPPER,
-       'h',    M_HST_UPPER,
-       'x',    M_FULLNAME,
-       'A',    M_ARPAFMT,
-       'U',    M_UGLYUUCP,
-       'e',    M_EXPENSIVE,
-       'X',    M_FULLSMTP,
-       '\0',   0
-};
-
-u_long
-mfencode(p)
-       register char *p;
+static BITMAP  StickyOpt;              /* set if option is stuck */
+extern char    *NetName;               /* name of home (local) network */
+
+setoption(opt, val, safe, sticky)
+       char opt;
+       char *val;
+       bool safe;
+       bool sticky;
 {
 {
-       register struct optlist *o;
-       register u_long opts = 0;
+       extern bool atobool();
+       extern time_t convtime();
+       extern int QueueLA;
+       extern int RefuseLA;
+       extern bool trusteduser();
+       extern char *username();
 
 
-       while (*p != '\0')
+       if (tTd(37, 1))
+               printf("setoption %c=%s", opt, val);
+
+       /*
+       **  See if this option is preset for us.
+       */
+
+       if (bitnset(opt, StickyOpt))
        {
        {
-               for (o = OptList; o->opt_name != '\0' && o->opt_name != *p; o++)
-                       continue;
-               if (o->opt_name == '\0')
-                       syserr("bad mailer option %c", *p);
-               opts |= o->opt_value;
-               p++;
+               if (tTd(37, 1))
+                       printf(" (ignored)\n");
+               return;
+       }
+
+       /*
+       **  Check to see if this option can be specified by this user.
+       */
+
+       if (!safe && getuid() == 0)
+               safe = TRUE;
+       if (!safe && index("deiLmorsv", opt) == NULL)
+       {
+               if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
+               {
+                       if (tTd(37, 1))
+                               printf(" (unsafe)");
+                       if (getuid() != geteuid())
+                       {
+                               printf("(Resetting uid)\n");
+                               (void) setgid(getgid());
+                               (void) setuid(getuid());
+                       }
+               }
        }
        }
-       return (opts);
+       else if (tTd(37, 1))
+               printf("\n");
+
+       switch (opt)
+       {
+         case 'A':             /* set default alias file */
+               if (val[0] == '\0')
+                       AliasFile = "aliases";
+               else
+                       AliasFile = newstr(val);
+               break;
+
+         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;
+
+         case 'C':             /* checkpoint after N connections */
+               CheckPointLimit = atoi(val);
+               break;
+
+         case 'd':             /* delivery mode */
+               switch (*val)
+               {
+                 case '\0':
+                       SendMode = SM_DELIVER;
+                       break;
+
+                 case SM_QUEUE:        /* queue only */
+#ifndef QUEUE
+                       syserr("need QUEUE to set -odqueue");
+#endif QUEUE
+                       /* fall through..... */
+
+                 case SM_DELIVER:      /* do everything */
+                 case SM_FORK:         /* fork after verification */
+                       SendMode = *val;
+                       break;
+
+                 default:
+                       syserr("Unknown delivery mode %c", *val);
+                       exit(EX_USAGE);
+               }
+               break;
+
+         case 'D':             /* rebuild alias database as needed */
+               AutoRebuild = atobool(val);
+               break;
+
+         case 'e':             /* set error processing mode */
+               switch (*val)
+               {
+                 case EM_QUIET:        /* be silent about it */
+                 case EM_MAIL:         /* mail back */
+                 case EM_BERKNET:      /* do berknet error processing */
+                 case EM_WRITE:        /* write back (or mail) */
+                       HoldErrs = TRUE;
+                       /* fall through... */
+
+                 case EM_PRINT:        /* print errors normally (default) */
+                       ErrorMode = *val;
+                       break;
+               }
+               break;
+
+         case 'F':             /* file mode */
+               FileMode = atooct(val) & 0777;
+               break;
+
+         case 'f':             /* save Unix-style From lines on front */
+               SaveFrom = atobool(val);
+               break;
+
+         case 'g':             /* default gid */
+               DefGid = atoi(val);
+               break;
+
+         case 'H':             /* help file */
+               if (val[0] == '\0')
+                       HelpFile = "sendmail.hf";
+               else
+                       HelpFile = newstr(val);
+               break;
+
+         case 'I':             /* use internet domain name server */
+               UseNameServer = atobool(val);
+               break;
+
+         case 'i':             /* ignore dot lines in message */
+               IgnrDot = atobool(val);
+               break;
+
+         case 'k':             /* checkpoint every N addresses */
+               CheckpointInterval = atoi(val);
+               break;
+
+         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;
+
+         case 'n':             /* validate RHS in newaliases */
+               CheckAliases = atobool(val);
+               break;
+
+# ifdef DAEMON
+         case 'N':             /* home (local?) network name */
+               NetName = newstr(val);
+               break;
+# endif DAEMON
+
+         case 'o':             /* assume old style headers */
+               if (atobool(val))
+                       CurEnv->e_flags |= EF_OLDSTYLE;
+               else
+                       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";
+               else
+                       QueueDir = newstr(val);
+               break;
+
+         case 'r':             /* read timeout */
+               ReadTimeout = convtime(val);
+               break;
+
+         case 'S':             /* status file */
+               if (val[0] == '\0')
+                       StatFile = "sendmail.st";
+               else
+                       StatFile = newstr(val);
+               break;
+
+         case 's':             /* be super safe, even if expensive */
+               SuperSafe = atobool(val);
+               break;
+
+         case 'T':             /* queue timeout */
+               TimeOut = convtime(val);
+               /*FALLTHROUGH*/
+
+         case 't':             /* time zone name */
+               break;
+
+         case 'u':             /* set default uid */
+               DefUid = atoi(val);
+               setdefuser();
+               break;
+
+         case 'v':             /* run in verbose mode */
+               Verbose = atobool(val);
+               break;
+
+         case 'x':             /* load avg at which to auto-queue msgs */
+               QueueLA = atoi(val);
+               break;
+
+         case 'X':             /* load avg at which to auto-reject connections */
+               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;
+       }
+       if (sticky)
+               setbitn(opt, StickyOpt);
+       return;
 }
 \f/*
 }
 \f/*
-**  MFDECODE -- decode mailer flags into external form.
+**  SETCLASS -- set a word into a class
 **
 **     Parameters:
 **
 **     Parameters:
-**             flags -- value of flags to decode.
-**             f -- file to write them onto.
+**             class -- the class to put the word in.
+**             word -- the word to enter
 **
 **     Returns:
 **             none.
 **
 **     Side Effects:
 **
 **     Returns:
 **             none.
 **
 **     Side Effects:
-**             none.
+**             puts the word into the symbol table.
 */
 
 */
 
-mfdecode(flags, f)
-       u_long flags;
-       FILE *f;
+setclass(class, word)
+       int class;
+       char *word;
 {
 {
-       register struct optlist *o;
+       register STAB *s;
 
 
-       putc('?', f);
-       for (o = OptList; o->opt_name != '\0'; o++)
-       {
-               if ((o->opt_value & flags) == o->opt_value)
-               {
-                       flags &= ~o->opt_value;
-                       putc(o->opt_name, f);
-               }
-       }
-       putc('?', f);
+       s = stab(word, ST_CLASS, ST_ENTER);
+       setbitn(class, s->s_class);
 }
 }