This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'.
[unix-history] / usr.sbin / sendmail / src / headers.c
index 02950f0..6679b50 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 1983 Eric P. Allman
 /*
  * Copyright (c) 1983 Eric P. Allman
- * Copyright (c) 1988 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1988, 1993
+ *     The 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
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)headers.c  5.15 (Berkeley) 6/1/90";
+static char sccsid[] = "@(#)headers.c  8.2 (Berkeley) 7/11/93";
 #endif /* not lint */
 
 #endif /* not lint */
 
-# include <sys/param.h>
 # include <errno.h>
 # include "sendmail.h"
 
 # include <errno.h>
 # include "sendmail.h"
 
@@ -48,6 +47,7 @@ static char sccsid[] = "@(#)headers.c 5.15 (Berkeley) 6/1/90";
 **     Parameters:
 **             line -- header as a text line.
 **             def -- if set, this is a default value.
 **     Parameters:
 **             line -- header as a text line.
 **             def -- if set, this is a default value.
+**             e -- the envelope including this header.
 **
 **     Returns:
 **             flags for this header.
 **
 **     Returns:
 **             flags for this header.
@@ -57,9 +57,10 @@ static char sccsid[] = "@(#)headers.c        5.15 (Berkeley) 6/1/90";
 **             Contents of 'line' are destroyed.
 */
 
 **             Contents of 'line' are destroyed.
 */
 
-chompheader(line, def)
+chompheader(line, def, e)
        char *line;
        bool def;
        char *line;
        bool def;
