more cleanup for DSN drafts
[unix-history] / usr / src / usr.sbin / sendmail / src / queue.c
index 36fd54c..5722110 100644 (file)
@@ -10,9 +10,9 @@
 
 #ifndef lint
 #ifdef QUEUE
 
 #ifndef lint
 #ifdef QUEUE
-static char sccsid[] = "@(#)queue.c    8.26 (Berkeley) %G% (with queueing)";
+static char sccsid[] = "@(#)queue.c    8.74 (Berkeley) %G% (with queueing)";
 #else
 #else
-static char sccsid[] = "@(#)queue.c    8.26 (Berkeley) %G% (without queueing)";
+static char sccsid[] = "@(#)queue.c    8.74 (Berkeley) %G% (without queueing)";
 #endif
 #endif /* not lint */
 
 #endif
 #endif /* not lint */
 
@@ -29,6 +29,8 @@ static char sccsid[] = "@(#)queue.c   8.26 (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 */
@@ -37,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.
 **
@@ -68,6 +72,7 @@ queueup(e, queueall, announce)
        bool newid;
        register char *p;
        MAILER nullmailer;
        bool newid;
        register char *p;
        MAILER nullmailer;
+       MCI mcibuf;
        char buf[MAXLINE], tf[MAXLINE];
 
        /*
        char buf[MAXLINE], tf[MAXLINE];
 
        /*
@@ -95,21 +100,21 @@ queueup(e, queueall, announce)
                                        break;
 #ifdef LOG
                                if (LogLevel > 0 && (i % 32) == 0)
                                        break;
 #ifdef LOG
                                if (LogLevel > 0 && (i % 32) == 0)
-                                       syslog(LOG_ALERT, "queueup: cannot create %s: %s",
-                                               tf, errstring(errno));
+                                       syslog(LOG_ALERT, "queueup: cannot create %s, uid=%d: %s",
+                                               tf, geteuid(), errstring(errno));
 #endif
 #endif
-                               continue;
                        }
                        }
-
-                       if (lockfile(fd, tf, NULL, LOCK_EX|LOCK_NB))
-                               break;
+                       else
+                       {
+                               if (lockfile(fd, tf, NULL, LOCK_EX|LOCK_NB))
+                                       break;
 #ifdef LOG
 #ifdef LOG
-                       else if (LogLevel > 0 && (i % 32) == 0)
-                               syslog(LOG_ALERT, "queueup: cannot lock %s: %s",
-                                       tf, errstring(errno));
+                               else if (LogLevel > 0 && (i % 32) == 0)
+                                       syslog(LOG_ALERT, "queueup: cannot lock %s: %s",
+                                               tf, errstring(errno));
 #endif
 #endif
-
-                       close(fd);
+                               close(fd);
+                       }
 
                        if ((i % 32) == 31)
                        {
 
                        if ((i % 32) == 31)
                        {
@@ -120,12 +125,28 @@ queueup(e, queueall, announce)
                                sleep(i % 32);
                }
                if (fd < 0 || (tfp = fdopen(fd, "w")) == NULL)
                                sleep(i % 32);
                }
                if (fd < 0 || (tfp = fdopen(fd, "w")) == NULL)
-                       syserr("!queueup: cannot create queue temp file %s", tf);
+               {
+                       printopenfds(TRUE);
+                       syserr("!queueup: cannot create queue temp file %s, uid=%d",
+                               tf, geteuid());
+               }
        }
 
        if (tTd(40, 1))
        }
 
        if (tTd(40, 1))
-               printf("\n>>>>> queueing %s%s >>>>>\n", e->e_id,
-                       newid ? " (new id)" : "");
+               printf("\n>>>>> queueing %s%s queueall=%d >>>>>\n", e->e_id,
+                       newid ? " (new id)" : "", queueall);
+       if (tTd(40, 3))
+       {
+               extern void printenvflags();
+
+               printf("  e_flags=");
+               printenvflags(e);
+       }
+       if (tTd(40, 32))
+       {
+               printf("  sendq=");
+               printaddr(e->e_sendqueue, TRUE);
+       }
        if (tTd(40, 9))
        {
                printf("  tfp=");
        if (tTd(40, 9))
        {
                printf("  tfp=");
@@ -141,18 +162,30 @@ queueup(e, queueall, announce)
        **  If there is no data file yet, create one.
        */
 
        **  If there is no data file yet, create one.
        */
 
