BSD 4_3_Net_2 release
[unix-history] / usr / src / usr.sbin / sendmail / src / parseaddr.c
index 42fb3b0..2ee24e0 100644 (file)
@@ -1,9 +1,45 @@
-# include "sendmail.h"
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)parseaddr.c        5.13 (Berkeley) 6/1/90";
+#endif /* not lint */
 
 
-SCCSID(@(#)parseaddr.c 3.56            %G%);
+# include "sendmail.h"
 
 /*
 
 /*
-**  PARSE -- Parse an address
+**  PARSEADDR -- Parse an address
 **
 **     Parses an address and breaks it up into three parts: a
 **     net to transmit the message on, the host to transmit it
 **
 **     Parses an address and breaks it up into three parts: a
 **     net to transmit the message on, the host to transmit it
@@ -28,6 +64,8 @@ SCCSID(@(#)parseaddr.c        3.56            %G%);
 **                     0 -- copy out the parsed user & host, but
 **                             don't copy the printname.
 **                     +1 -- copy everything.
 **                     0 -- copy out the parsed user & host, but
 **                             don't copy the printname.
 **                     +1 -- copy everything.
+**             delim -- the character to terminate the address, passed
+**                     to prescan.
 **
 **     Returns:
 **             A pointer to the address descriptor header (`a' if
 **
 **     Returns:
 **             A pointer to the address descriptor header (`a' if
@@ -38,31 +76,31 @@ SCCSID(@(#)parseaddr.c      3.56            %G%);
 **             none
 */
 
 **             none
 */
 
-# define DELIMCHARS    "$()<>,;\\\"\r\n"       /* word delimiters */
+/* following delimiters are inherent to the internal algorithms */
+# define DELIMCHARS    "\001()<>,;\\\"\r\n"    /* word delimiters */
 
 ADDRESS *
 
 ADDRESS *
-parse(addr, a, copyf)
+parseaddr(addr, a, copyf, delim)
        char *addr;
        register ADDRESS *a;
        int copyf;
        char *addr;
        register ADDRESS *a;
        int copyf;
+       char delim;
 {
        register char **pvp;
        register struct mailer *m;
 {
        register char **pvp;
        register struct mailer *m;
+       char pvpbuf[PSBUFSIZE];
        extern char **prescan();
        extern ADDRESS *buildaddr();
        extern char **prescan();
        extern ADDRESS *buildaddr();
-       static char nbuf[MAXNAME];
 
        /*
        **  Initialize and prescan address.
        */
 
        CurEnv->e_to = addr;
 
        /*
        **  Initialize and prescan address.
        */
 
        CurEnv->e_to = addr;
-# ifdef DEBUG
        if (tTd(20, 1))
        if (tTd(20, 1))
-               printf("\n--parse(%s)\n", addr);
-# endif DEBUG
+               printf("\n--parseaddr(%s)\n", addr);
 
 
-       pvp = prescan(addr, ',');
+       pvp = prescan(addr, delim, pvpbuf);
        if (pvp == NULL)
                return (NULL);
 
        if (pvp == NULL)
                return (NULL);
 
@@ -110,47 +148,65 @@ parse(addr, a, copyf)
        }
        else
                a->q_paddr = addr;
        }
        else
                a->q_paddr = addr;
+
+       if (a->q_user == NULL)
+               a->q_user = "";
+       if (a->q_host == NULL)
+               a->q_host = "";
+
        if (copyf >= 0)
        {
        if (copyf >= 0)
        {
-               if (a->q_host != NULL)
-                       a->q_host = newstr(a->q_host);
-               else
-                       a->q_host = "";
+               a->q_host = newstr(a->q_host);
                if (a->q_user != a->q_paddr)
                        a->q_user = newstr(a->q_user);
        }
 
        /*
                if (a->q_user != a->q_paddr)
                        a->q_user = newstr(a->q_user);
        }
 
        /*
-       **  Do UPPER->lower case mapping unless inhibited.
+       **  Convert host name to lower case if requested.
+       **      User name will be done later.
        */
 
        */
 
-       if (!bitset(M_HST_UPPER, m->m_flags))
+       if (!bitnset(M_HST_UPPER, m->m_flags))
                makelower(a->q_host);
                makelower(a->q_host);
-       if (!bitset(M_USR_UPPER, m->m_flags))
-               makelower(a->q_user);
 
        /*
        **  Compute return value.
        */
 
 
        /*
        **  Compute return value.
        */
 
-# ifdef DEBUG
        if (tTd(20, 1))
        {
        if (tTd(20, 1))
        {
-               printf("parse-->");
+               printf("parseaddr-->");
                printaddr(a, FALSE);
        }
                printaddr(a, FALSE);
        }
-# endif DEBUG
 
        return (a);
 }
 \f/*
 
        return (a);
 }
 \f/*
+**  LOWERADDR -- map UPPER->lower case on addresses as requested.
+**
+**     Parameters:
+**             a -- address to be mapped.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             none.
+*/
+
+loweraddr(a)
+       register ADDRESS *a;
+{
+       register MAILER *m = a->q_mailer;
+
+       if (!bitnset(M_USR_UPPER, m->m_flags))
+               makelower(a->q_user);
+}
+\f/*
 **  PRESCAN -- Prescan name and make it canonical
 **
 **  PRESCAN -- Prescan name and make it canonical
 **
-**     Scans a name and turns it into canonical form.  This involves
-**     deleting blanks, comments (in parentheses), and turning the
-**     word "at" into an at-sign ("@").  The name is copied as this
-**     is done; it is legal to copy a name onto itself, since this
-**     process can only make things smaller.
+**     Scans a name and turns it into a set of tokens.  This process
+**     deletes blanks and comments (in parentheses).
 **
 **     This routine knows about quoted strings and angle brackets.
 **
 **
 **     This routine knows about quoted strings and angle brackets.
 **
@@ -166,13 +222,16 @@ parse(addr, a, copyf)
 **             addr -- the name to chomp.
 **             delim -- the delimiter for the address, normally
 **                     '\0' or ','; \0 is accepted in any case.
 **             addr -- the name to chomp.
 **             delim -- the delimiter for the address, normally
 **                     '\0' or ','; \0 is accepted in any case.
+**                     If '\t' then we are reading the .cf file.
+**             pvpbuf -- place to put the saved text -- note that
+**                     the pointers are static.
 **
 **     Returns:
 **             A pointer to a vector of tokens.
 **             NULL on error.
 **
 **     Side Effects:
 **
 **     Returns:
 **             A pointer to a vector of tokens.
 **             NULL on error.
 **
 **     Side Effects:
-**             none.
+**             sets DelimChar to point to the character matching 'delim'.
 */
 
 /* states and character types */
 */
 
 /* states and character types */
@@ -193,9 +252,9 @@ parse(addr, a, copyf)
 static short StateTab[NSTATES][NSTATES] =
 {
    /*  oldst   chtype> OPR     ATM     QST     SPC     ONE     */
 static short StateTab[NSTATES][NSTATES] =
 {
    /*  oldst   chtype> OPR     ATM     QST     SPC     ONE     */
-       /*OPR*/         OPR|B,  ATM|B,  QST|MB, SPC|MB, ONE|B,
-       /*ATM*/         OPR|B,  ATM,    QST|MB, SPC|MB, ONE|B,
-       /*QST*/         QST,    QST,    OPR|MB, QST,    QST,
+       /*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,
 };
        /*SPC*/         OPR,    ATM,    QST,    SPC|M,  ONE,
        /*ONE*/         OPR,    OPR,    OPR,    OPR,    OPR,
 };
@@ -205,37 +264,41 @@ static short StateTab[NSTATES][NSTATES] =
 char   *DelimChar;             /* set to point to the delimiter */
 
 char **
 char   *DelimChar;             /* set to point to the delimiter */
 
 char **
-prescan(addr, delim)
+prescan(addr, delim, pvpbuf)
        char *addr;
        char delim;
        char *addr;
        char delim;
+       char pvpbuf[];
 {
        register char *p;
        register char *q;
 {
        register char *p;
        register char *q;
-       register char c;
+       register int c;
        char **avp;
        bool bslashmode;
        int cmntcnt;
        char **avp;
        bool bslashmode;
        int cmntcnt;
+       int anglecnt;
        char *tok;
        int state;
        int newstate;
        char *tok;
        int state;
        int newstate;
-       static char buf[MAXNAME+MAXATOM];
        static char *av[MAXATOM+1];
        static char *av[MAXATOM+1];
+       extern int errno;
+
+       /* make sure error messages don't have garbage on them */
+       errno = 0;
 
 
-       q = buf;
+       q = pvpbuf;
        bslashmode = FALSE;
        cmntcnt = 0;
        bslashmode = FALSE;
        cmntcnt = 0;
+       anglecnt = 0;
        avp = av;
        state = OPR;
        c = NOCHAR;
        p = addr;
        avp = av;
        state = OPR;
        c = NOCHAR;
        p = addr;
-# ifdef DEBUG
        if (tTd(22, 45))
        {
                printf("prescan: ");
                xputs(p);
        if (tTd(22, 45))
        {
                printf("prescan: ");
                xputs(p);
-               putchar('\n');
+               (void) putchar('\n');
        }
        }
-# endif DEBUG
 
        do
        {
 
        do
        {
@@ -246,13 +309,15 @@ prescan(addr, delim)
                        /* store away any old lookahead character */
                        if (c != NOCHAR)
                        {
                        /* store away any old lookahead character */
                        if (c != NOCHAR)
                        {
-                               /* squirrel it away */
-                               if (q >= &buf[sizeof buf - 5])
+                               /* see if there is room */
+                               if (q >= &pvpbuf[PSBUFSIZE - 5])
                                {
                                        usrerr("Address too long");
                                        DelimChar = p;
                                        return (NULL);
                                }
                                {
                                        usrerr("Address too long");
                                        DelimChar = p;
                                        return (NULL);
                                }
+
+                               /* squirrel it away */
                                *q++ = c;
                        }
 
                                *q++ = c;
                        }
 
@@ -260,17 +325,18 @@ prescan(addr, delim)
                        c = *p++;
                        if (c == '\0')
                                break;
                        c = *p++;
                        if (c == '\0')
                                break;
-# ifdef DEBUG
+                       c &= ~0200;
+
                        if (tTd(22, 101))
                                printf("c=%c, s=%d; ", c, state);
                        if (tTd(22, 101))
                                printf("c=%c, s=%d; ", c, state);
-# endif DEBUG
 
                        /* chew up special characters */
 
                        /* chew up special characters */
-                       c &= ~0200;
                        *q = '\0';
                        if (bslashmode)
                        {
                        *q = '\0';
                        if (bslashmode)
                        {
-                               c |= 0200;
+                               /* kludge \! for naive users */
+                               if (c != '!')
+                                       c |= 0200;
                                bslashmode = FALSE;
                        }
                        else if (c == '\\')
                                bslashmode = FALSE;
                        }
                        else if (c == '\\')
@@ -278,6 +344,10 @@ prescan(addr, delim)
                                bslashmode = TRUE;
                                c = NOCHAR;
                        }
                                bslashmode = TRUE;
                                c = NOCHAR;
                        }
+                       else if (state == QST)
+                       {
+                               /* do nothing, just avoid next clauses */
+                       }
                        else if (c == '(')
                        {
                                cmntcnt++;
                        else if (c == '(')
                        {
                                cmntcnt++;
@@ -296,30 +366,31 @@ prescan(addr, delim)
                        }
                        else if (cmntcnt > 0)
                                c = NOCHAR;
                        }
                        else if (cmntcnt > 0)
                                c = NOCHAR;
-                       else if (c == ':' && !CurEnv->e_oldstyle)
+                       else if (c == '<')
+                               anglecnt++;
+                       else if (c == '>')
                        {
                        {
-                               /* consume characters until a semicolon */
-                               while (*p != '\0' && *p != ';')
-                                       p++;
-                               if (*p == '\0')
-                                       usrerr("Unbalanced ':...;' group spec");
-                               else
-                                       p++;
-                               c = ' ';
+                               if (anglecnt <= 0)
+                               {
+                                       usrerr("Unbalanced '>'");
+                                       DelimChar = p;
+                                       return (NULL);
+                               }
+                               anglecnt--;
                        }
                        }
+                       else if (delim == ' ' && isspace(c))
+                               c = ' ';
 
                        if (c == NOCHAR)
                                continue;
 
                        /* see if this is end of input */
 
                        if (c == NOCHAR)
                                continue;
 
                        /* see if this is end of input */
-                       if (c == delim)
+                       if (c == delim && anglecnt <= 0 && state != QST)
                                break;
 
                        newstate = StateTab[state][toktype(c)];
                                break;
 
                        newstate = StateTab[state][toktype(c)];
-# ifdef DEBUG
                        if (tTd(22, 101))
                                printf("ns=%02o\n", newstate);
                        if (tTd(22, 101))
                                printf("ns=%02o\n", newstate);
-# endif DEBUG
                        state = newstate & TYPE;
                        if (bitset(M, newstate))
                                c = NOCHAR;
                        state = newstate & TYPE;
                        if (bitset(M, newstate))
                                c = NOCHAR;
@@ -331,14 +402,12 @@ prescan(addr, delim)
                if (tok != q)
                {
                        *q++ = '\0';
                if (tok != q)
                {
                        *q++ = '\0';
-# ifdef DEBUG
                        if (tTd(22, 36))
                        {
                                printf("tok=");
                                xputs(tok);
                        if (tTd(22, 36))
                        {
                                printf("tok=");
                                xputs(tok);
-                               putchar('\n');
+                               (void) putchar('\n');
                        }
                        }
-# endif DEBUG
                        if (avp >= &av[MAXATOM])
                        {
                                syserr("prescan: too many tokens");
                        if (avp >= &av[MAXATOM])
                        {
                                syserr("prescan: too many tokens");
@@ -347,11 +416,13 @@ prescan(addr, delim)
                        }
                        *avp++ = tok;
                }
                        }
                        *avp++ = tok;
                }
-       } while (c != '\0' && c != delim);
+       } while (c != '\0' && (c != delim || anglecnt > 0));
        *avp = NULL;
        DelimChar = --p;
        if (cmntcnt > 0)
                usrerr("Unbalanced '('");
        *avp = NULL;
        DelimChar = --p;
        if (cmntcnt > 0)
                usrerr("Unbalanced '('");
+       else if (anglecnt > 0)
+               usrerr("Unbalanced '<'");
        else if (state == QST)
                usrerr("Unbalanced '\"'");
        else if (av[0] != NULL)
        else if (state == QST)
                usrerr("Unbalanced '\"'");
        else if (av[0] != NULL)
@@ -380,10 +451,10 @@ toktype(c)
        if (firstime)
        {
                firstime = FALSE;
        if (firstime)
        {
                firstime = FALSE;
-               expand("$o", buf, &buf[sizeof buf - 1], CurEnv);
+               expand("\001o", buf, &buf[sizeof buf - 1], CurEnv);
                (void) strcat(buf, DELIMCHARS);
        }
                (void) strcat(buf, DELIMCHARS);
        }
-       if (c == MATCHCLASS || c == MATCHREPL)
+       if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS)
                return (ONE);
        if (c == '"')
                return (QST);
                return (ONE);
        if (c == '"')
                return (QST);
@@ -405,11 +476,11 @@ toktype(c)
 **     For each rewrite rule, 'avp' points the address vector we
 **     are trying to match against, and 'pvp' points to the pattern.
 **     If pvp points to a special match value (MATCHZANY, MATCHANY,
 **     For each rewrite rule, 'avp' points the address vector we
 **     are trying to match against, and 'pvp' points to the pattern.
 **     If pvp points to a special match value (MATCHZANY, MATCHANY,
-**     MATCHONE, MATCHCLASS) then the address in avp matched is
-**     saved away in the match vector (pointed to by 'mvp').
+**     MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
+**     matched is saved away in the match vector (pointed to by 'mvp').
 **
 **     When a match between avp & pvp does not match, we try to
 **
 **     When a match between avp & pvp does not match, we try to
-**     back out.  If we back up over a MATCHONE or a MATCHCLASS
+**     back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
 **     we must also back out the match in mvp.  If we reach a
 **     MATCHANY or MATCHZANY we just extend the match and start
 **     over again.
 **     we must also back out the match in mvp.  If we reach a
 **     MATCHANY or MATCHZANY we just extend the match and start
 **     over again.
@@ -448,15 +519,14 @@ rewrite(pvp, ruleset)
        register struct rewrite *rwr;   /* pointer to current rewrite rule */
        struct match mlist[MAXMATCH];   /* stores match on LHS */
        char *npvp[MAXATOM+1];          /* temporary space for rebuild */
        register struct rewrite *rwr;   /* pointer to current rewrite rule */
        struct match mlist[MAXMATCH];   /* stores match on LHS */
        char *npvp[MAXATOM+1];          /* temporary space for rebuild */
-       extern bool sameword();
 
 
-# ifdef DEBUG
-       if (tTd(21, 2))
+       if (OpMode == MD_TEST || tTd(21, 2))
        {
        {
-               printf("rewrite: ruleset %d, original pvp:", ruleset);
+               printf("rewrite: ruleset %2d   input:", ruleset);
                printav(pvp);
        }
                printav(pvp);
        }
-# endif DEBUG
+       if (pvp == NULL)
+               return;
 
        /*
        **  Run through the list of rewrite rules, applying
 
        /*
        **  Run through the list of rewrite rules, applying
@@ -465,13 +535,11 @@ rewrite(pvp, ruleset)
 
        for (rwr = RewriteRules[ruleset]; rwr != NULL; )
        {
 
        for (rwr = RewriteRules[ruleset]; rwr != NULL; )
        {
-# ifdef DEBUG
                if (tTd(21, 12))
                {
                        printf("-----trying rule:");
                        printav(rwr->r_lhs);
                }
                if (tTd(21, 12))
                {
                        printf("-----trying rule:");
                        printav(rwr->r_lhs);
                }
-# endif DEBUG
 
                /* try to match on this rule */
                mlp = mlist;
 
                /* try to match on this rule */
                mlp = mlist;
@@ -480,7 +548,6 @@ rewrite(pvp, ruleset)
                while ((ap = *avp) != NULL || *rvp != NULL)
                {
                        rp = *rvp;
                while ((ap = *avp) != NULL || *rvp != NULL)
                {
                        rp = *rvp;
-# ifdef DEBUG
                        if (tTd(21, 35))
                        {
                                printf("ap=");
                        if (tTd(21, 35))
                        {
                                printf("ap=");
@@ -489,7 +556,6 @@ rewrite(pvp, ruleset)
                                xputs(rp);
                                printf("\n");
                        }
                                xputs(rp);
                                printf("\n");
                        }
-# endif DEBUG
                        if (rp == NULL)
                        {
                                /* end-of-pattern before end-of-address */
                        if (rp == NULL)
                        {
                                /* end-of-pattern before end-of-address */
@@ -504,19 +570,17 @@ rewrite(pvp, ruleset)
                        switch (*rp)
                        {
                                register STAB *s;
                        switch (*rp)
                        {
                                register STAB *s;
-                               register int class;
 
                          case MATCHCLASS:
 
                          case MATCHCLASS:
-                               /* match any token in a class */
-                               class = rp[1];
-                               if (!isalpha(class))
-                                       goto backup;
-                               if (isupper(class))
-                                       class -= 'A';
-                               else
-                                       class -= 'a';
+                         case MATCHNCLASS:
+                               /* match any token in (not in) a class */
                                s = stab(ap, ST_CLASS, ST_FIND);
                                s = stab(ap, ST_CLASS, ST_FIND);
-                               if (s == NULL || (s->s_class & (1L << class)) == 0)
+                               if (s == NULL || !bitnset(rp[1], s->s_class))
+                               {
+                                       if (*rp == MATCHCLASS)
+                                               goto backup;
+                               }
+                               else if (*rp == MATCHNCLASS)
                                        goto backup;
 
                                /* explicit fall-through */
                                        goto backup;
 
                                /* explicit fall-through */
@@ -538,7 +602,7 @@ rewrite(pvp, ruleset)
 
                          default:
                                /* must have exact match */
 
                          default:
                                /* must have exact match */
-                               if (!sameword(rp, ap))
+                               if (strcasecmp(rp, ap))
                                        goto backup;
                                avp++;
                                break;
                                        goto backup;
                                avp++;
                                break;
@@ -562,7 +626,8 @@ rewrite(pvp, ruleset)
                                        break;
                                }
                                avp--;
                                        break;
                                }
                                avp--;
-                               if (*rp == MATCHONE || *rp == MATCHCLASS)
+                               if (*rp == MATCHONE || *rp == MATCHCLASS ||
+                                   *rp == MATCHNCLASS)
                                {
                                        /* back out binding */
                                        mlp--;
                                {
                                        /* back out binding */
                                        mlp--;
@@ -580,115 +645,183 @@ rewrite(pvp, ruleset)
                **  See if we successfully matched
                */
 
                **  See if we successfully matched
                */
 
-               if (rvp >= rwr->r_lhs && *rvp == NULL)
+               if (rvp < rwr->r_lhs || *rvp != NULL)
                {
                {
-                       rvp = rwr->r_rhs;
-# ifdef DEBUG
-                       if (tTd(21, 12))
-                       {
-                               printf("-----rule matches:");
-                               printav(rvp);
-                       }
-# endif DEBUG
+                       if (tTd(21, 10))
+                               printf("----- rule fails\n");
+                       rwr = rwr->r_next;
+                       continue;
+               }
 
 
-                       /* see if this is a "subroutine" call */
-                       rp = *rvp;
-                       if (*rp == CALLSUBR)
-                       {
-                               rp = *++rvp;
-# ifdef DEBUG
-                               if (tTd(21, 3))
-                                       printf("-----callsubr %s\n", rp);
-# endif DEBUG
-                               rewrite(pvp, atoi(rp));
-                               rwr = rwr->r_next;
-                               continue;
-                       }
-                       else if (*rp == CANONUSER)
-                       {
-                               rvp++;
-                               rwr = rwr->r_next;
-                       }
-                       else if (*rp == CANONHOST)
-                       {
-                               rvp++;
-                               rwr = NULL;
-                       }
-                       else if (*rp == CANONNET)
-                               rwr = NULL;
+               rvp = rwr->r_rhs;
+               if (tTd(21, 12))
+               {
+                       printf("-----rule matches:");
+                       printav(rvp);
+               }
 
 
-                       /* substitute */
-                       for (avp = npvp; *rvp != NULL; rvp++)
+               rp = *rvp;
+               if (*rp == CANONUSER)
+               {
+                       rvp++;
+                       rwr = rwr->r_next;
+               }
+               else if (*rp == CANONHOST)
+               {
+                       rvp++;
+                       rwr = NULL;
+               }
+               else if (*rp == CANONNET)
+                       rwr = NULL;
+
+               /* substitute */
+               for (avp = npvp; *rvp != NULL; rvp++)
+               {
+                       register struct match *m;
+                       register char **pp;
+
+                       rp = *rvp;
+                       if (*rp == MATCHREPL)
                        {
                        {
-                               rp = *rvp;
-                               if (*rp == MATCHREPL)
+                               /* substitute from LHS */
+                               m = &mlist[rp[1] - '1'];
+                               if (m >= mlp)
                                {
                                {
-                                       register struct match *m;
-                                       register char **pp;
-
-                                       m = &mlist[rp[1] - '1'];
-# ifdef DEBUG
-                                       if (tTd(21, 15))
-                                       {
-                                               printf("$%c:", rp[1]);
-                                               pp = m->first;
-                                               while (pp <= m->last)
-                                               {
-                                                       printf(" %x=\"", *pp);
-                                                       (void) fflush(stdout);
-                                                       printf("%s\"", *pp++);
-                                               }
-                                               printf("\n");
-                                       }
-# endif DEBUG
+                                       syserr("rewrite: ruleset %d: replacement out of bounds", ruleset);
+                                       return;
+                               }
+                               if (tTd(21, 15))
+                               {
+                                       printf("$%c:", rp[1]);
                                        pp = m->first;
                                        while (pp <= m->last)
                                        {
                                        pp = m->first;
                                        while (pp <= m->last)
                                        {
-                                               if (avp >= &npvp[MAXATOM])
-                                               {
-                                                       syserr("rewrite: expansion too long");
-                                                       return;
-                                               }
-                                               *avp++ = *pp++;
+                                               printf(" %x=\"", *pp);
+                                               (void) fflush(stdout);
+                                               printf("%s\"", *pp++);
                                        }
                                        }
+                                       printf("\n");
                                }
                                }
-                               else
+                               pp = m->first;
+                               while (pp <= m->last)
                                {
                                        if (avp >= &npvp[MAXATOM])
                                        {
                                                syserr("rewrite: expansion too long");
                                                return;
                                        }
                                {
                                        if (avp >= &npvp[MAXATOM])
                                        {
                                                syserr("rewrite: expansion too long");
                                                return;
                                        }
-                                       *avp++ = rp;
+                                       *avp++ = *pp++;
+                               }
+                       }
+                       else
+                       {
+                               /* vanilla replacement */
+                               if (avp >= &npvp[MAXATOM])
+                               {
+       toolong:
+                                       syserr("rewrite: expansion too long");
+                                       return;
                                }
                                }
+                               *avp++ = rp;
+                       }
+               }
+               *avp++ = NULL;
+
+               /*
+               **  Check for any hostname lookups.
+               */
+
+               for (rvp = npvp; *rvp != NULL; rvp++)
+               {
+                       char **hbrvp;
+                       char **xpvp;
+                       int trsize;
+                       char *olddelimchar;
+                       char buf[MAXNAME + 1];
+                       char *pvpb1[MAXATOM + 1];
+                       char pvpbuf[PSBUFSIZE];
+                       extern char *DelimChar;
+
+                       if (**rvp != HOSTBEGIN)
+                               continue;
+
+                       /*
+                       **  Got a hostname lookup.
+                       **
+                       **      This could be optimized fairly easily.
+                       */
+
+                       hbrvp = rvp;
+
+                       /* extract the match part */
+                       while (*++rvp != NULL && **rvp != HOSTEND)
+                               continue;
+                       if (*rvp != NULL)
+                               *rvp++ = NULL;
+
+                       /* save the remainder of the input string */
+                       trsize = (int) (avp - rvp + 1) * sizeof *rvp;
+                       bcopy((char *) rvp, (char *) pvpb1, trsize);
+
+                       /* look it up */
+                       cataddr(++hbrvp, buf, sizeof buf);
+                       maphostname(buf, sizeof buf);
+
+                       /* scan the new host name */
+                       olddelimchar = DelimChar;
+                       xpvp = prescan(buf, '\0', pvpbuf);
+                       DelimChar = olddelimchar;
+                       if (xpvp == NULL)
+                       {
+                               syserr("rewrite: cannot prescan canonical hostname: %s", buf);
+                               return;
                        }
                        }
-                       *avp++ = NULL;
-                       bmove((char *) npvp, (char *) pvp, (avp - npvp) * sizeof *avp);
-# ifdef DEBUG
-                       if (tTd(21, 4))
+
+                       /* append it to the token list */
+                       for (avp = --hbrvp; *xpvp != NULL; xpvp++)
                        {
                        {
-                               printf("rewritten as:");
-                               printav(pvp);
+                               *avp++ = newstr(*xpvp);
+                               if (avp >= &npvp[MAXATOM])
+                                       goto toolong;
                        }
                        }
-# endif DEBUG
+
+                       /* restore the old trailing information */
+                       for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
+                               if (avp >= &npvp[MAXATOM])
+                                       goto toolong;
+
+                       break;
+               }
+
+               /*
+               **  Check for subroutine calls.
+               */
+
+               if (*npvp != NULL && **npvp == CALLSUBR)
+               {
+                       bcopy((char *) &npvp[2], (char *) pvp,
+                               (int) (avp - npvp - 2) * sizeof *avp);
+                       if (tTd(21, 3))
+                               printf("-----callsubr %s\n", npvp[1]);
+                       rewrite(pvp, atoi(npvp[1]));
                }
                else
                {
                }
                else
                {
-# ifdef DEBUG
-                       if (tTd(21, 10))
-                               printf("----- rule fails\n");
-# endif DEBUG
-                       rwr = rwr->r_next;
+                       bcopy((char *) npvp, (char *) pvp,
+                               (int) (avp - npvp) * sizeof *avp);
+               }
+               if (tTd(21, 4))
+               {
+                       printf("rewritten as:");
+                       printav(pvp);
                }
        }
 
                }
        }
 
-# ifdef DEBUG
-       if (tTd(21, 2))
+       if (OpMode == MD_TEST || tTd(21, 2))
        {
        {
-               printf("rewrite: ruleset %d returns:", ruleset);
+               printf("rewrite: ruleset %2d returns:", ruleset);
                printav(pvp);
        }
                printav(pvp);
        }
-# endif DEBUG
 }
 \f/*
 **  BUILDADDR -- build address from token vector.
 }
 \f/*
 **  BUILDADDR -- build address from token vector.
@@ -714,11 +847,10 @@ buildaddr(tv, a)
        static char buf[MAXNAME];
        struct mailer **mp;
        register struct mailer *m;
        static char buf[MAXNAME];
        struct mailer **mp;
        register struct mailer *m;
-       extern bool sameword();
 
        if (a == NULL)
                a = (ADDRESS *) xalloc(sizeof *a);
 
        if (a == NULL)
                a = (ADDRESS *) xalloc(sizeof *a);
-       clear((char *) a, sizeof *a);
+       bzero((char *) a, sizeof *a);
 
        /* figure out what net/mailer to use */
        if (**tv != CANONNET)
 
        /* figure out what net/mailer to use */
        if (**tv != CANONNET)
@@ -727,9 +859,14 @@ buildaddr(tv, a)
                return (NULL);
        }
        tv++;
                return (NULL);
        }
        tv++;
-       if (sameword(*tv, "error"))
+       if (!strcasecmp(*tv, "error"))
        {
        {
-               if (**++tv != CANONUSER)
+               if (**++tv == CANONHOST)
+               {
+                       setstat(atoi(*++tv));
+                       tv++;
+               }
+               if (**tv != CANONUSER)
                        syserr("buildaddr: error: no user");
                buf[0] = '\0';
                while (*++tv != NULL)
                        syserr("buildaddr: error: no user");
                buf[0] = '\0';
                while (*++tv != NULL)
@@ -743,19 +880,19 @@ buildaddr(tv, a)
        }
        for (mp = Mailer; (m = *mp++) != NULL; )
        {
        }
        for (mp = Mailer; (m = *mp++) != NULL; )
        {
-               if (sameword(m->m_name, *tv))
+               if (!strcasecmp(m->m_name, *tv))
                        break;
        }
        if (m == NULL)
        {
                        break;
        }
        if (m == NULL)
        {
-               syserr("buildaddr: unknown net %s", *tv);
+               syserr("buildaddr: unknown mailer %s", *tv);
                return (NULL);
        }
        a->q_mailer = m;
 
        /* figure out what host (if any) */
        tv++;
                return (NULL);
        }
        a->q_mailer = m;
 
        /* figure out what host (if any) */
        tv++;
-       if (!bitset(M_LOCAL, m->m_flags))
+       if (!bitnset(M_LOCAL, m->m_flags))
        {
                if (**tv++ != CANONHOST)
                {
        {
                if (**tv++ != CANONHOST)
                {
@@ -771,12 +908,20 @@ buildaddr(tv, a)
                a->q_host = NULL;
 
        /* figure out the user */
                a->q_host = NULL;
 
        /* figure out the user */
-       if (**tv != CANONUSER)
+       if (*tv == NULL || **tv != CANONUSER)
        {
                syserr("buildaddr: no user");
                return (NULL);
        }
        {
                syserr("buildaddr: no user");
                return (NULL);
        }
-       cataddr(++tv, buf, sizeof buf);
+
+       /* rewrite according recipient mailer rewriting rules */
+       rewrite(++tv, 2);
+       if (m->m_r_rwset > 0)
+               rewrite(tv, m->m_r_rwset);
+       rewrite(tv, 4);
+
+       /* save the result for the command line/RCPT argument */
+       cataddr(tv, buf, sizeof buf);
        a->q_user = buf;
 
        return (a);
        a->q_user = buf;
 
        return (a);
@@ -806,17 +951,22 @@ cataddr(pvp, buf, sz)
        register int i;
        register char *p;
 
        register int i;
        register char *p;
 
+       if (pvp == NULL)
+       {
+               (void) strcpy(buf, "");
+               return;
+       }
        p = buf;
        p = buf;
-       sz--;
+       sz -= 2;
        while (*pvp != NULL && (i = strlen(*pvp)) < sz)
        {
                natomtok = (toktype(**pvp) == ATM);
                if (oatomtok && natomtok)
        while (*pvp != NULL && (i = strlen(*pvp)) < sz)
        {
                natomtok = (toktype(**pvp) == ATM);
                if (oatomtok && natomtok)
-                       *p++ = SPACESUB;
+                       *p++ = SpaceSub;
                (void) strcpy(p, *pvp);
                oatomtok = natomtok;
                p += i;
                (void) strcpy(p, *pvp);
                oatomtok = natomtok;
                p += i;
-               sz -= i;
+               sz -= i + 1;
                pvp++;
        }
        *p = '\0';
                pvp++;
        }
        *p = '\0';
@@ -829,8 +979,6 @@ cataddr(pvp, buf, sz)
 **
 **     Parameters:
 **             a, b -- pointers to the internal forms to compare.
 **
 **     Parameters:
 **             a, b -- pointers to the internal forms to compare.
-**             wildflg -- if TRUE, 'a' may have no user specified,
-**                     in which case it is to match anything.
 **
 **     Returns:
 **             TRUE -- they represent the same mailbox.
 **
 **     Returns:
 **             TRUE -- they represent the same mailbox.
@@ -841,21 +989,20 @@ cataddr(pvp, buf, sz)
 */
 
 bool
 */
 
 bool
-sameaddr(a, b, wildflg)
+sameaddr(a, b)
        register ADDRESS *a;
        register ADDRESS *b;
        register ADDRESS *a;
        register ADDRESS *b;
-       bool wildflg;
 {
        /* if they don't have the same mailer, forget it */
        if (a->q_mailer != b->q_mailer)
                return (FALSE);
 
        /* if the user isn't the same, we can drop out */
 {
        /* if they don't have the same mailer, forget it */
        if (a->q_mailer != b->q_mailer)
                return (FALSE);
 
        /* if the user isn't the same, we can drop out */
-       if ((!wildflg || a->q_user[0] != '\0') && strcmp(a->q_user, b->q_user) != 0)
+       if (strcmp(a->q_user, b->q_user) != 0)
                return (FALSE);
 
        /* if the mailer ignores hosts, we have succeeded! */
                return (FALSE);
 
        /* if the mailer ignores hosts, we have succeeded! */
-       if (bitset(M_LOCAL, a->q_mailer->m_flags))
+       if (bitnset(M_LOCAL, a->q_mailer->m_flags))
                return (TRUE);
 
        /* otherwise compare hosts (but be careful for NULL ptrs) */
                return (TRUE);
 
        /* otherwise compare hosts (but be careful for NULL ptrs) */
@@ -880,45 +1027,33 @@ sameaddr(a, b, wildflg)
 **             none.
 */
 
 **             none.
 */
 
-# ifdef DEBUG
-
 printaddr(a, follow)
        register ADDRESS *a;
        bool follow;
 {
        bool first = TRUE;
 
 printaddr(a, follow)
        register ADDRESS *a;
        bool follow;
 {
        bool first = TRUE;
 
-       static int indent;
-       register int i;
-
        while (a != NULL)
        {
                first = FALSE;
        while (a != NULL)
        {
                first = FALSE;
-               for (i = indent; i > 0; i--)
-                       printf("\t");
                printf("%x=", a);
                (void) fflush(stdout);
                printf("%x=", a);
                (void) fflush(stdout);
-               printf("%s: mailer %d (%s), host `%s', user `%s'\n", a->q_paddr,
-               for (i = indent; i > 0; i--)
-                       printf("\t");
-               printf("\tnext=%x, flags=%o, rmailer %d, alias=%x, sibling=%x, child=%x\n",
-                      a->q_next, a->q_flags, a->q_rmailer, a->q_alias,
-                      a->q_sibling, a->q_child);
-               
-               /* follow the chain if appropriate */
+               printf("%s: mailer %d (%s), host `%s', user `%s', ruser `%s'\n",
+                      a->q_paddr, a->q_mailer->m_mno, a->q_mailer->m_name,
+                      a->q_host, a->q_user, a->q_ruser? a->q_ruser: "<null>");
+               printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags,
+                      a->q_alias);
+               printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home,
+                      a->q_fullname);
+
                if (!follow)
                        return;
                if (!follow)
                        return;
-               
-               indent++;
-               printaddr(a->q_child, TRUE);
-               indent--;
-               a = a->q_sibling;
+               a = a->q_next;
        }
        if (first)
                printf("[NULL]\n");
 }
 
        }
        if (first)
                printf("[NULL]\n");
 }
 
-# endif DEBUG
 \f/*
 **  REMOTENAME -- return the name relative to the current mailer
 **
 \f/*
 **  REMOTENAME -- return the name relative to the current mailer
 **
@@ -928,6 +1063,8 @@ printaddr(a, follow)
 **                     to.
 **             senderaddress -- if set, uses the sender rewriting rules
 **                     rather than the recipient rewriting rules.
 **                     to.
 **             senderaddress -- if set, uses the sender rewriting rules
 **                     rather than the recipient rewriting rules.
+**             canonical -- if set, strip out any comment information,
+**                     etc.
 **
 **     Returns:
 **             the text string representing this address relative to
 **
 **     Returns:
 **             the text string representing this address relative to
@@ -942,10 +1079,11 @@ printaddr(a, follow)
 */
 
 char *
 */
 
 char *
-remotename(name, m, senderaddress)
+remotename(name, m, senderaddress, canonical)
        char *name;
        struct mailer *m;
        bool senderaddress;
        char *name;
        struct mailer *m;
        bool senderaddress;
+       bool canonical;
 {
        register char **pvp;
        char *fancy;
 {
        register char **pvp;
        char *fancy;
@@ -953,20 +1091,26 @@ remotename(name, m, senderaddress)
        char *oldg = macvalue('g', CurEnv);
        static char buf[MAXNAME];
        char lbuf[MAXNAME];
        char *oldg = macvalue('g', CurEnv);
        static char buf[MAXNAME];
        char lbuf[MAXNAME];
+       char pvpbuf[PSBUFSIZE];
        extern char **prescan();
        extern char *crackaddr();
 
        extern char **prescan();
        extern char *crackaddr();
 
-# ifdef DEBUG
        if (tTd(12, 1))
                printf("remotename(%s)\n", name);
        if (tTd(12, 1))
                printf("remotename(%s)\n", name);
-# endif DEBUG
+
+       /* don't do anything if we are tagging it as special */
+       if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0)
+               return (name);
 
        /*
        **  Do a heuristic crack of this name to extract any comment info.
        **      This will leave the name as a comment and a $g macro.
        */
 
 
        /*
        **  Do a heuristic crack of this name to extract any comment info.
        **      This will leave the name as a comment and a $g macro.
        */
 
-       fancy = crackaddr(name);
+       if (canonical)
+               fancy = "\001g";
+       else
+               fancy = crackaddr(name);
 
        /*
        **  Turn the name into canonical form.
 
        /*
        **  Turn the name into canonical form.
@@ -976,7 +1120,7 @@ remotename(name, m, senderaddress)
        **      domain will be appended.
        */
 
        **      domain will be appended.
        */
 
-       pvp = prescan(name, '\0');
+       pvp = prescan(name, '\0', pvpbuf);
        if (pvp == NULL)
                return (name);
        rewrite(pvp, 3);
        if (pvp == NULL)
                return (name);
        rewrite(pvp, 3);
@@ -985,19 +1129,22 @@ remotename(name, m, senderaddress)
                /* append from domain to this address */
                register char **pxp = pvp;
 
                /* append from domain to this address */
                register char **pxp = pvp;
 
+               /* see if there is an "@domain" in the current name */
                while (*pxp != NULL && strcmp(*pxp, "@") != 0)
                        pxp++;
                if (*pxp == NULL)
                {
                while (*pxp != NULL && strcmp(*pxp, "@") != 0)
                        pxp++;
                if (*pxp == NULL)
                {
+                       /* no.... append the "@domain" from the sender */
                        register char **qxq = CurEnv->e_fromdomain;
 
                        register char **qxq = CurEnv->e_fromdomain;
 
-                       while (*qxq != NULL)
-                               *pxp++ = *qxq++;
+                       while ((*pxp++ = *qxq++) != NULL)
+                               continue;
+                       rewrite(pvp, 3);
                }
        }
 
        /*
                }
        }
 
        /*
-       **  Now do more specific rewriting.
+       **  Do more specific rewriting.
        **      Rewrite using ruleset 1 or 2 depending on whether this is
        **              a sender address or not.
        **      Then run it through any receiving-mailer-specific rulesets.
        **      Rewrite using ruleset 1 or 2 depending on whether this is
        **              a sender address or not.
        **      Then run it through any receiving-mailer-specific rulesets.
@@ -1016,49 +1163,25 @@ remotename(name, m, senderaddress)
                        rewrite(pvp, m->m_r_rwset);
        }
 
                        rewrite(pvp, m->m_r_rwset);
        }
 
+       /*
+       **  Do any final sanitation the address may require.
+       **      This will normally be used to turn internal forms
+       **      (e.g., user@host.LOCAL) into external form.  This
+       **      may be used as a default to the above rules.
+       */
+
+       rewrite(pvp, 4);
+
        /*
        **  Now restore the comment information we had at the beginning.
        */
 
        cataddr(pvp, lbuf, sizeof lbuf);
        /*
        **  Now restore the comment information we had at the beginning.
        */
 
        cataddr(pvp, lbuf, sizeof lbuf);
-       define('g', lbuf);
+       define('g', lbuf, CurEnv);
        expand(fancy, buf, &buf[sizeof buf - 1], CurEnv);
        expand(fancy, buf, &buf[sizeof buf - 1], CurEnv);
-       define('g', oldg);
+       define('g', oldg, CurEnv);
 
 
-# ifdef DEBUG
        if (tTd(12, 1))
                printf("remotename => `%s'\n", buf);
        if (tTd(12, 1))
                printf("remotename => `%s'\n", buf);
-# endif DEBUG
        return (buf);
 }
        return (buf);
 }
-\f/*
-**  CANONNAME -- make name canonical
-**
-**     This is used for SMTP and misc. printing.  Given a print
-**     address, it strips out comments, etc.
-**
-**     Parameters:
-**             name -- the name to make canonical.
-**
-**     Returns:
-**             pointer to canonical name.
-**
-**     Side Effects:
-**             none.
-**
-**     Warning:
-**             result is saved in static buf; future calls will trash it.
-*/
-
-char *
-canonname(name)
-       char *name;
-{
-       static char nbuf[MAXNAME];
-       register char **pvp;
-
-       pvp = prescan(name, '\0');
-       rewrite(pvp, 3);
-       cataddr(pvp, nbuf, sizeof nbuf);
-       return (nbuf);
-}