change call to expand() to be more rational (and consistent!)
[unix-history] / usr / src / usr.sbin / sendmail / src / queue.c
index 165bbcd..0480576 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.
  *
  * %sccs.include.redist.c%
  */
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
 #ifdef QUEUE
 
 #ifndef lint
 #ifdef QUEUE
-static char sccsid[] = "@(#)queue.c    6.40 (Berkeley) %G% (with queueing)";
+static char sccsid[] = "@(#)queue.c    8.68 (Berkeley) %G% (with queueing)";
 #else
 #else
-static char sccsid[] = "@(#)queue.c    6.40 (Berkeley) %G% (without queueing)";
+static char sccsid[] = "@(#)queue.c    8.68 (Berkeley) %G% (without queueing)";
 #endif
 #endif /* not lint */
 
 #endif
 #endif /* not lint */
 
-# include <sys/stat.h>
-# include <sys/dir.h>
-# include <signal.h>
 # include <errno.h>
 # include <pwd.h>
 # include <errno.h>
 # include <pwd.h>
-# ifndef MAXNAMLEN
 # include <dirent.h>
 # include <dirent.h>
-# endif
 
 # ifdef QUEUE
 
 
 # ifdef QUEUE
 
@@ -34,6 +29,8 @@ static char sccsid[] = "@(#)queue.c   6.40 (Berkeley) %G% (without queueing)";
 struct work
 {
        char            *w_name;        /* name of control file */
 struct work
 {
        char            *w_name;        /* name of control file */
+       char            *w_host;        /* name of recipient host */
+       bool            w_lock;         /* is message locked? */
        long            w_pri;          /* priority of message, see below */
        time_t          w_ctime;        /* creation time of message */
        struct work     *w_next;        /* next in queue */
        long            w_pri;          /* priority of message, see below */
        time_t          w_ctime;        /* creation time of message */
        struct work     *w_next;        /* next in queue */
@@ -42,6 +39,8 @@ struct work
 typedef struct work    WORK;
 
 WORK   *WorkQ;                 /* queue of things to be done */
 typedef struct work    WORK;
 
 WORK   *WorkQ;                 /* queue of things to be done */
+
+#define QF_VERSION     1       /* version number of this queue format */
 \f/*
 **  QUEUEUP -- queue a message up for future transmission.
 **
 \f/*
 **  QUEUEUP -- queue a message up for future transmission.
 **
@@ -73,54 +72,84 @@ queueup(e, queueall, announce)
        bool newid;
        register char *p;
        MAILER nullmailer;
        bool newid;
        register char *p;
        MAILER nullmailer;
-       ADDRESS *lastctladdr;
+       MCI mcibuf;
        char buf[MAXLINE], tf[MAXLINE];
        char buf[MAXLINE], tf[MAXLINE];
-       extern char *macvalue();
-       extern ADDRESS *getctladdr();
 
        /*
        **  Create control file.
        */
 
 
        /*
        **  Create control file.
        */
 
-       newid = (e->e_id == NULL);
+       newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags);
+
+       /* if newid, queuename will create a locked qf file in e->lockfp */
        strcpy(tf, queuename(e, 't'));
        tfp = e->e_lockfp;
        if (tfp == NULL)
                newid = FALSE;
        strcpy(tf, queuename(e, 't'));
        tfp = e->e_lockfp;
        if (tfp == NULL)
                newid = FALSE;
-       if (newid)
-       {
-               tfp = e->e_lockfp;
-       }
-       else
+
+       /* if newid, just write the qf file directly (instead of tf file) */
+       if (!newid)
        {
                /* get a locked tf file */
        {
                /* get a locked tf file */
-               for (i = 100; --i >= 0; )
+               for (i = 0; i < 128; i++)
                {
                {
-                       extern bool lockfile();
-
                        fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode);
                        if (fd < 0)
                        {
                        fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode);
                        if (fd < 0)
                        {
-                               if (errno == EEXIST)
-                                       continue;
-notemp:
-                               syserr("!queueup: cannot create temp file %s", tf);
+                               if (errno != EEXIST)
+                                       break;
+#ifdef LOG
+                               if (LogLevel > 0 && (i % 32) == 0)
+                                       syslog(LOG_ALERT, "queueup: cannot create %s, uid=%d: %s",
+                                               tf, geteuid(), errstring(errno));
+#endif
+                       }
+                       else
+                       {
+                               if (lockfile(fd, tf, NULL, LOCK_EX|LOCK_NB))
+                                       break;
+#ifdef LOG
+                               else if (LogLevel > 0 && (i % 32) == 0)
+                                       syslog(LOG_ALERT, "queueup: cannot lock %s: %s",
+                                               tf, errstring(errno));
+#endif
+                               close(fd);
                        }
 
                        }
 
-                       if (lockfile(fd, tf, LOCK_EX|LOCK_NB))
-                               break;
-
-                       close(fd);
-                       sleep(i);
+                       if ((i % 32) == 31)
+                       {
+                               /* save the old temp file away */
+                               (void) rename(tf, queuename(e, 'T'));
+                       }
+                       else
+                               sleep(i % 32);
+               }
+               if (fd < 0 || (tfp = fdopen(fd, "w")) == NULL)
+               {
+                       printopenfds(TRUE);
+                       syserr("!queueup: cannot create queue temp file %s, uid=%d",
+                               tf, geteuid());
                }
                }
-               if (fd < 0)
-                       goto notemp;
-
-               tfp = fdopen(fd, "w");
        }
 
        if (tTd(40, 1))
        }
 
        if (tTd(40, 1))
-               printf("queueing %s\n", e->e_id);
+               printf("\n>>>>> queueing %s%s queueall=%d >>>>>\n", e->e_id,
+                       newid ? " (new id)" : "", queueall);
+       if (tTd(40, 32))
+       {
+               printf("  sendq=");
+               printaddr(e->e_sendqueue, TRUE);
+       }
+       if (tTd(40, 9))
+       {
+               printf("  tfp=");
+               dumpfd(fileno(tfp), TRUE, FALSE);
+               printf("  lockfp=");
+               if (e->e_lockfp == NULL)
+                       printf("NULL\n");
+               else
+                       dumpfd(fileno(e->e_lockfp), TRUE, FALSE);
+       }
 
        /*
        **  If there is no data file yet, create one.
 
        /*
        **  If there is no data file yet, create one.
@@ -129,14 +158,26 @@ notemp:
        if (e->e_df == NULL)
        {
                register FILE *dfp;
        if (e->e_df == NULL)
        {
                register FILE *dfp;
+               struct stat stbuf;
                extern putbody();
 
                extern putbody();
 
-               e->e_df = newstr(queuename(e, 'd'));
-               fd = open(e->e_df, O_WRONLY|O_CREAT, FileMode);
-               if (fd < 0)
-                       syserr("!queueup: cannot create %s", e->e_df);
-               dfp = fdopen(fd, "w");
-               (*e->e_putbody)(dfp, ProgMailer, e);
+               e->e_df = queuename(e, 'd');
+               e->e_df = newstr(e->e_df);
+               fd = open(e->e_df, O_WRONLY|O_CREAT|O_TRUNC, FileMode);
+               if (fd < 0 || (dfp = fdopen(fd, "w")) == NULL)
+                       syserr("!queueup: cannot create data temp file %s, uid=%d",
+                               e->e_df, geteuid());
+               if (fstat(fd, &stbuf) < 0)
+                       e->e_dfino = -1;
+               else
+               {
+                       e->e_dfdev = stbuf.st_dev;
+                       e->e_dfino = stbuf.st_ino;
+               }
+               bzero(&mcibuf, sizeof mcibuf);
+               mcibuf.mci_out = dfp;
+               mcibuf.mci_mailer = FileMailer;
+               (*e->e_putbody)(&mcibuf, e, NULL);
                (void) xfclose(dfp, "queueup dfp", e->e_id);
                e->e_putbody = putbody;
        }
                (void) xfclose(dfp, "queueup dfp", e->e_id);
                e->e_putbody = putbody;
        }
@@ -147,18 +188,35 @@ notemp:
        **      they are required by orderq.
        */
 
        **      they are required by orderq.
        */
 