-       if (e->e_df == NULL)
+       if (!bitset(EF_HAS_DF, e->e_flags))
        {
                register FILE *dfp;
        {
                register FILE *dfp;
+               char dfname[20];
+               struct stat stbuf;
                extern putbody();
 
                extern putbody();
 
-               e->e_df = queuename(e, 'd');
-               e->e_df = newstr(e->e_df);
-               fd = open(e->e_df, O_WRONLY|O_CREAT, FileMode);
+               strcpy(dfname, queuename(e, 'd'));
+               fd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode);
                if (fd < 0 || (dfp = fdopen(fd, "w")) == NULL)
                if (fd < 0 || (dfp = fdopen(fd, "w")) == NULL)
-                       syserr("!queueup: cannot create data temp file %s",
-                               e->e_df);
-               (*e->e_putbody)(dfp, FileMailer, e, NULL);
+                       syserr("!queueup: cannot create data temp file %s, uid=%d",
+                               dfname, geteuid());
+               if (fstat(fd, &stbuf) < 0)
+                       e->e_dfino = -1;
+               else
+               {
+                       e->e_dfdev = stbuf.st_dev;
+                       e->e_dfino = stbuf.st_ino;
+               }
+               e->e_flags |= EF_HAS_DF;
+               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;
        }
@@ -163,20 +196,34 @@ queueup(e, queueall, announce)
        **      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 type and 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 body type */
        if (e->e_bodytype != NULL)
                fprintf(tfp, "B%s\n", e->e_bodytype);
        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)
 
        /* 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;
@@ -184,46 +231,43 @@ queueup(e, queueall, announce)
                *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);
 
        /* $r and $s and $_ macro values */
        if ((p = macvalue('r', e)) != NULL)
        *p++ = '\0';
        if (buf[0] != '\0')
                fprintf(tfp, "F%s\n", buf);
 
        /* $r and $s and $_ macro values */
        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)
        if ((p = macvalue('_', e)) != NULL)
-               fprintf(tfp, "$_%s\n", p);
+               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 list of error recipients */
-       printctladdr(NULL, NULL);
-       for (q = e->e_errorqueue; q != NULL; q = q->q_next)
-       {
-               if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
-               {
-                       printctladdr(q, tfp);
-                       fprintf(tfp, "E%s\n", q->q_paddr);
-               }
-       }
+       /* output ESMTP-supplied "original" information */
+       if (e->e_envid != NULL)
 
        /* output list of recipient addresses */
 
        /* output list of recipient addresses */
+       printctladdr(NULL, NULL);
        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
        {
                if (bitset(QQUEUEUP, q->q_flags) ||
                    (queueall && !bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags)))
                {
                        printctladdr(q, tfp);
        for (q = e->e_sendqueue; q != NULL; q = q->q_next)
        {
                if (bitset(QQUEUEUP, q->q_flags) ||
                    (queueall && !bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags)))
                {
                        printctladdr(q, tfp);
-                       fprintf(tfp, "R%s\n", q->q_paddr);
+                       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))
@@ -246,8 +290,11 @@ queueup(e, queueall, announce)
 
        bzero((char *) &nullmailer, sizeof nullmailer);
        nullmailer.m_re_rwset = nullmailer.m_rh_rwset =
 
        bzero((char *) &nullmailer, sizeof nullmailer);
        nullmailer.m_re_rwset = nullmailer.m_rh_rwset =
-                       nullmailer.m_se_rwset = nullmailer.m_sh_rwset = 0;
+                       nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1;
        nullmailer.m_eol = "\n";
        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)
@@ -262,6 +309,14 @@ queueup(e, queueall, announce)
                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");
 
@@ -280,7 +335,6 @@ queueup(e, queueall, announce)
                /* 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))
                        fprintf(tfp, "%s: %s\n", h->h_field, buf);
                }
                else if (bitset(H_FROM|H_RCPT, h->h_flags))
@@ -293,8 +347,7 @@ queueup(e, queueall, announce)
                        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;
                }
 
                        TrafficLogFile = savetrace;
                }
@@ -319,7 +372,8 @@ queueup(e, queueall, announce)
                /* rename (locked) tf to be (locked) qf */
                qf = queuename(e, 'q');
                if (rename(tf, qf) < 0)
                /* rename (locked) tf to be (locked) qf */
                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), uid=%d",
