This patch adds control of queue lifetimes to qmail.

This patch permits user control of the length of time a message will
live in the queue before being bounced.  For example, a very urgent
message might be sent with a queue lifetime of 1 hour.  If it is not
sent in that time, it will be bounced, and the user will know to try
some other means.  This is only a part of the problem of handling
urgent e-mail, since the queue lifetime is not passed on to the next
host.  But a partial solution can still be helpful in some
circumstances.

This patch also permits the queue lifetime of a bounce message to be
controlled separately from that of ordinary messages.  Bounce messages
which are not delivered quickly are often the result of spam to bogus
local addresses or otherwise have undeliverable sender addresses.
There is often no reason to let them sit in the mail queue for a long
time before double bouncing.


With this patch, you can set the environment variable
QMAILQUEUELIFETIME when calling qmail-inject.  It should be set to a
decimal string: the number of seconds the mail message should live in
the queue.  If a delivery of the mail message fails after that many
seconds have passed, any temporary error will be treated as a
permanent error, and the mail message will be bounced to the sender.

This patch adds two new control files:

bouncequeuelifetime: the number of seconds that a bounce message
should remain in the queue before it is double bounced.  The default
is 604800 (1 week).

maxqueuelifetime: the maximum number of seconds that
QMAILQUEUELIFETIME may be set to.  This also controls queuelifetime
and bouncequeuelifetime.  The default is 1209600 (2 weeks).

It would be possible to add another control file to specify the queue
lifetime of a mail message being relayed for a remote system, as
opposed to one that is injected locally, but I did not implement that.


DJB, please consider implementing similar functionality in the next
release of qmail.  Thanks.

Ian


diff -u qmail-1.03/INTERNALS qmail-1.03-lifetime/INTERNALS
--- qmail-1.03/INTERNALS        Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/INTERNALS       Tue May  2 14:28:42 2000
@@ -140,11 +140,12 @@
 
 7. Further notes
 
-Currently info/457 serves two purposes: first, it records the envelope
+Currently info/457 serves three purposes: first, it records the envelope
 sender; second, its modification time is used to decide when a message
-has been in the queue too long. In the future info/457 may store more
-information. Any non-backwards-compatible changes will be identified by
-version numbers.
+has been in the queue too long; third, it optionally records the
+lifetime of the message in the queue. In the future info/457 may store
+more information. Any non-backwards-compatible changes will be
+identified by version numbers.
 
 When qmail-queue has successfully placed a message into the queue, it
 pulls a trigger offered by qmail-send. Here is the current triggering
diff -u qmail-1.03/Makefile qmail-1.03-lifetime/Makefile
--- qmail-1.03/Makefile Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/Makefile        Thu Apr 27 10:01:28 2000
@@ -1639,10 +1639,10 @@
 qreceipt: \
 load qreceipt.o headerbody.o hfield.o quote.o token822.o qmail.o \
 getln.a fd.a wait.a sig.a env.a stralloc.a alloc.a substdio.a error.a \
-str.a auto_qmail.o
+str.a fs.a auto_qmail.o
        ./load qreceipt headerbody.o hfield.o quote.o token822.o \
        qmail.o getln.a fd.a wait.a sig.a env.a stralloc.a alloc.a \
-       substdio.a error.a str.a auto_qmail.o 
+       substdio.a error.a str.a fs.a auto_qmail.o 
 
 qreceipt.0: \
 qreceipt.1
diff -u qmail-1.03/condredirect.c qmail-1.03-lifetime/condredirect.c
--- qmail-1.03/condredirect.c   Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/condredirect.c  Thu Apr 27 09:36:23 2000
@@ -77,7 +77,7 @@
  
   num[fmt_ulong(num,qmail_qp(&qqt))] = 0;
 
-  qmail_from(&qqt,sender);
+  qmail_from(&qqt,sender,0);
   qmail_to(&qqt,argv[1]);
   qqx = qmail_close(&qqt);
   if (*qqx) strerr_die2x(*qqx == 'D' ? 100 : 111,FATAL,qqx + 1);
diff -u qmail-1.03/forward.c qmail-1.03-lifetime/forward.c
--- qmail-1.03/forward.c        Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/forward.c       Thu Apr 27 09:36:24 2000
@@ -52,7 +52,7 @@
 
   num[fmt_ulong(num,qmail_qp(&qqt))] = 0;
  
