fixes for problems causing mail to be both sent and rejected
[unix-history] / usr / src / usr.sbin / sendmail / src / parseaddr.c
index 2cda5c4..8d11206 100644 (file)
@@ -7,7 +7,7 @@
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)parseaddr.c        6.3 (Berkeley) %G%";
+static char sccsid[] = "@(#)parseaddr.c        6.50 (Berkeley) %G%";
 #endif /* not lint */
 
 #include "sendmail.h"
 #endif /* not lint */
 
 #include "sendmail.h"
@@ -52,6 +52,8 @@ char  *DelimChar;             /* set to point to the delimiter */
 **                     +1 -- copy everything.
 **             delim -- the character to terminate the address, passed
 **                     to prescan.
 **                     +1 -- copy everything.
 **             delim -- the character to terminate the address, passed
 **                     to prescan.
+**             delimptr -- if non-NULL, set to the location of the
+**                     delim character that was found.
 **             e -- the envelope that will contain this address.
 **
 **     Returns:
 **             e -- the envelope that will contain this address.
 **
 **     Returns:
@@ -64,17 +66,20 @@ char        *DelimChar;             /* set to point to the delimiter */
 */
 
 /* following delimiters are inherent to the internal algorithms */
 */
 
 /* following delimiters are inherent to the internal algorithms */
-# define DELIMCHARS    "\001()<>,;\\\"\r\n"    /* word delimiters */
+# define DELIMCHARS    "()<>,;\r\n"    /* default word delimiters */
 
 ADDRESS *
 
 ADDRESS *
-parseaddr(addr, a, copyf, delim, e)
+parseaddr(addr, a, copyf, delim, delimptr, e)
        char *addr;
        register ADDRESS *a;
        int copyf;
        char *addr;
        register ADDRESS *a;
        int copyf;