+                               tf, qf, geteuid());
 
                /* close and unlock old (locked) qf */
                if (e->e_lockfp != NULL)
 
                /* close and unlock old (locked) qf */
                if (e->e_lockfp != NULL)
@@ -334,7 +388,7 @@ queueup(e, queueall, announce)
 # ifdef LOG
        /* save log info */
        if (LogLevel > 79)
 # 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);
+               syslog(LOG_DEBUG, "%s: queueup, qf=%s", e->e_id, qf);
 # endif /* LOG */
 
        if (tTd(40, 1))
 # endif /* LOG */
 
        if (tTd(40, 1))
@@ -383,9 +437,8 @@ printctladdr(a, tfp)
        else
                uname = pw->pw_name;
 
        else
                uname = pw->pw_name;
 
-       fprintf(tfp, "C%s:%s\n", uname, a->q_paddr);
+       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.
 **
@@ -406,10 +459,13 @@ printctladdr(a, tfp)
 
 ENVELOPE       QueueEnvelope;          /* the queue run envelope */
 
 
 ENVELOPE       QueueEnvelope;          /* the queue run envelope */
 
+void
 runqueue(forkflag)
        bool forkflag;
 {
        register ENVELOPE *e;
 runqueue(forkflag)
        bool forkflag;
 {
        register ENVELOPE *e;
+       int njobs;
+       int sequenceno = 0;
        extern ENVELOPE BlankEnvelope;
 
        /*
        extern ENVELOPE BlankEnvelope;
 
        /*
@@ -435,17 +491,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) setsignal(SIGCHLD, reapchild);
 #endif /* SIGCHLD */
                        if (QueueIntvl != 0)
                                (void) setevent(QueueIntvl, runqueue, TRUE);
 #endif /* SIGCHLD */
                        if (QueueIntvl != 0)
                                (void) setevent(QueueIntvl, runqueue, TRUE);
@@ -501,7 +558,7 @@ 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)
 
        /* process them once at a time */
        while (WorkQ != NULL)
@@ -514,21 +571,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);
+                               printf("\nSkipping %s (sequence %d of %d)\n",
+                                       w->w_name + 2, sequenceno, njobs);
                }
                else
                {
                        pid_t pid;
                        extern pid_t dowork();
 
                }
                else
                {
                        pid_t pid;
                        extern pid_t dowork();
 
+                       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;
                        pid = dowork(w->w_name + 2, ForkQueueRuns, FALSE, e);
                        errno = 0;
-                       (void) waitfor(pid);
+                       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);
        }
 
@@ -576,7 +641,7 @@ orderq(doall)
        register int i;
        WORK wlist[QUEUESIZE+1];
        int wn = -1;
        register int i;
        WORK wlist[QUEUESIZE+1];
        int wn = -1;
