add -q map flag to avoid quote stripping; clean up domain addition
[unix-history] / usr / src / usr.sbin / sendmail / src / parseaddr.c
index f0416b9..5396fd4 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1983, 1995 Eric P. Allman
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
  * Copyright (c) 1988, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -7,11 +7,13 @@
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)parseaddr.c        8.33 (Berkeley) %G%";
+static char sccsid[] = "@(#)parseaddr.c        8.69 (Berkeley) %G%";
 #endif /* not lint */
 
 #include "sendmail.h"
 
 #endif /* not lint */
 
 #include "sendmail.h"
 
+static int     callsubr __P((char **, int, ENVELOPE *));
+
 #ifdef CC_WONT_PROMOTE
 static int toktype __P((char));
 #else  /* !CC_WONT_PROMOTE */
 #ifdef CC_WONT_PROMOTE
 static int toktype __P((char));
 #else  /* !CC_WONT_PROMOTE */
@@ -103,7 +105,7 @@ parseaddr(addr, a, flags, delim, delimptr, e)
        if (delimptr == NULL)
                delimptr = &delimptrbuf;
 
        if (delimptr == NULL)
                delimptr = &delimptrbuf;
 
-       pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr);
+       pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL);
        if (pvp == NULL)
        {
                if (tTd(20, 1))
        if (pvp == NULL)
        {
                if (tTd(20, 1))
@@ -178,6 +180,7 @@ parseaddr(addr, a, flags, delim, delimptr, e)
                if (e->e_message == NULL)
                        e->e_message = newstr(msg);
                a->q_flags |= QQUEUEUP;
                if (e->e_message == NULL)
                        e->e_message = newstr(msg);
                a->q_flags |= QQUEUEUP;
+               a->q_status = "4.4.3";
        }
 
        /*
        }
 
        /*
@@ -208,7 +211,7 @@ invalidaddr(addr, delimptr)
        register char *addr;
        char *delimptr;
 {
        register char *addr;
        char *delimptr;
 {
-       char savedelim;
+       char savedelim = '\0';
 
        if (delimptr != NULL)
        {
 
        if (delimptr != NULL)
        {
@@ -231,14 +234,14 @@ invalidaddr(addr, delimptr)
        }
        if (*addr == '\0')
        {
        }
        if (*addr == '\0')
        {
-               if (savedelim != '\0' && delimptr != NULL)
+               if (delimptr != NULL && savedelim != '\0')
                        *delimptr = savedelim;
                return FALSE;
        }
        setstat(EX_USAGE);
        usrerr("553 Address contained invalid control characters");
   addrfailure:
                        *delimptr = savedelim;
                return FALSE;
        }
        setstat(EX_USAGE);
        usrerr("553 Address contained invalid control characters");
   addrfailure:
-       if (savedelim != '\0' && delimptr != NULL)
+       if (delimptr != NULL && savedelim != '\0')
                *delimptr = savedelim;
        return TRUE;
 }
                *delimptr = savedelim;
        return TRUE;
 }
@@ -266,7 +269,7 @@ allocaddr(a, flags, paddr)
        char *paddr;
 {
        if (tTd(24, 4))
        char *paddr;
 {
        if (tTd(24, 4))
-               printf("allocaddr(flags=%o, paddr=%s)\n", flags, paddr);
+               printf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
 
        a->q_paddr = paddr;
 
 
        a->q_paddr = paddr;
 
@@ -350,6 +353,8 @@ invalidaddr(addr)
 **             pvpbsize -- size of pvpbuf.
 **             delimptr -- if non-NULL, set to the location of the
 **                     terminating delimiter.
 **             pvpbsize -- size of pvpbuf.
 **             delimptr -- if non-NULL, set to the location of the
 **                     terminating delimiter.
+**             toktab -- if set, a token table to use for parsing.
+**                     If NULL, use the default table.
 **
 **     Returns:
 **             A pointer to a vector of tokens.
 **
 **     Returns:
 **             A pointer to a vector of tokens.
@@ -362,8 +367,9 @@ invalidaddr(addr)
 # define QST           2       /* in quoted string */
 # define SPC           3       /* chewing up spaces */
 # define ONE           4       /* pick up one character */
 # define QST           2       /* in quoted string */
 # define SPC           3       /* chewing up spaces */
 # define ONE           4       /* pick up one character */
+# define ILL           5       /* illegal character */
 
 
-# define NSTATES       5       /* number of states */
+# define NSTATES       6       /* number of states */
 # define TYPE          017     /* mask to select state type */
 
 /* meta bits for table */
 # define TYPE          017     /* mask to select state type */
 
 /* meta bits for table */
@@ -373,46 +379,101 @@ invalidaddr(addr)
 
 static short StateTab[NSTATES][NSTATES] =
 {
 
 static short StateTab[NSTATES][NSTATES] =
 {
-   /*  oldst   chtype> OPR     ATM     QST     SPC     ONE     */
-       /*OPR*/         OPR|B,  ATM|B,  QST|B,  SPC|MB, ONE|B,
-       /*ATM*/         OPR|B,  ATM,    QST|B,  SPC|MB, ONE|B,
-       /*QST*/         QST,    QST,    OPR,    QST,    QST,
-       /*SPC*/         OPR,    ATM,    QST,    SPC|M,  ONE,
-       /*ONE*/         OPR,    OPR,    OPR,    OPR,    OPR,
+   /*  oldst   chtype> OPR     ATM     QST     SPC     ONE     ILL     */
+       /*OPR*/         OPR|B,  ATM|B,  QST|B,  SPC|MB, ONE|B,  ILL|MB,
+       /*ATM*/         OPR|B,  ATM,    QST|B,  SPC|MB, ONE|B,  ILL|MB,
+       /*QST*/         QST,    QST,    OPR,    QST,    QST,    QST,
+       /*SPC*/         OPR,    ATM,    QST,    SPC|M,  ONE,    ILL|MB,
+       /*ONE*/         OPR,    OPR,    OPR,    OPR,    OPR,    ILL|MB,
+       /*ILL*/         OPR|B,  ATM|B,  QST|B,  SPC|MB, ONE|B,  ILL|M,
 };
 
 /* token type table -- it gets modified with $o characters */
 };
 
 /* token type table -- it gets modified with $o characters */
-static TokTypeTab[256] =
+static char TokTypeTab[256] =
 {
 {
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM,ATM,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
-       OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
-       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /* nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
+    /* dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
+       SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
+    /* 0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /* @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /* `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+
+    /* nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
+       OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
+    /* dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
+       OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
+    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /* 0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /* @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /* `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 };
 
 };
 
-#define toktype(c)     ((int) TokTypeTab[(c) & 0xff])
+/* token type table for MIME parsing */
+char MimeTokenTab[256] =
+{
+    /* nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL,
+    /* dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
+       SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,OPR,ATM,ATM,OPR,
+    /* 0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR,
+    /* @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
+       OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM,
+    /* `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
+       ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
+
+    /* nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+    /* dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+    /* 0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+    /* @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+    /* `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
+       ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
+};
 
 
 # define NOCHAR                -1      /* signal nothing in lookahead token */
 
 char **
 
 
 # define NOCHAR                -1      /* signal nothing in lookahead token */
 
 char **
-prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
+prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
        char *addr;
        char *addr;
-       char delim;
+       int delim;
        char pvpbuf[];
        char **delimptr;
        char pvpbuf[];
        char **delimptr;
+       char *toktab;
 {
        register char *p;
        register char *q;
 {
        register char *p;
        register char *q;
@@ -434,7 +495,7 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
                char obuf[50];
 
                firsttime = FALSE;
                char obuf[50];
 
                firsttime = FALSE;
-               expand("\201o", obuf, &obuf[sizeof obuf - sizeof DELIMCHARS], CurEnv);
+               expand("\201o", obuf, sizeof obuf - sizeof DELIMCHARS, CurEnv);
                strcat(obuf, DELIMCHARS);
                for (p = obuf; *p != '\0'; p++)
                {
                strcat(obuf, DELIMCHARS);
                for (p = obuf; *p != '\0'; p++)
                {
@@ -442,6 +503,8 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
                                TokTypeTab[*p & 0xff] = OPR;
                }
        }
                                TokTypeTab[*p & 0xff] = OPR;
                }
        }
+       if (toktab == NULL)
+               toktab = TokTypeTab;
 
        /* make sure error messages don't have garbage on them */
        errno = 0;
 
        /* make sure error messages don't have garbage on them */
        errno = 0;
@@ -475,6 +538,8 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
                                if (q >= &pvpbuf[pvpbsize - 5])
                                {
                                        usrerr("553 Address too long");
                                if (q >= &pvpbuf[pvpbsize - 5])
                                {
                                        usrerr("553 Address too long");
+                                       if (strlen(addr) > MAXNAME)
+                                               addr[MAXNAME] = '\0';
        returnnull:
                                        if (delimptr != NULL)
                                                *delimptr = p;
        returnnull:
                                        if (delimptr != NULL)
                                                *delimptr = p;
@@ -598,10 +663,17 @@ prescan(addr, delim, pvpbuf, pvpbsize, delimptr)
                        if (c == delim && anglecnt <= 0 && state != QST)
                                break;
 
                        if (c == delim && anglecnt <= 0 && state != QST)
                                break;
 
-                       newstate = StateTab[state][toktype(c)];
+                       newstate = StateTab[state][toktab[c & 0xff]];
                        if (tTd(22, 101))
                                printf("ns=%02o\n", newstate);
                        state = newstate & TYPE;
                        if (tTd(22, 101))
                                printf("ns=%02o\n", newstate);
                        state = newstate & TYPE;
+                       if (state == ILL)
+                       {
+                               if (isascii(c) && isprint(c))
+                                       usrerr("653 Illegal character %c", c);
+                               else
+                                       usrerr("653 Illegal character 0x%02x", c);
+                       }
                        if (bitset(M, newstate))
                                c = NOCHAR;
                        if (bitset(B, newstate))
                        if (bitset(M, newstate))
                                c = NOCHAR;
                        if (bitset(B, newstate))
@@ -1430,11 +1502,6 @@ backup:
                **  Then copy vector back into original space.
                */
 
                **  Then copy vector back into original space.
                */
 
-               callsubr(npvp);
-
-               for (avp = npvp; *avp++ != NULL;);
-                       subr = atoi(*++rvp);
-                       rvp++;
 
                else
                        subr = -1;
 
                else
                        subr = -1;
@@ -1492,6 +1559,70 @@ backup:
 **             pvp is modified.
 */
 
 **             pvp is modified.
 */
 
+static int
+callsubr(pvp, reclevel, e)
+       char **pvp;
+       int reclevel;
+       ENVELOPE *e;
+{
+       char **rvp;
+       int subr;
+       int stat;
+       STAB *s;
+
+       for (; *pvp != NULL; pvp++)
+       {
+               if ((**pvp & 0377) == CALLSUBR && pvp[1] != NULL)
+                       break;
+       }
+       if (*pvp == NULL)
+               return EX_OK;
+
+       if (tTd(21, 3))
+               printf("-----callsubr %s\n", pvp[1]);
+
+       s = stab(pvp[1], ST_RULESET, ST_FIND);
+       if (s == NULL)
+               subr = atoi(pvp[1]);
+       else
+               subr = s->s_ruleset;
+
+       /*
+       **  Take care of possible inner calls.
+       */
+
+       stat = callsubr(&pvp[2], reclevel, e);
+       if (stat != EX_OK)
+               return stat;
+
+       /*
+       **  Move vector up over calling opcode.
+       */
+
+       for (rvp = &pvp[2]; *rvp != NULL; rvp++)
+               rvp[-2] = rvp[0];
+       rvp[-2] = NULL;
+
+       /*
+       **  Call inferior ruleset.
+       */
+
+       stat = rewrite(pvp, subr, reclevel, e);
+       return stat;
+}
+\f/*
+**  CALLSUBR -- call subroutines in rewrite vector
+**
+**     Parameters:
+**             pvp -- pointer to token vector.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             pvp is modified.
+*/
+
 static void
 callsubr(pvp)
        char **pvp;
 static void
 callsubr(pvp)
        char **pvp;
@@ -1574,15 +1705,18 @@ buildaddr(tv, a, flags, e)
 {
        struct mailer **mp;
        register struct mailer *m;
 {
        struct mailer **mp;
        register struct mailer *m;
+       register char *p;
        char *bp;
        char *bp;
-       int spaceleft;
+       char *mname;
+       char **hostp;
+       char hbuf[MAXNAME + 1];
        static MAILER errormailer;
        static char *errorargv[] = { "ERROR", NULL };
        static MAILER errormailer;
        static char *errorargv[] = { "ERROR", NULL };
-       static char buf[MAXNAME];
+       static char ubuf[MAXNAME + 1];
 
        if (tTd(24, 5))
        {
 
        if (tTd(24, 5))
        {
-               printf("buildaddr, flags=%o, tv=", flags);
+               printf("buildaddr, flags=%x, tv=", flags);
                printav(tv);
        }
 
                printav(tv);
        }
 
@@ -1590,6 +1724,9 @@ buildaddr(tv, a, flags, e)
                a = (ADDRESS *) xalloc(sizeof *a);
        clear((char *) a, sizeof *a);
 
                a = (ADDRESS *) xalloc(sizeof *a);
        clear((char *) a, sizeof *a);
 
+       /* set up default error return flags */
+       a->q_flags |= QPINGONFAILURE|QPINGONDELAY;
+
        /* figure out what net/mailer to use */
        if (*tv == NULL || **tv != CANONNET)
        {
        /* figure out what net/mailer to use */
        if (*tv == NULL || **tv != CANONNET)
        {
@@ -1606,25 +1743,47 @@ badaddr:
                }
                return a;
        }
                }
                return a;
        }
-       tv++;
-       if (strcasecmp(*tv, "error") == 0)
+       mname = *++tv;
+
+       /* extract host and user portions */
+       if ((**++tv & 0377) == CANONHOST)
+               hostp = ++tv;
+       else
+               hostp = NULL;
+       while (*tv != NULL && (**tv & 0377) != CANONUSER)
+               tv++;
+       if (*tv == NULL)
+       {
+               syserr("554 buildaddr: no user");
+               goto badaddr;
+       }
+       if (hostp != NULL)
+               cataddr(hostp, tv - 1, hbuf, sizeof hbuf, '\0');
+       cataddr(++tv, NULL, ubuf, sizeof ubuf, '\0');
+
+       /* save away the host name */
+       if (strcasecmp(mname, "error") == 0)
        {
        {
-               if ((**++tv & 0377) == CANONHOST)
+               if (hostp != NULL)
                {
                        register struct errcodes *ep;
 
                {
                        register struct errcodes *ep;
 
-                       if (isascii(**++tv) && isdigit(**tv))
+                       if (strchr(hbuf, '.') != NULL)
                        {
                        {
-                               setstat(atoi(*tv));
+                               a->q_status = newstr(hbuf);
+                               setstat(dsntoexitstat(hbuf));
+                       }
+                       else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
+                       {
+                               setstat(atoi(hbuf));
                        }
                        else
                        {
                                for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
                        }
                        else
                        {
                                for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
-                                       if (strcasecmp(ep->ec_name, *tv) == 0)
+                                       if (strcasecmp(ep->ec_name, hbuf) == 0)
                                                break;
                                setstat(ep->ec_code);
                        }
                                                break;
                                setstat(ep->ec_code);
                        }
-                       tv++;
                }
                else
                        setstat(EX_UNAVAILABLE);
                }
                else
                        setstat(EX_UNAVAILABLE);
@@ -1635,41 +1794,45 @@ badaddr:
                                (void) strcat(buf, " ");
                        (void) strcat(buf, *tv);
                }
                                (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",
-                               CurEnv->e_id, buf);
-#endif /* LOG */
-               if (isascii(buf[0]) && isdigit(buf[0]) &&
-                   isascii(buf[1]) && isdigit(buf[1]) &&
-                   isascii(buf[2]) && isdigit(buf[2]) &&
-                   buf[3] == ' ')
+               stripquotes(ubuf);
+               if (isascii(ubuf[0]) && isdigit(ubuf[0]) &&
+                   isascii(ubuf[1]) && isdigit(ubuf[1]) &&
+                   isascii(ubuf[2]) && isdigit(ubuf[2]) &&
+                   ubuf[3] == ' ')
                {
                        char fmt[10];
 
                {
                        char fmt[10];
 
-                       strncpy(fmt, buf, 3);
+                       strncpy(fmt, ubuf, 3);
                        strcpy(&fmt[3], " %s");
                        strcpy(&fmt[3], " %s");
-                       usrerr(fmt, buf + 4);
+                       usrerr(fmt, ubuf + 4);
+
+                       /*
+                       **  If this is a 4xx code and we aren't running
+                       **  SMTP on our input, bounce this message;
+                       **  otherwise it disappears without a trace.
+                       */
+
+                       if (fmt[0] == '4' && OpMode != MD_SMTP &&
+                           OpMode != MD_DAEMON)
+                       {
+                               e->e_flags |= EF_FATALERRS;
+                       }
                }
                else
                {
                }
                else
                {
-                       usrerr("553 %s", buf);
+                       usrerr("553 %s", ubuf);
                }
                goto badaddr;
        }
 
        for (mp = Mailer; (m = *mp++) != NULL; )
        {
                }
                goto badaddr;
        }
 
        for (mp = Mailer; (m = *mp++) != NULL; )
        {
-               if (strcasecmp(m->m_name, *tv) == 0)
+               if (strcasecmp(m->m_name, mname) == 0)
                        break;
        }
        if (m == NULL)
        {
                        break;
        }
        if (m == NULL)
        {
-               syserr("554 buildaddr: unknown mailer %s", *tv);
+               syserr("554 buildaddr: unknown mailer %s", mname);
                goto badaddr;
        }
        a->q_mailer = m;
                goto badaddr;
        }
        a->q_mailer = m;
@@ -1686,58 +1849,43 @@ badaddr:
        }
        else
        {
        }
        else
        {
-               while (*++tv != NULL && **tv != CANONUSER)
-                       (void) strcat(buf, *tv);
-               a->q_host = newstr(buf);
-       }
 
        /* figure out the user */
 
        /* figure out the user */
-       if (*tv == NULL || (**tv & 0377) != CANONUSER)
+       p = ubuf;
+       if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')
        {
        {
-               syserr("554 buildaddr: no user");
-               goto badaddr;
+               p++;
+               tv++;
+               a->q_flags |= QNOTREMOTE;
        }
        }
-       tv++;
 
        /* do special mapping for local mailer */
 
        /* do special mapping for local mailer */
-       if (m == LocalMailer && *tv != NULL)
+       if (*p == '"')
+               p++;
+       if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
+               a->q_mailer = m = ProgMailer;
+       else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
+               a->q_mailer = m = FileMailer;
+       else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
        {
        {
-               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: */
+               stripquotes(ubuf);
+               if (strncasecmp(ubuf, ":include:", 9) == 0)
                {
                {
-                       /* 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 :include:, don't need further rewriting */
+                       a->q_mailer = m = InclMailer;
+                       a->q_user = newstr(&ubuf[9]);
+                       return a;
                }
        }
 
                }
        }
 
-       if (m == LocalMailer && *tv != NULL && strcmp(*tv, "@") == 0)
-       {
-               tv++;
-               a->q_flags |= QNOTREMOTE;
-       }
-
        if (m->m_r_rwset > 0)
                rewrite(tv, m->m_r_rwset);
        (void) rewrite(tv, 4, 0, e);
 
        /* save the result for the command line/RCPT argument */
        if (m->m_r_rwset > 0)
                rewrite(tv, m->m_r_rwset);
        (void) rewrite(tv, 4, 0, e);
 
        /* save the result for the command line/RCPT argument */
-       cataddr(tv, NULL, buf, sizeof buf, '\0');
-       a->q_user = buf;
+       cataddr(tv, NULL, ubuf, sizeof ubuf, '\0');
+       a->q_user = ubuf;
 
        /*
        **  Do mapping to lower case as requested by mailer
 
        /*
        **  Do mapping to lower case as requested by mailer
@@ -1748,7 +1896,7 @@ badaddr:
        if (!bitnset(M_USR_UPPER, m->m_flags))
                makelower(a->q_user);
 
        if (!bitnset(M_USR_UPPER, m->m_flags))
                makelower(a->q_user);
 
-       return (a);
+       return a;
 }
 \f/*
 **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
 }
 \f/*
 **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
@@ -1794,7 +1942,7 @@ cataddr(pvp, evp, buf, sz, spacesub)
        sz -= 2;
        while (*pvp != NULL && (i = strlen(*pvp)) < sz)
        {
        sz -= 2;
        while (*pvp != NULL && (i = strlen(*pvp)) < sz)
        {
-               natomtok = (toktype(**pvp) == ATM);
+               natomtok = (TokTypeTab[**pvp & 0xff] == ATM);
                if (oatomtok && natomtok)
                        *p++ = spacesub;
                (void) strcpy(p, *pvp);
                if (oatomtok && natomtok)
                        *p++ = spacesub;
                (void) strcpy(p, *pvp);
@@ -1879,21 +2027,59 @@ sameaddr(a, b)
 **             none.
 */
 
 **             none.
 */
 
+struct qflags
+{
+       char    *qf_name;
+       u_long  qf_bit;
+};
+
+struct qflags  AddressFlags[] =
+{
+       "QDONTSEND",            QDONTSEND,
+       "QBADADDR",             QBADADDR,
+       "QGOODUID",             QGOODUID,
+       "QPRIMARY",             QPRIMARY,
+       "QQUEUEUP",             QQUEUEUP,
+       "QSENT",                QSENT,
+       "QNOTREMOTE",           QNOTREMOTE,
+       "QSELFREF",             QSELFREF,
+       "QVERIFIED",            QVERIFIED,
+       "QBOGUSSHELL",          QBOGUSSHELL,
+       "QUNSAFEADDR",          QUNSAFEADDR,
+       "QPINGONSUCCESS",       QPINGONSUCCESS,
+       "QPINGONFAILURE",       QPINGONFAILURE,
+       "QPINGONDELAY",         QPINGONDELAY,
+       "QHASNOTIFY",           QHASNOTIFY,
+       "QRELAYED",             QRELAYED,
+       "QEXPANDED",            QEXPANDED,
+       "QDELIVERED",           QDELIVERED,
+       "QDELAYED",             QDELAYED,
+       "QTHISPASS",            QTHISPASS,
+       NULL
+};
+
+void
 void
 printaddr(a, follow)
        register ADDRESS *a;
        bool follow;
 {
 void
 printaddr(a, follow)
        register ADDRESS *a;
        bool follow;
 {
-       bool first = TRUE;
        register MAILER *m;
        MAILER pseudomailer;
        register MAILER *m;
        MAILER pseudomailer;
+       register struct qflags *qfp;
+       bool firstone;
+
+       if (a == NULL)
+       {
+               printf("[NULL]\n");
+               return;
+       }
 
        static int indent;
        register int i;
 
        while (a != NULL)
        {
 
        static int indent;
        register int i;
 
        while (a != NULL)
        {
-               first = FALSE;
                for (i = indent; i > 0; i--)
                        printf("\t");
                printf("%x=", a);
                for (i = indent; i > 0; i--)
                        printf("\t");
                printf("%x=", a);
@@ -1923,10 +2109,25 @@ printaddr(a, follow)
                indent--;
                a = a->q_sibling;
        }
                indent--;
                a = a->q_sibling;
        }
-       if (first)
-               printf("[NULL]\n");
 }
 }
+\f/*
+**  EMPTYADDR -- return TRUE if this address is empty (``<>'')
+**
+**     Parameters:
+**             a -- pointer to the address
+**
+**     Returns:
+**             TRUE -- if this address is "empty" (i.e., no one should
+**                     ever generate replies to it.
+**             FALSE -- if it is a "regular" (read: replyable) address.
+*/
 
 
+bool
+emptyaddr(a)
+       register ADDRESS *a;
+{
+       return strcmp(a->q_paddr, "<>") == 0 || strcmp(a->q_user, "<>") == 0;
+}
 \f/*
 **  REMOTENAME -- return the name relative to the current mailer
 **
 \f/*
 **  REMOTENAME -- return the name relative to the current mailer
 **
@@ -1962,8 +2163,8 @@ remotename(name, m, flags, pstat, e)
        char *fancy;
        char *oldg = macvalue('g', e);
        int rwset;
        char *fancy;
        char *oldg = macvalue('g', e);
        int rwset;
-       static char buf[MAXNAME];
-       char lbuf[MAXNAME];
+       static char buf[MAXNAME + 1];
+       char lbuf[MAXNAME + 1];
        char pvpbuf[PSBUFSIZE];
 
        if (tTd(12, 1))
        char pvpbuf[PSBUFSIZE];
 
        if (tTd(12, 1))
@@ -1991,7 +2192,7 @@ remotename(name, m, flags, pstat, e)
        **      domain will be appended.
        */
 
        **      domain will be appended.
        */
 
-       pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL);
+       pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
        if (pvp == NULL)
                return (name);
        if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
        if (pvp == NULL)
                return (name);
        if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
@@ -2051,9 +2252,9 @@ remotename(name, m, flags, pstat, e)
 
        /* need to make sure route-addrs have <angle brackets> */
        if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
 
        /* need to make sure route-addrs have <angle brackets> */
        if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
-               expand("<\201g>", buf, &buf[sizeof buf - 1], e);
+               expand("<\201g>", buf, sizeof buf, e);
        else
        else
-               expand(fancy, buf, &buf[sizeof buf - 1], e);
+               expand(fancy, buf, sizeof buf, e);
 
        define('g', oldg, e);
 
 
        define('g', oldg, e);
 
@@ -2138,14 +2339,17 @@ uurelativize(from, to, pvp)
 **             a -- the address to map (but just the user name part).
 **             sendq -- the sendq in which to install any replacement
 **                     addresses.
 **             a -- the address to map (but just the user name part).
 **             sendq -- the sendq in which to install any replacement
 **                     addresses.
+**             aliaslevel -- the alias nesting depth.
+**             e -- the envelope.
 **
 **     Returns:
 **             none.
 */
 
 **
 **     Returns:
 **             none.
 */
 
-maplocaluser(a, sendq, e)
+maplocaluser(a, sendq, aliaslevel, e)
        register ADDRESS *a;
        ADDRESS **sendq;
        register ADDRESS *a;
        ADDRESS **sendq;
+       int aliaslevel;
        ENVELOPE *e;
 {
        register char **pvp;
        ENVELOPE *e;
 {
        register char **pvp;
@@ -2158,7 +2362,7 @@ maplocaluser(a, sendq, e)
                printf("maplocaluser: ");
                printaddr(a, FALSE);
        }
                printf("maplocaluser: ");
                printaddr(a, FALSE);
        }
-       pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr);
+       pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL);
        if (pvp == NULL)
                return;
 
        if (pvp == NULL)
                return;
 
@@ -2180,7 +2384,7 @@ maplocaluser(a, sendq, e)
        }
        a1->q_alias = a;
        allocaddr(a1, RF_COPYALL, NULL);
        }
        a1->q_alias = a;
        allocaddr(a1, RF_COPYALL, NULL);