-  qmail_from(&qqt,sender);
+  qmail_from(&qqt,sender,0);
   while (*++argv) qmail_to(&qqt,*argv);
   qqx = qmail_close(&qqt);
   if (*qqx) strerr_die2x(*qqx == 'D' ? 100 : 111,FATAL,qqx + 1);
diff -u qmail-1.03/qmail-control.9 qmail-1.03-lifetime/qmail-control.9
--- qmail-1.03/qmail-control.9  Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/qmail-control.9 Tue May  2 14:24:32 2000
@@ -43,6 +43,7 @@
 .I badmailfrom \fR(none)       \fRqmail-smtpd
 .I bouncefrom  \fRMAILER-DAEMON        \fRqmail-send
 .I bouncehost  \fIme   \fRqmail-send
+.I bouncequeuelifetime \fR604800       \fRqmail-send
 .I concurrencylocal    \fR10   \fRqmail-send
 .I concurrencyremote   \fR20   \fRqmail-send
 .I defaultdomain       \fIme   \fRqmail-inject
@@ -55,6 +56,7 @@
 .I idhost      \fIme   \fRqmail-inject
 .I localiphost \fIme   \fRqmail-smtpd
 .I locals      \fIme   \fRqmail-send
+.I maxqueuelifetime    \fR1209600      \fRqmail-send
 .I morercpthosts       \fR(none)       \fRqmail-smtpd
 .I percenthack \fR(none)       \fRqmail-send
 .I plusdomain  \fIme   \fRqmail-inject
diff -u qmail-1.03/qmail-inject.8 qmail-1.03-lifetime/qmail-inject.8
--- qmail-1.03/qmail-inject.8   Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/qmail-inject.8  Tue May  2 09:11:12 2000
@@ -91,6 +91,11 @@
 does not add Mail-Followup-To
 to a message that already has one.
 
+If
+.B QMAILQUEUELIFETIME
+is set, it sets the maximum number of seconds the mail message will
+spend in the mail queue before being bounced back to the sender.
+
 The
 .B QMAILINJECT
 environment variable
diff -u qmail-1.03/qmail-inject.c qmail-1.03-lifetime/qmail-inject.c
--- qmail-1.03/qmail-inject.c   Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/qmail-inject.c  Thu Apr 27 09:57:22 2000
@@ -7,6 +7,7 @@
 #include "alloc.h"
 #include "str.h"
 #include "fmt.h"
+#include "scan.h"
 #include "hfield.h"
 #include "token822.h"
 #include "control.h"
@@ -39,6 +40,7 @@
 int mailusertokentype;
 char *mailrhost;
 char *mailruser;
+int lifetime = 0;
 
 stralloc control_idhost = {0};
 stralloc control_defaultdomain = {0};
@@ -99,7 +101,7 @@
    int i;
 
    if (!stralloc_0(&sender)) die_nomem();
-   qmail_from(&qqt,sender.s);
+   qmail_from(&qqt,sender.s,lifetime);
 
    for (i = 0;i < reciplist.len;++i)
     {
@@ -689,6 +691,7 @@
  int i;
  int opt;
  int recipstrategy;
+ char *envlifetime;
 
  sig_pipeignore();
 
@@ -721,6 +724,14 @@
  if (quote_need(mailuser,str_len(mailuser))) mailusertokentype = TOKEN822_QUOTE;
  mailruser = env_get("QMAILSUSER");
  if (!mailruser) mailruser = mailuser;
+
+ envlifetime = env_get("QMAILQUEUELIFETIME");
+ if (envlifetime)
+  {
+   unsigned long ult;
+   scan_ulong(envlifetime,&ult);
+   lifetime = ult;
+  }
 
  for (i = 0;i < H_NUM;++i) htypeseen[i] = 0;
 
diff -u qmail-1.03/qmail-local.c qmail-1.03-lifetime/qmail-local.c
--- qmail-1.03/qmail-local.c    Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/qmail-local.c   Thu Apr 27 09:36:28 2000
@@ -282,7 +282,7 @@
    qmail_put(&qqt,messline.s,messline.len);
   }
  while (match);
- qmail_from(&qqt,ueo.s);
+ qmail_from(&qqt,ueo.s,0);
  while (*recips) qmail_to(&qqt,*recips++);
  qqx = qmail_close(&qqt);
  if (!*qqx) return;