-       extern workcmpf();
+       int wc;
 
        if (tTd(41, 1))
        {
 
        if (tTd(41, 1))
        {
@@ -596,6 +661,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;
        }
@@ -616,7 +683,7 @@ orderq(doall)
        {
                FILE *cf;
                register char *p;
        {
                FILE *cf;
                register char *p;
-               char lbuf[MAXNAME];
+               char lbuf[MAXNAME + 1];
                extern bool strcontainedin();
 
                /* is this an interesting entry? */
                extern bool strcontainedin();
 
                /* is this an interesting entry? */
@@ -633,6 +700,7 @@ orderq(doall)
                    !strcontainedin(QueueLimitId, d->d_name))
                        continue;
 
                    !strcontainedin(QueueLimitId, d->d_name))
                        continue;
 
+#ifdef PICKY_QF_NAME_CHECK
                /*
                **  Check queue name for plausibility.  This handles
                **  both old and new type ids.
                /*
                **  Check queue name for plausibility.  This handles
                **  both old and new type ids.
@@ -651,20 +719,21 @@ orderq(doall)
                {
                        if (Verbose)
                                printf("orderq: bogus qf name %s\n", d->d_name);
                {
                        if (Verbose)
                                printf("orderq: bogus qf name %s\n", d->d_name);
-#ifdef LOG
-                       if (LogLevel > 3)
-                               syslog(LOG_CRIT, "orderq: bogus qf name %s",
+# ifdef LOG
+                       if (LogLevel > 0)
+                               syslog(LOG_ALERT, "orderq: bogus qf name %s",
                                        d->d_name);
                                        d->d_name);
-#endif
-                       if (strlen(d->d_name) >= MAXNAME)
-                               d->d_name[MAXNAME - 1] = '\0';
+# endif
+                       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);
                        continue;
                }
                        strcpy(lbuf, d->d_name);
                        lbuf[0] = 'Q';
                        (void) rename(d->d_name, lbuf);
                        continue;
                }
+#endif
 
 
-               /* yes -- open control file (if not too many files) */
+               /* open control file (if not too many files) */
                if (++wn >= QUEUESIZE)
                        continue;
 
                if (++wn >= QUEUESIZE)
                        continue;
 
@@ -682,6 +751,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;
@@ -691,7 +762,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)
                {
@@ -711,7 +782,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;
@@ -729,17 +803,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);
 
 
-       qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf);
+       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;
+                       }
+               }
+
+               /*
+               **  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.
@@ -747,10 +878,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;
@@ -766,7 +899,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.
@@ -781,7 +914,7 @@ orderq(doall)
 **             none.
 */
 
 **             none.
 */
 
-workcmpf(a, b)
+workcmpf0(a, b)
        register WORK *a;
        register WORK *b;
 {
        register WORK *a;
        register WORK *b;
 {
@@ -789,11 +922,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.
@@ -839,6 +1052,11 @@ dowork(id, forkflag, requeueflag, e)
                        syserr("dowork: cannot fork");
                        return 0;
                }
                        syserr("dowork: cannot fork");
                        return 0;
                }
+               else if (pid > 0)
+               {
+                       /* parent -- clean out connection cache */
+                       mci_flush(FALSE, NULL);
+               }
        }
        else
        {
        }
        else
        {
@@ -862,6 +1080,7 @@ dowork(id, forkflag, requeueflag, e)
                e->e_errormode = EM_MAIL;
                e->e_id = id;
                GrabTo = UseErrorsTo = FALSE;
                e->e_errormode = EM_MAIL;
                e->e_id = id;
                GrabTo = UseErrorsTo = FALSE;
+               ExitStat = EX_OK;
                if (forkflag)
                {
                        disconnect(1, e);
                if (forkflag)
                {
                        disconnect(1, e);
@@ -877,24 +1096,43 @@ dowork(id, forkflag, requeueflag, e)
                e->e_header = NULL;
 
                /* read the queue control file -- return if locked */
                e->e_header = NULL;
 
                /* read the queue control file -- return if locked */
-               if (!readqf(e, !requeueflag))
+               if (!readqf(e))
                {
                        if (tTd(40, 4))
                                printf("readqf(%s) failed\n", e->e_id);
                        if (forkflag)
                                exit(EX_OK);
                        else
                {
                        if (tTd(40, 4))
                                printf("readqf(%s) failed\n", e->e_id);
                        if (forkflag)
                                exit(EX_OK);
                        else
-                               return;
+                               return 0;
                }
 
                e->e_flags |= EF_INQUEUE;
                }
 
                e->e_flags |= EF_INQUEUE;
-               eatheader(e, requeueflag);
 
 
-               if (requeueflag)
-                       queueup(e, TRUE, FALSE);
+               /* 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);
 
 
-               /* do the delivery */
-               sendall(e, SM_DELIVER);
+                       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);
+               }
 
                /* finish up and exit */
                if (forkflag)
 
                /* finish up and exit */
                if (forkflag)
@@ -910,8 +1148,6 @@ dowork(id, forkflag, requeueflag, e)
 **
 **     Parameters:
 **             e -- the envelope of the job to run.
 **
 **     Parameters:
 **             e -- the envelope of the job to run.
-**             announcefile -- if set, announce the name of the queue
-**                     file in error messages.
 **
 **     Returns:
 **             TRUE if it successfully read the queue file.
 **
 **     Returns:
 **             TRUE if it successfully read the queue file.
@@ -922,18 +1158,21 @@ dowork(id, forkflag, requeueflag, e)
 */
 
 bool
 */
 
 bool
-readqf(e, announcefile)
+readqf(e)
        register ENVELOPE *e;
        register ENVELOPE *e;
-       bool announcefile;
 {
        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];
        extern long atol();
        extern ADDRESS *setctluser();
        char qf[20];
        char buf[MAXLINE];
        extern long atol();
        extern ADDRESS *setctluser();
+       extern void loseqfile();
        extern ADDRESS *sendto();
 
        /*
        extern ADDRESS *sendto();
 
        /*
@@ -955,9 +1194,7 @@ readqf(e, announcefile)
        if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB))
        {
                /* being processed by another queuer */
        if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB))
        {
                /* being processed by another queuer */
-               if (tTd(40, 8))
-                       printf("readqf(%s): locked\n", qf);
-               if (Verbose)
+               if (Verbose || tTd(40, 8))
                        printf("%s: locked\n", e->e_id);
 # ifdef LOG
                if (LogLevel > 19)
                        printf("%s: locked\n", e->e_id);
 # ifdef LOG
                if (LogLevel > 19)
@@ -981,7 +1218,7 @@ readqf(e, announcefile)
                return FALSE;
        }
 
                return FALSE;
        }
 