+       /* output queue version number (must be first!) */
+       fprintf(tfp, "V%d\n", QF_VERSION);
+
        /* output message priority */
        fprintf(tfp, "P%ld\n", e->e_msgpriority);
 
        /* output creation time */
        fprintf(tfp, "T%ld\n", e->e_ctime);
 
        /* output message priority */
        fprintf(tfp, "P%ld\n", e->e_msgpriority);
 
        /* output creation time */
        fprintf(tfp, "T%ld\n", e->e_ctime);
 
-       /* output name of data file */
+       /* output last delivery time */
+       fprintf(tfp, "K%ld\n", e->e_dtime);
+
+       /* output number of delivery attempts */
+       fprintf(tfp, "N%d\n", e->e_ntries);
+
+       /* output inode number of data file */
+       /* XXX should probably include device major/minor too */
+       if (e->e_dfino != -1)
+               fprintf(tfp, "I%d/%d/%ld\n",
+                       major(e->e_dfdev), minor(e->e_dfdev), e->e_dfino);
+
+       /* output type and name of data file */
+       if (e->e_bodytype != NULL)
+               fprintf(tfp, "B%s\n", e->e_bodytype);
        fprintf(tfp, "D%s\n", e->e_df);
 
        /* message from envelope, if it exists */
        if (e->e_message != NULL)
        fprintf(tfp, "D%s\n", e->e_df);
 
        /* message from envelope, if it exists */
        if (e->e_message != NULL)
-               fprintf(tfp, "M%s\n", e->e_message);
+               fprintf(tfp, "M%s\n", denlstring(e->e_message, TRUE, FALSE));
 
        /* send various flag bits through */
        p = buf;
 
        /* send various flag bits through */
        p = buf;
@@ -166,34 +224,34 @@ notemp:
                *p++ = 'w';
        if (bitset(EF_RESPONSE, e->e_flags))
                *p++ = 'r';
                *p++ = 'w';
        if (bitset(EF_RESPONSE, e->e_flags))
                *p++ = 'r';
+       if (bitset(EF_HAS8BIT, e->e_flags))
+               *p++ = '8';
        *p++ = '\0';
        if (buf[0] != '\0')
                fprintf(tfp, "F%s\n", buf);
 
        *p++ = '\0';
        if (buf[0] != '\0')
                fprintf(tfp, "F%s\n", buf);
 
-       /* $r and $s macro values */
+       /* $r and $s and $_ macro values */
        if ((p = macvalue('r', e)) != NULL)
        if ((p = macvalue('r', e)) != NULL)
-               fprintf(tfp, "$r%s\n", p);
+               fprintf(tfp, "$r%s\n", denlstring(p, TRUE, FALSE));
        if ((p = macvalue('s', e)) != NULL)
        if ((p = macvalue('s', e)) != NULL)
-               fprintf(tfp, "$s%s\n", p);
+               fprintf(tfp, "$s%s\n", denlstring(p, TRUE, FALSE));
+       if ((p = macvalue('_', e)) != NULL)
+               fprintf(tfp, "$_%s\n", denlstring(p, TRUE, FALSE));
 
        /* output name of sender */
 
        /* output name of sender */
-       fprintf(tfp, "S%s\n", e->e_from.q_paddr);
+       fprintf(tfp, "S%s\n", denlstring(p, TRUE, FALSE));
+
+       /* output ESMTP-supplied "original" information */
+       if (e->e_envid != NULL)
 
        /* output list of error recipients */
 
        /* output list of error recipients */
-       lastctladdr = NULL;
+       printctladdr(NULL, NULL);
        for (q = e->e_errorqueue; q != NULL; q = q->q_next)
        {
                if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
                {
        for (q = e->e_errorqueue; q != NULL; q = q->q_next)
        {
                if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
                {
-                       ADDRESS *ctladdr;
-
-                       ctladdr = getctladdr(q);
-                       if (ctladdr != lastctladdr)
-                       {
-                               printctladdr(ctladdr, tfp);
-                               lastctladdr = ctladdr;
-                       }
-                       fprintf(tfp, "E%s\n", q->q_paddr);
+                       printctladdr(q, tfp);
+                       fprintf(tfp, "E%s\n", denlstring(q->q_paddr, TRUE, FALSE));
                }
        }
 
                }
        }
 
@@ -203,21 +261,16 @@ notemp:
                if (bitset(QQUEUEUP, q->q_flags) ||
                    (queueall && !bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags)))
                {
                if (bitset(QQUEUEUP, q->q_flags) ||
                    (queueall && !bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags)))
                {
-                       ADDRESS *ctladdr;
-
-                       ctladdr = getctladdr(q);
-                       if (ctladdr != lastctladdr)
-                       {
-                               printctladdr(ctladdr, tfp);
-                               lastctladdr = ctladdr;
-                       }
-                       fprintf(tfp, "R%s\n", q->q_paddr);
+                       printctladdr(q, tfp);
+                       if (q->q_orcpt != NULL)
+                       fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE));
                        if (announce)
                        {
                                e->e_to = q->q_paddr;
                                message("queued");
                                if (LogLevel > 8)
                        if (announce)
                        {
                                e->e_to = q->q_paddr;
                                message("queued");
                                if (LogLevel > 8)
-                                       logdelivery(NULL, NULL, "queued", e);
+                                       logdelivery(NULL, NULL, "queued",
+                                                   NULL, (time_t) 0, e);
                                e->e_to = NULL;
                        }
                        if (tTd(40, 1))
                                e->e_to = NULL;
                        }
                        if (tTd(40, 1))
@@ -242,6 +295,9 @@ notemp:
        nullmailer.m_re_rwset = nullmailer.m_rh_rwset =
                        nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1;
        nullmailer.m_eol = "\n";
        nullmailer.m_re_rwset = nullmailer.m_rh_rwset =
                        nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1;
        nullmailer.m_eol = "\n";
+       bzero(&mcibuf, sizeof mcibuf);
+       mcibuf.mci_mailer = &nullmailer;
+       mcibuf.mci_out = tfp;
 
        define('g', "\201f", e);
        for (h = e->e_header; h != NULL; h = h->h_link)
 
        define('g', "\201f", e);
        for (h = e->e_header; h != NULL; h = h->h_link)
@@ -256,6 +312,14 @@ notemp:
                if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
                        continue;
 
                if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
                        continue;
 
+               /* expand macros; if null, don't output header at all */
+               if (bitset(H_DEFAULT, h->h_flags))
+               {
+                       (void) expand(h->h_value, buf, sizeof buf, e);
+                       if (buf[0] == '\0')
+                               continue;
+               }
+
                /* output this header */
                fprintf(tfp, "H");
 
                /* output this header */
                fprintf(tfp, "H");
 
@@ -274,18 +338,21 @@ notemp:
                /* output the header: expand macros, convert addresses */
                if (bitset(H_DEFAULT, h->h_flags))
                {
                /* output the header: expand macros, convert addresses */
                if (bitset(H_DEFAULT, h->h_flags))
                {
-                       (void) expand(h->h_value, buf, &buf[sizeof buf], e);
                        fprintf(tfp, "%s: %s\n", h->h_field, buf);
                }
                else if (bitset(H_FROM|H_RCPT, h->h_flags))
                {
                        bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
                        fprintf(tfp, "%s: %s\n", h->h_field, buf);
                }
                else if (bitset(H_FROM|H_RCPT, h->h_flags))
                {
                        bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
+                       FILE *savetrace = TrafficLogFile;
+
+                       TrafficLogFile = NULL;
 
                        if (bitset(H_FROM, h->h_flags))
                                oldstyle = FALSE;
 
 
                        if (bitset(H_FROM, h->h_flags))
                                oldstyle = FALSE;
 
-                       commaize(h, h->h_value, tfp, oldstyle,
-                                &nullmailer, e);
+                       commaize(h, h->h_value, oldstyle, &mcibuf, e);
+
+                       TrafficLogFile = savetrace;
                }
                else
                        fprintf(tfp, "%s: %s\n", h->h_field, h->h_value);
                }
                else
                        fprintf(tfp, "%s: %s\n", h->h_field, h->h_value);