-       (void) recipient(a1, sendq, e);
+       (void) recipient(a1, sendq, aliaslevel, e);
 }
 \f/*
 **  DEQUOTE_INIT -- initialize dequote map
 }
 \f/*
 **  DEQUOTE_INIT -- initialize dequote map
@@ -2213,6 +2417,10 @@ dequote_init(map, args)
                  case 'a':
                        map->map_app = ++p;
                        break;
                  case 'a':
                        map->map_app = ++p;
                        break;
+
+                 case 's':
+                       map->map_coldelim = *++p;
+                       break;
                }
                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
                        p++;
                }
                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
                        p++;
@@ -2249,19 +2457,13 @@ dequote_map(map, name, av, statp)
        register char *p;
        register char *q;
        register char c;
        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;
+       int anglecnt = 0;
+       int cmntcnt = 0;
+       int quotecnt = 0;
+       int spacecnt = 0;
+       bool quotemode = FALSE;
+       bool bslashmode = FALSE;
+       char spacesub = map->map_coldelim;
 
        for (p = q = name; (c = *p++) != '\0'; )
        {
 
        for (p = q = name; (c = *p++) != '\0'; )
        {
@@ -2272,6 +2474,9 @@ dequote_map(map, name, av, statp)
                        continue;
                }
 
                        continue;
                }
 
+               if (c == ' ' && spacesub != '\0')
+                       c = spacesub;
+
                switch (c)
                {
                  case '\\':
                switch (c)
                {
                  case '\\':