-       if (st.st_uid != geteuid())
+       if (st.st_uid != geteuid() || bitset(S_IWOTH|S_IWGRP, st.st_mode))
        {
 # ifdef LOG
                if (LogLevel > 0)
        {
 # ifdef LOG
                if (LogLevel > 0)
@@ -992,7 +1229,7 @@ readqf(e, announcefile)
 # 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);
-               rename(qf, queuename(e, 'Q'));
+               loseqfile(e, "bogus file uid in mqueue");
                fclose(qfp);
                return FALSE;
        }
                fclose(qfp);
                return FALSE;
        }
@@ -1021,34 +1258,47 @@ readqf(e, announcefile)
 
        /* do basic system initialization */
        initsys(e);
 
        /* do basic system initialization */
        initsys(e);
+       define('i', e->e_id, e);
 
 
-       if (announcefile)
-               FileName = qf;
        LineNumber = 0;
        e->e_flags |= EF_GLOBALERRS;
        OpMode = MD_DELIVER;
        LineNumber = 0;
        e->e_flags |= EF_GLOBALERRS;
        OpMode = MD_DELIVER;
-       if (Verbose)
-               printf("\nRunning %s\n", e->e_id);
        ctladdr = NULL;
        ctladdr = NULL;
+       e->e_dfino = -1;
+       e->e_msgsize = -1;
        while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL)
        {
                register char *p;
        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);
+                       /* no longer used */
                        break;
 
                  case 'H':             /* header */
                        break;
 
                  case 'H':             /* header */
@@ -1068,21 +1318,26 @@ readqf(e, announcefile)
                        break;
 
                  case 'D':             /* data file name */
                        break;
 
                  case 'D':             /* data file name */
-                       e->e_df = newstr(&bp[1]);
-                       e->e_dfp = fopen(e->e_df, "r");
-                       if (e->e_dfp == NULL)
-                       {
-                               syserr("readqf: cannot open %s", e->e_df);
-                               e->e_msgsize = -1;
-                       }
-                       else if (fstat(fileno(e->e_dfp), &st) >= 0)
-                               e->e_msgsize = st.st_size;
+                       /* obsolete -- ignore */
                        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;
@@ -1099,10 +1354,18 @@ readqf(e, announcefile)
                                  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;
@@ -1111,10 +1374,10 @@ readqf(e, announcefile)
                        break;
 
                  default:
                        break;
 
                  default:
-                       syserr("readqf: bad line \"%s\"", e->e_id,
-                               LineNumber, bp);
+                       syserr("readqf: %s: line %d: bad line \"%s\"",
+                               qf, LineNumber, bp);
                        fclose(qfp);
                        fclose(qfp);
-                       rename(qf, queuename(e, 'Q'));
+                       loseqfile(e, "unrecognized line");
                        return FALSE;
                }
 
                        return FALSE;
                }
 
@@ -1122,8 +1385,6 @@ readqf(e, announcefile)
                        free(bp);
        }
 
                        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.