@@ -295,8 +362,7 @@ notemp:
        **  Clean up.
        */
 
        **  Clean up.
        */
 
-       fflush(tfp);
-       if (ferror(tfp))
+       if (fflush(tfp) < 0 || fsync(fileno(tfp)) < 0 || ferror(tfp))
        {
                if (newid)
                        syserr("!552 Error writing control file %s", tf);
        {
                if (newid)
                        syserr("!552 Error writing control file %s", tf);
@@ -306,9 +372,13 @@ notemp:
 
        if (!newid)
        {
 
        if (!newid)
        {
+               /* rename (locked) tf to be (locked) qf */
                qf = queuename(e, 'q');
                if (rename(tf, qf) < 0)
                qf = queuename(e, 'q');
                if (rename(tf, qf) < 0)
-                       syserr("cannot rename(%s, %s), df=%s", tf, qf, e->e_df);
+                       syserr("cannot rename(%s, %s), df=%s, uid=%d",
+                               tf, qf, e->e_df, geteuid());
+
+               /* close and unlock old (locked) qf */
                if (e->e_lockfp != NULL)
                        (void) xfclose(e->e_lockfp, "queueup lockfp", e->e_id);
                e->e_lockfp = tfp;
                if (e->e_lockfp != NULL)
                        (void) xfclose(e->e_lockfp, "queueup lockfp", e->e_id);
                e->e_lockfp = tfp;
@@ -316,36 +386,62 @@ notemp:
        else
                qf = tf;
        errno = 0;
        else
                qf = tf;
        errno = 0;
+       e->e_flags |= EF_INQUEUE;
 
 # ifdef LOG
        /* save log info */
        if (LogLevel > 79)
                syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);
 # endif /* LOG */
 
 # ifdef LOG
        /* save log info */
        if (LogLevel > 79)
                syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);
 # endif /* LOG */
-       fflush(tfp);
+
+       if (tTd(40, 1))
+               printf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
        return;
 }
 
 printctladdr(a, tfp)
        return;
 }
 
 printctladdr(a, tfp)
-       ADDRESS *a;
+       register ADDRESS *a;
        FILE *tfp;
 {
        FILE *tfp;
 {
-       char *u;
-       struct passwd *pw;
-       extern struct passwd *getpwuid();
+       char *uname;
+       register struct passwd *pw;
+       register ADDRESS *q;
+       uid_t uid;
+       static ADDRESS *lastctladdr;
+       static uid_t lastuid;
 
 
-       if (a == NULL)
+       /* initialization */
+       if (a == NULL || a->q_alias == NULL || tfp == NULL)
        {
        {
-               fprintf(tfp, "C\n");
+               if (lastctladdr != NULL && tfp != NULL)
+                       fprintf(tfp, "C\n");
+               lastctladdr = NULL;
+               lastuid = 0;
                return;
        }
                return;
        }
-       if (a->q_uid == 0 || (pw = getpwuid(a->q_uid)) == NULL)
-               u = DefUser;
+
+       /* find the active uid */
+       q = getctladdr(a);
+       if (q == NULL)
+               uid = 0;
        else
        else
-               u = pw->pw_name;
-       fprintf(tfp, "C%s\n", u);
-}
+               uid = q->q_uid;
+       a = a->q_alias;
+
+       /* check to see if this is the same as last time */
+       if (lastctladdr != NULL && uid == lastuid &&
+           strcmp(lastctladdr->q_paddr, a->q_paddr) == 0)
+               return;
+       lastuid = uid;
+       lastctladdr = a;
 
 
+       if (uid == 0 || (pw = getpwuid(uid)) == NULL)
+               uname = "";
+       else
+               uname = pw->pw_name;
+
+       fprintf(tfp, "C%s:%s\n", uname, denlstring(a->q_paddr, TRUE, FALSE));
+}
 \f/*
 **  RUNQUEUE -- run the jobs in the queue.
 **
 \f/*
 **  RUNQUEUE -- run the jobs in the queue.
 **
@@ -366,13 +462,14 @@ printctladdr(a, tfp)
 
 ENVELOPE       QueueEnvelope;          /* the queue run envelope */
 
 
 ENVELOPE       QueueEnvelope;          /* the queue run envelope */
 
+void
 runqueue(forkflag)
        bool forkflag;
 {
 runqueue(forkflag)
        bool forkflag;
 {
-       extern bool shouldqueue();
        register ENVELOPE *e;
        register ENVELOPE *e;
+       int njobs;
+       int sequenceno = 0;
        extern ENVELOPE BlankEnvelope;
        extern ENVELOPE BlankEnvelope;
-       extern ENVELOPE *newenvelope();
 
        /*
        **  If no work will ever be selected, don't even bother reading
 
        /*
        **  If no work will ever be selected, don't even bother reading
@@ -397,17 +494,18 @@ runqueue(forkflag)
        if (forkflag)
        {
                int pid;
        if (forkflag)
        {
                int pid;
+#ifdef SIGCHLD
+               extern void reapchild();
+
+               (void) setsignal(SIGCHLD, reapchild);
+#endif
 
                pid = dofork();
                if (pid != 0)
                {
 
                pid = dofork();
                if (pid != 0)
                {
-                       extern void reapchild();
-
                        /* parent -- pick up intermediate zombie */
 #ifndef SIGCHLD
                        (void) waitfor(pid);
                        /* parent -- pick up intermediate zombie */
 #ifndef SIGCHLD
                        (void) waitfor(pid);
-#else /* SIGCHLD */
-                       (void) signal(SIGCHLD, reapchild);
 #endif /* SIGCHLD */
                        if (QueueIntvl != 0)
                                (void) setevent(QueueIntvl, runqueue, TRUE);
 #endif /* SIGCHLD */
                        if (QueueIntvl != 0)
                                (void) setevent(QueueIntvl, runqueue, TRUE);
@@ -418,7 +516,7 @@ runqueue(forkflag)
                if (fork() != 0)
                        exit(EX_OK);
 #else /* SIGCHLD */
                if (fork() != 0)
                        exit(EX_OK);
 #else /* SIGCHLD */
-               (void) signal(SIGCHLD, SIG_DFL);
+               (void) setsignal(SIGCHLD, SIG_DFL);
 #endif /* SIGCHLD */
        }
 
 #endif /* SIGCHLD */
        }
 
@@ -438,6 +536,9 @@ runqueue(forkflag)
        clrdaemon();
 # endif /* DAEMON */
 
        clrdaemon();
 # endif /* DAEMON */
 
+       /* force it to run expensive jobs */
+       NoConnect = FALSE;
+
        /*
        **  Create ourselves an envelope
        */
        /*
        **  Create ourselves an envelope
        */
@@ -450,7 +551,7 @@ runqueue(forkflag)
        **  Make sure the alias database is open.
        */
 
        **  Make sure the alias database is open.
        */
 
-       initaliases(AliasFile, FALSE, e);
+       initmaps(FALSE, e);
 
        /*
        **  Start making passes through the queue.
 
        /*
        **  Start making passes through the queue.
@@ -460,13 +561,12 @@ runqueue(forkflag)
        */
 
        /* order the existing work requests */
        */
 
        /* order the existing work requests */