+       register ENVELOPE *e;
 {
        register char *p;
        register HDR *h;
 {
        register char *p;
        register HDR *h;
@@ -69,7 +70,6 @@ chompheader(line, def)
        struct hdrinfo *hi;
        bool cond = FALSE;
        BITMAP mopts;
        struct hdrinfo *hi;
        bool cond = FALSE;
        BITMAP mopts;
-       extern char *crackaddr();
 
        if (tTd(31, 6))
                printf("chompheader: %s\n", line);
 
        if (tTd(31, 6))
                printf("chompheader: %s\n", line);
@@ -80,7 +80,7 @@ chompheader(line, def)
        if (*p == '?')
        {
                /* have some */
        if (*p == '?')
        {
                /* have some */
-               register char *q = index(p + 1, *p);
+               register char *q = strchr(p + 1, *p);
                
                if (q != NULL)
                {
                
                if (q != NULL)
                {
@@ -90,23 +90,22 @@ chompheader(line, def)
                        p = q;
                }
                else
                        p = q;
                }
                else
-                       usrerr("chompheader: syntax error, line \"%s\"", line);
+                       usrerr("553 header syntax error, line \"%s\"", line);
                cond = TRUE;
        }
 
        /* find canonical name */
        fname = p;
                cond = TRUE;
        }
 
        /* find canonical name */
        fname = p;
-       p = index(p, ':');
+       p = strchr(p, ':');
        if (p == NULL)
        {
        if (p == NULL)
        {
-               syserr("chompheader: syntax error, line \"%s\"", line);
+               syserr("553 header syntax error, line \"%s\"", line);
                return (0);
        }
        fvalue = &p[1];
                return (0);
        }
        fvalue = &p[1];
-       while (isspace(*--p))
+       while (isascii(*--p) && isspace(*p))
                continue;
        *++p = '\0';
                continue;
        *++p = '\0';
-       makelower(fname);
 
        /* strip field value on front */
        if (*fvalue == ' ')
 
        /* strip field value on front */
        if (*fvalue == ' ')
@@ -115,13 +114,13 @@ chompheader(line, def)
        /* see if it is a known type */
        for (hi = HdrInfo; hi->hi_field != NULL; hi++)
        {
        /* see if it is a known type */
        for (hi = HdrInfo; hi->hi_field != NULL; hi++)
        {
-               if (strcmp(hi->hi_field, fname) == 0)
+               if (strcasecmp(hi->hi_field, fname) == 0)
                        break;
        }
 
        /* see if this is a resent message */
        if (!def && bitset(H_RESENT, hi->hi_flags))
                        break;
        }
 
        /* see if this is a resent message */
        if (!def && bitset(H_RESENT, hi->hi_flags))
-               CurEnv->e_flags |= EF_RESENT;
+               e->e_flags |= EF_RESENT;
 
        /* if this means "end of header" quit now */
        if (bitset(H_EOH, hi->hi_flags))
 
        /* if this means "end of header" quit now */
        if (bitset(H_EOH, hi->hi_flags))
@@ -129,19 +128,25 @@ chompheader(line, def)
 
        /* drop explicit From: if same as what we would generate -- for MH */
        p = "resent-from";
 
        /* drop explicit From: if same as what we would generate -- for MH */
        p = "resent-from";
-       if (!bitset(EF_RESENT, CurEnv->e_flags))
+       if (!bitset(EF_RESENT, e->e_flags))
                p += 7;
                p += 7;
-       if (!def && !QueueRun && strcmp(fname, p) == 0)
+       if (!def && !bitset(EF_QUEUERUN, e->e_flags) && strcasecmp(fname, p) == 0)
        {
        {
-               if (CurEnv->e_from.q_paddr != NULL &&
-                   strcmp(fvalue, CurEnv->e_from.q_paddr) == 0)
+               if (tTd(31, 2))
+               {
+                       printf("comparing header from (%s) against default (%s or %s)\n",
+                               fvalue, e->e_from.q_paddr, e->e_from.q_user);
+               }
+               if (e->e_from.q_paddr != NULL &&
+                   (strcmp(fvalue, e->e_from.q_paddr) == 0 ||
+                    strcmp(fvalue, e->e_from.q_user) == 0))
                        return (hi->hi_flags);
        }
 
        /* delete default value for this header */
                        return (hi->hi_flags);
        }
 
        /* delete default value for this header */
-       for (hp = &CurEnv->e_header; (h = *hp) != NULL; hp = &h->h_link)
+       for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link)
        {
        {
-               if (strcmp(fname, h->h_field) == 0 &&
+               if (strcasecmp(fname, h->h_field) == 0 &&
                    bitset(H_DEFAULT, h->h_flags) &&
                    !bitset(H_FORCE, h->h_flags))
                        h->h_value = NULL;
                    bitset(H_DEFAULT, h->h_flags) &&
                    !bitset(H_FORCE, h->h_flags))
                        h->h_value = NULL;
@@ -165,10 +170,10 @@ chompheader(line, def)
 
        /* hack to see if this is a new format message */
        if (!def && bitset(H_RCPT|H_FROM, h->h_flags) &&
 
        /* hack to see if this is a new format message */
        if (!def && bitset(H_RCPT|H_FROM, h->h_flags) &&
-           (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL ||
-            index(fvalue, '<') != NULL || index(fvalue, ';') != NULL))
+           (strchr(fvalue, ',') != NULL || strchr(fvalue, '(') != NULL ||
+            strchr(fvalue, '<') != NULL || strchr(fvalue, ';') != NULL))
        {
        {
-               CurEnv->e_flags &= ~EF_OLDSTYLE;
+               e->e_flags &= ~EF_OLDSTYLE;
        }
 
        return (h->h_flags);
        }
 
        return (h->h_flags);
@@ -180,7 +185,7 @@ chompheader(line, def)
 **
 **     Parameters:
 **             field -- the name of the header field.
 **
 **     Parameters:
 **             field -- the name of the header field.
-**             value -- the value of the field.  It must be lower-cased.
+**             value -- the value of the field.
 **             e -- the envelope to add them to.
 **
 **     Returns:
 **             e -- the envelope to add them to.
 **
 **     Returns:
@@ -202,14 +207,14 @@ addheader(field, value, e)
        /* find info struct */
        for (hi = HdrInfo; hi->hi_field != NULL; hi++)
        {
        /* find info struct */
        for (hi = HdrInfo; hi->hi_field != NULL; hi++)
        {
-               if (strcmp(field, hi->hi_field) == 0)
+               if (strcasecmp(field, hi->hi_field) == 0)
                        break;
        }
 
        /* find current place in list -- keep back pointer? */
        for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link)
        {
                        break;
        }
 
        /* find current place in list -- keep back pointer? */
        for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link)
        {
-               if (strcmp(field, h->h_field) == 0)
+               if (strcasecmp(field, h->h_field) == 0)
                        break;
        }
 
                        break;
        }
 
@@ -230,6 +235,7 @@ addheader(field, value, e)
 **
 **     Parameters:
 **             field -- the field name.
 **
 **     Parameters:
 **             field -- the field name.
+**             e -- the envelope containing the header.
 **
 **     Returns:
 **             pointer to the value part.
 **
 **     Returns:
 **             pointer to the value part.
@@ -240,14 +246,16 @@ addheader(field, value, e)
 */
 
 char *
 */
 
 char *
-hvalue(field)
+hvalue(field, e)
        char *field;
        char *field;
+       register ENVELOPE *e;
 {
        register HDR *h;
 
 {
        register HDR *h;
 
-       for (h = CurEnv->e_header; h != NULL; h = h->h_link)
+       for (h = e->e_header; h != NULL; h = h->h_link)
        {
        {
-               if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0)
+               if (!bitset(H_DEFAULT, h->h_flags) &&
+                   strcasecmp(h->h_field, field) == 0)
                        return (h->h_value);
        }
        return (NULL);
                        return (h->h_value);
        }
        return (NULL);
@@ -277,7 +285,7 @@ isheader(s)
                s++;
 
        /* following technically violates RFC822 */
                s++;
 
        /* following technically violates RFC822 */
-       while (isspace(*s))
+       while (isascii(*s) && isspace(*s))
                s++;
 
        return (*s == ':');
                s++;
 
        return (*s == ':');
@@ -287,6 +295,8 @@ isheader(s)
 **
 **     Parameters:
 **             e -- the envelope to process.
 **
 **     Parameters:
 **             e -- the envelope to process.
+**             full -- if set, do full processing (e.g., compute
+**                     message priority).
 **
 **     Returns:
 **             none.
 **
 **     Returns:
 **             none.
@@ -297,21 +307,42 @@ isheader(s)
 **             Aborts the message if the hop count is exceeded.
 */
 
 **             Aborts the message if the hop count is exceeded.
 */
 
-eatheader(e)
+eatheader(e, full)
        register ENVELOPE *e;
        register ENVELOPE *e;
+       bool full;
 {
        register HDR *h;
        register char *p;
        int hopcnt = 0;
 {
        register HDR *h;
        register char *p;
        int hopcnt = 0;
+       char *msgid;
+       char buf[MAXLINE];
+
+       /*
+       **  Set up macros for possible expansion in headers.
+       */
+
+       define('f', e->e_sender, e);
+       define('g', e->e_sender, e);
 
        if (tTd(32, 1))
                printf("----- collected header -----\n");
 
        if (tTd(32, 1))
                printf("----- collected header -----\n");
+       msgid = "<none>";
        for (h = e->e_header; h != NULL; h = h->h_link)
        {
        for (h = e->e_header; h != NULL; h = h->h_link)
        {
-               extern char *capitalize();
+               /* do early binding */
+               if (bitset(H_DEFAULT, h->h_flags) && h->h_value != NULL)
+               {
+                       expand(h->h_value, buf, &buf[sizeof buf], e);
+                       if (buf[0] != '\0')
+                       {
+                               h->h_value = newstr(buf);
+                               h->h_flags &= ~H_DEFAULT;
+                       }
+               }
 
                if (tTd(32, 1))
 
                if (tTd(32, 1))
-                       printf("%s: %s\n", capitalize(h->h_field), h->h_value);
+                       printf("%s: %s\n", h->h_field, h->h_value);
+
                /* count the number of times it has been processed */
                if (bitset(H_TRACE, h->h_flags))
                        hopcnt++;
                /* count the number of times it has been processed */
                if (bitset(H_TRACE, h->h_flags))
                        hopcnt++;
@@ -319,107 +350,110 @@ eatheader(e)
                /* send to this person if we so desire */
                if (GrabTo && bitset(H_RCPT, h->h_flags) &&
                    !bitset(H_DEFAULT, h->h_flags) &&
                /* send to this person if we so desire */
                if (GrabTo && bitset(H_RCPT, h->h_flags) &&
                    !bitset(H_DEFAULT, h->h_flags) &&
-                   (!bitset(EF_RESENT, CurEnv->e_flags) || bitset(H_RESENT, h->h_flags)))
+                   (!bitset(EF_RESENT, e->e_flags) || bitset(H_RESENT, h->h_flags)))
                {
                {
-                       sendtolist(h->h_value, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
+                       (void) sendtolist(h->h_value, (ADDRESS *) NULL,
+                                         &e->e_sendqueue, e);
                }
 
                }
 
-               /* log the message-id */
-#ifdef LOG
-               if (!QueueRun && LogLevel > 8 && h->h_value != NULL &&
-                   strcmp(h->h_field, "message-id") == 0)
+               /* save the message-id for logging */
+               if (full && h->h_value != NULL &&
+                   strcasecmp(h->h_field, "message-id") == 0)
                {
                {
-                       char buf[MAXNAME];
-
-                       p = h->h_value;
-                       if (bitset(H_DEFAULT, h->h_flags))
-                       {
-                               expand(p, buf, &buf[sizeof buf], e);
-                               p = buf;
-                       }
-                       syslog(LOG_INFO, "%s: message-id=%s", e->e_id, p);
+                       msgid = h->h_value;
+                       while (isascii(*msgid) && isspace(*msgid))
+                               msgid++;
                }
                }
-#endif LOG
+
+               /* see if this is a return-receipt header */
+               if (bitset(H_RECEIPTTO, h->h_flags))
+                       e->e_receiptto = h->h_value;
+
+               /* see if this is an errors-to header */
+               if (UseErrorsTo && bitset(H_ERRORSTO, h->h_flags))
+                       (void) sendtolist(h->h_value, (ADDRESS *) NULL,
+                                         &e->e_errorqueue, e);
        }
        if (tTd(32, 1))
                printf("----------------------------\n");
 
        }
        if (tTd(32, 1))
                printf("----------------------------\n");
 
+       /* if we are just verifying (that is, sendmail -t -bv), drop out now */
+       if (OpMode == MD_VERIFY)
+               return;
+
        /* store hop count */
        if (hopcnt > e->e_hopcount)
                e->e_hopcount = hopcnt;
 
        /* message priority */
        /* store hop count */
        if (hopcnt > e->e_hopcount)
                e->e_hopcount = hopcnt;
 
        /* message priority */
-       p = hvalue("precedence");
+       p = hvalue("precedence", e);
        if (p != NULL)
                e->e_class = priencode(p);
        if (p != NULL)
                e->e_class = priencode(p);
-       if (!QueueRun)
+       if (full)
                e->e_msgpriority = e->e_msgsize
                                 - e->e_class * WkClassFact
                                 + e->e_nrcpts * WkRecipFact;
 
                e->e_msgpriority = e->e_msgsize
                                 - e->e_class * WkClassFact
                                 + e->e_nrcpts * WkRecipFact;
 
-       /* return receipt to */
-       p = hvalue("return-receipt-to");
-       if (p != NULL)
-               e->e_receiptto = p;
-
-       /* errors to */
-       p = hvalue("errors-to");
-       if (p != NULL)
-               sendtolist(p, (ADDRESS *) NULL, &e->e_errorqueue);
-
-       /* from person */
-       if (OpMode == MD_ARPAFTP)
-       {
-               register struct hdrinfo *hi = HdrInfo;
-
-               for (p = NULL; p == NULL && hi->hi_field != NULL; hi++)
-               {
-                       if (bitset(H_FROM, hi->hi_flags))
-                               p = hvalue(hi->hi_field);
-               }
-               if (p != NULL)
-                       setsender(p);
-       }
-
        /* full name of from person */
        /* full name of from person */
-       p = hvalue("full-name");
+       p = hvalue("full-name", e);
        if (p != NULL)
                define('x', p, e);
 
        /* date message originated */
        if (p != NULL)
                define('x', p, e);
 
        /* date message originated */
-       p = hvalue("posted-date");
+       p = hvalue("posted-date", e);
        if (p == NULL)
        if (p == NULL)
-               p = hvalue("date");
+               p = hvalue("date", e);
        if (p != NULL)
        if (p != NULL)
-       {
                define('a', p, e);
                define('a', p, e);
-               /* we don't have a good way to do canonical conversion ....
-               define('d', newstr(arpatounix(p)), e);
-               .... so we will ignore the problem for the time being */
-       }
 
        /*
        **  Log collection information.
        */
 
 # ifdef LOG
 
        /*
        **  Log collection information.
        */
 
 # ifdef LOG
-       if (!QueueRun && LogLevel > 1)
+       if (full && LogLevel > 4)
        {
        {
-               char hbuf[100], *name = hbuf;
-
-               if (RealHostName == NULL)
-                       name = "local";
+               char *name;
+               register char *sbp;
+               char hbuf[MAXNAME];
+               char sbuf[MAXLINE];
+
+               if (bitset(EF_RESPONSE, e->e_flags))
+                       name = "[RESPONSE]";
+               else if ((name = macvalue('_', e)) != NULL)
+                       ;
                else if (RealHostName[0] == '[')
                        name = RealHostName;
                else
                else if (RealHostName[0] == '[')
                        name = RealHostName;
                else
-                       (void)sprintf(hbuf, "%.90s (%s)", 
-                           RealHostName, inet_ntoa(RealHostAddr.sin_addr));
-               syslog(LOG_INFO,
-                   "%s: from=%s, size=%ld, class=%d, received from %s\n",
-                   CurEnv->e_id, CurEnv->e_from.q_paddr, CurEnv->e_msgsize,
-                   CurEnv->e_class, name);
+               {
+                       name = hbuf;
+                       (void) sprintf(hbuf, "%.80s", RealHostName);
+                       if (RealHostAddr.sa.sa_family != 0)
+                       {
+                               p = &hbuf[strlen(hbuf)];
+                               (void) sprintf(p, " (%s)",
+                                       anynet_ntoa(&RealHostAddr));
+                       }
+               }
+
+               /* some versions of syslog only take 5 printf args */
+               sbp = sbuf;
+               sprintf(sbp, "from=%.200s, size=%ld, class=%d, pri=%ld, nrcpts=%d, msgid=%.100s",
+                   e->e_from.q_paddr, e->e_msgsize, e->e_class,
+                   e->e_msgpriority, e->e_nrcpts, msgid);
+               sbp += strlen(sbp);
+               if (e->e_bodytype != NULL)
+               {
+                       (void) sprintf(sbp, ", bodytype=%.20s", e->e_bodytype);
+                       sbp += strlen(sbp);
+               }
+               p = macvalue('r', e);
+               if (p != NULL)
+                       (void) sprintf(sbp, ", proto=%.20s", p);
+               syslog(LOG_INFO, "%s: %s, relay=%s",
+                   e->e_id, sbuf, name);
        }
        }
-# endif LOG
+# endif /* LOG */
 }
 \f/*
 **  PRIENCODE -- encode external priority names into internal values.
 }
 \f/*
 **  PRIENCODE -- encode external priority names into internal values.
@@ -457,16 +491,10 @@ priencode(p)
 **     identical to what it started with.  However, it does leave
 **     something semantically identical.
 **
 **     identical to what it started with.  However, it does leave
 **     something semantically identical.
 **
-**     The process is kind of strange.  There are a number of
-**     interesting cases:
-**             1.  comment <address> comment   ==> comment <$g> comment
-**             2.  address                     ==> address
-**             3.  address (comment)           ==> $g (comment)
-**             4.  (comment) address           ==> (comment) $g
-**     And then there are the hard cases....
-**             5.  add (comment) ress          ==> $g (comment)
-**             6.  comment <address (comment)> ==> comment <$g (comment)>
-**             7.    .... etc ....
+**     This algorithm has been cleaned up to handle a wider range
+**     of cases -- notably quoted and backslash escaped strings.
+**     This modification makes it substantially better at preserving
+**     the original syntax.
 **
 **     Parameters:
 **             addr -- the address to be cracked.
 **
 **     Parameters:
 **             addr -- the address to be cracked.
@@ -487,132 +515,221 @@ crackaddr(addr)
        register char *addr;
 {
        register char *p;
        register char *addr;
 {
        register char *p;
-       register int i;
-       static char buf[MAXNAME];
-       char *rhs;
-       bool gotaddr;
+       register char c;
+       int cmtlev;
+       int realcmtlev;
+       int anglelev, realanglelev;
+       int copylev;
+       bool qmode;
+       bool realqmode;
+       bool skipping;
+       bool putgmac = FALSE;
+       bool quoteit = FALSE;
        register char *bp;
        register char *bp;
+       char *buflim;
+       static char buf[MAXNAME];
 
        if (tTd(33, 1))
                printf("crackaddr(%s)\n", addr);
 
 
        if (tTd(33, 1))
                printf("crackaddr(%s)\n", addr);
 
-       (void) strcpy(buf, "");
-       rhs = NULL;
-
        /* strip leading spaces */
        /* strip leading spaces */
-       while (*addr != '\0' && isspace(*addr))
+       while (*addr != '\0' && isascii(*addr) && isspace(*addr))
                addr++;
 
        /*
                addr++;
 
        /*
-       **  See if we have anything in angle brackets.  If so, that is
-       **  the address part, and the rest is the comment.
+       **  Start by assuming we have no angle brackets.  This will be
+       **  adjusted later if we find them.
        */
 
        */
 
-       p = index(addr, '<');
-       if (p != NULL)
+       bp = buf;
+       buflim = &buf[sizeof buf - 5];
+       p = addr;
+       copylev = anglelev = realanglelev = cmtlev = realcmtlev = 0;
+       qmode = realqmode = FALSE;
+
+       while ((c = *p++) != '\0')
        {
        {
-               /* copy the beginning of the addr field to the buffer */
-               *p = '\0';
-               (void) strcpy(buf, addr);
-               (void) strcat(buf, "<");
-               *p++ = '<';
+               /*
+               **  If the buffer is overful, go into a special "skipping"
+               **  mode that tries to keep legal syntax but doesn't actually
+               **  output things.
+               */
 
 
-               /* skip spaces */
-               while (isspace(*p))
-                       p++;
+               skipping = bp >= buflim;
 
 
-               /* find the matching right angle bracket */
-               addr = p;
-               for (i = 0; *p != '\0'; p++)
+               if (copylev > 0 && !skipping)
+                       *bp++ = c;
+
+               /* check for backslash escapes */
+               if (c == '\\')
                {
                {
-                       switch (*p)
-                       {
-                         case '<':
-                               i++;
-                               break;
+                       /* arrange to quote the address */
+                       if (cmtlev <= 0 && !qmode)
+                               quoteit = TRUE;
 
 
-                         case '>':
-                               i--;
-                               break;
+                       if ((c = *p++) == '\0')
+                       {
+                               /* too far */
+                               p--;
+                               goto putg;
                        }
                        }
-                       if (i < 0)
-                               break;
+                       if (copylev > 0 && !skipping)
+                               *bp++ = c;
+                       goto putg;
                }
 
                }
 
-               /* p now points to the closing quote (or a null byte) */
-               if (*p != '\0')
+               /* check for quoted strings */
+               if (c == '"')
                {
                {
-                       /* make rhs point to the extra stuff at the end */
-                       rhs = p;
-                       *p++ = '\0';
+                       qmode = !qmode;
+                       if (copylev > 0 && !skipping)
+                               realqmode = !realqmode;
+                       continue;
                }
                }
-       }
+               if (qmode)
+                       goto putg;
 
 
-       /*
-       **  Now parse the real address part.  "addr" points to the (null
-       **  terminated) version of what we are inerested in; rhs points
-       **  to the extra stuff at the end of the line, if any.
-       */
-
-       p = addr;
+               /* check for comments */
+               if (c == '(')
+               {
+                       cmtlev++;
 
 
-       /* now strip out comments */
-       bp = &buf[strlen(buf)];
-       gotaddr = FALSE;
-       for (; *p != '\0'; p++)
-       {
-               if (*p == '(')
+                       /* allow space for closing paren */
+                       if (!skipping)
+                       {
+                               buflim--;
+                               realcmtlev++;
+                               if (copylev++ <= 0)
+                               {
+                                       *bp++ = ' ';
+                                       *bp++ = c;
+                               }
+                       }
+               }
+               if (cmtlev > 0)
                {
                {
-                       /* copy to matching close paren */
-                       *bp++ = *p++;
-                       for (i = 0; *p != '\0'; p++)
+                       if (c == ')')
                        {
                        {
-                               *bp++ = *p;
-                               switch (*p)
+                               cmtlev--;
+                               copylev--;
+                               if (!skipping)
                                {
                                {
-                                 case '(':
-                                       i++;
-                                       break;
-
-                                 case ')':
-                                       i--;
-                                       break;
+                                       realcmtlev--;
+                                       buflim++;
                                }
                                }
-                               if (i < 0)
-                                       break;
                        }
                        continue;
                }
                        }
                        continue;
                }
+               else if (c == ')')
+               {
+                       /* syntax error: unmatched ) */
+                       if (!skipping)
+                               bp--;
+               }
 
 
-               /*
-               **  If this is the first "real" character we have seen,
-               **  then we put the "$g" in the buffer now.
-               */
 
 
-               if (isspace(*p))
-                       *bp++ = *p;
-               else if (!gotaddr)
+               /* check for characters that may have to be quoted */
+               if (strchr(".'@,;:\\()", c) != NULL)
                {
                {
-                       (void) strcpy(bp, "\001g");
-                       bp += 2;
-                       gotaddr = TRUE;
+                       /*
+                       **  If these occur as the phrase part of a <>
+                       **  construct, but are not inside of () or already
+                       **  quoted, they will have to be quoted.  Note that
+                       **  now (but don't actually do the quoting).
+                       */
+
+                       if (cmtlev <= 0 && !qmode)
+                               quoteit = TRUE;
                }
                }
-       }
 
 
-       /* hack, hack.... strip trailing blanks */
-       do
-       {
-               *bp-- = '\0';
-       } while (isspace(*bp));
-       bp++;
+               /* check for angle brackets */
+               if (c == '<')
+               {
+                       register char *q;
 
 
-       /* put any right hand side back on */
-       if (rhs != NULL)
-       {
-               *rhs = '>';
-               (void) strcpy(bp, rhs);
+                       /* oops -- have to change our mind */
+                       anglelev++;
+                       if (!skipping)
+                               realanglelev++;
+
+                       bp = buf;
+                       if (quoteit)
+                       {
+                               *bp++ = '"';
+
+                               /* back up over the '<' and any spaces */
+                               --p;
+                               while (isascii(*--p) && isspace(*p))
+                                       continue;
+                               p++;
+                       }
+                       for (q = addr; q < p; )
+                       {
+                               c = *q++;
+                               if (bp < buflim)
+                               {
+                                       if (quoteit && c == '"')
+                                               *bp++ = '\\';
+                                       *bp++ = c;
+                               }
+                       }
+                       if (quoteit)
+                       {
+                               *bp++ = '"';
+                               while ((c = *p++) != '<')
+                               {
+                                       if (bp < buflim)
+                                               *bp++ = c;
+                               }
+                               *bp++ = c;
+                       }
+                       copylev = 0;
+                       putgmac = quoteit = FALSE;
+                       continue;
+               }
+
+               if (c == '>')
+               {
+                       if (anglelev > 0)
+                       {
+                               anglelev--;
+                               if (!skipping)
+                               {
+                                       realanglelev--;
+                                       buflim++;
+                               }
+                       }
+                       else if (!skipping)
+                       {
+                               /* syntax error: unmatched > */
+                               if (copylev > 0)
+                                       bp--;
+                               continue;
+                       }
+                       if (copylev++ <= 0)
+                               *bp++ = c;
+                       continue;
+               }
+
+               /* must be a real address character */
+       putg:
+               if (copylev <= 0 && !putgmac)
+               {
+                       *bp++ = MACROEXPAND;
+                       *bp++ = 'g';
+                       putgmac = TRUE;
+               }
        }
 
        }
 
+       /* repair any syntactic damage */
+       if (realqmode)
+               *bp++ = '"';
+       while (realcmtlev-- > 0)
+               *bp++ = ')';
+       while (realanglelev-- > 0)
+               *bp++ = '>';
+       *bp++ = '\0';
+
        if (tTd(33, 1))
                printf("crackaddr=>`%s'\n", buf);
 
        if (tTd(33, 1))
                printf("crackaddr=>`%s'\n", buf);
 
@@ -633,29 +750,53 @@ crackaddr(addr)
 **             none.
 */
 
 **             none.
 */
 
+/*
+ * Macro for fast max (not available in e.g. DG/UX, 386/ix).
+ */
+#ifndef MAX
+# define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
 putheader(fp, m, e)
        register FILE *fp;
        register MAILER *m;
        register ENVELOPE *e;
 {
 putheader(fp, m, e)
        register FILE *fp;
        register MAILER *m;
        register ENVELOPE *e;
 {
-       char buf[MAX(MAXFIELD,BUFSIZ)];
+       char buf[MAX(MAXLINE,BUFSIZ)];
        register HDR *h;
        register HDR *h;
-       extern char *arpadate();
-       extern char *capitalize();
-       char obuf[MAX(MAXFIELD,MAXLINE)];
+       char obuf[MAXLINE];
+
+       if (tTd(34, 1))
+               printf("--- putheader, mailer = %s ---\n", m->m_name);
 
        for (h = e->e_header; h != NULL; h = h->h_link)
        {
                register char *p;
                extern bool bitintersect();
 
 
        for (h = e->e_header; h != NULL; h = h->h_link)
        {
                register char *p;
                extern bool bitintersect();
 
+               if (tTd(34, 11))
+               {
+                       printf("  %s: ", h->h_field);
+                       xputs(h->h_value);
+               }
+
                if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
                    !bitintersect(h->h_mflags, m->m_flags))
                if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
                    !bitintersect(h->h_mflags, m->m_flags))
+               {
+                       if (tTd(34, 11))
+                               printf(" (skipped)\n");
                        continue;
                        continue;
+               }
 
                /* handle Resent-... headers specially */
                if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
 
                /* handle Resent-... headers specially */
                if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
+               {
+                       if (tTd(34, 11))
+                               printf(" (skipped (resent))\n");
                        continue;
                        continue;
+               }
+               if (tTd(34, 11))
+                       printf("\n");
 
                p = h->h_value;
                if (bitset(H_DEFAULT, h->h_flags))
 
                p = h->h_value;
                if (bitset(H_DEFAULT, h->h_flags))
@@ -674,15 +815,15 @@ putheader(fp, m, e)
 
                        if (bitset(H_FROM, h->h_flags))
                                oldstyle = FALSE;
 
                        if (bitset(H_FROM, h->h_flags))
                                oldstyle = FALSE;
-                       commaize(h, p, fp, oldstyle, m);
+                       commaize(h, p, fp, oldstyle, m, e);
                }
                else
                {
                        /* vanilla header line */
                        register char *nlp;
 
                }
                else
                {
                        /* vanilla header line */
                        register char *nlp;
 
-                       (void) sprintf(obuf, "%s: ", capitalize(h->h_field));
-                       while ((nlp = index(p, '\n')) != NULL)
+                       (void) sprintf(obuf, "%s: ", h->h_field);
+                       while ((nlp = strchr(p, '\n')) != NULL)
                        {
                                *nlp = '\0';
                                (void) strcat(obuf, p);
                        {
                                *nlp = '\0';
                                (void) strcat(obuf, p);
@@ -706,6 +847,7 @@ putheader(fp, m, e)
 **             oldstyle -- TRUE if this is an old style header.
 **             m -- a pointer to the mailer descriptor.  If NULL,
 **                     don't transform the name at all.
 **             oldstyle -- TRUE if this is an old style header.
 **             m -- a pointer to the mailer descriptor.  If NULL,
 **                     don't transform the name at all.
+**             e -- the envelope containing the message.
 **
 **     Returns:
 **             none.
 **
 **     Returns:
 **             none.
@@ -714,12 +856,13 @@ putheader(fp, m, e)
 **             outputs "p" to file "fp".
 */
 
 **             outputs "p" to file "fp".
 */
 
-commaize(h, p, fp, oldstyle, m)
+commaize(h, p, fp, oldstyle, m, e)
        register HDR *h;
        register char *p;
        FILE *fp;
        bool oldstyle;
        register MAILER *m;
        register HDR *h;
        register char *p;
        FILE *fp;
        bool oldstyle;
        register MAILER *m;
+       register ENVELOPE *e;
 {
        register char *obp;
        int opos;
 {
        register char *obp;
        int opos;
@@ -735,7 +878,7 @@ commaize(h, p, fp, oldstyle, m)
                printf("commaize(%s: %s)\n", h->h_field, p);
 
        obp = obuf;
                printf("commaize(%s: %s)\n", h->h_field, p);
 
        obp = obuf;
-       (void) sprintf(obp, "%s: ", capitalize(h->h_field));
+       (void) sprintf(obp, "%s: ", h->h_field);
        opos = strlen(h->h_field) + 2;
        obp += opos;
 
        opos = strlen(h->h_field) + 2;
        obp += opos;
 
@@ -746,9 +889,10 @@ commaize(h, p, fp, oldstyle, m)
        while (*p != '\0')
        {
                register char *name;
        while (*p != '\0')
        {
                register char *name;
+               register int c;
                char savechar;
                char savechar;
-               extern char *remotename();
-               extern char *DelimChar;         /* defined in prescan */
+               int flags;
+               auto int stat;
 
                /*
                **  Find the end of the name.  New style names
 
                /*
                **  Find the end of the name.  New style names
@@ -759,37 +903,35 @@ commaize(h, p, fp, oldstyle, m)
                */
 
                /* find end of name */
                */
 
                /* find end of name */
-               while (isspace(*p) || *p == ',')
+               while ((isascii(*p) && isspace(*p)) || *p == ',')
                        p++;
                name = p;
                for (;;)
                {
                        p++;
                name = p;
                for (;;)
                {
-                       char *oldp;
+                       auto char *oldp;
                        char pvpbuf[PSBUFSIZE];
                        char pvpbuf[PSBUFSIZE];
-                       extern bool isatword();
-                       extern char **prescan();
 
 
-                       (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf);
-                       p = DelimChar;
+                       (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf, &oldp);
+                       p = oldp;
 
                        /* look to see if we have an at sign */
 
                        /* look to see if we have an at sign */
-                       oldp = p;
-                       while (*p != '\0' && isspace(*p))
+                       while (*p != '\0' && isascii(*p) && isspace(*p))
                                p++;
 
                                p++;
 
-                       if (*p != '@' && !isatword(p))
+                       if (*p != '@')
                        {
                                p = oldp;
                                break;
                        }
                        p += *p == '@' ? 1 : 2;
                        {
                                p = oldp;
                                break;
                        }
                        p += *p == '@' ? 1 : 2;
-                       while (*p != '\0' && isspace(*p))
+                       while (*p != '\0' && isascii(*p) && isspace(*p))
                                p++;
                }
                /* at the end of one complete name */
 
                /* strip off trailing white space */
                                p++;
                }
                /* at the end of one complete name */
 
                /* strip off trailing white space */
-               while (p >= name && (isspace(*p) || *p == ',' || *p == '\0'))
+               while (p >= name &&
+                      ((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0'))
                        p--;
                if (++p == name)
                        continue;
                        p--;
                if (++p == name)
                        continue;
@@ -797,7 +939,11 @@ commaize(h, p, fp, oldstyle, m)
                *p = '\0';
 
                /* translate the name to be relative */
                *p = '\0';
 
                /* translate the name to be relative */
-               name = remotename(name, m, bitset(H_FROM, h->h_flags), FALSE);
+               flags = RF_HEADERADDR|RF_ADDDOMAIN;
+               if (bitset(H_FROM, h->h_flags))
+                       flags |= RF_SENDERADDR;
+               stat = EX_OK;
+               name = remotename(name, m, flags, &stat, e);
                if (*name == '\0')
                {
                        *p = savechar;
                if (*name == '\0')
                {
                        *p = savechar;
@@ -805,7 +951,7 @@ commaize(h, p, fp, oldstyle, m)
                }
 
                /* output the name with nice formatting */
                }
 
                /* output the name with nice formatting */
-               opos += qstrlen(name);
+               opos += strlen(name);
                if (!firstone)
                        opos += 2;
                if (opos > 78 && !firstone)
                if (!firstone)
                        opos += 2;
                if (opos > 78 && !firstone)
@@ -816,7 +962,7 @@ commaize(h, p, fp, oldstyle, m)
                        (void) sprintf(obp, "        ");
                        opos = strlen(obp);
                        obp += opos;
                        (void) sprintf(obp, "        ");
                        opos = strlen(obp);
                        obp += opos;
-                       opos += qstrlen(name);
+                       opos += strlen(name);
                }
                else if (!firstone)
                {
                }
                else if (!firstone)
                {
@@ -824,13 +970,8 @@ commaize(h, p, fp, oldstyle, m)
                        obp += 2;
                }
 
                        obp += 2;
                }
 
-               /* strip off quote bits as we output */
-               while (*name != '\0' && obp < &obuf[MAXLINE])
-               {
-                       if (bitset(0200, *name))
-                               *obp++ = '\\';
-                       *obp++ = *name++ & ~0200;
-               }
+               while ((c = *name++) != '\0' && obp < &obuf[MAXLINE])
+                       *obp++ = c;
                firstone = FALSE;
                *p = savechar;
        }
                firstone = FALSE;
                *p = savechar;
        }
@@ -838,27 +979,37 @@ commaize(h, p, fp, oldstyle, m)
        putline(obuf, fp, m);
 }
 \f/*
        putline(obuf, fp, m);
 }
 \f/*
-**  ISATWORD -- tell if the word we are pointing to is "at".
+**  COPYHEADER -- copy header list
+**
+**     This routine is the equivalent of newstr for header lists
 **
 **     Parameters:
 **
 **     Parameters:
-**             p -- word to check.
+**             header -- list of header structures to copy.
 **
 **     Returns:
 **
 **     Returns:
-**             TRUE -- if p is the word at.
-**             FALSE -- otherwise.
+**             a copy of 'header'.
 **
 **     Side Effects:
 **             none.
 */
 
 **
 **     Side Effects:
 **             none.
 */
 
-bool
-isatword(p)
-       register char *p;
+HDR *
+copyheader(header)
+       register HDR *header;
 {
 {
-       extern char lower();
+       register HDR *newhdr;
+       HDR *ret;
+       register HDR **tail = &ret;
 
 
-       if (lower(p[0]) == 'a' && lower(p[1]) == 't' &&
-           p[2] != '\0' && isspace(p[2]))
-               return (TRUE);
-       return (FALSE);
+       while (header != NULL)
+       {
+               newhdr = (HDR *) xalloc(sizeof(HDR));
+               STRUCTCOPY(*header, *newhdr);
+               *tail = newhdr;
+               tail = &newhdr->h_link;
+               header = header->h_link;
+       }
+       *tail = NULL;
+       
+       return ret;
 }
 }