-       char delim;
+       int delim;
+       char **delimptr;
        register ENVELOPE *e;
 {
        register char **pvp;
        register ENVELOPE *e;
 {
        register char **pvp;
+       auto char *delimptrbuf;
+       bool queueup;
        char pvpbuf[PSBUFSIZE];
 
        /*
        char pvpbuf[PSBUFSIZE];
 
        /*
@@ -108,7 +113,10 @@ parseaddr(addr, a, copyf, delim, e)
                        return (NULL);
        }
 
                        return (NULL);
        }
 
-       pvp = prescan(addr, delim, pvpbuf);
+       if (delimptr == NULL)
+               delimptr = &delimptrbuf;
+
+       pvp = prescan(addr, delim, pvpbuf, delimptr);
        if (pvp == NULL)
        {
                if (tTd(20, 1))
        if (pvp == NULL)
        {
                if (tTd(20, 1))
@@ -121,17 +129,20 @@ parseaddr(addr, a, copyf, delim, e)
        **      Ruleset 0 does basic parsing.  It must resolve.
        */
 
        **      Ruleset 0 does basic parsing.  It must resolve.
        */
 
-       rewrite(pvp, 3);
-       rewrite(pvp, 0);
+       queueup = FALSE;
+       if (rewrite(pvp, 3, e) == EX_TEMPFAIL)
+               queueup = TRUE;
+       if (rewrite(pvp, 0, e) == EX_TEMPFAIL)
+               queueup = TRUE;
 
        /*
        **  See if we resolved to a real mailer.
        */
 
 
        /*
        **  See if we resolved to a real mailer.
        */
 
-       if (pvp[0][0] != CANONNET)
+       if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
        {
                setstat(EX_USAGE);
        {
                setstat(EX_USAGE);
-               usrerr("cannot resolve name");
+               syserr("554 cannot resolve name");
                return (NULL);
        }
 
                return (NULL);
        }
 
@@ -139,7 +150,7 @@ parseaddr(addr, a, copyf, delim, e)
        **  Build canonical address from pvp.
        */
 
        **  Build canonical address from pvp.
        */
 
-       a = buildaddr(pvp, a);
+       a = buildaddr(pvp, a, e);
        if (a == NULL)
                return (NULL);
 
        if (a == NULL)
                return (NULL);
 
@@ -148,7 +159,23 @@ parseaddr(addr, a, copyf, delim, e)
        **  transport them out.
        */
 
        **  transport them out.
        */
 
-       allocaddr(a, copyf, addr);
+       allocaddr(a, copyf, addr, *delimptr);
+
+       /*
+       **  If there was a parsing failure, mark it for queueing.
+       */
+
+       if (queueup)
+       {
+               char *msg = "Transient parse error -- message queued for future delivery";
+
+               if (tTd(20, 1))
+                       printf("parseaddr: queuing message\n");
+               message(msg);
+               if (e->e_message == NULL)
+                       e->e_message = msg;
+               a->q_flags |= QQUEUEUP;
+       }
 
        /*
        **  Compute return value.
 
        /*
        **  Compute return value.
@@ -179,10 +206,10 @@ invalidaddr(addr)
 {
        for (; *addr != '\0'; addr++)
        {
 {
        for (; *addr != '\0'; addr++)
        {
-               if ((((int) *addr) & 0377) >= '\040' || isspace(*addr))
+               if ((*addr & 0340) != 0200)
                        continue;
                setstat(EX_USAGE);
                        continue;
                setstat(EX_USAGE);
-               usrerr("Address contained invalid control characters");
+               usrerr("553 Address contained invalid control characters");
                return TRUE;
        }
        return FALSE;
                return TRUE;
        }
        return FALSE;
@@ -196,6 +223,7 @@ invalidaddr(addr)
 **             a -- the address to reallocate.
 **             copyf -- the copy flag (see parseaddr for description).
 **             paddr -- the printname of the address.
 **             a -- the address to reallocate.
 **             copyf -- the copy flag (see parseaddr for description).
 **             paddr -- the printname of the address.
+**             delimptr -- a pointer to the address delimiter.  Must be set.
 **
 **     Returns:
 **             none.
 **
 **     Returns:
 **             none.
@@ -204,20 +232,26 @@ invalidaddr(addr)
 **             Copies portions of a into local buffers as requested.
 */
 
 **             Copies portions of a into local buffers as requested.
 */
 
-allocaddr(a, copyf, paddr)
+allocaddr(a, copyf, paddr, delimptr)
        register ADDRESS *a;
        int copyf;
        char *paddr;
        register ADDRESS *a;
        int copyf;
        char *paddr;
+       char *delimptr;
 {
        register MAILER *m = a->q_mailer;
 
 {
        register MAILER *m = a->q_mailer;
 
+       if (tTd(24, 4))
+               printf("allocaddr(copyf=%d, paddr=%s)\n", copyf, paddr);
+
        if (copyf > 0 && paddr != NULL)
        {
        if (copyf > 0 && paddr != NULL)
        {
-               char savec = *DelimChar;
+               char savec = *delimptr;
 
 
-               *DelimChar = '\0';
+               if (savec != '\0')
+                       *delimptr = '\0';
                a->q_paddr = newstr(paddr);
                a->q_paddr = newstr(paddr);
-               *DelimChar = savec;
+               if (savec != '\0')
+                       *delimptr = savec;
        }
        else
                a->q_paddr = paddr;
        }
        else
                a->q_paddr = paddr;
@@ -236,36 +270,6 @@ allocaddr(a, copyf, paddr)
 
        if (a->q_paddr == NULL)
                a->q_paddr = a->q_user;
 
        if (a->q_paddr == NULL)
                a->q_paddr = a->q_user;
-
-       /*
-       **  Convert host name to lower case if requested.
-       **      User name will be done later.
-       */
-
-       if (!bitnset(M_HST_UPPER, m->m_flags))
-               makelower(a->q_host);
-}
-\f/*
-**  LOWERADDR -- map UPPER->lower case on addresses as requested.
-**
-**     Parameters:
-**             a -- address to be mapped.
-**
-**     Returns:
-**             none.
-**
-**     Side Effects:
-**             none.
-*/
-
-void
-loweraddr(a)
-       register ADDRESS *a;
-{
-       register MAILER *m = a->q_mailer;
-
-       if (!bitnset(M_USR_UPPER, m->m_flags))
-               makelower(a->q_user);
 }
 \f/*
 **  INVALIDADDR -- check an address string for invalid control characters.
 }
 \f/*
 **  INVALIDADDR -- check an address string for invalid control characters.
@@ -329,13 +333,12 @@ invalidaddr(addr)
 **                     If '\t' then we are reading the .cf file.
 **             pvpbuf -- place to put the saved text -- note that
 **                     the pointers are static.
 **                     If '\t' then we are reading the .cf file.
 **             pvpbuf -- place to put the saved text -- note that
 **                     the pointers are static.
+**             delimptr -- if non-NULL, set to the location of the
+**                     terminating delimiter.
 **
 **     Returns:
 **             A pointer to a vector of tokens.
 **             NULL on error.
 **
 **     Returns:
 **             A pointer to a vector of tokens.
 **             NULL on error.
-**
-**     Side Effects:
-**             sets DelimChar to point to the character matching 'delim'.
 */
 
 /* states and character types */
 */
 
 /* states and character types */
@@ -366,10 +369,11 @@ static short StateTab[NSTATES][NSTATES] =
 # define NOCHAR                -1      /* signal nothing in lookahead token */
 
 char **
 # define NOCHAR                -1      /* signal nothing in lookahead token */
 
 char **
-prescan(addr, delim, pvpbuf)
+prescan(addr, delim, pvpbuf, delimptr)
        char *addr;
        char delim;
        char pvpbuf[];
        char *addr;
        char delim;
        char pvpbuf[];
+       char **delimptr;
 {
        register char *p;
        register char *q;
 {
        register char *p;
        register char *q;
@@ -381,6 +385,7 @@ prescan(addr, delim, pvpbuf)
        char *tok;
        int state;
        int newstate;
        char *tok;
        int state;
        int newstate;
+       char *saveto = CurEnv->e_to;
        static char *av[MAXATOM+1];
 
        /* make sure error messages don't have garbage on them */
        static char *av[MAXATOM+1];
 
        /* make sure error messages don't have garbage on them */
@@ -394,6 +399,7 @@ prescan(addr, delim, pvpbuf)
        state = ATM;
        c = NOCHAR;
        p = addr;
        state = ATM;
        c = NOCHAR;
        p = addr;
+       CurEnv->e_to = p;
        if (tTd(22, 11))
        {
                printf("prescan: ");
        if (tTd(22, 11))
        {
                printf("prescan: ");
@@ -408,13 +414,15 @@ prescan(addr, delim, pvpbuf)
                for (;;)
                {
                        /* store away any old lookahead character */
                for (;;)
                {
                        /* store away any old lookahead character */
-                       if (c != NOCHAR)
+                       if (c != NOCHAR && !bslashmode)
                        {
                                /* see if there is room */
                                if (q >= &pvpbuf[PSBUFSIZE - 5])
                                {
                        {
                                /* see if there is room */
                                if (q >= &pvpbuf[PSBUFSIZE - 5])
                                {
-                                       usrerr("Address too long");
-                                       DelimChar = p;
+                                       usrerr("553 Address too long");
+                                       if (delimptr != NULL)
+                                               *delimptr = p;
+                                       CurEnv->e_to = saveto;
                                        return (NULL);
                                }
 
                                        return (NULL);
                                }
 
@@ -424,29 +432,32 @@ prescan(addr, delim, pvpbuf)
 
                        /* read a new input character */
                        c = *p++;
 
                        /* read a new input character */
                        c = *p++;
-                       if (c == '\0' || (c == delim && anglecnt <= 0))
+                       if (c == '\0')
                        {
                                /* diagnose and patch up bad syntax */
                                if (state == QST)
                                {
                        {
                                /* diagnose and patch up bad syntax */
                                if (state == QST)
                                {
-                                       usrerr("Unbalanced '\"'");
+                                       usrerr("553 Unbalanced '\"'");
                                        c = '"';
                                }
                                else if (cmntcnt > 0)
                                {
                                        c = '"';
                                }
                                else if (cmntcnt > 0)
                                {
-                                       usrerr("Unbalanced '('");
+                                       usrerr("553 Unbalanced '('");
                                        c = ')';
                                }
                                else if (anglecnt > 0)
                                {
                                        c = '>';
                                        c = ')';
                                }
                                else if (anglecnt > 0)
                                {
                                        c = '>';
-                                       usrerr("Unbalanced '<'");
+                                       usrerr("553 Unbalanced '<'");
                                }
                                else
                                        break;
 
                                p--;
                        }
                                }
                                else
                                        break;
 
                                p--;
                        }
+                       else if (c == delim && anglecnt <= 0 &&
+                                       cmntcnt <= 0 && state != QST)
+                               break;
 
                        if (tTd(22, 101))
                                printf("c=%c, s=%d; ", c, state);
 
                        if (tTd(22, 101))
                                printf("c=%c, s=%d; ", c, state);
@@ -455,20 +466,24 @@ prescan(addr, delim, pvpbuf)
                        *q = '\0';
                        if (bslashmode)
                        {
                        *q = '\0';
                        if (bslashmode)
                        {
-                               /* kludge \! for naive users */
-                               if (c != '!')
-                                       *q++ = '\\';
                                bslashmode = FALSE;
                                bslashmode = FALSE;
+
+                               /* kludge \! for naive users */
                                if (cmntcnt > 0)
                                if (cmntcnt > 0)
+                               {
                                        c = NOCHAR;
                                        c = NOCHAR;
-                               continue;
+                                       continue;
+                               }
+                               else if (c != '!' || state == QST)
+                               {
+                                       *q++ = '\\';
+                                       continue;
+                               }
                        }
 
                        if (c == '\\')
                        {
                                bslashmode = TRUE;
                        }
 
                        if (c == '\\')
                        {
                                bslashmode = TRUE;
-                               c = NOCHAR;
-                               continue;
                        }
                        if (state == QST)
                        {
                        }
                        if (state == QST)
                        {
@@ -483,8 +498,10 @@ prescan(addr, delim, pvpbuf)
                        {
                                if (cmntcnt <= 0)
                                {
                        {
                                if (cmntcnt <= 0)
                                {
-                                       usrerr("Unbalanced ')'");
-                                       DelimChar = p;
+                                       usrerr("553 Unbalanced ')'");
+                                       if (delimptr != NULL)
+                                               *delimptr = p;
+                                       CurEnv->e_to = saveto;
                                        return (NULL);
                                }
                                else
                                        return (NULL);
                                }
                                else
@@ -498,13 +515,15 @@ prescan(addr, delim, pvpbuf)
                        {
                                if (anglecnt <= 0)
                                {
                        {
                                if (anglecnt <= 0)
                                {
-                                       usrerr("Unbalanced '>'");
-                                       DelimChar = p;
+                                       usrerr("553 Unbalanced '>'");
+                                       if (delimptr != NULL)
+                                               *delimptr = p;
+                                       CurEnv->e_to = saveto;
                                        return (NULL);
                                }
                                anglecnt--;
                        }
                                        return (NULL);
                                }
                                anglecnt--;
                        }
-                       else if (delim == ' ' && isspace(c))
+                       else if (delim == ' ' && isascii(c) && isspace(c))
                                c = ' ';
                        else if (c == ':' && !CurEnv->e_oldstyle)
                        {
                                c = ' ';
                        else if (c == ':' && !CurEnv->e_oldstyle)
                        {
@@ -550,23 +569,28 @@ prescan(addr, delim, pvpbuf)
                        }
                        if (avp >= &av[MAXATOM])
                        {
                        }
                        if (avp >= &av[MAXATOM])
                        {
-                               syserr("prescan: too many tokens");
-                               DelimChar = p;
+                               syserr("553 prescan: too many tokens");
+                               if (delimptr != NULL)
+                                       *delimptr = p;
+                               CurEnv->e_to = saveto;
                                return (NULL);
                        }
                        *avp++ = tok;
                }
        } while (c != '\0' && (c != delim || anglecnt > 0));
        *avp = NULL;
                                return (NULL);
                        }
                        *avp++ = tok;
                }
        } while (c != '\0' && (c != delim || anglecnt > 0));
        *avp = NULL;
-       DelimChar = --p;
+       p--;
+       if (delimptr != NULL)
+               *delimptr = p;
        if (tTd(22, 12))
        {
                printf("prescan==>");
                printav(av);
        }
        if (tTd(22, 12))
        {
                printf("prescan==>");
                printav(av);
        }
-       if (av[0] != NULL)
-               return (av);
-       return (NULL);
+       CurEnv->e_to = saveto;
+       if (av[0] == NULL)
+               return (NULL);
+       return (av);
 }
 \f/*
 **  TOKTYPE -- return token type
 }
 \f/*
 **  TOKTYPE -- return token type
@@ -583,7 +607,7 @@ prescan(addr, delim, pvpbuf)
 
 static int
 toktype(c)
 
 static int
 toktype(c)
-       register char c;
+       register int c;
 {
        static char buf[50];
        static bool firstime = TRUE;
 {
        static char buf[50];
        static bool firstime = TRUE;
@@ -591,22 +615,27 @@ toktype(c)
        if (firstime)
        {
                firstime = FALSE;
        if (firstime)
        {
                firstime = FALSE;
-               expand("\001o", buf, &buf[sizeof buf - 1], CurEnv);
+               expand("\201o", buf, &buf[sizeof buf - 1], CurEnv);
                (void) strcat(buf, DELIMCHARS);
        }
                (void) strcat(buf, DELIMCHARS);
        }
+       c &= 0377;
        if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS)
                return (ONE);
        if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS)
                return (ONE);
+       if (c == MACRODEXPAND)
+               return (ONE);
 #ifdef MACVALUE
        if (c == MACVALUE)
                return (ONE);
 #endif /* MACVALUE */
        if (c == '"')
                return (QST);
 #ifdef MACVALUE
        if (c == MACVALUE)
                return (ONE);
 #endif /* MACVALUE */
        if (c == '"')
                return (QST);
+       if ((c & 0340) == 0200)
+               return (OPR);
        if (!isascii(c))
                return (ATM);
        if (isspace(c) || c == ')')
                return (SPC);
        if (!isascii(c))
                return (ATM);
        if (isspace(c) || c == ')')
                return (SPC);
-       if (iscntrl(c) || strchr(buf, c) != NULL)
+       if (strchr(buf, c) != NULL)
                return (OPR);
        return (ATM);
 }
                return (OPR);
        return (ATM);
 }
@@ -634,9 +663,12 @@ toktype(c)
 **
 **     Parameters:
 **             pvp -- pointer to token vector.
 **
 **     Parameters:
 **             pvp -- pointer to token vector.
+**             ruleset -- the ruleset to use for rewriting.
+**             e -- the current envelope.
 **
 **     Returns:
 **
 **     Returns:
-**             none.
+**             A status code.  If EX_TEMPFAIL, higher level code should
+**                     attempt recovery.
 **
 **     Side Effects:
 **             pvp is modified.
 **
 **     Side Effects:
 **             pvp is modified.
@@ -651,6 +683,7 @@ struct match
 {
        char    **first;        /* first token matched */
        char    **last;         /* last token matched */
 {
        char    **first;        /* first token matched */
        char    **last;         /* last token matched */
+       char    **pattern;      /* pointer to pattern */
        char    **source;       /* left hand source operand */
        char    flags;          /* attributes of this operator */
 };
        char    **source;       /* left hand source operand */
        char    flags;          /* attributes of this operator */
 };
@@ -660,6 +693,7 @@ struct match
 
 static char control_opts[MAX_CONTROL];
 
 
 static char control_opts[MAX_CONTROL];
 
+int
 static char control_init_data[] = { 
        MATCHZANY,      OP_VARLEN,
        MATCHONE,       OP_NONZLEN,
 static char control_init_data[] = { 
        MATCHZANY,      OP_VARLEN,
        MATCHONE,       OP_NONZLEN,
@@ -674,9 +708,10 @@ static char control_init_data[] = {
 static int nrw;
 
 void
 static int nrw;
 
 void
-rewrite(pvp, ruleset)
+rewrite(pvp, ruleset, e)
        char **pvp;
        int ruleset;
        char **pvp;
        int ruleset;
+       register ENVELOPE *e;
 {
        nrw = 0;
        _rewrite(pvp, ruleset);
 {
        nrw = 0;
        _rewrite(pvp, ruleset);
@@ -693,9 +728,12 @@ _rewrite(pvp, ruleset)
        register char **rvp;            /* rewrite vector pointer */
        register struct match *mlp;     /* cur ptr into mlist */
        register struct rewrite *rwr;   /* pointer to current rewrite rule */
        register char **rvp;            /* rewrite vector pointer */
        register struct match *mlp;     /* cur ptr into mlist */
        register struct rewrite *rwr;   /* pointer to current rewrite rule */
+       int ruleno;                     /* current rule number */
+       int rstat = EX_OK;              /* return status */
        int subr;                       /* subroutine number if >= 0 */
        bool dolookup;                  /* do host aliasing */
        char *npvp[MAXATOM+1];          /* temporary space for rebuild */
        int subr;                       /* subroutine number if >= 0 */
        bool dolookup;                  /* do host aliasing */
        char *npvp[MAXATOM+1];          /* temporary space for rebuild */
+       extern char *macvalue();
        char tokbuf[MAXNAME+1];         /* for concatenated class tokens */
        int nloops, nmatches = 0;       /* for looping rule checks */
        struct rewrite *prev_rwr;       /* pointer to previous rewrite rule */
        char tokbuf[MAXNAME+1];         /* for concatenated class tokens */
        int nloops, nmatches = 0;       /* for looping rule checks */
        struct rewrite *prev_rwr;       /* pointer to previous rewrite rule */
@@ -710,11 +748,11 @@ _rewrite(pvp, ruleset)
        }
        if (ruleset < 0 || ruleset >= MAXRWSETS)
        {
        }
        if (ruleset < 0 || ruleset >= MAXRWSETS)
        {
-               syserr("rewrite: illegal ruleset number %d", ruleset);
-               return;
+               syserr("554 rewrite: illegal ruleset number %d", ruleset);
+               return EX_CONFIG;
        }
        if (pvp == NULL)
        }
        if (pvp == NULL)
-               return;
+               return EX_USAGE;
 
        if (++nrw > 100)
        {
 
        if (++nrw > 100)
        {
@@ -734,6 +772,7 @@ _rewrite(pvp, ruleset)
        **  Run through the list of rewrite rules, applying any that match.
        */
 
        **  Run through the list of rewrite rules, applying any that match.
        */
 
+       ruleno = 1;
        for (rwr = RewriteRules[ruleset]; rwr != NULL; )
        {
                int loopcount = 0;
        for (rwr = RewriteRules[ruleset]; rwr != NULL; )
        {
                int loopcount = 0;
@@ -783,7 +822,7 @@ _rewrite(pvp, ruleset)
                nloops = 0;
                extend_match = FALSE;
 
                nloops = 0;
                extend_match = FALSE;
 
-               while ((ap = *avp) != NULL || *rvp != NULL)
+               if (++loopcount > 100)
                {
                        if (nloops++ > 400)
                        {
                {
                        if (nloops++ > 400)
                        {
@@ -792,21 +831,26 @@ _rewrite(pvp, ruleset)
                                mlp = mlist - 1; /* force rule failure */
                                break;
                        }
                                mlp = mlist - 1; /* force rule failure */
                                break;
                        }
-                       if (++loopcount > 100)
+                       syserr("554 Infinite loop in ruleset %d, rule %d",
+                               ruleset, ruleno);
+                       if (tTd(21, 1))
                        {
                        {
-                               syserr("Infinite loop in ruleset %d", ruleset);
                                printf("workspace: ");
                                printav(pvp);
                                printf("workspace: ");
                                printav(pvp);
-                               break;
                        }
                        }
+                       break;
+               }
+
+               while ((ap = *avp) != NULL || *rvp != NULL)
+               {
                        rp = *rvp;
 
                        if (tTd(21, 35))
                        {
                        rp = *rvp;
 
                        if (tTd(21, 35))
                        {
-                               printf("operator=");
-                               xputs(ap);
-                               printf(", token=");
+                               printf("ADVANCE rp=");
                                xputs(rp);
                                xputs(rp);
+                               printf(", ap=");
+                               xputs(ap);
                                printf("\n");
                        }
 
                                printf("\n");
                        }
 
@@ -881,22 +925,52 @@ _rewrite(pvp, ruleset)
                        {
                                switch (*rp)
                                {
                        {
                                switch (*rp)
                                {
-                                       register STAB *s;
-
-                                   case MATCHNCLASS:
-                                       /* match any single token not in a class */
-                                       s = stab(ap, ST_CLASS, ST_FIND);
-                                       if (s != NULL && bitnset(rp[1], s->s_class))
-                                               goto backup;
                                        break;
                                }
 
                                avp = mlp->last;
                                rvp++;
                                mlp++;
                                        break;
                                }
 
                                avp = mlp->last;
                                rvp++;
                                mlp++;
+                               break;
+
+                         case MATCHZERO:
+                               /* match zero tokens */
                                continue;
                        }
 
                                continue;
                        }
 
+                         case MACRODEXPAND:
+                               /*
+                               **  Match against run-time macro.
+                               **  This algorithm is broken for the
+                               **  general case (no recursive macros,
+                               **  improper tokenization) but should
+                               **  work for the usual cases.
+                               */
+
+                               ap = macvalue(rp[1], e);
+                               mlp->first = avp;
+                               if (tTd(21, 2))
+                                       printf("rewrite: LHS $&%c => \"%s\"\n",
+                                               rp[1],
+                                               ap == NULL ? "(NULL)" : ap);
+
+                               if (ap == NULL)
+                                       break;
+                               while (*ap != NULL)
+                               {
+                                       if (*avp == NULL ||
+                                           strncasecmp(ap, *avp, strlen(*avp)) != 0)
+                                       {
+                                               /* no match */
+                                               avp = mlp->first;
+                                               goto backup;
+                                       }
+                                       ap += strlen(*avp++);
+                               }
+
+                               /* match */
+                               break;
+
                        /*
                        **  We now have a variable length item.  It could
                        **  be $+ or $* in which case no special checking
                        /*
                        **  We now have a variable length item.  It could
                        **  be $+ or $* in which case no special checking
@@ -1032,6 +1106,7 @@ backup:
 
                        if (*avp == NULL)
                        {
 
                        if (*avp == NULL)
                        {
+                               rvp = mlp->pattern;
                                while (--mlp > mlist)
                                {
                                        if ((mlp->flags & OP_CLASS) &&
                                while (--mlp > mlist)
                                {
                                        if ((mlp->flags & OP_CLASS) &&
@@ -1088,6 +1163,7 @@ backup:
                        if (tTd(21, 10))
                                printf("----- rule fails\n");
                        rwr = rwr->r_next;
                        if (tTd(21, 10))
                                printf("----- rule fails\n");
                        rwr = rwr->r_next;
+                       ruleno++;
                        nmatches = 0;
                        continue;
                }
                        nmatches = 0;
                        continue;
                }
@@ -1109,18 +1185,19 @@ backup:
                }
 
                rp = *rvp;
                }
 
                rp = *rvp;
-               if (*rp == CANONUSER)
+               if ((*rp & 0377) == CANONUSER)
                {
                        rvp++;
                        rwr = rwr->r_next;
                {
                        rvp++;
                        rwr = rwr->r_next;
+                       ruleno++;
                        nmatches = 0;
                }
                        nmatches = 0;
                }
-               else if (*rp == CANONHOST)
+               else if ((*rp & 0377) == CANONHOST)
                {
                        rvp++;
                        rwr = NULL;
                }
                {
                        rvp++;
                        rwr = NULL;
                }
-               else if (*rp == CANONNET)
+               else if ((*rp & 0377) == CANONNET)
                        rwr = NULL;
 
                /* substitute */
                        rwr = NULL;
 
                /* substitute */
@@ -1146,7 +1223,7 @@ backup:
                                  toolong:
                                        syserr("rewrite: ruleset %d: replacement #%c out of bounds",
                                                ruleset, rp[1]);
                                  toolong:
                                        syserr("rewrite: ruleset %d: replacement #%c out of bounds",
                                                ruleset, rp[1]);
-                                       return;
+                                       return EX_CONFIG;
                                }
                                if (tTd(21, 15))
                                {
                                }
                                if (tTd(21, 15))
                                {
@@ -1216,7 +1293,8 @@ backup:
                        char begintype;
                        char db = '\0';
 
                        char begintype;
                        char db = '\0';
 
-                       if (**rvp != HOSTBEGIN && **rvp != LOOKUPBEGIN)
+                       if ((**rvp & 0377) != HOSTBEGIN &&
+                           (**rvp & 0377) != LOOKUPBEGIN)
                                continue;
 
                        /*
                                continue;
 
                        /*
@@ -1228,7 +1306,7 @@ backup:
                        begintype = **rvp;
                        hbrvp = rvp;
                        ubrvp = NULL;
                        begintype = **rvp;
                        hbrvp = rvp;
                        ubrvp = NULL;
-                       if (**rvp == HOSTBEGIN)
+                       if ((**rvp & 0377) == HOSTBEGIN)
                        {
                                endtoken = HOSTEND;
                                mapname = "host";
                        {
                                endtoken = HOSTEND;
                                mapname = "host";
@@ -1240,7 +1318,7 @@ backup:
                        }
                        map = stab(mapname, ST_MAP, ST_FIND);
                        if (map == NULL)
                        }
                        map = stab(mapname, ST_MAP, ST_FIND);
                        if (map == NULL)
-                               syserr("rewrite: map %s not found", mapname);
+                               syserr("554 rewrite: map %s not found", mapname);
 
                        /* extract the match part */
                        key_rvp = ++rvp;
 
                        /* extract the match part */
                        key_rvp = ++rvp;
@@ -1248,9 +1326,9 @@ backup:
                        arg_rvp = argvect;
                        xpvp = NULL;
                        replac = pvpbuf;
                        arg_rvp = argvect;
                        xpvp = NULL;
                        replac = pvpbuf;
-                       while (*rvp != NULL && **rvp != endtoken)
+                       while (*rvp != NULL && (**rvp & 0377) != endtoken)
                        {
                        {
-                               int nodetype = **rvp;
+                               int nodetype = **rvp & 0377;
 
                                if (nodetype != CANONHOST && nodetype != CANONUSER)
                                {
 
                                if (nodetype != CANONHOST && nodetype != CANONUSER)
                                {
@@ -1262,8 +1340,9 @@ backup:
 
                                if (xpvp != NULL)
                                {
 
                                if (xpvp != NULL)
                                {
-                                       cataddr(xpvp, replac,
-                                               &pvpbuf[sizeof pvpbuf] - replac);
+                                       cataddr(xpvp, NULL, replac,
+                                               &pvpbuf[sizeof pvpbuf] - replac,
+                                               '\0');
                                        *++arg_rvp = replac;
                                        replac += strlen(replac) + 1;
                                        xpvp = NULL;
                                        *++arg_rvp = replac;
                                        replac += strlen(replac) + 1;
                                        xpvp = NULL;
@@ -1283,8 +1362,9 @@ backup:
                                *rvp++ = NULL;
                        if (xpvp != NULL)
                        {
                                *rvp++ = NULL;
                        if (xpvp != NULL)
                        {
-                               cataddr(xpvp, replac,
-                                       &pvpbuf[sizeof pvpbuf] - replac);
+                               cataddr(xpvp, NULL, replac,
+                                       &pvpbuf[sizeof pvpbuf] - replac, 
+                                       '\0');
                                *++arg_rvp = replac;
                        }
                        *++arg_rvp = NULL;
                                *++arg_rvp = replac;
                        }
                        *++arg_rvp = NULL;
@@ -1374,6 +1454,8 @@ backup:
                printf("rewrite: ruleset %2d returns:", ruleset);
                printcav(pvp);
        }
                printf("rewrite: ruleset %2d returns:", ruleset);
                printcav(pvp);
        }
+
+       return rstat;
 }
 \f/*
 **  CALLSUBR -- call subroutines in rewrite vector
 }
 \f/*
 **  CALLSUBR -- call subroutines in rewrite vector
@@ -1430,6 +1512,7 @@ callsubr(pvp)
 **             tv -- token vector.
 **             a -- pointer to address descriptor to fill.
 **                     If NULL, one will be allocated.
 **             tv -- token vector.
 **             a -- pointer to address descriptor to fill.
 **                     If NULL, one will be allocated.
+**             e -- the current envelope.
 **
 **     Returns:
 **             NULL if there was an error.
 **
 **     Returns:
 **             NULL if there was an error.
@@ -1459,13 +1542,16 @@ struct errcodes
 };
 
 static ADDRESS *
 };
 
 static ADDRESS *
-buildaddr(tv, a)
+buildaddr(tv, a, e)
        register char **tv;
        register ADDRESS *a;
        register char **tv;
        register ADDRESS *a;
+       register ENVELOPE *e;
 {
 {
-       static char buf[MAXNAME];
        struct mailer **mp;
        register struct mailer *m;
        struct mailer **mp;
        register struct mailer *m;
+       char *bp;
+       int spaceleft;
+       static char buf[MAXNAME];
 
        if (a == NULL)
                a = (ADDRESS *) xalloc(sizeof *a);
 
        if (a == NULL)
                a = (ADDRESS *) xalloc(sizeof *a);
@@ -1474,17 +1560,17 @@ buildaddr(tv, a)
        /* figure out what net/mailer to use */
        if (*tv == NULL || **tv != CANONNET)
        {
        /* figure out what net/mailer to use */
        if (*tv == NULL || **tv != CANONNET)
        {
-               syserr("buildaddr: no net");
+               syserr("554 buildaddr: no net");
                return (NULL);
        }
        tv++;
                return (NULL);
        }
        tv++;
-       if (!strcasecmp(*tv, "error"))
+       if (strcasecmp(*tv, "error") == 0)
        {
        {
-               if (**++tv == CANONHOST)
+               if ((**++tv & 0377) == CANONHOST)
                {
                        register struct errcodes *ep;
 
                {
                        register struct errcodes *ep;
 
-                       if (isdigit(**++tv))
+                       if (isascii(**++tv) && isdigit(**tv))
                        {
                                setstat(atoi(*tv));
                        }
                        {
                                setstat(atoi(*tv));
                        }
@@ -1504,14 +1590,10 @@ buildaddr(tv, a)
                                (void) strcat(buf, " ");
                        (void) strcat(buf, *tv);
                }
                                (void) strcat(buf, " ");
                        (void) strcat(buf, *tv);
                }
-               if (**tv != CANONUSER)
-                       syserr("buildaddr: error: no user");
-               while (*++tv != NULL)
-               {
-                       if (buf[0] != '\0')
-                               (void) strcat(buf, " ");
-                       (void) strcat(buf, *tv);
-               }
+               if ((**tv & 0377) != CANONUSER)
+                       syserr("554 buildaddr: error: no user");
+               cataddr(++tv, NULL, buf, sizeof buf, ' ');
+               stripquotes(buf);
 #ifdef LOG
                if (LogLevel > 8)
                        syslog (LOG_DEBUG, "%s: Trace: $#ERROR $: %s",
 #ifdef LOG
                if (LogLevel > 8)
                        syslog (LOG_DEBUG, "%s: Trace: $#ERROR $: %s",
@@ -1520,14 +1602,15 @@ buildaddr(tv, a)
                usrerr(buf);
                return (NULL);
        }
                usrerr(buf);
                return (NULL);
        }
+
        for (mp = Mailer; (m = *mp++) != NULL; )
        {
        for (mp = Mailer; (m = *mp++) != NULL; )
        {
-               if (!strcasecmp(m->m_name, *tv))
+               if (strcasecmp(m->m_name, *tv) == 0)
                        break;
        }
        if (m == NULL)
        {
                        break;
        }
        if (m == NULL)
        {
-               syserr("buildaddr: unknown mailer %s", *tv);
+               syserr("554 buildaddr: unknown mailer %s", *tv);
                return (NULL);
        }
        a->q_mailer = m;
                return (NULL);
        }
        a->q_mailer = m;
@@ -1535,11 +1618,6 @@ buildaddr(tv, a)
        /* figure out what host (if any) */
        if (**++tv != CANONHOST)
        {
        /* figure out what host (if any) */
        if (**++tv != CANONHOST)
        {
-               if (!bitnset(M_LOCAL, m->m_flags))
-               {
-                       syserr("buildaddr: no host");
-                       return (NULL);
-               }
                a->q_host = NULL;
        }
        else
                a->q_host = NULL;
        }
        else
@@ -1549,38 +1627,68 @@ buildaddr(tv, a)
        }
        else
        {
        }
        else
        {
-               buf[0] = '\0';
                while (*++tv != NULL && **tv != CANONUSER)
                        (void) strcat(buf, *tv);
                a->q_host = newstr(buf);
        }
 
        /* figure out the user */
                while (*++tv != NULL && **tv != CANONUSER)
                        (void) strcat(buf, *tv);
                a->q_host = newstr(buf);
        }
 
        /* figure out the user */
-       if (*tv == NULL || **tv != CANONUSER)
+       if (*tv == NULL || (**tv & 0377) != CANONUSER)
        {
        {
-               syserr("buildaddr: no user");
+               syserr("554 buildaddr: no user");
                return (NULL);
        }
                return (NULL);
        }
+       tv++;
 
 
-       /* define tohost before running mailer rulesets */
-       define('h', a->q_host, CurEnv);
+       /* do special mapping for local mailer */
+       if (m == LocalMailer && *tv != NULL)
+       {
+               register char *p = *tv;
+
+               if (*p == '"')
+                       p++;
+               if (*p == '|')
+                       a->q_mailer = m = ProgMailer;
+               else if (*p == '/')
+                       a->q_mailer = m = FileMailer;
+               else if (*p == ':')
+               {
+                       /* may be :include: */
+                       cataddr(tv, NULL, buf, sizeof buf, '\0');
+                       stripquotes(buf);
+                       if (strncasecmp(buf, ":include:", 9) == 0)
+                       {
+                               /* if :include:, don't need further rewriting */
+                               a->q_mailer = m = InclMailer;
+                               a->q_user = &buf[9];
+                               return (a);
+                       }
+               }
+       }
 
 
-       if (m == LocalMailer && tv[1] != NULL && strcmp(tv[1], "@") == 0)
+       if (m == LocalMailer && *tv != NULL && strcmp(*tv, "@") == 0)
        {
                tv++;
                a->q_flags |= QNOTREMOTE;
        }
 
        {
                tv++;
                a->q_flags |= QNOTREMOTE;
        }
 
-       /* rewrite according recipient mailer rewriting rules */
-       rewrite(++tv, 2);
        if (m->m_r_rwset > 0)
                rewrite(tv, m->m_r_rwset);
        if (m->m_r_rwset > 0)
                rewrite(tv, m->m_r_rwset);
-       rewrite(tv, 4);
+       (void) rewrite(tv, 4, e);
 
        /* save the result for the command line/RCPT argument */
 
        /* save the result for the command line/RCPT argument */
-       cataddr(tv, buf, sizeof buf);
+       cataddr(tv, NULL, buf, sizeof buf, '\0');
        a->q_user = buf;
 
        a->q_user = buf;
 
+       /*
+       **  Do mapping to lower case as requested by mailer
+       */
+
+       if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
+               makelower(a->q_host);
+       if (!bitnset(M_USR_UPPER, m->m_flags))
+               makelower(a->q_user);
+
        return (a);
 }
 \f/*
        return (a);
 }
 \f/*
@@ -1588,8 +1696,12 @@ buildaddr(tv, a)
 **
 **     Parameters:
 **             pvp -- parameter vector to rebuild.
 **
 **     Parameters:
 **             pvp -- parameter vector to rebuild.
+**             evp -- last parameter to include.  Can be NULL to
+**                     use entire pvp.
 **             buf -- buffer to build the string into.
 **             sz -- size of buf.
 **             buf -- buffer to build the string into.
 **             sz -- size of buf.
+**             spacesub -- the space separator character; if null,
+**                     use SpaceSub.
 **
 **     Returns:
 **             none.
 **
 **     Returns:
 **             none.
@@ -1599,16 +1711,21 @@ buildaddr(tv, a)
 */
 
 void
 */
 
 void
-cataddr(pvp, buf, sz)
+cataddr(pvp, evp, buf, sz, spacesub)
        char **pvp;
        char **pvp;
+       char **evp;
        char *buf;
        register int sz;
        char *buf;
        register int sz;
+       char spacesub;
 {
        bool oatomtok = FALSE;
        bool natomtok;
        register int i;
        register char *p;
 
 {
        bool oatomtok = FALSE;
        bool natomtok;
        register int i;
        register char *p;
 
+       if (spacesub == '\0')
+               spacesub = SpaceSub;
+
        if (pvp == NULL)
        {
                (void) strcpy(buf, "");
        if (pvp == NULL)
        {
                (void) strcpy(buf, "");
@@ -1620,12 +1737,13 @@ cataddr(pvp, buf, sz)
        {
                natomtok = (toktype(**pvp) == ATM);
                if (oatomtok && natomtok)
        {
                natomtok = (toktype(**pvp) == ATM);
                if (oatomtok && natomtok)
-                       *p++ = SpaceSub;
+                       *p++ = spacesub;
                (void) strcpy(p, *pvp);
                oatomtok = natomtok;
                p += i;
                sz -= i + 1;
                (void) strcpy(p, *pvp);
                oatomtok = natomtok;
                p += i;
                sz -= i + 1;
-               pvp++;
+               if (pvp++ == evp)
+                       break;
        }
        *p = '\0';
 }
        }
        *p = '\0';
 }
@@ -1659,13 +1777,21 @@ sameaddr(a, b)
        if (strcasecmp(a->q_user, b->q_user))
                return (FALSE);
 
        if (strcasecmp(a->q_user, b->q_user))
                return (FALSE);
 
-       /* if the mailer ignores hosts, we have succeeded! */
-       if (bitnset(M_LOCAL, a->q_mailer->m_flags))
-               return (TRUE);
+       /* if we have good uids for both but the differ, these are different */
+       if (bitset(QGOODUID, a->q_flags & b->q_flags) && a->q_uid != b->q_uid)
+               return (FALSE);
 
        /* otherwise compare hosts (but be careful for NULL ptrs) */
 
        /* otherwise compare hosts (but be careful for NULL ptrs) */
+       if (a->q_host == b->q_host)
+       {
+               /* probably both null pointers */
+               return (TRUE);
+       }
        if (a->q_host == NULL || b->q_host == NULL)
        if (a->q_host == NULL || b->q_host == NULL)
+       {
+               /* only one is a null pointer */
                return (FALSE);
                return (FALSE);
+       }
        if (strcasecmp(a->q_host, b->q_host))
                return (FALSE);
 
        if (strcasecmp(a->q_host, b->q_host))
                return (FALSE);
 
@@ -1691,6 +1817,8 @@ printaddr(a, follow)
        bool follow;
 {
        bool first = TRUE;
        bool follow;
 {
        bool first = TRUE;
+       register MAILER *m;
+       MAILER pseudomailer;
 
        static int indent;
        register int i;
 
        static int indent;
        register int i;
@@ -1702,6 +1830,16 @@ printaddr(a, follow)
                        printf("\t");
                printf("%x=", a);
                (void) fflush(stdout);
                        printf("\t");
                printf("%x=", a);
                (void) fflush(stdout);
+
+               /* find the mailer -- carefully */
+               m = a->q_mailer;
+               if (m == NULL)
+               {
+                       m = &pseudomailer;
+                       m->m_mno = -1;
+                       m->m_name = "NULL";
+               }
+
                for (i = indent; i > 0; i--)
                        printf("\t");
                printf("\tnext=%x, flags=%o, rmailer %d, alias=%x, sibling=%x, child=%x\n",
                for (i = indent; i > 0; i--)
                        printf("\t");
                printf("\tnext=%x, flags=%o, rmailer %d, alias=%x, sibling=%x, child=%x\n",
@@ -1728,10 +1866,9 @@ printaddr(a, follow)
 **             name -- the name to translate.
 **             m -- the mailer that we want to do rewriting relative
 **                     to.
 **             name -- the name to translate.
 **             m -- the mailer that we want to do rewriting relative
 **                     to.
-**             senderaddress -- if set, uses the sender rewriting rules
-**                     rather than the recipient rewriting rules.
-**             canonical -- if set, strip out any comment information,
-**                     etc.
+**             flags -- fine tune operations.
+**             pstat -- pointer to status word.
+**             e -- the current envelope.
 **
 **     Returns:
 **             the text string representing this address relative to
 **
 **     Returns:
 **             the text string representing this address relative to
@@ -1746,16 +1883,17 @@ printaddr(a, follow)
 */
 
 char *
 */
 
 char *
-remotename(name, m, senderaddress, canonical, e)
+remotename(name, m, flags, pstat, e)
        char *name;
        MAILER *m;
        char *name;
        MAILER *m;
-       bool senderaddress;
-       bool canonical;
+       int flags;
+       int *pstat;
        register ENVELOPE *e;
 {
        register char **pvp;
        char *fancy;
        char *oldg = macvalue('g', e);
        register ENVELOPE *e;
 {
        register char **pvp;
        char *fancy;
        char *oldg = macvalue('g', e);
+       int rwset;
        static char buf[MAXNAME];
        char lbuf[MAXNAME];
        char pvpbuf[PSBUFSIZE];
        static char buf[MAXNAME];
        char lbuf[MAXNAME];
        char pvpbuf[PSBUFSIZE];
@@ -1772,8 +1910,8 @@ remotename(name, m, senderaddress, canonical, e)
        **      This will leave the name as a comment and a $g macro.
        */
 
        **      This will leave the name as a comment and a $g macro.
        */
 
-       if (canonical)
-               fancy = "\001g";
+       if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
+               fancy = "\201g";
        else
                fancy = crackaddr(name);
 
        else
                fancy = crackaddr(name);
 
@@ -1785,11 +1923,12 @@ remotename(name, m, senderaddress, canonical, e)
        **      domain will be appended.
        */
 
        **      domain will be appended.
        */
 
-       pvp = prescan(name, '\0', pvpbuf);
+       pvp = prescan(name, '\0', pvpbuf, NULL);
        if (pvp == NULL)
                return (name);
        if (pvp == NULL)
                return (name);
-       rewrite(pvp, 3);
-       if (e->e_fromdomain != NULL)
+       if (rewrite(pvp, 3, e) == EX_TEMPFAIL)
+               *pstat = EX_TEMPFAIL;
+       if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
        {
                /* append from domain to this address */
                register char **pxp = pvp;
        {
                /* append from domain to this address */
                register char **pxp = pvp;
@@ -1804,7 +1943,8 @@ remotename(name, m, senderaddress, canonical, e)
 
                        while ((*pxp++ = *qxq++) != NULL)
                                continue;
 
                        while ((*pxp++ = *qxq++) != NULL)
                                continue;
-                       rewrite(pvp, 3);
+                       if (rewrite(pvp, 3, e) == EX_TEMPFAIL)
+                               *pstat = EX_TEMPFAIL;
                }
        }
 
                }
        }
 
@@ -1816,17 +1956,12 @@ remotename(name, m, senderaddress, canonical, e)
        **      Then run it through any receiving-mailer-specific rulesets.
        */
 
        **      Then run it through any receiving-mailer-specific rulesets.
        */
 
-       if (senderaddress)
-       {
-               rewrite(pvp, 1);
-               if (m->m_s_rwset > 0)
-                       rewrite(pvp, m->m_s_rwset);
-       }
        else
        {
        else
        {
-               rewrite(pvp, 2);
-               if (m->m_r_rwset > 0)
-                       rewrite(pvp, m->m_r_rwset);
+       if (rwset > 0)
+       {
+               if (rewrite(pvp, rwset, e) == EX_TEMPFAIL)
+                       *pstat = EX_TEMPFAIL;
        }
 
        /*
        }
 
        /*
@@ -1836,13 +1971,14 @@ remotename(name, m, senderaddress, canonical, e)
        **      may be used as a default to the above rules.
        */
 
        **      may be used as a default to the above rules.
        */
 
-       rewrite(pvp, 4);
+       if (rewrite(pvp, 4, e) == EX_TEMPFAIL)
+               *pstat = EX_TEMPFAIL;
 
        /*
        **  Now restore the comment information we had at the beginning.
        */
 
 
        /*
        **  Now restore the comment information we had at the beginning.
        */
 
-       cataddr(pvp, lbuf, sizeof lbuf);
+       cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
        define('g', lbuf, e);
        expand(fancy, buf, &buf[sizeof buf - 1], e);
        define('g', oldg, e);
        define('g', lbuf, e);
        expand(fancy, buf, &buf[sizeof buf - 1], e);
        define('g', oldg, e);
@@ -1940,6 +2076,7 @@ maplocaluser(a, sendq, e)
 {
        register char **pvp;
        register ADDRESS *a1 = NULL;
 {
        register char **pvp;
        register ADDRESS *a1 = NULL;
+       auto char *delimptr;
        char pvpbuf[PSBUFSIZE];
 
        if (tTd(29, 1))
        char pvpbuf[PSBUFSIZE];
 
        if (tTd(29, 1))
@@ -1947,22 +2084,172 @@ maplocaluser(a, sendq, e)
                printf("maplocaluser: ");
                printaddr(a, FALSE);
        }
                printf("maplocaluser: ");
                printaddr(a, FALSE);
        }
-       pvp = prescan(a->q_user, '\0', pvpbuf);
+       pvp = prescan(a->q_user, '\0', pvpbuf, &delimptr);
        if (pvp == NULL)
                return;
 
        if (pvp == NULL)
                return;
 
-       rewrite(pvp, 5);
-       if (pvp[0] == NULL || pvp[0][0] != CANONNET)
+       (void) rewrite(pvp, 5, e);
+       if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
                return;
 
        /* if non-null, mailer destination specified -- has it changed? */
                return;
 
        /* if non-null, mailer destination specified -- has it changed? */
-       a1 = buildaddr(pvp, NULL);
+       a1 = buildaddr(pvp, NULL, e);
        if (a1 == NULL || sameaddr(a, a1))
                return;
 
        /* mark old address as dead; insert new address */
        a->q_flags |= QDONTSEND;
        if (a1 == NULL || sameaddr(a, a1))
                return;
 
        /* mark old address as dead; insert new address */
        a->q_flags |= QDONTSEND;
+       if (tTd(29, 5))
+       {
+               printf("maplocaluser: QDONTSEND ");
+               printaddr(a, FALSE);
+       }
        a1->q_alias = a;
        a1->q_alias = a;
-       allocaddr(a1, 1, NULL);
+       allocaddr(a1, 1, NULL, delimptr);
        (void) recipient(a1, sendq, e);
 }
        (void) recipient(a1, sendq, e);
 }
+\f/*
+**  DEQUOTE_INIT -- initialize dequote map
+**
+**     This is a no-op.
+**
+**     Parameters:
+**             map -- the internal map structure.
+**             mapname -- the name of the mapl.
+**             args -- arguments.
+**
+**     Returns:
+**             TRUE.
+*/
+
+bool
+dequote_init(map, mapname, args)
+       MAP *map;
+       char *mapname;
+       char *args;
+{
+       register char *p = args;
+
+       for (;;)
+       {
+               while (isascii(*p) && isspace(*p))
+                       p++;
+               if (*p != '-')
+                       break;
+               switch (*++p)
+               {
+                 case 'a':
+                       map->map_app = ++p;
+                       break;
+               }
+               while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+                       p++;
+               if (*p != '\0')
+                       *p = '\0';
+       }
+       if (map->map_app != NULL)
+               map->map_app = newstr(map->map_app);
+
+       return TRUE;
+}
+\f/*
+**  DEQUOTE_MAP -- unquote an address
+**
+**     Parameters:
+**             map -- the internal map structure (ignored).
+**             buf -- the buffer to dequote.
+**             bufsiz -- the size of that buffer.
+**             av -- arguments (ignored).
+**             statp -- pointer to status out-parameter.
+**
+**     Returns:
+**             NULL -- if there were no quotes, or if the resulting
+**                     unquoted buffer would not be acceptable to prescan.
+**             else -- The dequoted buffer.
+*/
+
+char *
+dequote_map(map, buf, bufsiz, av, statp)
+       MAP *map;
+       char buf[];
+       int bufsiz;
+       char **av;
+       int *statp;
+{
+       register char *p;
+       register char *q;
+       register char c;
+       int anglecnt;
+       int cmntcnt;
+       int quotecnt;
+       int spacecnt;
+       bool quotemode;
+       bool bslashmode;
+
+       anglecnt = 0;
+       cmntcnt = 0;
+       quotecnt = 0;
+       spacecnt = 0;
+       quotemode = FALSE;
+       bslashmode = FALSE;
+
+       for (p = q = buf; (c = *p++) != '\0'; )
+       {
+               if (bslashmode)
+               {
+                       bslashmode = FALSE;
+                       *q++ = c;
+                       continue;
+               }
+
+               switch (c)
+               {
+                 case '\\':
+                       bslashmode = TRUE;
+                       break;
+
+                 case '(':
+                       cmntcnt++;
+                       break;
+
+                 case ')':
+                       if (cmntcnt-- <= 0)
+                               return NULL;
+                       break;
+
+                 case ' ':
+                       spacecnt++;
+                       break;
+               }
+
+               if (cmntcnt > 0)
+               {
+                       *q++ = c;
+                       continue;
+               }
+
+               switch (c)
+               {
+                 case '"':
+                       quotemode = !quotemode;
+                       quotecnt++;
+                       continue;
+
+                 case '<':
+                       anglecnt++;
+                       break;
+
+                 case '>':
+                       if (anglecnt-- <= 0)
+                               return NULL;
+                       break;
+               }
+               *q++ = c;
+       }
+
+       if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
+           quotemode || quotecnt <= 0 || spacecnt != 0)
+               return NULL;
+       *q++ = '\0';
+       return buf;
+}