-       (void) orderq(FALSE);
+       njobs = orderq(FALSE);
 
        /* process them once at a time */
        while (WorkQ != NULL)
        {
                WORK *w = WorkQ;
 
        /* process them once at a time */
        while (WorkQ != NULL)
        {
                WORK *w = WorkQ;
-               extern bool shouldqueue();
 
                WorkQ = WorkQ->w_next;
 
 
                WorkQ = WorkQ->w_next;
 
@@ -474,15 +574,29 @@ runqueue(forkflag)
                **  Ignore jobs that are too expensive for the moment.
                */
 
                **  Ignore jobs that are too expensive for the moment.
                */
 
+               sequenceno++;
                if (shouldqueue(w->w_pri, w->w_ctime))
                {
                        if (Verbose)
                if (shouldqueue(w->w_pri, w->w_ctime))
                {
                        if (Verbose)
-                               printf("\nSkipping %s\n", w->w_name + 2);
-                       return;
+                               printf("\nSkipping %s (sequence %d of %d)\n",
+                                       w->w_name + 2, sequenceno, njobs);
                }
                }
+               else
+               {
+                       pid_t pid;
+                       extern pid_t dowork();
 
 
-               dowork(w->w_name + 2, ForkQueueRuns, e);
+                       if (Verbose)
+                               printf("\nRunning %s (sequence %d of %d)\n",
+                                       w->w_name + 2, sequenceno, njobs);
+                       pid = dowork(w->w_name + 2, ForkQueueRuns, FALSE, e);
+                       errno = 0;
+                       if (pid != 0)
+                               (void) waitfor(pid);
+               }
                free(w->w_name);
                free(w->w_name);
+               if (w->w_host)
+                       free(w->w_host);
                free((char *) w);
        }
 
                free((char *) w);
        }
 
@@ -524,13 +638,13 @@ static struct dir dbuf;
 orderq(doall)
        bool doall;
 {
 orderq(doall)
        bool doall;
 {
-       register struct direct *d;
+       register struct dirent *d;
        register WORK *w;
        DIR *f;
        register int i;
        WORK wlist[QUEUESIZE+1];
        int wn = -1;
        register WORK *w;
        DIR *f;
        register int i;
        WORK wlist[QUEUESIZE+1];
        int wn = -1;
-       extern workcmpf();
+       int wc;
 
        if (tTd(41, 1))
        {
 
        if (tTd(41, 1))
        {
@@ -550,6 +664,8 @@ orderq(doall)
 
                WorkQ = nw;
                free(w->w_name);
 
                WorkQ = nw;
                free(w->w_name);
+               if (w->w_host)
+                       free(w->w_host);
                free((char *) w);
                w = nw;
        }
                free((char *) w);
                w = nw;
        }
@@ -569,8 +685,8 @@ orderq(doall)
        while ((d = readdir(f)) != NULL)
        {
                FILE *cf;
        while ((d = readdir(f)) != NULL)
        {
                FILE *cf;
-               char lbuf[MAXNAME];
-               extern bool shouldqueue();
+               register char *p;
+               char lbuf[MAXNAME + 1];
                extern bool strcontainedin();
 
                /* is this an interesting entry? */
                extern bool strcontainedin();
 
                /* is this an interesting entry? */
@@ -592,18 +708,26 @@ orderq(doall)
                **  both old and new type ids.
                */
 
                **  both old and new type ids.
                */
 
-               i = strlen(d->d_name);
-               if (i != 9 && i != 10)
+               p = d->d_name + 2;
+               if (isupper(p[0]) && isupper(p[2]))
+                       p += 3;
+               else if (isupper(p[1]))
+                       p += 2;
+               else
+                       p = d->d_name;
+               for (i = 0; isdigit(*p); p++)
+                       i++;
+               if (i < 5 || *p != '\0')
                {
                        if (Verbose)
                                printf("orderq: bogus qf name %s\n", d->d_name);
 #ifdef LOG
                        if (LogLevel > 3)
                {
                        if (Verbose)
                                printf("orderq: bogus qf name %s\n", d->d_name);
 #ifdef LOG
                        if (LogLevel > 3)
-                               syslog(LOG_NOTICE, "orderq: bogus qf name %s",
+                               syslog(LOG_CRIT, "orderq: bogus qf name %s",
                                        d->d_name);
 #endif
                                        d->d_name);
 #endif
-                       if (strlen(d->d_name) >= MAXNAME)
-                               d->d_name[MAXNAME - 1] = '\0';
+                       if (strlen(d->d_name) > MAXNAME)
+                               d->d_name[MAXNAME] = '\0';
                        strcpy(lbuf, d->d_name);
                        lbuf[0] = 'Q';
                        (void) rename(d->d_name, lbuf);
                        strcpy(lbuf, d->d_name);
                        lbuf[0] = 'Q';
                        (void) rename(d->d_name, lbuf);
@@ -628,6 +752,8 @@ orderq(doall)
                }
                w = &wlist[wn];
                w->w_name = newstr(d->d_name);
                }
                w = &wlist[wn];
                w->w_name = newstr(d->d_name);
+               w->w_host = NULL;
+               w->w_lock = !lockfile(fileno(cf), w->w_name, NULL, LOCK_SH|LOCK_NB);
 
                /* make sure jobs in creation don't clog queue */
                w->w_pri = 0x7fffffff;
 
                /* make sure jobs in creation don't clog queue */
                w->w_pri = 0x7fffffff;
@@ -637,7 +763,7 @@ orderq(doall)
                i = NEED_P | NEED_T;
                if (QueueLimitSender != NULL)
                        i |= NEED_S;
                i = NEED_P | NEED_T;
                if (QueueLimitSender != NULL)
                        i |= NEED_S;
-               if (QueueLimitRecipient != NULL)
+               if (QueueSortOrder == QS_BYHOST || QueueLimitRecipient != NULL)
                        i |= NEED_R;
                while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL)
                {
                        i |= NEED_R;
                while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL)
                {
@@ -657,7 +783,10 @@ orderq(doall)
                                break;
 
                          case 'R':
                                break;
 
                          case 'R':
-                               if (QueueLimitRecipient != NULL &&
+                               if (w->w_host == NULL &&
+                                   (p = strrchr(&lbuf[1], '@')) != NULL)
+                                       w->w_host = newstr(&p[1]);
+                               if (QueueLimitRecipient == NULL ||
                                    strcontainedin(QueueLimitRecipient, &lbuf[1]))
                                        i &= ~NEED_R;
                                break;
                                    strcontainedin(QueueLimitRecipient, &lbuf[1]))
                                        i &= ~NEED_R;
                                break;
@@ -675,17 +804,74 @@ orderq(doall)
                    bitset(NEED_R|NEED_S, i))
                {
                        /* don't even bother sorting this job in */
                    bitset(NEED_R|NEED_S, i))
                {
                        /* don't even bother sorting this job in */
+                       free(w->w_name);
+                       if (w->w_host)
+                               free(w->w_host);
                        wn--;
                }
        }
        (void) closedir(f);
        wn++;
 
                        wn--;
                }
        }
        (void) closedir(f);
        wn++;
 