diff -u qmail-1.03/qmail-qmqpd.c qmail-1.03-lifetime/qmail-qmqpd.c
--- qmail-1.03/qmail-qmqpd.c    Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/qmail-qmqpd.c   Thu Apr 27 09:36:31 2000
@@ -133,9 +133,9 @@
   getcomma();
 
   if (getbuf())
-    qmail_from(&qq,buf);
+    qmail_from(&qq,buf,0);
   else {
-    qmail_from(&qq,"");
+    qmail_from(&qq,"",0);
     qmail_fail(&qq);
     flagok = 0;
   }
diff -u qmail-1.03/qmail-qmtpd.c qmail-1.03-lifetime/qmail-qmtpd.c
--- qmail-1.03/qmail-qmtpd.c    Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/qmail-qmtpd.c   Thu Apr 27 09:36:33 2000
@@ -180,7 +180,7 @@
     getcomma();
  
     flagbother = 0;
-    qmail_from(&qq,buf);
+    qmail_from(&qq,buf,0);
     if (!flagsenderok) qmail_fail(&qq);
  
     biglen = getlen();
diff -u qmail-1.03/qmail-qread.c qmail-1.03-lifetime/qmail-qread.c
--- qmail-1.03/qmail-qread.c    Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/qmail-qread.c   Tue May  2 08:44:53 2000
@@ -50,6 +50,7 @@
 
 char inbuf[1024];
 stralloc sender = {0};
+stralloc lifetime = {0};
 
 unsigned long id;
 datetime_sec qtime;
@@ -73,6 +74,11 @@
  i = fmt_str(s,"  <"); len += i; if (s) s += i;
  i = fmt_str(s,sender.s + 1); len += i; if (s) s += i;
  i = fmt_str(s,"> "); len += i; if (s) s += i;