@@ -1134,6 +1395,30 @@ readqf(e, announcefile)
                errno = 0;
                e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
        }
                errno = 0;
                e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
        }
+       else
+       {
+               /*
+               **  Arrange to read the data file.
+               */
+
+               p = queuename(e, 'd');
+               e->e_dfp = fopen(p, "r");
+               if (e->e_dfp == NULL)
+               {
+                       syserr("readqf: cannot open %s", p);
+               }
+               else
+               {
+                       e->e_flags |= EF_HAS_DF;
+                       if (fstat(fileno(e->e_dfp), &st) >= 0)
+                       {
+                               e->e_msgsize = st.st_size;
+                               e->e_dfdev = st.st_dev;
+                               e->e_dfino = st.st_ino;
+                       }
+               }
+       }
+
        return TRUE;
 }
 \f/*
        return TRUE;
 }
 \f/*
@@ -1221,10 +1506,11 @@ printqueue()
        {
                struct stat st;
                auto time_t submittime = 0;
        {
                struct stat st;
                auto time_t submittime = 0;
-               long dfsize = -1;
+               long dfsize;
                int flags = 0;
                int flags = 0;
+               int qfver;
                char message[MAXLINE];
                char message[MAXLINE];
-               char bodytype[MAXNAME];
+               char bodytype[MAXNAME + 1];
 
                printf("%8s", w->w_name + 2);
                f = fopen(w->w_name, "r");
 
                printf("%8s", w->w_name + 2);
                f = fopen(w->w_name, "r");
@@ -1234,7 +1520,12 @@ printqueue()
                        errno = 0;
                        continue;
                }
                        errno = 0;
                        continue;
                }
-               if (!lockfile(fileno(f), w->w_name, NULL, LOCK_SH|LOCK_NB))
+               w->w_name[0] = 'd';
+               if (stat(w->w_name, &st) >= 0)
+                       dfsize = st.st_size;
+               else
+                       dfsize = -1;
+               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");
@@ -1243,6 +1534,7 @@ printqueue()
                errno = 0;
 
                message[0] = bodytype[0] = '\0';
                errno = 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;
@@ -1251,6 +1543,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;
@@ -1291,21 +1587,24 @@ 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 */
                                submittime = atol(&buf[1]);
                                break;
 
                                break;
 
                          case 'T':     /* creation time */
                                submittime = atol(&buf[1]);
                                break;
 
-                         case 'D':     /* data file name */
-                               if (stat(&buf[1], &st) >= 0)
-                                       dfsize = st.st_size;
-                               break;
-
                          case 'F':     /* flag bits */
                                for (p = &buf[1]; *p != '\0'; p++)
                                {
                          case 'F':     /* flag bits */
                                for (p = &buf[1]; *p != '\0'; p++)
                                {
@@ -1358,7 +1657,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];
+       static char buf[MAXNAME + 1];
 
        if (e->e_id == NULL)
        {
 
        if (e->e_id == NULL)
        {
@@ -1517,16 +1816,21 @@ setctluser(user)
                *p++ = '\0';
        if (*user != '\0' && (pw = getpwnam(user)) != 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_uid = pw->pw_uid;
                a->q_gid = pw->pw_gid;
-               a->q_user = newstr(user);
                a->q_flags |= QGOODUID;
        }
                a->q_flags |= QGOODUID;
        }
+
+       if (*user != '\0')
+               a->q_user = newstr(user);
+       else if (p != NULL)
+               a->q_user = newstr(p);
        else
        else
-       {
                a->q_user = newstr(DefUser);
                a->q_user = newstr(DefUser);
-       }
 
        a->q_flags |= QPRIMARY;         /* flag as a "ctladdr"  */
        a->q_mailer = LocalMailer;
 
        a->q_flags |= QPRIMARY;         /* flag as a "ctladdr"  */
        a->q_mailer = LocalMailer;
@@ -1536,3 +1840,35 @@ setctluser(user)
                a->q_paddr = newstr(p);
        return a;
 }
                a->q_paddr = newstr(p);
        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 *p;
+       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'));
+       p = queuename(e, 'Q');
+       if (rename(buf, p) < 0)
+               syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid());
+#ifdef LOG
+       else if (LogLevel > 0)
+               syslog(LOG_ALERT, "Losing %s: %s", buf, why);
+#endif
+}