-       /*
-       **  Sort the work directory.
-       */
+       wc = min(wn, QUEUESIZE);
+
+       if (QueueSortOrder == QS_BYHOST)
+       {
+               extern workcmpf1();
+               extern workcmpf2();
+
+               /*
+               **  Sort the work directory for the first time,
+               **  based on host name, lock status, and priority.
+               */
+
+               qsort((char *) wlist, wc, sizeof *wlist, workcmpf1);
+
+               /*
+               **  If one message to host is locked, "lock" all messages
+               **  to that host.
+               */
+
+               i = 0;
+               while (i < wc)
+               {
+                       if (!wlist[i].w_lock)
+                       {
+                               i++;
+                               continue;
+                       }
+                       w = &wlist[i];
+                       while (++i < wc)
+                       {
+                               if (wlist[i].w_host == NULL &&
+                                   w->w_host == NULL)
+                                       wlist[i].w_lock = TRUE;
+                               else if (wlist[i].w_host != NULL &&
+                                        w->w_host != NULL &&
+                                        strcmp(wlist[i].w_host, w->w_host) == 0)
+                                       wlist[i].w_lock = TRUE;
+                               else
+                                       break;
+                       }
+               }
 
 
-       qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf);
+               /*
+               **  Sort the work directory for the second time,
+               **  based on lock status, host name, and priority.
+               */
+
+               qsort((char *) wlist, wc, sizeof *wlist, workcmpf2);
+       }
+       else
+       {
+               extern workcmpf0();
+
+               /*
+               **  Simple sort based on queue priority only.
+               */
+
+               qsort((char *) wlist, wc, sizeof *wlist, workcmpf0);
+       }
 
        /*
        **  Convert the work list into canonical form.
 
        /*
        **  Convert the work list into canonical form.
@@ -693,10 +879,12 @@ orderq(doall)
        */
 
        WorkQ = NULL;
        */
 
        WorkQ = NULL;
-       for (i = min(wn, QUEUESIZE); --i >= 0; )
+       for (i = wc; --i >= 0; )
        {
                w = (WORK *) xalloc(sizeof *w);
                w->w_name = wlist[i].w_name;
        {
                w = (WORK *) xalloc(sizeof *w);
                w->w_name = wlist[i].w_name;
+               w->w_host = wlist[i].w_host;
+               w->w_lock = wlist[i].w_lock;
                w->w_pri = wlist[i].w_pri;
                w->w_ctime = wlist[i].w_ctime;
                w->w_next = WorkQ;
                w->w_pri = wlist[i].w_pri;
                w->w_ctime = wlist[i].w_ctime;
                w->w_next = WorkQ;
@@ -712,7 +900,7 @@ orderq(doall)
        return (wn);
 }
 \f/*
        return (wn);
 }
 \f/*
-**  WORKCMPF -- compare function for ordering work.
+**  WORKCMPF0 -- simple priority-only compare function.
 **
 **     Parameters:
 **             a -- the first argument.
 **
 **     Parameters:
 **             a -- the first argument.
@@ -727,7 +915,7 @@ orderq(doall)
 **             none.
 */
 
 **             none.
 */
 
-workcmpf(a, b)
+workcmpf0(a, b)
        register WORK *a;
        register WORK *b;
 {
        register WORK *a;
        register WORK *b;
 {
@@ -735,11 +923,91 @@ workcmpf(a, b)
        long pb = b->w_pri;
 
        if (pa == pb)
        long pb = b->w_pri;
 
        if (pa == pb)
-               return (0);
+               return 0;
        else if (pa > pb)
        else if (pa > pb)
-               return (1);
+               return 1;
        else
        else
-               return (-1);
+               return -1;
+}
+\f/*
+**  WORKCMPF1 -- first compare function for ordering work based on host name.
+**
+**     Sorts on host name, lock status, and priority in that order.
+**
+**     Parameters:
+**             a -- the first argument.
+**             b -- the second argument.
+**
+**     Returns:
+**             <0 if a < b
+**              0 if a == b
+**             >0 if a > b
+**
+**     Side Effects:
+**             none.
+*/
+
+workcmpf1(a, b)
+       register WORK *a;
+       register WORK *b;
+{
+       int i;
+
+       /* host name */
+       if (a->w_host != NULL && b->w_host == NULL)
+               return 1;
+       else if (a->w_host == NULL && b->w_host != NULL)
+               return -1;
+       if (a->w_host != NULL && b->w_host != NULL &&
+           (i = strcmp(a->w_host, b->w_host)))
+               return i;
+
+       /* lock status */
+       if (a->w_lock != b->w_lock)
+               return b->w_lock - a->w_lock;
+
+       /* job priority */
+       return a->w_pri - b->w_pri;
+}
+\f/*
+**  WORKCMPF2 -- second compare function for ordering work based on host name.
+**
+**     Sorts on lock status, host name, and priority in that order.
+**
+**     Parameters:
+**             a -- the first argument.
+**             b -- the second argument.
+**
+**     Returns:
+**             <0 if a < b
+**              0 if a == b
+**             >0 if a > b
+**
+**     Side Effects:
+**             none.
+*/
+
+workcmpf2(a, b)
+       register WORK *a;
+       register WORK *b;
+{
+       int i;
+
+       /* lock status */
+       if (a->w_lock != b->w_lock)
+               return a->w_lock - b->w_lock;
+
+       /* host name */
+       if (a->w_host != NULL && b->w_host == NULL)
+               return 1;
+       else if (a->w_host == NULL && b->w_host != NULL)
+               return -1;
+       if (a->w_host != NULL && b->w_host != NULL &&
+           (i = strcmp(a->w_host, b->w_host)))
+               return i;
+
+       /* job priority */
+       return a->w_pri - b->w_pri;
 }
 \f/*
 **  DOWORK -- do a work request.
 }
 \f/*
 **  DOWORK -- do a work request.
@@ -747,21 +1015,27 @@ workcmpf(a, b)
 **     Parameters:
 **             id -- the ID of the job to run.
 **             forkflag -- if set, run this in background.
 **     Parameters:
 **             id -- the ID of the job to run.
 **             forkflag -- if set, run this in background.
+**             requeueflag -- if set, reinstantiate the queue quickly.
+**                     This is used when expanding aliases in the queue.
+**                     If forkflag is also set, it doesn't wait for the
+**                     child.
 **             e - the envelope in which to run it.
 **
 **     Returns:
 **             e - the envelope in which to run it.
 **
 **     Returns:
-**             none.
+**             process id of process that is running the queue job.
 **
 **     Side Effects:
 **             The work request is satisfied if possible.
 */
 
 **
 **     Side Effects:
 **             The work request is satisfied if possible.
 */
 
-dowork(id, forkflag, e)
+pid_t
+dowork(id, forkflag, requeueflag, e)
        char *id;
        bool forkflag;
        char *id;
        bool forkflag;
+       bool requeueflag;
        register ENVELOPE *e;
 {
        register ENVELOPE *e;
 {
-       register int i;
+       register pid_t pid;
        extern bool readqf();
 
        if (tTd(40, 1))
        extern bool readqf();
 
        if (tTd(40, 1))
@@ -773,19 +1047,24 @@ dowork(id, forkflag, e)
 
        if (forkflag)
        {
 
        if (forkflag)
        {
-               i = fork();
-               if (i < 0)
+               pid = fork();
+               if (pid < 0)
                {
                        syserr("dowork: cannot fork");
                {
                        syserr("dowork: cannot fork");
-                       return;
+                       return 0;
+               }
+               else if (pid > 0)
+               {
+                       /* parent -- clean out connection cache */
+                       mci_flush(FALSE, NULL);
                }
        }
        else
        {
                }
        }
        else
        {
-               i = 0;
+               pid = 0;
        }
 
        }
 
-       if (i == 0)
+       if (pid == 0)
        {
                /*
                **  CHILD
        {
                /*
                **  CHILD
@@ -798,9 +1077,16 @@ dowork(id, forkflag, e)
                /* set basic modes, etc. */
                (void) alarm(0);
                clearenvelope(e, FALSE);
                /* set basic modes, etc. */
                (void) alarm(0);
                clearenvelope(e, FALSE);
-               e->e_flags |= EF_QUEUERUN;
+               e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
                e->e_errormode = EM_MAIL;
                e->e_id = id;
                e->e_errormode = EM_MAIL;
                e->e_id = id;
+               GrabTo = UseErrorsTo = FALSE;
+               ExitStat = EX_OK;
+               if (forkflag)
+               {
+                       disconnect(1, e);
+                       OpMode = MD_DELIVER;
+               }
 # ifdef LOG
                if (LogLevel > 76)
                        syslog(LOG_DEBUG, "%s: dowork, pid=%d", e->e_id,
 # ifdef LOG
                if (LogLevel > 76)
                        syslog(LOG_DEBUG, "%s: dowork, pid=%d", e->e_id,
@@ -818,15 +1104,36 @@ dowork(id, forkflag, e)
                        if (forkflag)
                                exit(EX_OK);
                        else
                        if (forkflag)
                                exit(EX_OK);
                        else
-                               return;
+                               return 0;
                }
 
                e->e_flags |= EF_INQUEUE;
                }
 
                e->e_flags |= EF_INQUEUE;
-               eatheader(e);
 
 
-               /* do the delivery */
-               if (!bitset(EF_FATALERRS, e->e_flags))
+               /* if this has been tried recently, let it be */
+               if (e->e_ntries > 0 && (curtime() - e->e_dtime) < MinQueueAge)
+               {
+                       char *howlong = pintvl(curtime() - e->e_dtime, TRUE);
+
+                       e->e_flags |= EF_KEEPQUEUE;
+                       if (Verbose || tTd(40, 8))
+                               printf("%s: too young (%s)\n",
+                                       e->e_id, howlong);
+#ifdef LOG
+                       if (LogLevel > 19)
+                               syslog(LOG_DEBUG, "%s: too young (%s)",
+                                       e->e_id, howlong);
+#endif
+               }
+               else
+               {
+                       eatheader(e, requeueflag);
+
+                       if (requeueflag)
+                               queueup(e, TRUE, FALSE);
+
+                       /* do the delivery */
                        sendall(e, SM_DELIVER);
                        sendall(e, SM_DELIVER);
+               }
 
                /* finish up and exit */
                if (forkflag)
 
                /* finish up and exit */
                if (forkflag)
@@ -834,15 +1141,8 @@ dowork(id, forkflag, e)
                else
                        dropenvelope(e);
        }
                else
                        dropenvelope(e);
        }
-       else
-       {
-               /*
-               **  Parent -- pick up results.
-               */
-
-               errno = 0;
-               (void) waitfor(i);
-       }
+       e->e_id = NULL;
+       return pid;
 }
 \f/*
 **  READQF -- read queue file and set up environment.
 }
 \f/*
 **  READQF -- read queue file and set up environment.
@@ -862,23 +1162,25 @@ bool
 readqf(e)
        register ENVELOPE *e;
 {
 readqf(e)
        register ENVELOPE *e;
 {
-       char *qf;
        register FILE *qfp;
        ADDRESS *ctladdr;
        struct stat st;
        char *bp;
        register FILE *qfp;
        ADDRESS *ctladdr;
        struct stat st;
        char *bp;
+       int qfver = 0;
+       register char *p;
+       char *orcpt = NULL;
+       char qf[20];
        char buf[MAXLINE];
        char buf[MAXLINE];
-       extern char *fgetfolded();
        extern long atol();
        extern ADDRESS *setctluser();
        extern long atol();
        extern ADDRESS *setctluser();
-       extern bool lockfile();
+       extern void loseqfile();
        extern ADDRESS *sendto();
 
        /*
        **  Read and process the file.
        */
 
        extern ADDRESS *sendto();
 
        /*
        **  Read and process the file.
        */
 
-       qf = queuename(e, 'q');
+       strcpy(qf, queuename(e, 'q'));
        qfp = fopen(qf, "r+");
        if (qfp == NULL)
        {
        qfp = fopen(qf, "r+");
        if (qfp == NULL)
        {
@@ -890,6 +1192,19 @@ readqf(e)
                return FALSE;
        }
 
                return FALSE;
        }
 
+       if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB))
+       {
+               /* being processed by another queuer */
+               if (Verbose || tTd(40, 8))
+                       printf("%s: locked\n", e->e_id);
+# ifdef LOG
+               if (LogLevel > 19)
+                       syslog(LOG_DEBUG, "%s: locked", e->e_id);
+# endif /* LOG */
+               (void) fclose(qfp);
+               return FALSE;
+       }
+
        /*
        **  Check the queue file for plausibility to avoid attacks.
        */
        /*
        **  Check the queue file for plausibility to avoid attacks.
        */