+ if (lifetime.len)
+  {
+   i = fmt_str(s," lifetime "); len += i; if (s) s += i;
+   i = fmt_str(s,lifetime.s + 1); len += i; if (s) s += i;
+  }
  if (flagbounce)
   {
    i = fmt_str(s," bouncing"); len += i; if (s) s += i;
@@ -135,6 +141,11 @@
      if (fd == -1) { err(id); continue; }
      substdio_fdbuf(&ss,read,fd,inbuf,sizeof(inbuf));
      if (getln(&ss,&sender,&match,0) == -1) die_nomem();
+     if (sender.s[0] == 'L')
+      {
+       stralloc_copy(&lifetime,&sender);
+       if (getln(&ss,&sender,&match,0) == -1) die_nomem();
+      }
      if (fstat(fd,&st) == -1) { close(fd); err(id); continue; }
      close(fd);
      qtime = st.st_mtime;
diff -u qmail-1.03/qmail-queue.8 qmail-1.03-lifetime/qmail-queue.8
--- qmail-1.03/qmail-queue.8    Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/qmail-queue.8   Tue May  2 09:13:37 2000
@@ -12,8 +12,12 @@
 .BR qmail-send .
 
 The envelope information is
+an optional lifetime
+followed by
 an envelope sender address
 followed by a list of envelope recipient addresses.
+The optional lifetime is a decimal string, preceded by the letter L
+and terminated by a 0 byte.
 The sender address is preceded by the letter F
 and terminated by a 0 byte.
 Each recipient address is preceded by the letter T
@@ -67,7 +71,7 @@
 indicate permanent errors:
 .TP 5
 .B 11
-Address too long.
+Address or lifetime string too long.
 .TP
 .B 31
 Mail server permanently refuses to send the message to any recipients.
diff -u qmail-1.03/qmail-queue.c qmail-1.03-lifetime/qmail-queue.c
--- qmail-1.03/qmail-queue.c    Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/qmail-queue.c   Thu Apr 27 09:39:34 2000
@@ -217,6 +217,18 @@
  if (substdio_bput(&ssout,"",1) == -1) die_write();
 
  if (substdio_get(&ssin,&ch,1) < 1) die_read();
+ if (ch == 'L')
+  {
+   if (substdio_bput(&ssout,&ch,1) == -1) die_write();
+   for (len = 0;len < FMT_ULONG+1;++len)
+    {
+     if (substdio_get(&ssin,&ch,1) < 1) die_read();
+     if (substdio_put(&ssout,&ch,1) == -1) die_write();
+     if (!ch) break;
+    }
+   if (len >= FMT_ULONG+1) die(11);
+   if (substdio_get(&ssin,&ch,1) < 1) die_read();
+  }
  if (ch != 'F') die(91);
  if (substdio_bput(&ssout,&ch,1) == -1) die_write();
  for (len = 0;len < ADDR;++len)
diff -u qmail-1.03/qmail-send.9 qmail-1.03-lifetime/qmail-send.9
--- qmail-1.03/qmail-send.9     Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/qmail-send.9    Tue May  2 14:23:22 2000
@@ -77,6 +77,10 @@
 .B From: \fIbouncefrom\fB@\fIbouncehost\fR,
 although its envelope sender is empty.
 .TP 5
+.I bouncequeuelifetime
+The number of seconds a bounce message will stay in the queue before
+it is double-bounced.
+.TP 5
 .I concurrencylocal
 Maximum number of simultaneous local delivery attempts.
 Default: 10.
@@ -147,6 +151,11 @@
 is listed in
 .IR locals .
 .TP 5
+.I maxqueuelifetime
+The maximum number of seconds a message can stay in the queue.  This
+sets a limit to the use of QMAILQUEUELIFETIME in
+.BR qmail-inject .
+.TP 5
 .I percenthack
 List of domain names where the percent hack is applied.
 If
@@ -242,5 +251,6 @@
 qmail-log(5),
 qmail-queue(8),
 qmail-clean(8),
+qmail-inject(8),
 qmail-lspawn(8),
 qmail-rspawn(8)
diff -u qmail-1.03/qmail-send.c qmail-1.03-lifetime/qmail-send.c
--- qmail-1.03/qmail-send.c     Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/qmail-send.c    Thu Apr 27 09:53:31 2000
@@ -43,6 +43,8 @@
 #define OSSIFIED 129600 /* 36 hours; _must_ exceed q-q's DEATH (24 hours) */
 
 int lifetime = 604800;
+int maxlifetime = 1209600;
+int bouncelifetime = 604800;
 
 stralloc percenthack = {0};
 struct constmap mappercenthack;
@@ -195,9 +197,10 @@
 
 /* this file is too long ---------------------------------------------- INFO */
 
-int getinfo(sa,dt,id)
+int getinfo(sa,dt,lt,id)
 stralloc *sa;
 datetime_sec *dt;
+int *lt;
 unsigned long id;
 {
  int fdinfo;
@@ -213,6 +216,16 @@
  if (fstat(fdinfo,&st) == -1) { close(fdinfo); return 0; }
  substdio_fdbuf(&ss,read,fdinfo,buf,sizeof(buf));
  if (getln(&ss,&line,&match,'\0') == -1) { close(fdinfo); return 0; }
+ if (!match) { close(fdinfo); return 0; }
+ *lt = lifetime;
+ if (line.s[0] == 'L')
+  {
+   unsigned long ult;
+   scan_ulong(line.s+1,&ult);
+   if (ult > maxlifetime) ult = maxlifetime;
+   *lt = ult;
+   if (getln(&ss,&line,&match,'\0') == -1) { close(fdinfo); return 0; }
+  }
  close(fdinfo);
  if (!match) return 0;
  if (line.s[0] != 'F') return 0;
@@ -660,9 +673,10 @@
  static stralloc sender = {0};
  static stralloc quoted = {0};
  datetime_sec birth;
+ int lt;
  unsigned long qp;
 
- if (!getinfo(&sender,&birth,id)) return 0; /* XXX: print warning */
+ if (!getinfo(&sender,&birth,&lt,id)) return 0; /* XXX: print warning */
 
  /* owner-@host-@[] -> owner-@host */
  if (sender.len >= 5)
@@ -748,7 +762,7 @@
        qmail_fail(&qqt);
     }
 
-   qmail_from(&qqt,bouncesender);
+   qmail_from(&qqt,bouncesender,bouncelifetime);
    qmail_to(&qqt,bouncerecip);
    if (*qmail_close(&qqt))
     { log1("warning: trouble injecting bounce message, will try later\n"); return 0; }
@@ -1057,6 +1071,7 @@
 int c;
 {
  datetime_sec birth;
+ int lt;
  struct prioq_elt pe;
  static stralloc line = {0};
  int match;
@@ -1074,12 +1089,12 @@
    pass[c].mpos = 0;
    pass[c].fd = open_read(fn.s);
    if (pass[c].fd == -1) goto trouble;
-   if (!getinfo(&line,&birth,pe.id)) { close(pass[c].fd); goto trouble; }
+   if (!getinfo(&line,&birth,&lt,pe.id)) { close(pass[c].fd); goto trouble; }
    pass[c].id = pe.id;
    substdio_fdbuf(&pass[c].ss,read,pass[c].fd,pass[c].buf,sizeof(pass[c].buf));
    pass[c].j = job_open(pe.id,c);
    jo[pass[c].j].retry = nextretry(birth,c);
-   jo[pass[c].j].flagdying = (recent > birth + lifetime);
+   jo[pass[c].j].flagdying = (recent > birth + lt);
    while (!stralloc_copy(&jo[pass[c].j].sender,&line)) nomem();
   }
 
@@ -1346,6 +1361,13 @@
      case 'p':
        scan_ulong(todoline.s + 1,&pid);
        break;
+     case 'L':
+       if (substdio_putflush(&ssinfo,todoline.s,todoline.len) == -1)
+       {
+        fnmake_info(id);
+        log3("warning: trouble writing to ",fn.s,"\n"); goto fail;
+       }
+       break;
      case 'F':
        if (substdio_putflush(&ssinfo,todoline.s,todoline.len) == -1)
        {
@@ -1443,6 +1465,10 @@
 
 int getcontrols() { if (control_init() == -1) return 0;
  if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0;
+ if (control_readint(&maxlifetime,"control/maxqueuelifetime") == -1) return 0;
+ if (control_readint(&bouncelifetime,"control/bouncequeuelifetime") == -1) return 0;
+ if (lifetime > maxlifetime) lifetime = maxlifetime;
+ if (bouncelifetime > maxlifetime) bouncelifetime = maxlifetime;
  if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0;
  if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0;
  if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0;
diff -u qmail-1.03/qmail-smtpd.c qmail-1.03-lifetime/qmail-smtpd.c
--- qmail-1.03/qmail-smtpd.c    Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/qmail-smtpd.c   Thu Apr 27 09:36:38 2000
@@ -382,7 +382,7 @@
   blast(&hops);
   hops = (hops >= MAXHOPS);
   if (hops) qmail_fail(&qqt);
-  qmail_from(&qqt,mailfrom.s);
+  qmail_from(&qqt,mailfrom.s,0);
   qmail_put(&qqt,rcptto.s,rcptto.len);
  
   qqx = qmail_close(&qqt);
diff -u qmail-1.03/qmail.c qmail-1.03-lifetime/qmail.c
--- qmail-1.03/qmail.c  Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/qmail.c Thu Apr 27 09:37:31 2000
@@ -4,6 +4,7 @@
 #include "exit.h"
 #include "fork.h"
 #include "fd.h"
+#include "fmt.h"
 #include "qmail.h"
 #include "auto_qmail.h"
 
@@ -60,11 +61,19 @@
   if (!qq->flagerr) if (substdio_puts(&qq->ss,s) == -1) qq->flagerr = 1;
 }
 
-void qmail_from(qq,s) struct qmail *qq; char *s;
+void qmail_from(qq,s,lt) struct qmail *qq; char *s; int lt;
 {
   if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1;
   close(qq->fdm);
   substdio_fdbuf(&qq->ss,write,qq->fde,qq->buf,sizeof(qq->buf));
+  if (lt)
+   {
+    char strnum[FMT_ULONG];
+    qmail_put(qq,"L",1);
+    strnum[fmt_ulong(strnum,(unsigned long) lt)] = 0;
+    qmail_puts(qq,strnum);
+    qmail_put(qq,"",1);
+   }
   qmail_put(qq,"F",1);
   qmail_puts(qq,s);
   qmail_put(qq,"",1);
diff -u qmail-1.03/qreceipt.c qmail-1.03-lifetime/qreceipt.c
--- qmail-1.03/qreceipt.c       Mon Jun 15 03:53:16 1998
+++ qmail-1.03-lifetime/qreceipt.c      Thu Apr 27 09:36:44 2000
@@ -88,7 +88,7 @@
    qmail_put(&qqt,messageid.s,messageid.len);
   }
 
- qmail_from(&qqt,"");
+ qmail_from(&qqt,"",0);
  qmail_to(&qqt,returnpath);
  qqx = qmail_close(&qqt);
 

Reply via email to