@@ -904,7 +1219,7 @@ readqf(e)
                return FALSE;
        }
 
                return FALSE;
        }
 
-       if (st.st_uid != geteuid() || (st.st_mode & 07777) != FileMode)
+       if (st.st_uid != geteuid())
        {
 # ifdef LOG
                if (LogLevel > 0)
        {
 # ifdef LOG
                if (LogLevel > 0)
@@ -915,55 +1230,76 @@ readqf(e)
 # endif /* LOG */
                if (tTd(40, 8))
                        printf("readqf(%s): bogus file\n", qf);
 # endif /* LOG */
                if (tTd(40, 8))
                        printf("readqf(%s): bogus file\n", qf);
+               loseqfile(e, "bogus file uid in mqueue");
                fclose(qfp);
                return FALSE;
        }
 
                fclose(qfp);
                return FALSE;
        }
 
-       if (!lockfile(fileno(qfp), qf, LOCK_EX|LOCK_NB))
+       if (st.st_size == 0)
        {
        {
-               /* being processed by another queuer */
-               if (tTd(40, 8))
-                       printf("readqf(%s): locked\n", qf);
-               if (Verbose)
-                       printf("%s: locked\n", e->e_id);
-# ifdef LOG
-               if (LogLevel > 19)
-                       syslog(LOG_DEBUG, "%s: locked", e->e_id);
-# endif /* LOG */
-               (void) fclose(qfp);
+               /* must be a bogus file -- just remove it */
+               (void) unlink(qf);
+               fclose(qfp);
                return FALSE;
        }
 
                return FALSE;
        }
 
-       /* save this lock */
+       if (st.st_nlink == 0)
+       {
+               /*
+               **  Race condition -- we got a file just as it was being
+               **  unlinked.  Just assume it is zero length.
+               */
+
+               fclose(qfp);
+               return FALSE;
+       }
+
+       /* good file -- save this lock */
        e->e_lockfp = qfp;
 
        /* do basic system initialization */
        initsys(e);
        e->e_lockfp = qfp;
 
        /* do basic system initialization */
        initsys(e);
+       define('i', e->e_id, e);
 
 
-       FileName = qf;
        LineNumber = 0;
        LineNumber = 0;
-       if (Verbose)
-               printf("\nRunning %s\n", e->e_id);
+       e->e_flags |= EF_GLOBALERRS;
+       OpMode = MD_DELIVER;
        ctladdr = NULL;
        ctladdr = NULL;
+       e->e_dfino = -1;
        while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL)
        {
                register char *p;
                struct stat st;
        while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL)
        {
                register char *p;
                struct stat st;
+               u_long qflags;
+               ADDRESS *q;
 
                if (tTd(40, 4))
                        printf("+++++ %s\n", bp);
                switch (bp[0])
                {
 
                if (tTd(40, 4))
                        printf("+++++ %s\n", bp);
                switch (bp[0])
                {
+                 case 'V':             /* queue file version number */
+                       qfver = atoi(&bp[1]);
+                       if (qfver > QF_VERSION)
+                       {
+                               syserr("Version number in qf (%d) greater than max (%d)",
+                                       qfver, QF_VERSION);
+                       }
+                       break;
+
                  case 'C':             /* specify controlling user */
                        ctladdr = setctluser(&bp[1]);
                        break;
 
                  case 'C':             /* specify controlling user */
                        ctladdr = setctluser(&bp[1]);
                        break;
 
+                 case 'Q':             /* original recipient */
+                       orcpt = newstr(&bp[1]);
+                       break;
+
                  case 'R':             /* specify recipient */
                        (void) sendto(&buf[1], 1, (ADDRESS *) NULL, 0);
                        break;
 
                  case 'E':             /* specify error recipient */
                  case 'R':             /* specify recipient */
                        (void) sendto(&buf[1], 1, (ADDRESS *) NULL, 0);
                        break;
 
                  case 'E':             /* specify error recipient */
-                       (void) sendtolist(&bp[1], ctladdr, &e->e_errorqueue, e);
+                       (void) sendtolist(&bp[1], ctladdr, &e->e_errorqueue, 0, e);
                        break;
 
                  case 'H':             /* header */
                        break;
 
                  case 'H':             /* header */
@@ -971,13 +1307,17 @@ readqf(e)
                        break;
 
                  case 'M':             /* message */
                        break;
 
                  case 'M':             /* message */
-                       e->e_message = newstr(&bp[1]);
+                       /* ignore this; we want a new message next time */
                        break;
 
                  case 'S':             /* sender */
                        setsender(newstr(&bp[1]), e, NULL, TRUE);
                        break;
 
                        break;
 
                  case 'S':             /* sender */
                        setsender(newstr(&bp[1]), e, NULL, TRUE);
                        break;
 
+                 case 'B':             /* body type */
+                       e->e_bodytype = newstr(&bp[1]);
+                       break;
+
                  case 'D':             /* data file name */
                        e->e_df = newstr(&bp[1]);
                        e->e_dfp = fopen(e->e_df, "r");
                  case 'D':             /* data file name */
                        e->e_df = newstr(&bp[1]);
                        e->e_dfp = fopen(e->e_df, "r");
@@ -987,13 +1327,30 @@ readqf(e)
                                e->e_msgsize = -1;
                        }
                        else if (fstat(fileno(e->e_dfp), &st) >= 0)
                                e->e_msgsize = -1;
                        }
                        else if (fstat(fileno(e->e_dfp), &st) >= 0)
+                       {
                                e->e_msgsize = st.st_size;
                                e->e_msgsize = st.st_size;
+                               e->e_dfdev = st.st_dev;
+                               e->e_dfino = st.st_ino;
+                       }
                        break;
 
                  case 'T':             /* init time */
                        e->e_ctime = atol(&bp[1]);
                        break;
 
                        break;
 
                  case 'T':             /* init time */
                        e->e_ctime = atol(&bp[1]);
                        break;
 
+                 case 'I':             /* data file's inode number */
+                       if (e->e_dfino == -1)
+                               e->e_dfino = atol(&buf[1]);
+                       break;
+
+                 case 'K':             /* time of last deliver attempt */
+                       e->e_dtime = atol(&buf[1]);
+                       break;
+
+                 case 'N':             /* number of delivery attempts */
+                       e->e_ntries = atoi(&buf[1]);
+                       break;
+
                  case 'P':             /* message priority */
                        e->e_msgpriority = atol(&bp[1]) + WkTimeFact;
                        break;
                  case 'P':             /* message priority */
                        e->e_msgpriority = atol(&bp[1]) + WkTimeFact;
                        break;
@@ -1010,10 +1367,18 @@ readqf(e)
                                  case 'r':     /* response */
                                        e->e_flags |= EF_RESPONSE;
                                        break;
                                  case 'r':     /* response */
                                        e->e_flags |= EF_RESPONSE;
                                        break;
+
+                                 case '8':     /* has 8 bit data */
+                                       e->e_flags |= EF_HAS8BIT;
+                                       break;
                                }
                        }
                        break;
 
                                }
                        }
                        break;
 
+                 case 'Z':             /* original envelope id from ESMTP */
+                       e->e_envid = newstr(&bp[1]);
+                       break;
+
                  case '$':             /* define macro */
                        define(bp[1], newstr(&bp[2]), e);
                        break;
                  case '$':             /* define macro */
                        define(bp[1], newstr(&bp[2]), e);
                        break;
@@ -1022,17 +1387,17 @@ readqf(e)
                        break;
 
                  default:
                        break;
 
                  default:
-                       syserr("readqf(%s:%d): bad line \"%s\"", e->e_id,
-                               LineNumber, bp);
-                       break;
+                       syserr("readqf: %s: line %d: bad line \"%s\"",
+                               qf, LineNumber, bp);
+                       fclose(qfp);
+                       loseqfile(e, "unrecognized line");
+                       return FALSE;
                }
 
                if (bp != buf)
                        free(bp);
        }
 
                }
 
                if (bp != buf)
                        free(bp);
        }
 
-       FileName = NULL;
-
        /*
        **  If we haven't read any lines, this queue file is empty.
        **  Arrange to remove it without referencing any null pointers.
        /*
        **  If we haven't read any lines, this queue file is empty.
        **  Arrange to remove it without referencing any null pointers.
@@ -1069,12 +1434,12 @@ printqueue()
        **  Check for permission to print the queue
        */
 
        **  Check for permission to print the queue
        */
 
-       if (bitset(PRIV_RESTRMAILQ, PrivacyFlags) && getuid() != 0)
+       if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0)
        {
                struct stat st;
 # ifdef NGROUPS
                int n;
        {
                struct stat st;
 # ifdef NGROUPS
                int n;
-               int gidset[NGROUPS];
+               GIDSET_T gidset[NGROUPS];
 # endif
 
                if (stat(QueueDir, &st) < 0)
 # endif
 
                if (stat(QueueDir, &st) < 0)
@@ -1091,7 +1456,7 @@ printqueue()
                }
                if (n < 0)
 # else
                }
                if (n < 0)
 # else
-               if (getgid() != st.st_gid)
+               if (RealGid != st.st_gid)
 # endif
                {
                        usrerr("510 You are not permitted to see the queue");
 # endif
                {
                        usrerr("510 You are not permitted to see the queue");
@@ -1132,18 +1497,19 @@ printqueue()
                auto time_t submittime = 0;
                long dfsize = -1;
                int flags = 0;
                auto time_t submittime = 0;
                long dfsize = -1;
                int flags = 0;
+               int qfver;
                char message[MAXLINE];
                char message[MAXLINE];
-               extern bool shouldqueue();
-               extern bool lockfile();
+               char bodytype[MAXNAME + 1];
 
 
+               printf("%8s", w->w_name + 2);
                f = fopen(w->w_name, "r");
                if (f == NULL)
                {
                f = fopen(w->w_name, "r");
                if (f == NULL)
                {
+                       printf(" (job completed)\n");
                        errno = 0;
                        continue;
                }
                        errno = 0;
                        continue;
                }
-               printf("%8s", w->w_name + 2);
-               if (!lockfile(fileno(f), w->w_name, LOCK_SH|LOCK_NB))
+               if (w->w_lock)
                        printf("*");
                else if (shouldqueue(w->w_pri, w->w_ctime))
                        printf("X");
                        printf("*");
                else if (shouldqueue(w->w_pri, w->w_ctime))
                        printf("X");
@@ -1151,7 +1517,8 @@ printqueue()
                        printf(" ");
                errno = 0;
 
                        printf(" ");
                errno = 0;
 
-               message[0] = '\0';
+               message[0] = bodytype[0] = '\0';
+               qfver = 0;
                while (fgets(buf, sizeof buf, f) != NULL)
                {
                        register int i;
                while (fgets(buf, sizeof buf, f) != NULL)
                {
                        register int i;
@@ -1160,6 +1527,10 @@ printqueue()
                        fixcrlf(buf, TRUE);
                        switch (buf[0])
                        {
                        fixcrlf(buf, TRUE);
                        switch (buf[0])
                        {
+                         case 'V':     /* queue file version */
+                               qfver = atoi(&buf[1]);
+                               break;
+
                          case 'M':     /* error message */
                                if ((i = strlen(&buf[1])) >= sizeof message)
                                        i = sizeof message - 1;
                          case 'M':     /* error message */
                                if ((i = strlen(&buf[1])) >= sizeof message)
                                        i = sizeof message - 1;
@@ -1167,6 +1538,13 @@ printqueue()
                                message[i] = '\0';
                                break;
 
                                message[i] = '\0';
                                break;
 
+                         case 'B':     /* body type */
+                               if ((i = strlen(&buf[1])) >= sizeof bodytype)
+                                       i = sizeof bodytype - 1;
+                               bcopy(&buf[1], bodytype, i);
+                               bodytype[i] = '\0';
+                               break;
+
                          case 'S':     /* sender name */
                                if (Verbose)
                                        printf("%8ld %10ld%c%.12s %.38s",
                          case 'S':     /* sender name */
                                if (Verbose)
                                        printf("%8ld %10ld%c%.12s %.38s",
@@ -1178,8 +1556,12 @@ printqueue()
                                else
                                        printf("%8ld %.16s %.45s", dfsize,
                                            ctime(&submittime), &buf[1]);
                                else
                                        printf("%8ld %.16s %.45s", dfsize,
                                            ctime(&submittime), &buf[1]);
-                               if (message[0] != '\0')
-                                       printf("\n\t\t (%.60s)", message);
+                               if (message[0] != '\0' || bodytype[0] != '\0')
+                               {
+                                       printf("\n    %10.10s", bodytype);
+                                       if (message[0] != '\0')
+                                               printf("   (%.60s)", message);
+                               }
                                break;
 
                          case 'C':     /* controlling user */
                                break;
 
                          case 'C':     /* controlling user */
@@ -1189,10 +1571,18 @@ printqueue()
                                break;
 
                          case 'R':     /* recipient name */
                                break;
 
                          case 'R':     /* recipient name */
+                               p = &buf[1];
+                               if (qfver >= 1)
+                               {
+                                       p = strchr(p, ':');
+                                       if (p == NULL)
+                                               break;
+                                       p++;
+                               }
                                if (Verbose)
                                if (Verbose)
-                                       printf("\n\t\t\t\t\t  %.38s", &buf[1]);
+                                       printf("\n\t\t\t\t\t  %.38s", p);
                                else
                                else
-                                       printf("\n\t\t\t\t   %.45s", &buf[1]);
+                                       printf("\n\t\t\t\t   %.45s", p);
                                break;
 
                          case 'T':     /* creation time */
                                break;
 
                          case 'T':     /* creation time */
@@ -1248,7 +1638,7 @@ printqueue()
 char *
 queuename(e, type)
        register ENVELOPE *e;
 char *
 queuename(e, type)
        register ENVELOPE *e;
-       char type;
+       int type;
 {
        static int pid = -1;
        static char c0;
 {
        static int pid = -1;
        static char c0;
@@ -1256,8 +1646,7 @@ queuename(e, type)
        static char c2;
        time_t now;
        struct tm *tm;
        static char c2;
        time_t now;
        struct tm *tm;
-       static char buf[MAXNAME];
-       extern bool lockfile();
+       static char buf[MAXNAME + 1];
 
        if (e->e_id == NULL)
        {
 
        if (e->e_id == NULL)
        {
@@ -1295,11 +1684,11 @@ queuename(e, type)
                        {
                                if (errno == EEXIST)
                                        continue;
                        {
                                if (errno == EEXIST)
                                        continue;
-                               syserr("queuename: Cannot create \"%s\" in \"%s\"",
-                                       qf, QueueDir);
+                               syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)",
+                                       qf, QueueDir, geteuid());
                                exit(EX_UNAVAILABLE);
                        }
                                exit(EX_UNAVAILABLE);
                        }
-                       if (lockfile(i, qf, LOCK_EX|LOCK_NB))
+                       if (lockfile(i, qf, NULL, LOCK_EX|LOCK_NB))
                        {
                                e->e_lockfp = fdopen(i, "w");
                                break;
                        {
                                e->e_lockfp = fdopen(i, "w");
                                break;
@@ -1310,14 +1699,19 @@ queuename(e, type)
                }
                if (c1 >= '~' && c2 >= 'Z')
                {
                }
                if (c1 >= '~' && c2 >= 'Z')
                {
-                       syserr("queuename: Cannot create \"%s\" in \"%s\"",
-                               qf, QueueDir);
+                       syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)",
+                               qf, QueueDir, geteuid());
                        exit(EX_OSERR);
                }
                e->e_id = newstr(&qf[2]);
                define('i', e->e_id, e);
                if (tTd(7, 1))
                        printf("queuename: assigned id %s, env=%x\n", e->e_id, e);
                        exit(EX_OSERR);
                }
                e->e_id = newstr(&qf[2]);
                define('i', e->e_id, e);
                if (tTd(7, 1))
                        printf("queuename: assigned id %s, env=%x\n", e->e_id, e);
+               if (tTd(7, 9))
+               {
+                       printf("  lockfd=");
+                       dumpfd(fileno(e->e_lockfp), TRUE, FALSE);
+               }
 # ifdef LOG
                if (LogLevel > 93)
                        syslog(LOG_DEBUG, "%s: assigned id", e->e_id);
 # ifdef LOG
                if (LogLevel > 93)
                        syslog(LOG_DEBUG, "%s: assigned id", e->e_id);
@@ -1390,13 +1784,14 @@ setctluser(user)
 {
        register ADDRESS *a;
        struct passwd *pw;
 {
        register ADDRESS *a;
        struct passwd *pw;
+       char *p;
 
        /*
        **  See if this clears our concept of controlling user.
        */
 
        if (user == NULL || *user == '\0')
 
        /*
        **  See if this clears our concept of controlling user.
        */
 
        if (user == NULL || *user == '\0')
-               user = DefUser;
+               return NULL;
 
        /*
        **  Set up addr fields for controlling user.
 
        /*
        **  Set up addr fields for controlling user.
@@ -1404,21 +1799,59 @@ setctluser(user)
 
        a = (ADDRESS *) xalloc(sizeof *a);
        bzero((char *) a, sizeof *a);
 
        a = (ADDRESS *) xalloc(sizeof *a);
        bzero((char *) a, sizeof *a);
-       if ((pw = getpwnam(user)) != NULL)
+
+       p = strchr(user, ':');
+       if (p != NULL)
+               *p++ = '\0';
+       if (*user != '\0' && (pw = getpwnam(user)) != NULL)
        {
        {
-               a->q_home = newstr(pw->pw_dir);
+               if (strcmp(pw->pw_dir, "/") == 0)
+                       a->q_home = "";
+               else
+                       a->q_home = newstr(pw->pw_dir);
                a->q_uid = pw->pw_uid;
                a->q_gid = pw->pw_gid;
                a->q_user = newstr(user);
                a->q_uid = pw->pw_uid;
                a->q_gid = pw->pw_gid;
                a->q_user = newstr(user);
+               a->q_flags |= QGOODUID;
        }
        else
        {
        }
        else
        {
-               a->q_uid = DefUid;
-               a->q_gid = DefGid;
                a->q_user = newstr(DefUser);
        }
 
                a->q_user = newstr(DefUser);
        }
 
-       a->q_flags |= QGOODUID|QPRIMARY;        /* flag as a "ctladdr"  */
+       a->q_flags |= QPRIMARY;         /* flag as a "ctladdr"  */
        a->q_mailer = LocalMailer;
        a->q_mailer = LocalMailer;
+       if (p == NULL)
+               a->q_paddr = a->q_user;
+       else
+               a->q_paddr = newstr(p);
        return a;
 }
        return a;
 }
+\f/*
+**  LOSEQFILE -- save the qf as Qf and try to let someone know
+**
+**     Parameters:
+**             e -- the envelope (e->e_id will be used).
+**             why -- reported to whomever can hear.
+**
+**     Returns:
+**             none.
+*/
+
+void
+loseqfile(e, why)
+       register ENVELOPE *e;
+       char *why;
+{
+       char buf[40];
+
+       if (e == NULL || e->e_id == NULL)
+               return;
+       if (strlen(e->e_id) > sizeof buf - 4)
+               return;
+       strcpy(buf, queuename(e, 'q'));
+       rename(buf, queuename(e, 'Q'));
+#ifdef LOG
+       syslog(LOG_ALERT, "Losing %s: %s", buf, why);
+#endif
+}