[packages/exim] - rel 2; include git patch (contains fix for exim crash while handling hashed queue)

arekm arekm at pld-linux.org
Thu Sep 10 10:23:44 CEST 2015


commit 4022a9ecb0e07f48081d5a0bdcd7e81bbcb71728
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date:   Thu Sep 10 10:23:35 2015 +0200

    - rel 2; include git patch (contains fix for exim crash while handling hashed queue)

 exim-git.patch | 3697 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 exim.spec      |    6 +-
 2 files changed, 3702 insertions(+), 1 deletion(-)
---
diff --git a/exim.spec b/exim.spec
index 6067837..c612712 100644
--- a/exim.spec
+++ b/exim.spec
@@ -21,7 +21,7 @@ Summary(pl.UTF-8):	Agent Transferu Poczty Uniwersytetu w Cambridge
 Summary(pt_BR.UTF-8):	Servidor de correio eletrônico exim
 Name:		exim
 Version:	4.86
-Release:	1
+Release:	2
 Epoch:		2
 License:	GPL
 Group:		Networking/Daemons/SMTP
@@ -45,6 +45,8 @@ Source14:	ftp://ftp.exim.org/pub/exim/exim4/old/config.samples.tar.bz2
 # Source14-md5:	4b93321938a800caa6127c48ad60a42b
 Source15:	%{name}4-smtp.pamd
 Source16:	%{name}on.png
+# git log -p exim-4_86..master --reverse -- . ":(exclude)doc/doc-*" ":(exclude)test" ":(exclude).*" > exim-git.patch
+Patch100:	%{name}-git.patch
 Patch0:		%{name}4-EDITME.patch
 Patch1:		%{name}4-monitor-EDITME.patch
 Patch2:		%{name}4-cflags.patch
@@ -165,6 +167,8 @@ Pliki nagłówkowe dla Exima.
 
 %prep
 %setup -q -a1 -a7
+%patch100 -p2
+
 %patch0 -p1
 %patch1 -p1
 %patch2 -p1
diff --git a/exim-git.patch b/exim-git.patch
new file mode 100644
index 0000000..7b70b89
--- /dev/null
+++ b/exim-git.patch
@@ -0,0 +1,3697 @@
+commit aa7751be078fe9a20efa2cf2a8856fadb98f4178
+Author: Jeremy Harris <jgh146exb at wizmail.org>
+Date:   Sun Jun 28 15:14:02 2015 +0100
+
+    Compiler quietening
+
+diff --git a/src/src/dns.c b/src/src/dns.c
+index 64958d9..a239bec 100644
+--- a/src/src/dns.c
++++ b/src/src/dns.c
+@@ -40,7 +40,6 @@ fakens_search(const uschar *domain, int type, uschar *answerptr, int size)
+ {
+ int len = Ustrlen(domain);
+ int asize = size;                  /* Locally modified */
+-uschar *endname;
+ uschar name[256];
+ uschar utilname[256];
+ uschar *aptr = answerptr;          /* Locally modified */
+@@ -51,7 +50,6 @@ struct stat statbuf;
+ if (domain[len - 1] == '.') len--;
+ Ustrncpy(name, domain, len);
+ name[len] = 0;
+-endname = name + len;
+ 
+ /* Look for the fakens utility, and if it exists, call it. */
+ 
+@@ -86,7 +84,7 @@ if (stat(CS utilname, &statbuf) >= 0)
+     asize -= rc;      /* may need to be passed on to res_search(). */
+     }
+ 
+-  /* If we ran out of output buffer before exhasting the return,
++  /* If we ran out of output buffer before exhausting the return,
+   carry on reading and counting it. */
+ 
+   if (asize == 0)
+diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
+index cf0a5d6..711ee86 100644
+--- a/src/src/smtp_in.c
++++ b/src/src/smtp_in.c
+@@ -3293,7 +3293,7 @@ while (done <= 0)
+   pid_t pid;
+   int start, end, sender_domain, recipient_domain;
+   int ptr, size, rc;
+-  int c, i;
++  int c;
+   auth_instance *au;
+   uschar *orcpt = NULL;
+   int flags;
+diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
+index e1dcd77..d8377fd 100644
+--- a/src/src/tls-openssl.c
++++ b/src/src/tls-openssl.c
+@@ -708,7 +708,7 @@ if (  (nid = OBJ_sn2nid       (CCS exp_curve)) == NID_undef
+ 
+ if (!(ecdh = EC_KEY_new_by_curve_name(nid)))
+   {
+-  tls_error("Unable to create ec curve", host, NULL);
++  tls_error(US"Unable to create ec curve", host, NULL);
+   return FALSE;
+   }
+ 
+diff --git a/src/src/utf8.c b/src/src/utf8.c
+index a0ec003..8a7cf38 100644
+--- a/src/src/utf8.c
++++ b/src/src/utf8.c
+@@ -127,7 +127,7 @@ if ((rc = punycode_decode(p_len, CCS alabel+4, &p_len, p, NULL)) != PUNYCODE_SUC
+   return NULL;
+   }
+ 
+-s = stringprep_ucs4_to_utf8(p, p_len, NULL, &p_len);
++s = US stringprep_ucs4_to_utf8(p, p_len, NULL, &p_len);
+ res = string_copyn(s, p_len);
+ free(s);
+ return res;
+
+commit 5b881b5a8e0d7bc540f4b63cc9559d2cb1775965
+Author: Heiko Schlittermann (HS12-RIPE) <hs at schlittermann.de>
+Date:   Thu Jul 30 09:43:51 2015 +0200
+
+    Docs: Add a note about the maximum spam bar length
+
+diff --git a/src/src/spam.h b/src/src/spam.h
+index 05ab655..2fe7380 100644
+--- a/src/src/spam.h
++++ b/src/src/spam.h
+@@ -12,7 +12,8 @@
+ /* timeout for reading and writing spamd */
+ #define SPAMD_TIMEOUT 120
+ 
+-/* maximum length of the spam bar */
++/* maximum length of the spam bar, please update the
++ * spec, the max length is mentioned there */
+ #define MAX_SPAM_BAR_CHARS 50
+ 
+ /* SHUT_WR seems to be undefined on Unixware ? */
+
+commit 98716abe2b636d275e866f3ad6374cb70bf6e504
+Author: Jeremy Harris <jgh146exb at wizmail.org>
+Date:   Sun Aug 2 13:44:31 2015 +0100
+
+    Testsuite: Add testcase for OCSP-nonaware client, to supporting server.  Bug 1664
+    
+    The logfile here is for (I hope) the passing case, though the fixed GnuTLS library
+    is not yet available.  Also due to the bug, client-gnutls is not usable for the
+    test; client-openssl must be used - meaning that a GnuTLS-only system cannot run
+    the testcase:
+    
+      OCSP-GnuTLS/5650 OCSP stapling, server
+      ** Command 15 ("client-ssl", starting at line 98)
+      ** Return code 127 (expected 0)
+
+diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
+index 61ed0e8..e2ac17c 100644
+--- a/src/src/tls-gnu.c
++++ b/src/src/tls-gnu.c
+@@ -842,7 +842,7 @@ if (  !host	/* server */
+   gnutls_certificate_set_ocsp_status_request_function(state->x509_cred,
+     server_ocsp_stapling_cb, state->exp_tls_ocsp_file);
+ 
+-  DEBUG(D_tls) debug_printf("Set OCSP response file %s\n", &state->exp_tls_ocsp_file);
++  DEBUG(D_tls) debug_printf("OCSP response file = %s\n", state->exp_tls_ocsp_file);
+   }
+ #endif
+ 
+
+commit 9196d5bf543d75a81ae0825a352920d27241c325
+Author: Jeremy Harris <jgh146exb at wizmail.org>
+Date:   Sun Aug 2 13:53:15 2015 +0100
+
+    GnuTLS: avoid using OCSP on buggy library versions.  Bug 1664
+
+diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
+index e2ac17c..8aabc5c 100644
+--- a/src/src/tls-gnu.c
++++ b/src/src/tls-gnu.c
+@@ -176,6 +176,8 @@ static const char * const exim_default_gnutls_priority = "NORMAL";
+ 
+ static BOOL exim_gnutls_base_init_done = FALSE;
+ 
++static BOOL gnutls_buggy_ocsp = FALSE;
++
+ 
+ /* ------------------------------------------------------------------------ */
+ /* macros */
+@@ -831,18 +833,25 @@ if (  !host	/* server */
+    && tls_ocsp_file
+    )
+   {
+-  if (!expand_check(tls_ocsp_file, US"tls_ocsp_file",
+-	&state->exp_tls_ocsp_file))
+-    return DEFER;
++  if (gnutls_buggy_ocsp)
++    {
++    DEBUG(D_tls) debug_printf("GnuTLS library is buggy for OCSP; avoiding\n");
++    }
++  else
++    {
++    if (!expand_check(tls_ocsp_file, US"tls_ocsp_file",
++	  &state->exp_tls_ocsp_file))
++      return DEFER;
+ 
+-  /* Use the full callback method for stapling just to get observability.
+-  More efficient would be to read the file once only, if it never changed
+-  (due to SNI). Would need restart on file update, or watch datestamp.  */
++    /* Use the full callback method for stapling just to get observability.
++    More efficient would be to read the file once only, if it never changed
++    (due to SNI). Would need restart on file update, or watch datestamp.  */
+ 
+-  gnutls_certificate_set_ocsp_status_request_function(state->x509_cred,
+-    server_ocsp_stapling_cb, state->exp_tls_ocsp_file);
++    gnutls_certificate_set_ocsp_status_request_function(state->x509_cred,
++      server_ocsp_stapling_cb, state->exp_tls_ocsp_file);
+ 
+-  DEBUG(D_tls) debug_printf("OCSP response file = %s\n", state->exp_tls_ocsp_file);
++    DEBUG(D_tls) debug_printf("OCSP response file = %s\n", state->exp_tls_ocsp_file);
++    }
+   }
+ #endif
+ 
+@@ -1011,6 +1020,35 @@ return OK;
+ *            Initialize for GnuTLS               *
+ *************************************************/
+ 
++
++static BOOL
++tls_is_buggy_ocsp(void)
++{
++const uschar * s;
++uschar maj, mid, mic;
++
++s = CUS gnutls_check_version(NULL);
++maj = atoi(CCS s);
++if (maj == 3)
++  {
++  while (*s && *s != '.') s++;
++  mid = atoi(CCS ++s);
++  if (mid <= 2)
++    return TRUE;
++  else if (mid >= 5)
++    return FALSE;
++  else
++    {
++    while (*s && *s != '.') s++;
++    mic = atoi(CCS ++s);
++    return mic <= (mid == 3 ? 16 : 3);
++    }
++  }
++return FALSE;
++}
++
++
++
+ /* Called from both server and client code. In the case of a server, errors
+ before actual TLS negotiation return DEFER.
+ 
+@@ -1074,6 +1112,9 @@ if (!exim_gnutls_base_init_done)
+     }
+ #endif
+ 
++  if ((gnutls_buggy_ocsp = tls_is_buggy_ocsp()))
++    log_write(0, LOG_MAIN, "OCSP unusable with this GnuTLS library version");
++
+   exim_gnutls_base_init_done = TRUE;
+   }
+ 
+
+commit 63f0dbe0ca0aba7be3bc8807f45c8703a1cfafe1
+Author: Jeremy Harris <jgh146exb at wizmail.org>
+Date:   Thu Aug 6 21:38:33 2015 +0100
+
+    OpenSSL: fix complile on pre-EC-capable library versions
+
+diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
+index d8377fd..b1dccb8 100644
+--- a/src/src/tls-openssl.c
++++ b/src/src/tls-openssl.c
+@@ -659,15 +659,15 @@ Returns:    TRUE if OK (nothing to set up, or setup worked)
+ static BOOL
+ init_ecdh(SSL_CTX * sctx, host_item * host)
+ {
++#ifdef OPENSSL_NO_ECDH
++return TRUE;
++#else
++
+ EC_KEY * ecdh;
+ uschar * exp_curve;
+ int nid;
+ BOOL rv;
+ 
+-#ifdef OPENSSL_NO_ECDH
+-return TRUE;
+-#else
+-
+ if (host)	/* No ECDH setup for clients, only for servers */
+   return TRUE;
+ 
+
+commit 755762fd4c420cabbcba4a9c79947e926fa82219
+Author: Heiko Schlittermann (HS12-RIPE) <hs at schlittermann.de>
+Date:   Sun Aug 9 23:29:44 2015 +0200
+
+    Compiler quietening
+
+diff --git a/src/src/malware.c b/src/src/malware.c
+index 141c6ea..96af1e8 100644
+--- a/src/src/malware.c
++++ b/src/src/malware.c
+@@ -886,7 +886,7 @@ if (!malware_ok)
+ 	      string_sprintf("unable to read result (%s)", strerror(errno)),
+ 	      sock);
+ 
+-	  for (p[bread] = '\0'; q = Ustrchr(p, '\n'); p = q+1)
++	  for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1)
+ 	    {
+ 	    *q = '\0';
+ 
+@@ -1880,6 +1880,9 @@ if (!malware_ok)
+ 
+ 	      /* here for any unexpected response from the scanner */
+ 	      goto endloop;
++
++	    case AVA_DONE:	log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
++			    __FILE__, __LINE__, __FUNCTION__);
+ 	    }
+ 	  }
+ 	}
+diff --git a/src/src/spam.c b/src/src/spam.c
+index ca8d207..457aa3a 100644
+--- a/src/src/spam.c
++++ b/src/src/spam.c
+@@ -297,7 +297,7 @@ start = time(NULL);
+     sd = (spamd_address_container *)store_get(sizeof(spamd_address_container));
+ 
+     for (sublist = address, args = 0, spamd_param_init(sd);
+-	 s = string_nextinlist(&sublist, &sublist_sep, NULL, 0);
++	 (s = string_nextinlist(&sublist, &sublist_sep, NULL, 0));
+ 	 args++
+ 	 )
+       {
+diff --git a/src/src/tlscert-openssl.c b/src/src/tlscert-openssl.c
+index 72808a7..19db040 100644
+--- a/src/src/tlscert-openssl.c
++++ b/src/src/tlscert-openssl.c
+@@ -127,7 +127,7 @@ else
+   {
+   struct tm tm;
+   struct tm * tm_p = &tm;
+-  BOOL mod_tz;
++  BOOL mod_tz = TRUE;
+   uschar * tz = to_tz(US"GMT0");    /* need to call strptime with baseline TZ */
+ 
+   /* Parse OpenSSL ASN1_TIME_print output.  A shame there seems to
+@@ -164,7 +164,7 @@ else
+       }
+     }
+ 
+-  if (mod_tz);
++  if (mod_tz)
+     restore_tz(tz);
+   }
+ BIO_free(bp);
+
+commit 4bd6107db73131e1b48f1902833fd7c637c08bda
+Author: Heiko Schlittermann (HS12-RIPE) <hs at schlittermann.de>
+Date:   Mon Aug 10 00:39:36 2015 +0200
+
+    Really re-select() when interrupted.
+
+diff --git a/src/src/ip.c b/src/src/ip.c
+index ead7299..cb54f16 100644
+--- a/src/src/ip.c
++++ b/src/src/ip.c
+@@ -499,7 +499,7 @@ do
+ 
+   /* If the socket is ready, break out of the loop. */
+   }
+-while (!FD_ISSET(fd, &select_inset));
++while (rc < 0 || !FD_ISSET(fd, &select_inset));
+ return TRUE;
+ }
+ 
+
+commit 505d976aa23de4294751162dee6466e335c96fbf
+Author: Heiko Schlittermann (HS12-RIPE) <hs at schlittermann.de>
+Date:   Tue Aug 11 09:13:11 2015 +0200
+
+    Build: Make test_{os,parse,dbfn,string} work
+
+diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base
+index 1d5a5f6..95110e6 100644
+--- a/src/OS/Makefile-Base
++++ b/src/OS/Makefile-Base
+@@ -743,11 +743,11 @@ sa-os.o:         $(HDRS) os.c
+ # These are the test targets themselves
+ 
+ test_dbfn:   config.h dbfn.c dummies.o sa-globals.o sa-os.o store.o \
+-	       string.o tod.o version.o
++	       string.o tod.o version.o utf8.o
+ 	$(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE dbfn.c
+ 	$(LNCC) -o test_dbfn $(LFLAGS) dbfn.o \
+ 	  dummies.o sa-globals.o sa-os.o store.o string.o \
+-	  tod.o version.o $(LIBS) $(DBMLIB)
++	  tod.o version.o utf8.o $(LIBS) $(DBMLIB) $(LDFLAGS)
+ 	rm -f dbfn.o
+ 
+ test_host:   config.h child.c host.c dns.c dummies.c sa-globals.o os.o \
+@@ -761,23 +761,24 @@ test_host:   config.h child.c host.c dns.c dummies.c sa-globals.o os.o \
+ 	  tod.o tree.o $(LIBS) $(LIBRESOLV)
+ 	rm -f child.o dummies.o host.o dns.o
+ 
+-test_os:     os.h os.c dummies.o sa-globals.o store.o string.o tod.o
++test_os:     os.h os.c dummies.o sa-globals.o store.o string.o tod.o utf8.o
+ 	$(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE os.c
+ 	$(LNCC) -o test_os $(LFLAGS) os.o dummies.o \
+-	  sa-globals.o store.o string.o tod.o $(LIBS)
++	  sa-globals.o store.o string.o tod.o utf8.o $(LIBS) $(LDFLAGS)
+ 	rm -f os.o
+ 
+ test_parse:  config.h parse.c dummies.o sa-globals.o \
+-	     store.o string.o tod.o version.o
++	     store.o string.o tod.o version.o utf8.o
+ 	$(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE parse.c
+ 	$(LNCC) -o test_parse $(LFLAGS) parse.o \
+-	  dummies.o sa-globals.o store.o string.o tod.o version.o
++	  dummies.o sa-globals.o store.o string.o tod.o version.o \
++	  utf8.o $(LDFLAGS)
+ 	rm -f parse.o
+ 
+-test_string: config.h string.c dummies.o sa-globals.o store.o tod.o
++test_string: config.h string.c dummies.o sa-globals.o store.o tod.o utf8.o
+ 	$(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE string.c
+ 	$(LNCC) -o test_string $(LFLAGS) -DSTAND_ALONE string.o \
+-	  dummies.o sa-globals.o store.o tod.o $(LIBS)
++	  dummies.o sa-globals.o store.o tod.o utf8.o $(LIBS) $(LDFLAGS)
+ 	rm -f string.o
+ 
+ # End
+diff --git a/src/src/host.c b/src/src/host.c
+index 94126d4..31c2bbf 100644
+--- a/src/src/host.c
++++ b/src/src/host.c
+@@ -3212,7 +3212,7 @@ while (Ufgets(buffer, 256, stdin) != NULL)
+   else
+     {
+     int flags = whichrrs;
+-    dnssec d;
++    dnssec_domains d;
+ 
+     h.name = buffer;
+     h.next = NULL;
+
+commit 2ef7ed082481b2dccd3c2e0eae849b24bf0b172a
+Author: Heiko Schlittermann (HS12-RIPE) <hs at schlittermann.de>
+Date:   Tue Aug 11 17:36:29 2015 +0200
+
+    Fix ESMTP MAIL command option processing
+    
+    If the address containes spaces, the option processing
+    was confused.
+
+diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
+index 711ee86..effc636 100644
+--- a/src/src/smtp_in.c
++++ b/src/src/smtp_in.c
+@@ -9,6 +9,7 @@
+ 
+ 
+ #include "exim.h"
++#include <assert.h>
+ 
+ 
+ /* Initialize for TCP wrappers if so configured. It appears that the macro
+@@ -232,6 +233,7 @@ static uschar *protocols[] = {
+ 
+ /* Sanity check and validate optional args to MAIL FROM: envelope */
+ enum {
++  ENV_MAIL_OPT_NULL,
+   ENV_MAIL_OPT_SIZE, ENV_MAIL_OPT_BODY, ENV_MAIL_OPT_AUTH,
+ #ifndef DISABLE_PRDR
+   ENV_MAIL_OPT_PRDR,
+@@ -240,7 +242,6 @@ enum {
+ #ifdef EXPERIMENTAL_INTERNATIONAL
+   ENV_MAIL_OPT_UTF8,
+ #endif
+-  ENV_MAIL_OPT_NULL
+   };
+ typedef struct {
+   uschar *   name;  /* option requested during MAIL cmd */
+@@ -260,7 +261,8 @@ static env_mail_type_t env_mail_type_list[] = {
+ #ifdef EXPERIMENTAL_INTERNATIONAL
+     { US"SMTPUTF8",ENV_MAIL_OPT_UTF8,  FALSE },		/* rfc6531 */
+ #endif
+-    { US"NULL",   ENV_MAIL_OPT_NULL,   FALSE }
++    /* keep this the last entry */
++    { US"NULL",   ENV_MAIL_OPT_NULL,   FALSE },
+   };
+ 
+ /* When reading SMTP from a remote host, we have to use our own versions of the
+@@ -3887,7 +3889,7 @@ while (done <= 0)
+       if (!extract_option(&name, &value)) break;
+ 
+       for (mail_args = env_mail_type_list;
+-           (char *)mail_args < (char *)env_mail_type_list + sizeof(env_mail_type_list);
++           mail_args->value != ENV_MAIL_OPT_NULL;
+            mail_args++
+           )
+         if (strcmpic(name, mail_args->name) == 0)
+@@ -4066,15 +4068,17 @@ while (done <= 0)
+ 	    }
+ 	  break;
+ #endif
+-        /* Unknown option. Stick back the terminator characters and break
++        /* No valid option. Stick back the terminator characters and break
+         the loop.  Do the name-terminator second as extract_option sets
+-	value==name when it found no equal-sign.
+-	An error for a malformed address will occur. */
+-        default:
++        value==name when it found no equal-sign.
++        An error for a malformed address will occur. */
++        case ENV_MAIL_OPT_NULL:
+           value[-1] = '=';
+           name[-1] = ' ';
+           arg_error = TRUE;
+           break;
++
++        default:  assert(0);
+         }
+       /* Break out of for loop if switch() had bad argument or
+          when start of the email address is reached */
+
+commit 4fb7df6d044a39151e72346ac0d67ac09686f704
+Author: Jeremy Harris <jgh146exb at wizmail.org>
+Date:   Tue Aug 11 22:54:53 2015 +0100
+
+    GnuTLS: avoid whining about OCSP when not requested by config
+
+diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
+index 8aabc5c..fe18094 100644
+--- a/src/src/tls-gnu.c
++++ b/src/src/tls-gnu.c
+@@ -176,7 +176,9 @@ static const char * const exim_default_gnutls_priority = "NORMAL";
+ 
+ static BOOL exim_gnutls_base_init_done = FALSE;
+ 
++#ifndef DISABLE_OCSP
+ static BOOL gnutls_buggy_ocsp = FALSE;
++#endif
+ 
+ 
+ /* ------------------------------------------------------------------------ */
+@@ -1021,6 +1023,8 @@ return OK;
+ *************************************************/
+ 
+ 
++#ifndef DISABLE_OCSP
++
+ static BOOL
+ tls_is_buggy_ocsp(void)
+ {
+@@ -1047,6 +1051,7 @@ if (maj == 3)
+ return FALSE;
+ }
+ 
++#endif
+ 
+ 
+ /* Called from both server and client code. In the case of a server, errors
+@@ -1112,8 +1117,10 @@ if (!exim_gnutls_base_init_done)
+     }
+ #endif
+ 
+-  if ((gnutls_buggy_ocsp = tls_is_buggy_ocsp()))
++#ifndef DISABLE_OCSP
++  if (tls_ocsp_file && (gnutls_buggy_ocsp = tls_is_buggy_ocsp()))
+     log_write(0, LOG_MAIN, "OCSP unusable with this GnuTLS library version");
++#endif
+ 
+   exim_gnutls_base_init_done = TRUE;
+   }
+
+commit c528cec4dbfdb6e367a6ac0ed72e2e768a9c4392
+Author: Heiko Schlittermann (HS12-RIPE) <hs at schlittermann.de>
+Date:   Wed Aug 12 23:45:44 2015 +0200
+
+    Adjust the timeout after interrupted select()
+
+diff --git a/src/src/ip.c b/src/src/ip.c
+index cb54f16..7991a58 100644
+--- a/src/src/ip.c
++++ b/src/src/ip.c
+@@ -451,8 +451,8 @@ BOOL
+ fd_ready(int fd, int timeout)
+ {
+ fd_set select_inset;
+-struct timeval tv;
+ time_t start_recv = time(NULL);
++int time_left = timeout;
+ int rc;
+ 
+ if (timeout <= 0)
+@@ -464,10 +464,9 @@ if (timeout <= 0)
+ 
+ do
+   {
++  struct timeval tv = { time_left, 0 };
+   FD_ZERO (&select_inset);
+   FD_SET (fd, &select_inset);
+-  tv.tv_sec = timeout;
+-  tv.tv_usec = 0;
+ 
+   /*DEBUG(D_transport) debug_printf("waiting for data on fd\n");*/
+   rc = select(fd + 1, (SELECT_ARG2_TYPE *)&select_inset, NULL, NULL, &tv);
+@@ -479,25 +478,24 @@ do
+   Aug 2004: Somebody set up a cron job that ran exiwhat every 2 minutes, making
+   the interrupt not at all rare. Since the timeout is typically more than 2
+   minutes, the effect was to block the timeout completely. To prevent this
+-  happening again, we do an explicit time test. */
++  happening again, we do an explicit time test and adjust the timeout
++  accordingly */
+ 
+   if (rc < 0 && errno == EINTR)
+     {
+     DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n");
+-    if (time(NULL) - start_recv < timeout) continue;
+-    DEBUG(D_transport) debug_printf("total wait time exceeds timeout\n");
++    /* Watch out, 'continue' jumps to the condition, not to the loops top */
++    if (time_left = timeout - (time(NULL) - start_recv)) continue;
+     }
+ 
+-  /* Handle a timeout, and treat any other select error as a timeout, including
+-  an EINTR when we have been in this loop for longer than timeout. */
+-
+   if (rc <= 0)
+     {
+     errno = ETIMEDOUT;
+     return FALSE;
+     }
+ 
+-  /* If the socket is ready, break out of the loop. */
++  /* Checking the FD_ISSET is not enough, if we're interrupted, the
++  select_inset may still contain the 'input'. */
+   }
+ while (rc < 0 || !FD_ISSET(fd, &select_inset));
+ return TRUE;
+
+commit 85ff3cf9f3ab78c4dfa9f9ff34d27e6fe8f73c39
+Author: Heiko Schlittermann (HS12-RIPE) <hs at schlittermann.de>
+Date:   Thu Aug 13 00:20:12 2015 +0200
+
+    Fix timeout adjustment in c528cec4
+
+diff --git a/src/src/ip.c b/src/src/ip.c
+index 7991a58..2d71705 100644
+--- a/src/src/ip.c
++++ b/src/src/ip.c
+@@ -455,7 +455,7 @@ time_t start_recv = time(NULL);
+ int time_left = timeout;
+ int rc;
+ 
+-if (timeout <= 0)
++if (time_left <= 0)
+   {
+   errno = ETIMEDOUT;
+   return FALSE;
+@@ -484,8 +484,10 @@ do
+   if (rc < 0 && errno == EINTR)
+     {
+     DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n");
++
+     /* Watch out, 'continue' jumps to the condition, not to the loops top */
+-    if (time_left = timeout - (time(NULL) - start_recv)) continue;
++    time_left = timeout - (time(NULL) - start_recv);
++    if (time_left > 0) continue;
+     }
+ 
+   if (rc <= 0)
+
+commit 6c6d6e483411af2c087ff258f4041d38eb65e775
+Author: Tony Finch <dot at dotat.at>
+Date:   Thu Aug 13 15:16:48 2015 +0100
+
+    Overhaul the debug_selector and log_selector machinery to support variable-length bit vectors. No functional change.
+
+diff --git a/src/src/acl.c b/src/src/acl.c
+index 91ee571..f2e0ef2 100644
+--- a/src/src/acl.c
++++ b/src/src/acl.c
+@@ -4287,7 +4287,7 @@ while (acl != NULL)
+     case ACL_WARN:
+     if (cond == OK)
+       acl_warn(where, *user_msgptr, *log_msgptr);
+-    else if (cond == DEFER && (log_extra_selector & LX_acl_warn_skipped) != 0)
++    else if (cond == DEFER && LOGGING(acl_warn_skipped))
+       log_write(0, LOG_MAIN, "%s Warning: ACL \"warn\" statement skipped: "
+         "condition test deferred%s%s", host_and_ident(TRUE),
+         (*log_msgptr == NULL)? US"" : US": ",
+diff --git a/src/src/daemon.c b/src/src/daemon.c
+index 894a96f..2d10387 100644
+--- a/src/src/daemon.c
++++ b/src/src/daemon.c
+@@ -145,7 +145,7 @@ int dup_accept_socket = -1;
+ int max_for_this_host = 0;
+ int wfsize = 0;
+ int wfptr = 0;
+-int use_log_write_selector = log_write_selector;
++int save_log_selector = *log_selector;
+ uschar *whofrom = NULL;
+ 
+ void *reset_point = store_get(0);
+@@ -206,11 +206,11 @@ memory is reclaimed. */
+ 
+ whofrom = string_append(whofrom, &wfsize, &wfptr, 3, "[", sender_host_address, "]");
+ 
+-if ((log_extra_selector & LX_incoming_port) != 0)
++if (LOGGING(incoming_port))
+   whofrom = string_append(whofrom, &wfsize, &wfptr, 2, ":", string_sprintf("%d",
+     sender_host_port));
+ 
+-if ((log_extra_selector & LX_incoming_interface) != 0)
++if (LOGGING(incoming_interface))
+   whofrom = string_append(whofrom, &wfsize, &wfptr, 4, " I=[",
+     interface_address, "]:", string_sprintf("%d", interface_port));
+ 
+@@ -338,11 +338,11 @@ the generalized logging code each time when the selector is false. If the
+ selector is set, check whether the host is on the list for logging. If not,
+ arrange to unset the selector in the subprocess. */
+ 
+-if ((log_write_selector & L_smtp_connection) != 0)
++if (LOGGING(smtp_connection))
+   {
+   uschar *list = hosts_connection_nolog;
+   if (list != NULL && verify_check_host(&list) == OK)
+-    use_log_write_selector &= ~L_smtp_connection;
++    save_log_selector &= ~L_smtp_connection;
+   else
+     log_write(L_smtp_connection, LOG_MAIN, "SMTP connection from %s "
+       "(TCP/IP connection count = %d)", whofrom, smtp_accept_count + 1);
+@@ -372,7 +372,7 @@ if (pid == 0)
+ 
+   /* May have been modified for the subprocess */
+ 
+-  log_write_selector = use_log_write_selector;
++  *log_selector = save_log_selector;
+ 
+   /* Get the local interface address into permanent store */
+ 
+diff --git a/src/src/deliver.c b/src/src/deliver.c
+index 78f8f4b..c796de0 100644
+--- a/src/src/deliver.c
++++ b/src/src/deliver.c
+@@ -682,7 +682,7 @@ d_hostlog(uschar * s, int * sizep, int * ptrp, address_item * addr)
+ {
+   s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name,
+     US" [", addr->host_used->address, US"]");
+-  if ((log_extra_selector & LX_outgoing_port) != 0)
++  if (LOGGING(outgoing_port))
+     s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
+       addr->host_used->port));
+   return s;
+@@ -692,10 +692,9 @@ d_hostlog(uschar * s, int * sizep, int * ptrp, address_item * addr)
+ static uschar *
+ d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr)
+ {
+-  if ((log_extra_selector & LX_tls_cipher) != 0 && addr->cipher != NULL)
++  if (LOGGING(tls_cipher) && addr->cipher != NULL)
+     s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher);
+-  if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
+-       addr->cipher != NULL)
++  if (LOGGING(tls_certificate_verified) && addr->cipher != NULL)
+     s = string_append(s, sizep, ptrp, 2, US" CV=",
+       testflag(addr, af_cert_verified)
+       ?
+@@ -706,7 +705,7 @@ d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr)
+ #endif
+         "yes"
+       : "no");
+-  if ((log_extra_selector & LX_tls_peerdn) != 0 && addr->peerdn != NULL)
++  if (LOGGING(tls_peerdn) && addr->peerdn != NULL)
+     s = string_append(s, sizep, ptrp, 3, US" DN=\"",
+       string_printing(addr->peerdn), US"\"");
+   return s;
+@@ -808,7 +807,7 @@ pointer to a single host item in their host list, for use by the transport. */
+ 
+ s = reset_point = store_get(size);
+ 
+-log_address = string_log_address(addr, (log_write_selector & L_all_parents) != 0, TRUE);
++log_address = string_log_address(addr, LOGGING(all_parents), TRUE);
+ if (msg)
+   s = string_append(s, &size, &ptr, 3, host_and_ident(TRUE), US" ", log_address);
+ else
+@@ -817,11 +816,11 @@ else
+   s = string_append(s, &size, &ptr, 2, US"> ", log_address);
+   }
+ 
+-if (log_extra_selector & LX_incoming_interface  &&  sending_ip_address)
++if (LOGGING(incoming_interface) && sending_ip_address)
+   s = string_append(s, &size, &ptr, 3, US" I=[", sending_ip_address, US"]");
+   /* for the port:  string_sprintf("%d", sending_port) */
+ 
+-if ((log_extra_selector & LX_sender_on_delivery) != 0  ||  msg)
++if (LOGGING(sender_on_delivery) || msg)
+   s = string_append(s, &size, &ptr, 3, US" F=<",
+ #ifdef EXPERIMENTAL_INTERNATIONAL
+     testflag(addr, af_utf8_downcvt)
+@@ -841,8 +840,7 @@ delivery; indeed, I did for some time, until this statement crashed. The case
+ when it is not set is for a delivery to /dev/null which is optimised by not
+ being run at all. */
+ 
+-if (used_return_path != NULL &&
+-      (log_extra_selector & LX_return_path_on_delivery) != 0)
++if (used_return_path != NULL && LOGGING(return_path_on_delivery))
+   s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">");
+ 
+ if (msg)
+@@ -854,7 +852,7 @@ if (addr->router != NULL)
+ 
+ s = string_append(s, &size, &ptr, 2, US" T=", addr->transport->name);
+ 
+-if ((log_extra_selector & LX_delivery_size) != 0)
++if (LOGGING(delivery_size))
+   s = string_append(s, &size, &ptr, 2, US" S=",
+     string_sprintf("%d", transport_count));
+ 
+@@ -901,7 +899,7 @@ else
+     if (addr->auth_id)
+       {
+       s = string_append(s, &size, &ptr, 2, US":", addr->auth_id);
+-      if (log_extra_selector & LX_smtp_mailauth  &&  addr->auth_sndr)
++      if (LOGGING(smtp_mailauth) && addr->auth_sndr)
+         s = string_append(s, &size, &ptr, 2, US":", addr->auth_sndr);
+       }
+     }
+@@ -914,8 +912,7 @@ else
+ 
+ /* confirmation message (SMTP (host_used) and LMTP (driver_name)) */
+ 
+-if (log_extra_selector & LX_smtp_confirmation &&
+-    addr->message &&
++if (LOGGING(smtp_confirmation) && addr->message &&
+     (addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0))
+   {
+   unsigned i;
+@@ -935,11 +932,11 @@ if (log_extra_selector & LX_smtp_confirmation &&
+ 
+ /* Time on queue and actual time taken to deliver */
+ 
+-if ((log_extra_selector & LX_queue_time) != 0)
++if (LOGGING(queue_time))
+   s = string_append(s, &size, &ptr, 2, US" QT=",
+     readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) );
+ 
+-if ((log_extra_selector & LX_deliver_time) != 0)
++if (LOGGING(deliver_time))
+   s = string_append(s, &size, &ptr, 2, US" DT=",
+     readconf_printtime(addr->more_errno));
+ 
+@@ -1230,8 +1227,7 @@ else if (result == DEFER || result == PANIC)
+     /* Create the address string for logging. Must not do this earlier, because
+     an OK result may be changed to FAIL when a pipe returns text. */
+ 
+-    log_address = string_log_address(addr,
+-      (log_write_selector & L_all_parents) != 0, result == OK);
++    log_address = string_log_address(addr, LOGGING(all_parents), result == OK);
+ 
+     s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address));
+ 
+@@ -1342,18 +1338,16 @@ else
+   /* Create the address string for logging. Must not do this earlier, because
+   an OK result may be changed to FAIL when a pipe returns text. */
+ 
+-  log_address = string_log_address(addr,
+-    (log_write_selector & L_all_parents) != 0, result == OK);
++  log_address = string_log_address(addr, LOGGING(all_parents), result == OK);
+ 
+   s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address));
+ 
+-  if ((log_extra_selector & LX_sender_on_delivery) != 0)
++  if (LOGGING(sender_on_delivery))
+     s = string_append(s, &size, &ptr, 3, US" F=<", sender_address, US">");
+ 
+   /* Return path may not be set if no delivery actually happened */
+ 
+-  if (used_return_path != NULL &&
+-      (log_extra_selector & LX_return_path_on_delivery) != 0)
++  if (used_return_path != NULL && LOGGING(return_path_on_delivery))
+     s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">");
+ 
+   if (addr->router != NULL)
+@@ -4449,7 +4443,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
+       }
+ 
+     /* Local interface address/port */
+-    if (log_extra_selector & LX_incoming_interface  &&  sending_ip_address)
++    if (LOGGING(incoming_interface) && sending_ip_address)
+       {
+       uschar * ptr = big_buffer;
+       sprintf(CS ptr, "%.128s", sending_ip_address);
+@@ -7365,7 +7359,7 @@ if (addr_defer == NULL)
+ 
+   /* Log the end of this message, with queue time if requested. */
+ 
+-  if ((log_extra_selector & LX_queue_time_overall) != 0)
++  if (LOGGING(queue_time_overall))
+     log_write(0, LOG_MAIN, "Completed QT=%s",
+       readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) );
+   else
+diff --git a/src/src/exim.c b/src/src/exim.c
+index f9d57ab..d7cb5d8 100644
+--- a/src/src/exim.c
++++ b/src/src/exim.c
+@@ -1639,6 +1639,10 @@ if (log_buffer == NULL)
+   exit(EXIT_FAILURE);
+   }
+ 
++/* Initialize the default log options. */
++
++bits_set(log_selector, log_selector_size, log_default);
++
+ /* Set log_stderr to stderr, provided that stderr exists. This gets reset to
+ NULL when the daemon is run and the file is closed. We have to use this
+ indirection, because some systems don't allow writing to the variable "stderr".
+@@ -2451,8 +2455,8 @@ for (i = 1; i < argc; i++)
+         argrest++;
+         }
+       if (*argrest != 0)
+-        decode_bits(&selector, NULL, D_memory, 0, argrest, debug_options,
+-          debug_options_count, US"debug", 0);
++        decode_bits(&selector, 1, debug_notall, argrest,
++          debug_options, debug_options_count, US"debug", 0);
+       debug_selector = selector;
+       }
+     break;
+@@ -3787,14 +3791,17 @@ else
+ 
+ /* Handle the decoding of logging options. */
+ 
+-decode_bits(&log_write_selector, &log_extra_selector, 0, 0,
++decode_bits(log_selector, log_selector_size, log_notall,
+   log_selector_string, log_options, log_options_count, US"log", 0);
+ 
+ DEBUG(D_any)
+   {
++  int i;
+   debug_printf("configuration file is %s\n", config_main_filename);
+-  debug_printf("log selectors = %08x %08x\n", log_write_selector,
+-    log_extra_selector);
++  debug_printf("log selectors =");
++  for (i = 0; i < log_selector_size; i++)
++    debug_printf(" %08x", log_selector[i]);
++  debug_printf("\n");
+   }
+ 
+ /* If domain literals are not allowed, check the sender address that was
+@@ -4001,7 +4008,7 @@ a debugging feature for finding out what arguments certain MUAs actually use.
+ Don't attempt it if logging is disabled, or if listing variables or if
+ verifying/testing addresses or expansions. */
+ 
+-if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0)
++if (((debug_selector & D_any) != 0 || LOGGING(arguments))
+       && really_exim && !list_options && !checking)
+   {
+   int i;
+@@ -4036,7 +4043,7 @@ if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0)
+     while (*p) p++;
+     }
+ 
+-  if ((log_extra_selector & LX_arguments) != 0)
++  if (LOGGING(arguments))
+     log_write(0, LOG_MAIN, "%s", big_buffer);
+   else
+     debug_printf("%s\n", big_buffer);
+@@ -5021,7 +5028,7 @@ if (host_checking)
+       sender_host_address);
+ 
+   if (verify_check_host(&hosts_connection_nolog) == OK)
+-    log_write_selector &= ~L_smtp_connection;
++    BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection);
+   log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info());
+ 
+   /* NOTE: We do *not* call smtp_log_no_mail() if smtp_start_session() fails,
+@@ -5195,7 +5202,7 @@ if (smtp_input)
+   smtp_in = stdin;
+   smtp_out = stdout;
+   if (verify_check_host(&hosts_connection_nolog) == OK)
+-    log_write_selector &= ~L_smtp_connection;
++    BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection);
+   log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info());
+   if (!smtp_start_session())
+     {
+diff --git a/src/src/functions.h b/src/src/functions.h
+index 0257904..4af0017 100644
+--- a/src/src/functions.h
++++ b/src/src/functions.h
+@@ -99,6 +99,9 @@ extern int     auth_get_no64_data(uschar **, uschar *);
+ extern uschar *auth_xtextencode(uschar *, int);
+ extern int     auth_xtextdecode(uschar *, uschar **);
+ 
++extern void    bits_clear(unsigned int *, size_t, int *);
++extern void    bits_set(unsigned int *, size_t, int *);
++
+ extern void    cancel_cutthrough_connection(const char *);
+ extern int     check_host(void *, const uschar *, const uschar **, uschar **);
+ extern uschar **child_exec_exim(int, BOOL, int *, BOOL, int, ...);
+@@ -123,8 +126,8 @@ extern void    debug_print_ids(uschar *);
+ extern void    debug_print_string(uschar *);
+ extern void    debug_print_tree(tree_node *);
+ extern void    debug_vprintf(const char *, va_list);
+-extern void    decode_bits(unsigned int *, unsigned int *,
+-                  int, int, uschar *, bit_table *, int, uschar *, int);
++extern void    decode_bits(unsigned int *, size_t, int *,
++	           uschar *, bit_table *, int, uschar *, int);
+ extern address_item *deliver_make_addr(uschar *, BOOL);
+ extern void    deliver_init(void);
+ extern void    delivery_log(int, address_item *, int, uschar *);
+diff --git a/src/src/globals.c b/src/src/globals.c
+index 66baffe..1344b5a 100644
+--- a/src/src/globals.c
++++ b/src/src/globals.c
+@@ -536,40 +536,45 @@ uschar *dccifd_options         = US"header";
+ BOOL    debug_daemon           = FALSE;
+ int     debug_fd               = -1;
+ FILE   *debug_file             = NULL;
+-bit_table debug_options[]      = {
+-  { US"acl",            D_acl },
+-  { US"all",            D_all },
+-  { US"auth",           D_auth },
+-  { US"deliver",        D_deliver },
+-  { US"dns",            D_dns },
+-  { US"dnsbl",          D_dnsbl },
+-  { US"exec",           D_exec },
+-  { US"expand",         D_expand },
+-  { US"filter",         D_filter },
+-  { US"hints_lookup",   D_hints_lookup },
+-  { US"host_lookup",    D_host_lookup },
+-  { US"ident",          D_ident },
+-  { US"interface",      D_interface },
+-  { US"lists",          D_lists },
+-  { US"load",           D_load },
+-  { US"local_scan",     D_local_scan },
+-  { US"lookup",         D_lookup },
+-  { US"memory",         D_memory },
+-  { US"pid",            D_pid },
+-  { US"process_info",   D_process_info },
+-  { US"queue_run",      D_queue_run },
+-  { US"receive",        D_receive },
+-  { US"resolver",       D_resolver },
+-  { US"retry",          D_retry },
+-  { US"rewrite",        D_rewrite },
+-  { US"route",          D_route },
+-  { US"timestamp",      D_timestamp },
+-  { US"tls",            D_tls },
+-  { US"transport",      D_transport },
+-  { US"uid",            D_uid },
+-  { US"verify",         D_verify }
++int     debug_notall[]         = {
++  Di_memory,
++  -1
+ };
+-int     debug_options_count    = sizeof(debug_options)/sizeof(bit_table);
++bit_table debug_options[]      = { /* must be in alphabetical order */
++  BIT_TABLE(D, acl),
++  BIT_TABLE(D, all),
++  BIT_TABLE(D, auth),
++  BIT_TABLE(D, deliver),
++  BIT_TABLE(D, dns),
++  BIT_TABLE(D, dnsbl),
++  BIT_TABLE(D, exec),
++  BIT_TABLE(D, expand),
++  BIT_TABLE(D, filter),
++  BIT_TABLE(D, hints_lookup),
++  BIT_TABLE(D, host_lookup),
++  BIT_TABLE(D, ident),
++  BIT_TABLE(D, interface),
++  BIT_TABLE(D, lists),
++  BIT_TABLE(D, load),
++  BIT_TABLE(D, local_scan),
++  BIT_TABLE(D, lookup),
++  BIT_TABLE(D, memory),
++  BIT_TABLE(D, pid),
++  BIT_TABLE(D, process_info),
++  BIT_TABLE(D, queue_run),
++  BIT_TABLE(D, receive),
++  BIT_TABLE(D, resolver),
++  BIT_TABLE(D, retry),
++  BIT_TABLE(D, rewrite),
++  BIT_TABLE(D, route),
++  BIT_TABLE(D, timestamp),
++  BIT_TABLE(D, tls),
++  BIT_TABLE(D, transport),
++  BIT_TABLE(D, uid),
++  BIT_TABLE(D, verify),
++};
++int     debug_options_count    = nelem(debug_options);
++
+ unsigned int debug_selector    = 0;
+ int     delay_warning[DELAY_WARNING_SIZE] = { DELAY_WARNING_SIZE, 1, 24*60*60 };
+ uschar *delay_warning_condition=
+@@ -813,78 +818,91 @@ uid_t   local_user_uid         = (uid_t)(-1);
+ tree_node *localpartlist_anchor= NULL;
+ int     localpartlist_count    = 0;
+ uschar *log_buffer             = NULL;
+-unsigned int log_extra_selector = LX_default;
++
++int     log_default[]          = { /* for initializing log_selector */
++  Li_acl_warn_skipped,
++  Li_connection_reject,
++  Li_delay_delivery,
++  Li_dnslist_defer,
++  Li_etrn,
++  Li_host_lookup_failed,
++  Li_lost_incoming_connection,
++  Li_queue_run,
++  Li_rejected_header,
++  Li_retry_defer,
++  Li_sender_verify_fail,
++  Li_size_reject,
++  Li_skip_delivery,
++  Li_smtp_confirmation,
++  Li_tls_certificate_verified,
++  Li_tls_cipher,
++  -1
++};
++
+ uschar *log_file_path          = US LOG_FILE_PATH
+                            "\0<--------------Space to patch log_file_path->";
+ 
+-/* Those log options with L_xxx identifiers have values less than 0x800000 and
+-are the ones that get put into log_write_selector. They can be used in calls to
+-log_write() to test for the bit. The options with LX_xxx identifiers have
+-values greater than 0x80000000 and are put into log_extra_selector (without the
+-top bit). They are never used in calls to log_write(), but are tested
+-independently. This separation became necessary when the number of log
+-selectors was getting close to filling a 32-bit word. */
+-
+-/* Note that this list must be in alphabetical order. */
+-
+-bit_table log_options[]        = {
+-  { US"8bitmime",                     LX_8bitmime },
+-  { US"acl_warn_skipped",             LX_acl_warn_skipped },
+-  { US"address_rewrite",              L_address_rewrite },
+-  { US"all",                          L_all },
+-  { US"all_parents",                  L_all_parents },
+-  { US"arguments",                    LX_arguments },
+-  { US"connection_reject",            L_connection_reject },
+-  { US"delay_delivery",               L_delay_delivery },
+-  { US"deliver_time",                 LX_deliver_time },
+-  { US"delivery_size",                LX_delivery_size },
+-  { US"dnslist_defer",                L_dnslist_defer },
+-  { US"etrn",                         L_etrn },
+-  { US"host_lookup_failed",           L_host_lookup_failed },
+-  { US"ident_timeout",                LX_ident_timeout },
+-  { US"incoming_interface",           LX_incoming_interface },
+-  { US"incoming_port",                LX_incoming_port },
+-  { US"lost_incoming_connection",     L_lost_incoming_connection },
+-  { US"outgoing_port",                LX_outgoing_port },
+-  { US"pid",                          LX_pid },
++int     log_notall[]           = {
++  -1
++};
++bit_table log_options[]        = { /* must be in alphabetical order */
++  BIT_TABLE(L, 8bitmime),
++  BIT_TABLE(L, acl_warn_skipped),
++  BIT_TABLE(L, address_rewrite),
++  BIT_TABLE(L, all),
++  BIT_TABLE(L, all_parents),
++  BIT_TABLE(L, arguments),
++  BIT_TABLE(L, connection_reject),
++  BIT_TABLE(L, delay_delivery),
++  BIT_TABLE(L, deliver_time),
++  BIT_TABLE(L, delivery_size),
++  BIT_TABLE(L, dnslist_defer),
++  BIT_TABLE(L, etrn),
++  BIT_TABLE(L, host_lookup_failed),
++  BIT_TABLE(L, ident_timeout),
++  BIT_TABLE(L, incoming_interface),
++  BIT_TABLE(L, incoming_port),
++  BIT_TABLE(L, lost_incoming_connection),
++  BIT_TABLE(L, outgoing_port),
++  BIT_TABLE(L, pid),
+ #ifdef EXPERIMENTAL_PROXY
+-  { US"proxy",                        LX_proxy },
++  BIT_TABLE(L, proxy),
+ #endif
+-  { US"queue_run",                    L_queue_run },
+-  { US"queue_time",                   LX_queue_time },
+-  { US"queue_time_overall",           LX_queue_time_overall },
+-  { US"received_recipients",          LX_received_recipients },
+-  { US"received_sender",              LX_received_sender },
+-  { US"rejected_header",              LX_rejected_header },
+-  { US"rejected_headers",             LX_rejected_header },
+-  { US"retry_defer",                  L_retry_defer },
+-  { US"return_path_on_delivery",      LX_return_path_on_delivery },
+-  { US"sender_on_delivery",           LX_sender_on_delivery },
+-  { US"sender_verify_fail",           LX_sender_verify_fail },
+-  { US"size_reject",                  L_size_reject },
+-  { US"skip_delivery",                L_skip_delivery },
+-  { US"smtp_confirmation",            LX_smtp_confirmation },
+-  { US"smtp_connection",              L_smtp_connection },
+-  { US"smtp_incomplete_transaction",  L_smtp_incomplete_transaction },
+-  { US"smtp_mailauth",                LX_smtp_mailauth },
+-  { US"smtp_no_mail",                 LX_smtp_no_mail },
+-  { US"smtp_protocol_error",          L_smtp_protocol_error },
+-  { US"smtp_syntax_error",            L_smtp_syntax_error },
+-  { US"subject",                      LX_subject },
+-  { US"tls_certificate_verified",     LX_tls_certificate_verified },
+-  { US"tls_cipher",                   LX_tls_cipher },
+-  { US"tls_peerdn",                   LX_tls_peerdn },
+-  { US"tls_sni",                      LX_tls_sni },
+-  { US"unknown_in_list",              LX_unknown_in_list }
++  BIT_TABLE(L, queue_run),
++  BIT_TABLE(L, queue_time),
++  BIT_TABLE(L, queue_time_overall),
++  BIT_TABLE(L, received_recipients),
++  BIT_TABLE(L, received_sender),
++  BIT_TABLE(L, rejected_header),
++  { US"rejected_headers", Li_rejected_header },
++  BIT_TABLE(L, retry_defer),
++  BIT_TABLE(L, return_path_on_delivery),
++  BIT_TABLE(L, sender_on_delivery),
++  BIT_TABLE(L, sender_verify_fail),
++  BIT_TABLE(L, size_reject),
++  BIT_TABLE(L, skip_delivery),
++  BIT_TABLE(L, smtp_confirmation),
++  BIT_TABLE(L, smtp_connection),
++  BIT_TABLE(L, smtp_incomplete_transaction),
++  BIT_TABLE(L, smtp_mailauth),
++  BIT_TABLE(L, smtp_no_mail),
++  BIT_TABLE(L, smtp_protocol_error),
++  BIT_TABLE(L, smtp_syntax_error),
++  BIT_TABLE(L, subject),
++  BIT_TABLE(L, tls_certificate_verified),
++  BIT_TABLE(L, tls_cipher),
++  BIT_TABLE(L, tls_peerdn),
++  BIT_TABLE(L, tls_sni),
++  BIT_TABLE(L, unknown_in_list),
+ };
++int     log_options_count      = nelem(log_options);
+ 
+-int     log_options_count      = sizeof(log_options)/sizeof(bit_table);
+ int     log_reject_target      = 0;
++unsigned int log_selector[log_selector_size]; /* initialized in main() */
+ uschar *log_selector_string    = NULL;
+ FILE   *log_stderr             = NULL;
+ BOOL    log_testing_mode       = FALSE;
+ BOOL    log_timezone           = FALSE;
+-unsigned int log_write_selector= L_default;
+ uschar *login_sender_address   = NULL;
+ uschar *lookup_dnssec_authenticated = NULL;
+ int     lookup_open_max        = 25;
+diff --git a/src/src/globals.h b/src/src/globals.h
+index ab03302..978a4cc 100644
+--- a/src/src/globals.h
++++ b/src/src/globals.h
+@@ -319,6 +319,7 @@ extern uschar *dccifd_options;         /* options for the dccifd daemon */
+ extern BOOL    debug_daemon;           /* Debug the daemon process only */
+ extern int     debug_fd;               /* The fd for debug_file */
+ extern FILE   *debug_file;             /* Where to write debugging info */
++extern int     debug_notall[];         /* Debug options excluded from +all */
+ extern bit_table debug_options[];      /* Table of debug options */
+ extern int     debug_options_count;    /* Size of table */
+ extern int     delay_warning[];        /* Times between warnings */
+@@ -531,16 +532,17 @@ extern uid_t   local_user_uid;         /* As it says; may be set in routers */
+ extern tree_node *localpartlist_anchor;/* Tree of defined localpart lists */
+ extern int     localpartlist_count;    /* Number defined */
+ extern uschar *log_buffer;             /* For constructing log entries */
+-extern unsigned int log_extra_selector;/* Bit map of logging options other than used by log_write() */
++extern int     log_default[];          /* Initialization list for log_selector */
+ extern uschar *log_file_path;          /* If unset, use default */
++extern int     log_notall[];           /* Log options excluded from +all */
+ extern bit_table log_options[];        /* Table of options */
+ extern int     log_options_count;      /* Size of table */
+ extern int     log_reject_target;      /* Target log for ACL rejections */
++extern unsigned int log_selector[];    /* Bit map of logging options */
+ extern uschar *log_selector_string;    /* As supplied in the config */
+ extern FILE   *log_stderr;             /* Copy of stderr for log use, or NULL */
+ extern BOOL    log_testing_mode;       /* TRUE in various testing modes */
+ extern BOOL    log_timezone;           /* TRUE to include the timezone in log lines */
+-extern unsigned int log_write_selector;/* Bit map of logging options for log_write() */
+ extern uschar *login_sender_address;   /* The actual sender address */
+ extern lookup_info **lookup_list;      /* Array of pointers to available lookups */
+ extern int     lookup_list_count;      /* Number of entries in the list */
+diff --git a/src/src/host.c b/src/src/host.c
+index 31c2bbf..5c69c7f 100644
+--- a/src/src/host.c
++++ b/src/src/host.c
+@@ -544,7 +544,7 @@ use this directly as the first item for Received: because it ain't an RFC 2822
+ domain. Sigh. */
+ 
+ address = string_sprintf("[%s]:%d", sender_host_address, sender_host_port);
+-if ((log_extra_selector & LX_incoming_port) == 0 || sender_host_port <= 0)
++if (!LOGGING(incoming_port) || sender_host_port <= 0)
+   *(Ustrrchr(address, ':')) = 0;
+ 
+ /* If there's no EHLO/HELO data, we can't show it. */
+@@ -695,8 +695,7 @@ else
+   {
+   uschar *flag = useflag? US"H=" : US"";
+   uschar *iface = US"";
+-  if ((log_extra_selector & LX_incoming_interface) != 0 &&
+-       interface_address != NULL)
++  if (LOGGING(incoming_interface) && interface_address != NULL)
+     iface = string_sprintf(" I=[%s]:%d", interface_address, interface_port);
+   if (sender_ident == NULL)
+     (void)string_format(big_buffer, big_buffer_size, "%s%s%s",
+diff --git a/src/src/log.c b/src/src/log.c
+index 11b3edf..b2d1fcf 100644
+--- a/src/src/log.c
++++ b/src/src/log.c
+@@ -613,7 +613,7 @@ If a message_id exists, we include it after the timestamp.
+ 
+ Arguments:
+   selector  write to main log or LOG_INFO only if this value is zero, or if
+-              its bit is set in log_write_selector
++              its bit is set in log_selector[0]
+   flags     each bit indicates some independent action:
+               LOG_SENDER      add raw sender to the message
+               LOG_RECIPIENTS  add raw recipients list to message
+@@ -749,15 +749,12 @@ DEBUG(D_any|D_v)
+   Ustrcpy(ptr, "LOG:");
+   ptr += 4;
+ 
+-  /* Show the options that were passed into the call. These are those whose
+-  flag values do not have the 0x80000000 bit in them. Note that this
+-  automatically exclude the "all" setting. */
++  /* Show the selector that was passed into the call. */
+ 
+   for (i = 0; i < log_options_count; i++)
+     {
+     unsigned int bit = log_options[i].bit;
+-    if ((bit & 0x80000000) != 0) continue;
+-    if ((selector & bit) != 0)
++    if (bit < BITWORDSIZE && selector == BIT(bit))
+       {
+       *ptr++ = ' ';
+       Ustrcpy(ptr, log_options[i].name);
+@@ -809,7 +806,7 @@ ptr = log_buffer;
+ sprintf(CS ptr, "%s ", tod_stamp(tod_log));
+ while(*ptr) ptr++;
+ 
+-if ((log_extra_selector & LX_pid) != 0)
++if (LOGGING(pid))
+   {
+   sprintf(CS ptr, "[%d] ", (int)getpid());
+   while (*ptr) ptr++;
+@@ -869,7 +866,7 @@ or unless there is no log_stderr (expn called from daemon, for example). */
+ if (!really_exim || log_testing_mode)
+   {
+   if (debug_selector == 0 && log_stderr != NULL &&
+-      (selector == 0 || (selector & log_write_selector) != 0))
++      (selector == 0 || (selector & log_selector[0]) != 0))
+     {
+     if (host_checking)
+       fprintf(log_stderr, "LOG: %s", CS(log_buffer + 20));  /* no timestamp */
+@@ -887,7 +884,7 @@ has been renamed. Therefore, do a stat() and see if the inode has changed, and
+ if so, re-open. */
+ 
+ if ((flags & LOG_MAIN) != 0 &&
+-    (selector == 0 || (selector & log_write_selector) != 0))
++    (selector == 0 || (selector & log_selector[0]) != 0))
+   {
+   if ((logging_mode & LOG_MODE_SYSLOG) != 0 &&
+       (syslog_duplication || (flags & (LOG_REJECT|LOG_PANIC)) == 0))
+@@ -956,7 +953,7 @@ if ((flags & LOG_REJECT) != 0)
+   {
+   header_line *h;
+ 
+-  if (header_list != NULL && (log_extra_selector & LX_rejected_header) != 0)
++  if (header_list != NULL && LOGGING(rejected_header))
+     {
+     if (recipients_count > 0)
+       {
+@@ -1142,6 +1139,35 @@ syslog_open = FALSE;
+ 
+ 
+ /*************************************************
++*             Multi-bit set or clear             *
++*************************************************/
++
++/* These functions take a list of bit indexes (terminated by -1) and
++clear or set the corresponding bits in the selector.
++
++Arguments:
++  selector       address of the bit string
++  selsize        number of words in the bit string
++  bits           list of bits to set
++*/
++
++void
++bits_clear(unsigned int *selector, size_t selsize, int *bits)
++{
++for(; *bits != -1; ++bits)
++  BIT_CLEAR(selector, selsize, *bits);
++}
++
++void
++bits_set(unsigned int *selector, size_t selsize, int *bits)
++{
++for(; *bits != -1; ++bits)
++  BIT_SET(selector, selsize, *bits);
++}
++
++
++
++/*************************************************
+ *         Decode bit settings for log/debug      *
+ *************************************************/
+ 
+@@ -1151,13 +1177,9 @@ also recognizes a numeric setting of the form =<number>, but this is not
+ intended for user use. It's an easy way for Exim to pass the debug settings
+ when it is re-exec'ed.
+ 
+-The log options are held in two unsigned ints (because there became too many
+-for one). The top bit in the table means "put in 2nd selector". This does not
+-yet apply to debug options, so the "=" facility sets only the first selector.
+-
+-The "all" selector, which must be equal to 0xffffffff, is recognized specially.
+-It sets all the bits in both selectors. However, there is a facility for then
+-unsetting certain bits, because we want to turn off "memory" in the debug case.
++The option table is a list of names and bit indexes. The index -1
++means "set all bits, except for those listed in notall". The notall
++list is terminated by -1.
+ 
+ The action taken for bad values varies depending upon why we're here.
+ For log messages, or if the debugging is triggered from config, then we write
+@@ -1165,10 +1187,9 @@ to the log on the way out.  For debug setting triggered from the command-line,
+ we treat it as an unknown option: error message to stderr and die.
+ 
+ Arguments:
+-  selector1      address of the first bit string
+-  selector2      address of the second bit string, or NULL
+-  notall1        bits to exclude from "all" for selector1
+-  notall2        bits to exclude from "all" for selector2
++  selector       address of the bit string
++  selsize        number of words in the bit string
++  notall         list of bits to exclude from "all"
+   string         the configured string
+   options        the table of option names
+   count          size of table
+@@ -1179,9 +1200,8 @@ Returns:         nothing on success - bomb out on failure
+ */
+ 
+ void
+-decode_bits(unsigned int *selector1, unsigned int *selector2, int notall1,
+-  int notall2, uschar *string, bit_table *options, int count, uschar *which,
+-  int flags)
++decode_bits(unsigned int *selector, size_t selsize, int *notall,
++  uschar *string, bit_table *options, int count, uschar *which, int flags)
+ {
+ uschar *errmsg;
+ if (string == NULL) return;
+@@ -1189,7 +1209,8 @@ if (string == NULL) return;
+ if (*string == '=')
+   {
+   char *end;    /* Not uschar */
+-  *selector1 = strtoul(CS string+1, &end, 0);
++  memset(selector, 0, sizeof(*selector)*selsize);
++  *selector = strtoul(CS string+1, &end, 0);
+   if (*end == 0) return;
+   errmsg = string_sprintf("malformed numeric %s_selector setting: %s", which,
+     string);
+@@ -1232,40 +1253,22 @@ else for(;;)
+       if (middle->name[len] != 0) c = -1; else
+         {
+         unsigned int bit = middle->bit;
+-        unsigned int *selector;
+-
+-        /* The value with all bits set means "force all bits in both selectors"
+-        in the case where two are being handled. However, the top bit in the
+-        second selector is never set. When setting, some bits can be excluded.
+-        */
+-
+-        if (bit == 0xffffffff)
+-          {
+-          if (adding)
+-            {
+-            *selector1 = 0xffffffff ^ notall1;
+-            if (selector2 != NULL) *selector2 = 0x7fffffff ^ notall2;
+-            }
+-          else
+-            {
+-            *selector1 = 0;
+-            if (selector2 != NULL) *selector2 = 0;
+-            }
+-          }
+-
+-        /* Otherwise, the 0x80000000 bit means "this value, without the top
+-        bit, belongs in the second selector". */
+ 
+-        else
+-          {
+-          if ((bit & 0x80000000) != 0)
+-            {
+-            selector = selector2;
+-            bit &= 0x7fffffff;
+-            }
+-          else selector = selector1;
+-          if (adding) *selector |= bit; else *selector &= ~bit;
+-          }
++	if (bit == -1)
++	  {
++	  if (adding)
++	    {
++	    memset(selector, -1, sizeof(*selector)*selsize);
++	    bits_clear(selector, selsize, notall);
++	    }
++	  else
++	    memset(selector, 0, sizeof(*selector)*selsize);
++	  }
++	else if (adding)
++	  BIT_SET(selector, selsize, bit);
++	else
++	  BIT_CLEAR(selector, selsize, bit);
++
+         break;  /* Out of loop to match selector name */
+         }
+       }
+@@ -1335,10 +1338,8 @@ if (tag_name != NULL && (Ustrchr(tag_name, '/') != NULL))
+ 
+ debug_selector = D_default;
+ if (opts)
+-  {
+-  decode_bits(&debug_selector, NULL, D_memory, 0, opts,
++  decode_bits(&debug_selector, 1, debug_notall, opts,
+       debug_options, debug_options_count, US"debug", DEBUG_FROM_CONFIG);
+-  }
+ 
+ /* When activating from a transport process we may never have logged at all
+ resulting in certain setup not having been done.  Hack this for now so we
+diff --git a/src/src/macros.h b/src/src/macros.h
+index 61f9ca6..d63025e 100644
+--- a/src/src/macros.h
++++ b/src/src/macros.h
+@@ -321,46 +321,84 @@ for having to swallow the rest of an SMTP message is whether the value is
+ #define END_SIZE       4    /* Reading ended because message too big */
+ #define END_WERROR     5    /* Write error while reading the message */
+ 
+-/* Options bits for debugging; D_v and D_local_scan are also in local_scan.h */
+-
+-#define D_v                          0x00000001
+-#define D_local_scan                 0x00000002
+-
+-#define D_acl                        0x00000004
+-#define D_auth                       0x00000008
+-#define D_deliver                    0x00000010
+-#define D_dns                        0x00000020
+-#define D_dnsbl                      0x00000040
+-#define D_exec                       0x00000080
+-#define D_expand                     0x00000100
+-#define D_filter                     0x00000200
+-#define D_hints_lookup               0x00000400
+-#define D_host_lookup                0x00000800
+-#define D_ident                      0x00001000
+-#define D_interface                  0x00002000
+-#define D_lists                      0x00004000
+-#define D_load                       0x00008000
+-#define D_lookup                     0x00010000
+-#define D_memory                     0x00020000
+-#define D_pid                        0x00040000
+-#define D_process_info               0x00080000
+-#define D_queue_run                  0x00100000
+-#define D_receive                    0x00200000
+-#define D_resolver                   0x00400000
+-#define D_retry                      0x00800000
+-#define D_rewrite                    0x01000000
+-#define D_route                      0x02000000
+-#define D_timestamp                  0x04000000
+-#define D_tls                        0x08000000
+-#define D_transport                  0x10000000
+-#define D_uid                        0x20000000
+-#define D_verify                     0x40000000
+-
+-/* The D_all value must always have all bits set, as it is recognized specially
+-by the function that decodes debug and log selectors. This is to enable it to
+-set all the bits in a multi-word selector. Debug doesn't use this yet, but we
+-are getting close. In fact, we want to omit "memory" for -d+all, but can't
+-handle this here. It is fudged externally. */
++/* Bit masks for debug and log selectors */
++
++/* Assume words are 32 bits wide. Tiny waste of space on 64 bit
++platforms, but this ensures bit vectors always work the same way. */
++#define BITWORDSIZE 32
++
++/* This macro is for single-word bit vectors: the debug selector,
++and the first word of the log selector. */
++#define BIT(n) (1 << (n))
++
++/* And these are for multi-word vectors. */
++#define BITWORD(n) (     (n) / BITWORDSIZE)
++#define BITMASK(n) (1 << (n) % BITWORDSIZE)
++
++#define BIT_CLEAR(s,z,n) ((s)[BITWORD(n)] &= ~BITMASK(n))
++#define BIT_SET(s,z,n)   ((s)[BITWORD(n)] |=  BITMASK(n))
++#define BIT_TEST(s,z,n) (((s)[BITWORD(n)] &   BITMASK(n)) != 0)
++
++/* Used in globals.c for initializing bit_table structures. T will be either
++D or L correspondong to the debug and log selector bits declared below. */
++
++#define BIT_TABLE(T,name) { US #name, T##i_##name }
++
++/* IOTA allows us to keep an implicit sequential count, like a simple enum,
++but we can have sequentially numbered identifiers which are not declared
++sequentially. We use this for more compact declarations of bit indexes and
++masks, alternating between sequential bit index and corresponding mask. */
++
++#define IOTA(iota)      (__LINE__ - iota)
++#define IOTA_INIT(zero) (__LINE__ - zero + 1)
++
++/* Options bits for debugging. DEBUG_BIT() declares both a bit index and the
++corresponding mask. Di_all is a special value recognized by decode_bits().
++
++Exim's code assumes in a number of places that the debug_selector is one
++word, and this is exposed in the local_scan ABI. The D_v and D_local_scan bit
++masks are part of the local_scan API so are #defined in local_scan.h */
++
++#define DEBUG_BIT(name) Di_##name = IOTA(Di_iota), D_##name = BIT(Di_##name)
++
++enum {
++  Di_all        = -1,
++  Di_v          = 0,
++  Di_local_scan = 1,
++
++  Di_iota = IOTA_INIT(2),
++  DEBUG_BIT(acl),
++  DEBUG_BIT(auth),
++  DEBUG_BIT(deliver),
++  DEBUG_BIT(dns),
++  DEBUG_BIT(dnsbl),
++  DEBUG_BIT(exec),
++  DEBUG_BIT(expand),
++  DEBUG_BIT(filter),
++  DEBUG_BIT(hints_lookup),
++  DEBUG_BIT(host_lookup),
++  DEBUG_BIT(ident),
++  DEBUG_BIT(interface),
++  DEBUG_BIT(lists),
++  DEBUG_BIT(load),
++  DEBUG_BIT(lookup),
++  DEBUG_BIT(memory),
++  DEBUG_BIT(pid),
++  DEBUG_BIT(process_info),
++  DEBUG_BIT(queue_run),
++  DEBUG_BIT(receive),
++  DEBUG_BIT(resolver),
++  DEBUG_BIT(retry),
++  DEBUG_BIT(rewrite),
++  DEBUG_BIT(route),
++  DEBUG_BIT(timestamp),
++  DEBUG_BIT(tls),
++  DEBUG_BIT(transport),
++  DEBUG_BIT(uid),
++  DEBUG_BIT(verify),
++};
++
++/* Multi-bit debug masks */
+ 
+ #define D_all                        0xffffffff
+ 
+@@ -380,81 +418,67 @@ handle this here. It is fudged externally. */
+                                          D_timestamp   | \
+                                          D_resolver))
+ 
+-/* Options bits for logging. Those that will end up in log_write_selector have
+-values < 0x80000000. They can be used in calls to log_write(). The others have
+-values > 0x80000000 and are put into log_extra_selector (without the top bit).
+-These are only ever tested independently. "All" is a magic value that is used
+-only in the name table to set all options in both bit maps. */
+-
+-/* The L_all value must always have all bits set, as it is recognized specially
+-by the function that decodes debug and log selectors. This is to enable it to
+-set all the bits in a multi-word selector. */
+-
+-#define L_all                          0xffffffff
+-
+-#define L_address_rewrite              0x00000001
+-#define L_all_parents                  0x00000002
+-#define L_connection_reject            0x00000004
+-#define L_delay_delivery               0x00000008
+-#define L_dnslist_defer                0x00000010
+-#define L_etrn                         0x00000020
+-#define L_host_lookup_failed           0x00000040
+-#define L_lost_incoming_connection     0x00000080
+-#define L_queue_run                    0x00000100
+-#define L_retry_defer                  0x00000200
+-#define L_size_reject                  0x00000400
+-#define L_skip_delivery                0x00000800
+-#define L_smtp_connection              0x00001000
+-#define L_smtp_incomplete_transaction  0x00002000
+-#define L_smtp_protocol_error          0x00004000
+-#define L_smtp_syntax_error            0x00008000
+-
+-#define LX_acl_warn_skipped            0x80000001
+-#define LX_arguments                   0x80000002
+-#define LX_deliver_time                0x80000004
+-#define LX_delivery_size               0x80000008
+-#define LX_ident_timeout               0x80000010
+-#define LX_incoming_interface          0x80000020
+-#define LX_incoming_port               0x80000040
+-#define LX_outgoing_port               0x80000080
+-#define LX_pid                         0x80000100
+-#define LX_queue_time                  0x80000200
+-#define LX_queue_time_overall          0x80000400
+-#define LX_received_sender             0x80000800
+-#define LX_received_recipients         0x80001000
+-#define LX_rejected_header             0x80002000
+-#define LX_return_path_on_delivery     0x80004000
+-#define LX_sender_on_delivery          0x80008000
+-#define LX_sender_verify_fail          0x80010000
+-#define LX_smtp_confirmation           0x80020000
+-#define LX_smtp_no_mail                0x80040000
+-#define LX_subject                     0x80080000
+-#define LX_tls_certificate_verified    0x80100000
+-#define LX_tls_cipher                  0x80200000
+-#define LX_tls_peerdn                  0x80400000
+-#define LX_tls_sni                     0x80800000
+-#define LX_unknown_in_list             0x81000000
+-#define LX_8bitmime                    0x82000000
+-#define LX_smtp_mailauth               0x84000000
+-#define LX_proxy                       0x88000000
+-
+-#define L_default     (L_connection_reject        | \
+-                       L_delay_delivery           | \
+-                       L_dnslist_defer            | \
+-                       L_etrn                     | \
+-                       L_host_lookup_failed       | \
+-                       L_lost_incoming_connection | \
+-                       L_queue_run                | \
+-                       L_retry_defer              | \
+-                       L_size_reject              | \
+-                       L_skip_delivery)
+-
+-#define LX_default   ((LX_acl_warn_skipped        | \
+-                       LX_rejected_header         | \
+-                       LX_sender_verify_fail      | \
+-                       LX_smtp_confirmation       | \
+-                       LX_tls_certificate_verified| \
+-                       LX_tls_cipher) & 0x7fffffff)
++/* Options bits for logging. Those that have values < BITWORDSIZE can be used
++in calls to log_write(). The others are put into later words in log_selector
++and are only ever tested independently, so they do not need bit mask
++declarations. The Li_all value is recognized specially by decode_bits(). */
++
++#define LOG_BIT(name) Li_##name = IOTA(Li_iota), L_##name = BIT(Li_##name)
++
++enum {
++  Li_all = -1,
++
++  Li_iota = IOTA_INIT(0),
++  LOG_BIT(address_rewrite),
++  LOG_BIT(all_parents),
++  LOG_BIT(connection_reject),
++  LOG_BIT(delay_delivery),
++  LOG_BIT(dnslist_defer),
++  LOG_BIT(etrn),
++  LOG_BIT(host_lookup_failed),
++  LOG_BIT(lost_incoming_connection),
++  LOG_BIT(queue_run),
++  LOG_BIT(retry_defer),
++  LOG_BIT(size_reject),
++  LOG_BIT(skip_delivery),
++  LOG_BIT(smtp_connection),
++  LOG_BIT(smtp_incomplete_transaction),
++  LOG_BIT(smtp_protocol_error),
++  LOG_BIT(smtp_syntax_error),
++
++  Li_acl_warn_skipped = BITWORDSIZE,
++  Li_arguments,
++  Li_deliver_time,
++  Li_delivery_size,
++  Li_ident_timeout,
++  Li_incoming_interface,
++  Li_incoming_port,
++  Li_outgoing_port,
++  Li_pid,
++  Li_queue_time,
++  Li_queue_time_overall,
++  Li_received_sender,
++  Li_received_recipients,
++  Li_rejected_header,
++  Li_return_path_on_delivery,
++  Li_sender_on_delivery,
++  Li_sender_verify_fail,
++  Li_smtp_confirmation,
++  Li_smtp_no_mail,
++  Li_subject,
++  Li_tls_certificate_verified,
++  Li_tls_cipher,
++  Li_tls_peerdn,
++  Li_tls_sni,
++  Li_unknown_in_list,
++  Li_8bitmime,
++  Li_smtp_mailauth,
++  Li_proxy,
++
++  log_selector_size = BITWORD(Li_proxy) + 1
++};
++
++#define LOGGING(opt) BIT_TEST(log_selector, log_selector_size, Li_##opt)
+ 
+ /* Private error numbers for delivery failures, set negative so as not
+ to conflict with system errno values. */
+diff --git a/src/src/match.c b/src/src/match.c
+index 9e47110..fa42187 100644
+--- a/src/src/match.c
++++ b/src/src/match.c
+@@ -771,7 +771,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
+             include_unknown? "yes":"no", error);
+           if (!include_unknown)
+             {
+-            if ((log_extra_selector & LX_unknown_in_list) != 0)
++            if (LOGGING(unknown_in_list))
+               log_write(0, LOG_MAIN, "list matching forced to fail: %s", error);
+             return FAIL;
+             }
+@@ -880,7 +880,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
+           (void)fclose(f);
+           if (!include_unknown)
+             {
+-            if ((log_extra_selector & LX_unknown_in_list) != 0)
++            if (LOGGING(unknown_in_list))
+               log_write(0, LOG_MAIN, "list matching forced to fail: %s", error);
+             return FAIL;
+             }
+diff --git a/src/src/rda.c b/src/src/rda.c
+index 7596466..2afd6dc 100644
+--- a/src/src/rda.c
++++ b/src/src/rda.c
+@@ -635,7 +635,7 @@ if ((pid = fork()) == 0)
+     {
+     DEBUG(D_rewrite) debug_printf("turned off address rewrite logging (not "
+       "root or exim in this process)\n");
+-    log_write_selector &= ~L_address_rewrite;
++    BIT_CLEAR(log_selector, log_selector_size, Li_address_rewrite);
+     }
+ 
+   /* Now do the business */
+diff --git a/src/src/receive.c b/src/src/receive.c
+index 64cf1ae..b430ee2 100644
+--- a/src/src/receive.c
++++ b/src/src/receive.c
+@@ -1118,8 +1118,7 @@ add_host_info_for_log(uschar *s, int *sizeptr, int *ptrptr)
+ if (sender_fullhost != NULL)
+   {
+   s = string_append(s, sizeptr, ptrptr, 2, US" H=", sender_fullhost);
+-  if ((log_extra_selector & LX_incoming_interface) != 0 &&
+-       interface_address != NULL)
++  if (LOGGING(incoming_interface) && interface_address != NULL)
+     {
+     uschar *ss = string_sprintf(" I=[%s]:%d", interface_address,
+       interface_port);
+@@ -2529,7 +2528,7 @@ if (msgid_header == NULL &&
+ rewriting. Must copy the count, because later ACLs and the local_scan()
+ function may mess with the real recipients. */
+ 
+-if ((log_extra_selector & LX_received_recipients) != 0)
++if (LOGGING(received_recipients))
+   {
+   raw_recipients = store_get(recipients_count * sizeof(uschar *));
+   for (i = 0; i < recipients_count; i++)
+@@ -3573,7 +3572,7 @@ else
+     goto TEMPREJECT;
+ 
+     case LOCAL_SCAN_REJECT_NOLOGHDR:
+-    log_extra_selector &= ~LX_rejected_header;
++    BIT_CLEAR(log_selector, log_selector_size, Li_rejected_header);
+     /* Fall through */
+ 
+     case LOCAL_SCAN_REJECT:
+@@ -3582,7 +3581,7 @@ else
+     break;
+ 
+     case LOCAL_SCAN_TEMPREJECT_NOLOGHDR:
+-    log_extra_selector &= ~LX_rejected_header;
++    BIT_CLEAR(log_selector, log_selector_size, Li_rejected_header);
+     /* Fall through */
+ 
+     case LOCAL_SCAN_TEMPREJECT:
+@@ -3747,15 +3746,15 @@ if (message_reference != NULL)
+ s = add_host_info_for_log(s, &size, &sptr);
+ 
+ #ifdef SUPPORT_TLS
+-if (log_extra_selector & LX_tls_cipher && tls_in.cipher)
++if (LOGGING(tls_cipher) && tls_in.cipher)
+   s = string_append(s, &size, &sptr, 2, US" X=", tls_in.cipher);
+-if (log_extra_selector & LX_tls_certificate_verified && tls_in.cipher)
++if (LOGGING(tls_certificate_verified) && tls_in.cipher)
+   s = string_append(s, &size, &sptr, 2, US" CV=",
+     tls_in.certificate_verified? "yes":"no");
+-if (log_extra_selector & LX_tls_peerdn && tls_in.peerdn)
++if (LOGGING(tls_peerdn) && tls_in.peerdn)
+   s = string_append(s, &size, &sptr, 3, US" DN=\"",
+     string_printing(tls_in.peerdn), US"\"");
+-if (log_extra_selector & LX_tls_sni && tls_in.sni)
++if (LOGGING(tls_sni) && tls_in.sni)
+   s = string_append(s, &size, &sptr, 3, US" SNI=\"",
+     string_printing(tls_in.sni), US"\"");
+ #endif
+@@ -3766,7 +3765,7 @@ if (sender_host_authenticated)
+   if (authenticated_id != NULL)
+     {
+     s = string_append(s, &size, &sptr, 2, US":", authenticated_id);
+-    if (log_extra_selector & LX_smtp_mailauth  &&  authenticated_sender != NULL)
++    if (LOGGING(smtp_mailauth) && authenticated_sender != NULL)
+       s = string_append(s, &size, &sptr, 2, US":", authenticated_sender);
+     }
+   }
+@@ -3777,7 +3776,7 @@ if (prdr_requested)
+ #endif
+ 
+ #ifdef EXPERIMENTAL_PROXY
+-if (proxy_session &&  log_extra_selector & LX_proxy)
++if (proxy_session && LOGGING(proxy))
+   s = string_append(s, &size, &sptr, 2, US" PRX=", proxy_host_address);
+ #endif
+ 
+@@ -3788,7 +3787,7 @@ s = string_append(s, &size, &sptr, 2, US" S=", big_buffer);
+    0 ... no BODY= used
+    7 ... 7BIT
+    8 ... 8BITMIME */
+-if (log_extra_selector & LX_8bitmime)
++if (LOGGING(8bitmime))
+   {
+   sprintf(CS big_buffer, "%d", body_8bitmime);
+   s = string_append(s, &size, &sptr, 2, US" M8S=", big_buffer);
+@@ -3814,7 +3813,7 @@ if (msgid_header != NULL)
+ /* If subject logging is turned on, create suitable printing-character
+ text. By expanding $h_subject: we make use of the MIME decoding. */
+ 
+-if ((log_extra_selector & LX_subject) != 0 && subject_header != NULL)
++if (LOGGING(subject) && subject_header != NULL)
+   {
+   int i;
+   uschar *p = big_buffer;
+@@ -4003,8 +4002,8 @@ if(!smtp_reply)
+ #endif
+   {
+   log_write(0, LOG_MAIN |
+-    (((log_extra_selector & LX_received_recipients) != 0)? LOG_RECIPIENTS : 0) |
+-    (((log_extra_selector & LX_received_sender) != 0)? LOG_SENDER : 0),
++    (LOGGING(received_recipients)? LOG_RECIPIENTS : 0) |
++    (LOGGING(received_sender)? LOG_SENDER : 0),
+     "%s", s);
+ 
+   /* Log any control actions taken by an ACL or local_scan(). */
+diff --git a/src/src/rewrite.c b/src/src/rewrite.c
+index 296fe8c..ca7fb6a 100644
+--- a/src/src/rewrite.c
++++ b/src/src/rewrite.c
+@@ -247,8 +247,7 @@ for (rule = rewrite_rules;
+ 
+   /* We have a validly rewritten address */
+ 
+-  if ((log_write_selector & L_address_rewrite) != 0 ||
+-      (debug_selector & D_rewrite) != 0)
++  if (LOGGING(address_rewrite) || (debug_selector & D_rewrite) != 0)
+     {
+     int i;
+     const uschar *where = CUS"?";
+diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
+index effc636..9982451 100644
+--- a/src/src/smtp_in.c
++++ b/src/src/smtp_in.c
+@@ -1234,8 +1234,7 @@ if (sender_host_unknown || sender_host_notsocket)
+ if (is_inetd)
+   return string_sprintf("SMTP connection from %s (via inetd)", hostname);
+ 
+-if ((log_extra_selector & LX_incoming_interface) != 0 &&
+-     interface_address != NULL)
++if (LOGGING(incoming_interface) && interface_address != NULL)
+   return string_sprintf("SMTP connection from %s I=[%s]:%d", hostname,
+     interface_address, interface_port);
+ 
+@@ -1260,16 +1259,15 @@ s_tlslog(uschar * s, int * sizep, int * ptrp)
+   int size = sizep ? *sizep : 0;
+   int ptr = ptrp ? *ptrp : 0;
+ 
+-  if ((log_extra_selector & LX_tls_cipher) != 0 && tls_in.cipher != NULL)
++  if (LOGGING(tls_cipher) && tls_in.cipher != NULL)
+     s = string_append(s, &size, &ptr, 2, US" X=", tls_in.cipher);
+-  if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
+-       tls_in.cipher != NULL)
++  if (LOGGING(tls_certificate_verified) && tls_in.cipher != NULL)
+     s = string_append(s, &size, &ptr, 2, US" CV=",
+       tls_in.certificate_verified? "yes":"no");
+-  if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_in.peerdn != NULL)
++  if (LOGGING(tls_peerdn) && tls_in.peerdn != NULL)
+     s = string_append(s, &size, &ptr, 3, US" DN=\"",
+       string_printing(tls_in.peerdn), US"\"");
+-  if ((log_extra_selector & LX_tls_sni) != 0 && tls_in.sni != NULL)
++  if (LOGGING(tls_sni) && tls_in.sni != NULL)
+     s = string_append(s, &size, &ptr, 3, US" SNI=\"",
+       string_printing(tls_in.sni), US"\"");
+ 
+@@ -1301,7 +1299,7 @@ smtp_log_no_mail(void)
+ int size, ptr, i;
+ uschar *s, *sep;
+ 
+-if (smtp_mailcmd_count > 0 || (log_extra_selector & LX_smtp_no_mail) == 0)
++if (smtp_mailcmd_count > 0 || !LOGGING(smtp_no_mail))
+   return;
+ 
+ s = NULL;
+@@ -2510,8 +2508,8 @@ static void
+ incomplete_transaction_log(uschar *what)
+ {
+ if (sender_address == NULL ||                 /* No transaction in progress */
+-    (log_write_selector & L_smtp_incomplete_transaction) == 0  /* Not logging */
+-  ) return;
++    !LOGGING(smtp_incomplete_transaction))
++  return;
+ 
+ /* Build list of recipients for logging */
+ 
+@@ -2762,7 +2760,7 @@ if (sender_verified_failed != NULL &&
+ 
+   setflag(sender_verified_failed, af_sverify_told);
+ 
+-  if (rc != FAIL || (log_extra_selector & LX_sender_verify_fail) != 0)
++  if (rc != FAIL || LOGGING(sender_verify_fail))
+     log_write(0, LOG_MAIN|LOG_REJECT, "%s sender verify %s for <%s>%s",
+       host_and_ident(TRUE),
+       ((sender_verified_failed->special_action & 255) == DEFER)? "defer":"fail",
+diff --git a/src/src/structs.h b/src/src/structs.h
+index 6f143d6..438b521 100644
+--- a/src/src/structs.h
++++ b/src/src/structs.h
+@@ -38,7 +38,7 @@ typedef struct macro_item {
+ 
+ typedef struct bit_table {
+   uschar *name;
+-  unsigned int bit;
++  int bit;
+ } bit_table;
+ 
+ /* Block for holding a uid and gid, possibly unset, and an initgroups flag. */
+diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
+index b1dccb8..73ac807 100644
+--- a/src/src/tls-openssl.c
++++ b/src/src/tls-openssl.c
+@@ -1111,8 +1111,7 @@ len = SSL_get_tlsext_status_ocsp_resp(s, &p);
+ if(!p)
+  {
+   /* Expect this when we requested ocsp but got none */
+-  if (  cbinfo->u_ocsp.client.verify_required
+-     && log_extra_selector & LX_tls_cipher)
++  if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher))
+     log_write(0, LOG_MAIN, "Received TLS status callback, null content");
+   else
+     DEBUG(D_tls) debug_printf(" null\n");
+@@ -1122,7 +1121,7 @@ if(!p)
+ if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
+  {
+   tls_out.ocsp = OCSP_FAILED;
+-  if (log_extra_selector & LX_tls_cipher)
++  if (LOGGING(tls_cipher))
+     log_write(0, LOG_MAIN, "Received TLS cert status response, parse error");
+   else
+     DEBUG(D_tls) debug_printf(" parse error\n");
+@@ -1132,7 +1131,7 @@ if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
+ if(!(bs = OCSP_response_get1_basic(rsp)))
+   {
+   tls_out.ocsp = OCSP_FAILED;
+-  if (log_extra_selector & LX_tls_cipher)
++  if (LOGGING(tls_cipher))
+     log_write(0, LOG_MAIN, "Received TLS cert status response, error parsing response");
+   else
+     DEBUG(D_tls) debug_printf(" error parsing response\n");
+@@ -1163,7 +1162,7 @@ if(!(bs = OCSP_response_get1_basic(rsp)))
+ 	      cbinfo->u_ocsp.client.verify_store, 0)) <= 0)
+       {
+       tls_out.ocsp = OCSP_FAILED;
+-      if (log_extra_selector & LX_tls_cipher)
++      if (LOGGING(tls_cipher))
+ 	log_write(0, LOG_MAIN, "Received TLS cert status response, itself unverifiable");
+       BIO_printf(bp, "OCSP response verify failure\n");
+       ERR_print_errors(bp);
+diff --git a/src/src/transports/lmtp.c b/src/src/transports/lmtp.c
+index 0cd89af..1f4d7a6 100644
+--- a/src/src/transports/lmtp.c
++++ b/src/src/transports/lmtp.c
+@@ -654,7 +654,7 @@ if (send_data)
+     if (lmtp_read_response(out, buffer, sizeof(buffer), '2', timeout))
+       {
+       addr->transport_return = OK;
+-      if ((log_extra_selector & LX_smtp_confirmation) != 0)
++      if (LOGGING(smtp_confirmation))
+         {
+         const uschar *s = string_printing(buffer);
+ 	/* de-const safe here as string_printing known to have alloc'n'copied */
+diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
+index a952413..609dba3 100644
+--- a/src/src/transports/smtp.c
++++ b/src/src/transports/smtp.c
+@@ -638,7 +638,7 @@ if (addr->message)
+   }
+ else
+   {
+-  if (log_extra_selector & LX_outgoing_port)
++  if (LOGGING(outgoing_port))
+     message = string_sprintf("%s:%d", message,
+ 		host->port == PORT_NONE ? 25 : host->port);
+   log_write(0, LOG_MAIN, "%s %s", message, strerror(addr->basic_errno));
+@@ -2380,7 +2380,7 @@ if (!ok) ok = TRUE; else
+ 
+     if (
+ #ifndef EXPERIMENTAL_EVENT
+-          (log_extra_selector & LX_smtp_confirmation) != 0 &&
++          LOGGING(smtp_confirmation) &&
+ #endif
+           !lmtp
+        )
+@@ -2435,7 +2435,7 @@ if (!ok) ok = TRUE; else
+           continue;
+           }
+         completed_address = TRUE;   /* NOW we can set this flag */
+-        if ((log_extra_selector & LX_smtp_confirmation) != 0)
++        if (LOGGING(smtp_confirmation))
+           {
+           const uschar *s = string_printing(buffer);
+ 	  /* deconst cast ok here as string_printing was checked to have alloc'n'copied */
+diff --git a/src/src/verify.c b/src/src/verify.c
+index e00e7b9..7992d58 100644
+--- a/src/src/verify.c
++++ b/src/src/verify.c
+@@ -2916,7 +2916,7 @@ if (ip_bind(sock, host_af, interface_address, 0) < 0)
+ if (ip_connect(sock, host_af, sender_host_address, port, rfc1413_query_timeout)
+      < 0)
+   {
+-  if (errno == ETIMEDOUT && (log_extra_selector & LX_ident_timeout) != 0)
++  if (errno == ETIMEDOUT && LOGGING(ident_timeout))
+     {
+     log_write(0, LOG_MAIN, "ident connection to %s timed out",
+       sender_host_address);
+
+commit ac881e2749754fbe167b5f38784dd85b088571cf
+Author: Tony Finch <dot at dotat.at>
+Date:   Thu Aug 13 15:16:51 2015 +0100
+
+    Improve the consistency of logging incoming and outgoing interfaces.
+    
+    The I= interface field on outgoing lines is now after the H= remote
+    host field, same as incoming lines. There is a separate outgoing_interface
+    log selector which allows you to disable the outgoing I= field.
+    
+    (slight massaging by JH)
+
+diff --git a/src/src/deliver.c b/src/src/deliver.c
+index c796de0..0e7cea3 100644
+--- a/src/src/deliver.c
++++ b/src/src/deliver.c
+@@ -676,39 +676,78 @@ while (addr->parent != NULL)
+ 
+ 
+ 
++/*************************************************
++*      Delivery logging support functions        *
++*************************************************/
++
++/* The LOGGING() checks in d_log_interface() are complicated for backwards
++compatibility. When outgoing interface logging was originally added, it was
++conditional on just incoming_interface (which is off by default). The
++outgoing_interface option is on by default to preserve this behaviour, but
++you can enable incoming_interface and disable outgoing_interface to get I=
++fields on incoming lines only.
++
++Arguments:
++  s         The log line buffer
++  sizep     Pointer to the buffer size
++  ptrp      Pointer to current index into buffer
++  addr      The address to be logged
++
++Returns:    New value for s
++*/
+ 
+ static uschar *
+-d_hostlog(uschar * s, int * sizep, int * ptrp, address_item * addr)
++d_log_interface(uschar *s, int *sizep, int *ptrp)
+ {
+-  s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name,
+-    US" [", addr->host_used->address, US"]");
++if (LOGGING(incoming_interface) && LOGGING(outgoing_interface)
++    && sending_ip_address != NULL)
++  {
++  s = string_append(s, sizep, ptrp, 2, US" I=[", sending_ip_address);
+   if (LOGGING(outgoing_port))
+-    s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
+-      addr->host_used->port));
+-  return s;
++    s = string_append(s, sizep, ptrp, 2, US"]:",
++      string_sprintf("%d", sending_port));
++  else
++    s = string_cat(s, sizep, ptrp, "]", 1);
++  }
++return s;
+ }
+ 
++
++
++static uschar *
++d_hostlog(uschar *s, int *sizep, int *ptrp, address_item *addr)
++{
++s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name,
++  US" [", addr->host_used->address, US"]");
++if (LOGGING(outgoing_port))
++  s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
++    addr->host_used->port));
++return d_log_interface(s, sizep, ptrp);
++}
++
++
++
+ #ifdef SUPPORT_TLS
+ static uschar *
+ d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr)
+ {
+-  if (LOGGING(tls_cipher) && addr->cipher != NULL)
+-    s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher);
+-  if (LOGGING(tls_certificate_verified) && addr->cipher != NULL)
+-    s = string_append(s, sizep, ptrp, 2, US" CV=",
+-      testflag(addr, af_cert_verified)
+-      ?
++if (LOGGING(tls_cipher) && addr->cipher != NULL)
++  s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher);
++if (LOGGING(tls_certificate_verified) && addr->cipher != NULL)
++  s = string_append(s, sizep, ptrp, 2, US" CV=",
++    testflag(addr, af_cert_verified)
++    ?
+ #ifdef EXPERIMENTAL_DANE
+-        testflag(addr, af_dane_verified)
+-      ? "dane"
+-      :
++      testflag(addr, af_dane_verified)
++    ? "dane"
++    :
+ #endif
+-        "yes"
+-      : "no");
+-  if (LOGGING(tls_peerdn) && addr->peerdn != NULL)
+-    s = string_append(s, sizep, ptrp, 3, US" DN=\"",
+-      string_printing(addr->peerdn), US"\"");
+-  return s;
++      "yes"
++    : "no");
++if (LOGGING(tls_peerdn) && addr->peerdn != NULL)
++  s = string_append(s, sizep, ptrp, 3, US" DN=\"",
++    string_printing(addr->peerdn), US"\"");
++return s;
+ }
+ #endif
+ 
+@@ -816,10 +855,6 @@ else
+   s = string_append(s, &size, &ptr, 2, US"> ", log_address);
+   }
+ 
+-if (LOGGING(incoming_interface) && sending_ip_address)
+-  s = string_append(s, &size, &ptr, 3, US" I=[", sending_ip_address, US"]");
+-  /* for the port:  string_sprintf("%d", sending_port) */
+-
+ if (LOGGING(sender_on_delivery) || msg)
+   s = string_append(s, &size, &ptr, 3, US" F=<",
+ #ifdef EXPERIMENTAL_INTERNATIONAL
+@@ -862,6 +897,7 @@ if (addr->transport->info->local)
+   {
+   if (addr->host_list)
+     s = string_append(s, &size, &ptr, 2, US" H=", addr->host_list->name);
++  s = d_log_interface(s, &size, &ptr);
+   if (addr->shadow_message != NULL)
+     s = string_cat(s, &size, &ptr, addr->shadow_message,
+       Ustrlen(addr->shadow_message));
+diff --git a/src/src/globals.c b/src/src/globals.c
+index 1344b5a..4188b4d 100644
+--- a/src/src/globals.c
++++ b/src/src/globals.c
+@@ -827,6 +827,7 @@ int     log_default[]          = { /* for initializing log_selector */
+   Li_etrn,
+   Li_host_lookup_failed,
+   Li_lost_incoming_connection,
++  Li_outgoing_interface, /* see d_log_interface in deliver.c */
+   Li_queue_run,
+   Li_rejected_header,
+   Li_retry_defer,
+@@ -863,6 +864,7 @@ bit_table log_options[]        = { /* must be in alphabetical order */
+   BIT_TABLE(L, incoming_interface),
+   BIT_TABLE(L, incoming_port),
+   BIT_TABLE(L, lost_incoming_connection),
++  BIT_TABLE(L, outgoing_interface),
+   BIT_TABLE(L, outgoing_port),
+   BIT_TABLE(L, pid),
+ #ifdef EXPERIMENTAL_PROXY
+diff --git a/src/src/log.c b/src/src/log.c
+index b2d1fcf..558c000 100644
+--- a/src/src/log.c
++++ b/src/src/log.c
+@@ -753,8 +753,8 @@ DEBUG(D_any|D_v)
+ 
+   for (i = 0; i < log_options_count; i++)
+     {
+-    unsigned int bit = log_options[i].bit;
+-    if (bit < BITWORDSIZE && selector == BIT(bit))
++    unsigned int bitnum = log_options[i].bit;
++    if (bitnum < BITWORDSIZE && selector == BIT(bitnum))
+       {
+       *ptr++ = ' ';
+       Ustrcpy(ptr, log_options[i].name);
+diff --git a/src/src/macros.h b/src/src/macros.h
+index d63025e..0ce24f8 100644
+--- a/src/src/macros.h
++++ b/src/src/macros.h
+@@ -474,8 +474,9 @@ enum {
+   Li_8bitmime,
+   Li_smtp_mailauth,
+   Li_proxy,
++  Li_outgoing_interface,
+ 
+-  log_selector_size = BITWORD(Li_proxy) + 1
++  log_selector_size = BITWORD(Li_outgoing_interface) + 1
+ };
+ 
+ #define LOGGING(opt) BIT_TEST(log_selector, log_selector_size, Li_##opt)
+
+commit 6b51df8340eacc95e3def9a4376506610e91996c
+Author: Heiko Schlittermann (HS12-RIPE) <hs at schlittermann.de>
+Date:   Wed Aug 19 15:22:41 2015 +0200
+
+    Fix post-transport-crash.
+    
+    The crash probably was introduced in a39bd74d3e94 and
+    needs 'split_spool_directory=yes' to expose.
+    
+    Thanks to Wolfgang Breyha, who found the same fix.
+
+diff --git a/src/src/transport.c b/src/src/transport.c
+index fa6f869..a6ad3ed 100644
+--- a/src/src/transport.c
++++ b/src/src/transport.c
+@@ -1752,7 +1752,7 @@ while (1)
+     {
+     if (split_spool_directory)
+ 	sprintf(CS spool_file, "%s%c/%s-D",
+-		      spool_dir, new_message_id[5], msgq[i].message_id);
++		      spool_dir, msgq[i].message_id[5], msgq[i].message_id);
+     else
+ 	sprintf(CS spool_file, "%s%s-D", spool_dir, msgq[i].message_id);
+ 
+
+commit b20b82a0b4169cb23380a373ed2a898b0cb337d2
+Author: Heiko Schlittermann (HS12-RIPE) <hs at schlittermann.de>
+Date:   Fri Aug 21 12:26:50 2015 +0200
+
+    Add a .ctags file to src
+
+diff --git a/src/.ctags b/src/.ctags
+new file mode 100644
+index 0000000..c764086
+--- /dev/null
++++ b/src/.ctags
+@@ -0,0 +1,2 @@
++--recurse
++--exclude=build-*
+
+commit dadff1d47e54962b0fdf98e8ce5cef42b6cb7fb5
+Author: Heiko Schlittermann (HS12-RIPE) <hs at schlittermann.de>
+Date:   Thu Aug 20 13:58:06 2015 +0200
+
+    Fix post-transport-crash: safeguard for missing spool BUG 1671
+    
+    Based on a proposal from Wolfgang Breyha.
+
+diff --git a/src/src/deliver.c b/src/src/deliver.c
+index 0e7cea3..b5aa9b9 100644
+--- a/src/src/deliver.c
++++ b/src/src/deliver.c
+@@ -9,6 +9,7 @@
+ 
+ 
+ #include "exim.h"
++#include <assert.h>
+ 
+ 
+ /* Data block for keeping track of subprocesses for parallel remote
+@@ -7934,17 +7935,36 @@ if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
+ uschar *
+ deliver_get_sender_address (uschar * id)
+ {
++int rc;
++uschar * new_sender_address,
++       * save_sender_address;
++
+ if (!spool_open_datafile(id))
+   return NULL;
+ 
++/* Save and restore the global sender_address.  I'm not sure if we should
++not save/restore all the other global variables too, because
++spool_read_header() may change all of them. But OTOH, when this
++deliver_get_sender_address() gets called, the current message is done
++already and nobody needs the globals anymore. (HS12, 2015-08-21) */
++
+ sprintf(CS spoolname, "%s-H", id);
+-if (spool_read_header(spoolname, TRUE, TRUE) != spool_read_OK)
++save_sender_address = sender_address;
++
++rc = spool_read_header(spoolname, TRUE, TRUE);
++
++new_sender_address = sender_address;
++sender_address = save_sender_address;
++
++if (rc != spool_read_OK)
+   return NULL;
+ 
++assert(new_sender_address);
++
+ (void)close(deliver_datafile);
+ deliver_datafile = -1;
+ 
+-return sender_address;
++return new_sender_address;
+ }
+ 
+ /* vi: aw ai sw=2
+diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
+index 609dba3..c93f2ef 100644
+--- a/src/src/transports/smtp.c
++++ b/src/src/transports/smtp.c
+@@ -1274,14 +1274,19 @@ we will veto this new message.  */
+ static BOOL
+ smtp_are_same_identities(uschar * message_id, smtp_compare_t * s_compare)
+ {
+-uschar * save_sender_address = sender_address;
+-uschar * current_local_identity =
++
++uschar * message_local_identity,
++       * current_local_identity,
++       * new_sender_address;
++
++current_local_identity =
+   smtp_local_identity(s_compare->current_sender_address, s_compare->tblock);
+-uschar * new_sender_address = deliver_get_sender_address(message_id);
+-uschar * message_local_identity =
+-  smtp_local_identity(new_sender_address, s_compare->tblock);
+ 
+-sender_address = save_sender_address;
++if (!(new_sender_address = deliver_get_sender_address(message_id)))
++    return 0;
++
++message_local_identity =
++  smtp_local_identity(new_sender_address, s_compare->tblock);
+ 
+ return Ustrcmp(current_local_identity, message_local_identity) == 0;
+ }
+
+commit 3703d8187af01d13ca71f7918c7ef78529bb784d
+Author: Jeremy Harris <jgh146exb at wizmail.org>
+Date:   Fri Aug 21 18:08:39 2015 +0100
+
+    Remember the fail reason for verify=headers_syntax.  Bug 264
+
+diff --git a/src/src/acl.c b/src/src/acl.c
+index f2e0ef2..064ee6c 100644
+--- a/src/src/acl.c
++++ b/src/src/acl.c
+@@ -1803,27 +1803,27 @@ switch(vp->value)
+     test whether it was successful or not. (This is for optional verification; for
+     mandatory verification, the connection doesn't last this long.) */
+ 
+-      if (tls_in.certificate_verified) return OK;
+-      *user_msgptr = US"no verified certificate";
+-      return FAIL;
++    if (tls_in.certificate_verified) return OK;
++    *user_msgptr = US"no verified certificate";
++    return FAIL;
+ 
+   case VERIFY_HELO:
+     /* We can test the result of optional HELO verification that might have
+     occurred earlier. If not, we can attempt the verification now. */
+ 
+-      if (!helo_verified && !helo_verify_failed) smtp_verify_helo();
+-      return helo_verified? OK : FAIL;
++    if (!helo_verified && !helo_verify_failed) smtp_verify_helo();
++    return helo_verified? OK : FAIL;
+ 
+   case VERIFY_CSA:
+     /* Do Client SMTP Authorization checks in a separate function, and turn the
+     result code into user-friendly strings. */
+ 
+-      rc = acl_verify_csa(list);
+-      *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s",
++    rc = acl_verify_csa(list);
++    *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s",
+                                               csa_reason_string[rc]);
+-      csa_status = csa_status_string[rc];
+-      DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status);
+-      return csa_return_code[rc];
++    csa_status = csa_status_string[rc];
++    DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status);
++    return csa_return_code[rc];
+ 
+   case VERIFY_HDR_SYNTAX:
+     /* Check that all relevant header lines have the correct syntax. If there is
+@@ -1832,8 +1832,11 @@ switch(vp->value)
+     always). */
+ 
+     rc = verify_check_headers(log_msgptr);
+-    if (rc != OK && smtp_return_error_details && *log_msgptr != NULL)
+-      *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
++    if (rc != OK && *log_msgptr)
++      if (smtp_return_error_details)
++	*user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
++      else
++	acl_verify_message = *log_msgptr;
+     return rc;
+ 
+   case VERIFY_HDR_NAMES_ASCII:
+@@ -3788,7 +3791,8 @@ for (; cb != NULL; cb = cb->next)
+ 
+     case ACLC_VERIFY:
+     rc = acl_verify(where, addr, arg, user_msgptr, log_msgptr, basic_errno);
+-    acl_verify_message = *user_msgptr;
++    if (*user_msgptr)
++      acl_verify_message = *user_msgptr;
+     if (verb == ACL_WARN) *user_msgptr = NULL;
+     break;
+ 
+
+commit c8899c20aa08c9ae6a4c291aad23ba90512bebe4
+Author: Jeremy Harris <jgh146exb at wizmail.org>
+Date:   Tue Aug 25 10:36:27 2015 +0100
+
+    Close logs after daemon-process exceptional write.  Bug 728
+
+diff --git a/src/src/daemon.c b/src/src/daemon.c
+index 2d10387..e1ff9a1 100644
+--- a/src/src/daemon.c
++++ b/src/src/daemon.c
+@@ -735,6 +735,7 @@ else (void)close(dup_accept_socket);
+ /* Release any store used in this process, including the store used for holding
+ the incoming host address and an expanded active_hostname. */
+ 
++log_close_all();
+ store_reset(reset_point);
+ sender_host_address = NULL;
+ }
+
+commit f38917cc94ab337c15ff70c254dd564ee2dcafe7
+Author: Jeremy Harris <jgh146exb at wizmail.org>
+Date:   Tue Sep 8 23:05:20 2015 +0100
+
+        Capture substrings in ACL regex= .  Bug 425.
+
+diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults
+index c33e098..596e651 100644
+--- a/src/src/config.h.defaults
++++ b/src/src/config.h.defaults
+@@ -116,6 +116,8 @@ it's a default value. */
+ #define RADIUS_CONFIG_FILE
+ #define RADIUS_LIB_TYPE
+ 
++#define REGEX_VARS                 9
++
+ #define ROUTER_ACCEPT
+ #define ROUTER_DNSLOOKUP
+ #define ROUTER_IPLITERAL
+diff --git a/src/src/exim.c b/src/src/exim.c
+index d7cb5d8..999b94c 100644
+--- a/src/src/exim.c
++++ b/src/src/exim.c
+@@ -1753,6 +1753,8 @@ regex_whitelisted_macro =
+   regex_must_compile(US"^[A-Za-z0-9_/.-]*$", FALSE, TRUE);
+ #endif
+ 
++for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL;
++
+ 
+ /* If the program is called as "mailq" treat it as equivalent to "exim -bp";
+ this seems to be a generally accepted convention, since one finds symbolic
+diff --git a/src/src/expand.c b/src/src/expand.c
+index 89e0ac7..1bff521 100644
+--- a/src/src/expand.c
++++ b/src/src/expand.c
+@@ -1726,7 +1726,14 @@ if (Ustrncmp(name, "auth", 4) == 0)
+   uschar *endptr;
+   int n = Ustrtoul(name + 4, &endptr, 10);
+   if (*endptr == 0 && n != 0 && n <= AUTH_VARS)
+-    return (auth_vars[n-1] == NULL)? US"" : auth_vars[n-1];
++    return !auth_vars[n-1] ? US"" : auth_vars[n-1];
++  }
++else if (Ustrncmp(name, "regex", 5) == 0)
++  {
++  uschar *endptr;
++  int n = Ustrtoul(name + 5, &endptr, 10);
++  if (*endptr == 0 && n != 0 && n <= REGEX_VARS)
++    return !regex_vars[n-1] ? US"" : regex_vars[n-1];
+   }
+ 
+ /* For all other variables, search the table */
+diff --git a/src/src/globals.c b/src/src/globals.c
+index 4188b4d..8445f00 100644
+--- a/src/src/globals.c
++++ b/src/src/globals.c
+@@ -1090,8 +1090,9 @@ const pcre *regex_From         = NULL;
+ const pcre *regex_IGNOREQUOTA  = NULL;
+ const pcre *regex_PIPELINING   = NULL;
+ const pcre *regex_SIZE         = NULL;
+-const pcre *regex_smtp_code    = NULL;
+ const pcre *regex_ismsgid      = NULL;
++const pcre *regex_smtp_code    = NULL;
++uschar *regex_vars[REGEX_VARS];
+ #ifdef WHITELIST_D_MACROS
+ const pcre *regex_whitelisted_macro = NULL;
+ #endif
+diff --git a/src/src/globals.h b/src/src/globals.h
+index 978a4cc..3c69e43 100644
+--- a/src/src/globals.h
++++ b/src/src/globals.h
+@@ -717,8 +717,9 @@ extern const pcre  *regex_From;        /* For recognizing "From_" lines */
+ extern const pcre  *regex_IGNOREQUOTA; /* For recognizing IGNOREQUOTA (LMTP) */
+ extern const pcre  *regex_PIPELINING;  /* For recognizing PIPELINING */
+ extern const pcre  *regex_SIZE;        /* For recognizing SIZE settings */
+-extern const pcre  *regex_smtp_code;   /* For recognizing SMTP codes */
+ extern const pcre  *regex_ismsgid;     /* Compiled r.e. for message it */
++extern const pcre  *regex_smtp_code;   /* For recognizing SMTP codes */
++extern uschar *regex_vars[];           /* $regexN variables */
+ #ifdef WHITELIST_D_MACROS
+ extern const pcre  *regex_whitelisted_macro; /* For -D macro values */
+ #endif
+diff --git a/src/src/regex.c b/src/src/regex.c
+index ed73b6e..93422fa 100644
+--- a/src/src/regex.c
++++ b/src/src/regex.c
+@@ -25,109 +25,120 @@ uschar regex_match_string_buffer[1024];
+ extern FILE *mime_stream;
+ extern uschar *mime_current_boundary;
+ 
+-int
+-regex(const uschar **listptr)
++static pcre_list *
++compile(const uschar * list)
+ {
+   int sep = 0;
+-  const uschar *list = *listptr;
+   uschar *regex_string;
+   uschar regex_string_buffer[1024];
+-  unsigned long mbox_size;
+-  FILE *mbox_file;
+-  pcre *re;
+-  pcre_list *re_list_head = NULL;
+-  pcre_list *re_list_item;
+   const char *pcre_error;
+   int pcre_erroffset;
++  pcre_list *re_list_head = NULL;
++  pcre_list *ri;
++
++  /* precompile our regexes */
++  while ((regex_string = string_nextinlist(&list, &sep,
++                                           regex_string_buffer,
++                                           sizeof(regex_string_buffer))) != NULL) {
++    pcre *re;
++
++    /* parse option */
++    if ( (strcmpic(regex_string,US"false") == 0) ||
++         (Ustrcmp(regex_string,"0") == 0) )
++      continue;				/* explicitly no matching */
++
++    /* compile our regular expression */
++    if (!(re = pcre_compile( CS regex_string,
++                       0, &pcre_error, &pcre_erroffset, NULL ))) {
++      log_write(0, LOG_MAIN,
++           "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.",
++	   regex_string, pcre_error, pcre_erroffset);
++      continue;
++    }
++
++    ri = store_get(sizeof(pcre_list));
++    ri->re = re;
++    ri->pcre_text = string_copy(regex_string);
++    ri->next = re_list_head;
++    re_list_head = ri;
++  }
++  return re_list_head;
++}
++
++static int
++matcher(pcre_list * re_list_head, uschar * linebuffer, int len)
++{
++  pcre_list * ri;
++
++  for(ri = re_list_head; ri; ri = ri->next)
++    {
++    int ovec[3*(REGEX_VARS+1)];
++    int n, nn;
++
++    /* try matcher on the line */
++    n = pcre_exec(ri->re, NULL,
++	 CS linebuffer, len, 0, 0,
++	 ovec, nelem(ovec));
++    if (n > 0)
++      {
++      Ustrncpy(regex_match_string_buffer, ri->pcre_text, 1023);
++      regex_match_string = regex_match_string_buffer;
++
++      for (nn = 1; nn < n; nn++)
++	regex_vars[nn-1] =
++	  string_copyn(linebuffer + ovec[nn*2], ovec[nn*2+1] - ovec[nn*2]);
++
++      return OK;
++      }
++    }
++  return FAIL;
++}
++
++int
++regex(const uschar **listptr)
++{
++  unsigned long mbox_size;
++  FILE *mbox_file;
++  pcre_list *re_list_head;
+   uschar *linebuffer;
+   long f_pos = 0;
++  int ret = FAIL;
+ 
+   /* reset expansion variable */
+   regex_match_string = NULL;
+ 
+-  if (mime_stream == NULL) {
+-    /* We are in the DATA ACL */
++  if (mime_stream == NULL) {			/* We are in the DATA ACL */
+     mbox_file = spool_mbox(&mbox_size, NULL);
+-    if (mbox_file == NULL) {
+-      /* error while spooling */
++    if (mbox_file == NULL) {			/* error while spooling */
+       log_write(0, LOG_MAIN|LOG_PANIC,
+              "regex acl condition: error while creating mbox spool file");
+       return DEFER;
+-    };
++    }
+   }
+   else {
+     f_pos = ftell(mime_stream);
+     mbox_file = mime_stream;
+-  };
++  }
+ 
+   /* precompile our regexes */
+-  while ((regex_string = string_nextinlist(&list, &sep,
+-                                           regex_string_buffer,
+-                                           sizeof(regex_string_buffer))) != NULL) {
+-
+-    /* parse option */
+-    if ( (strcmpic(regex_string,US"false") == 0) ||
+-         (Ustrcmp(regex_string,"0") == 0) ) {
+-      /* explicitly no matching */
+-      continue;
+-    };
+-
+-    /* compile our regular expression */
+-    re = pcre_compile( CS regex_string,
+-                       0,
+-                       &pcre_error,
+-                       &pcre_erroffset,
+-                       NULL );
+-
+-    if (re == NULL) {
+-      log_write(0, LOG_MAIN,
+-           "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset);
+-      continue;
+-    }
+-    else {
+-      re_list_item = store_get(sizeof(pcre_list));
+-      re_list_item->re = re;
+-      re_list_item->pcre_text = string_copy(regex_string);
+-      re_list_item->next = re_list_head;
+-      re_list_head = re_list_item;
+-    };
+-  };
+-
+-  /* no regexes -> nothing to do */
+-  if (re_list_head == NULL) {
+-    return FAIL;
+-  };
++  if (!(re_list_head = compile(*listptr)))
++    return FAIL;			/* no regexes -> nothing to do */
+ 
+   /* match each line against all regexes */
+   linebuffer = store_get(32767);
+   while (fgets(CS linebuffer, 32767, mbox_file) != NULL) {
+-    if ( (mime_stream != NULL) && (mime_current_boundary != NULL) ) {
+-      /* check boundary */
+-      if (Ustrncmp(linebuffer,"--",2) == 0) {
+-        if (Ustrncmp((linebuffer+2),mime_current_boundary,Ustrlen(mime_current_boundary)) == 0)
+-          /* found boundary */
+-          break;
+-      };
+-    };
+-    re_list_item = re_list_head;
+-    do {
+-      /* try matcher on the line */
+-      if (pcre_exec(re_list_item->re, NULL, CS linebuffer,
+-      (int)Ustrlen(linebuffer), 0, 0, NULL, 0) >= 0) {
+-        Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023);
+-        regex_match_string = regex_match_string_buffer;
+-        if (mime_stream == NULL)
+-          (void)fclose(mbox_file);
+-        else {
+-          clearerr(mime_stream);
+-          fseek(mime_stream,f_pos,SEEK_SET);
+-        };
+-        return OK;
+-      };
+-      re_list_item = re_list_item->next;
+-    } while (re_list_item != NULL);
+-  };
+ 
++    if (  mime_stream && mime_current_boundary		/* check boundary */
++       && Ustrncmp(linebuffer,"--",2) == 0
++       && Ustrncmp((linebuffer+2),mime_current_boundary,Ustrlen(mime_current_boundary)) == 0)
++	break;						/* found boundary */
++
++    if ((ret = matcher(re_list_head, linebuffer, (int)Ustrlen(linebuffer))) == OK)
++      goto done;
++  }
++  /* no matches ... */
++
++done:
+   if (mime_stream == NULL)
+     (void)fclose(mbox_file);
+   else {
+@@ -135,67 +146,25 @@ regex(const uschar **listptr)
+     fseek(mime_stream,f_pos,SEEK_SET);
+   };
+ 
+-  /* no matches ... */
+-  return FAIL;
++  return ret;
+ }
+ 
+ 
+ int
+ mime_regex(const uschar **listptr)
+ {
+-  int sep = 0;
+-  const uschar *list = *listptr;
+-  uschar *regex_string;
+-  uschar regex_string_buffer[1024];
+-  pcre *re;
+   pcre_list *re_list_head = NULL;
+-  pcre_list *re_list_item;
+-  const char *pcre_error;
+-  int pcre_erroffset;
+   FILE *f;
+   uschar *mime_subject = NULL;
+   int mime_subject_len = 0;
++  int ret;
+ 
+   /* reset expansion variable */
+   regex_match_string = NULL;
+ 
+   /* precompile our regexes */
+-  while ((regex_string = string_nextinlist(&list, &sep,
+-                                           regex_string_buffer,
+-                                           sizeof(regex_string_buffer))) != NULL) {
+-
+-    /* parse option */
+-    if ( (strcmpic(regex_string,US"false") == 0) ||
+-         (Ustrcmp(regex_string,"0") == 0) ) {
+-      /* explicitly no matching */
+-      continue;
+-    };
+-
+-    /* compile our regular expression */
+-    re = pcre_compile( CS regex_string,
+-                       0,
+-                       &pcre_error,
+-                       &pcre_erroffset,
+-                       NULL );
+-
+-    if (re == NULL) {
+-      log_write(0, LOG_MAIN,
+-           "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset);
+-      continue;
+-    }
+-    else {
+-      re_list_item = store_get(sizeof(pcre_list));
+-      re_list_item->re = re;
+-      re_list_item->pcre_text = string_copy(regex_string);
+-      re_list_item->next = re_list_head;
+-      re_list_head = re_list_item;
+-    };
+-  };
+-
+-  /* no regexes -> nothing to do */
+-  if (re_list_head == NULL) {
+-    return FAIL;
+-  };
++  if (!(re_list_head = compile(*listptr)))
++    return FAIL;			/* no regexes -> nothing to do */
+ 
+   /* check if the file is already decoded */
+   if (mime_decoded_filename == NULL) {
+@@ -207,43 +176,25 @@ mime_regex(const uschar **listptr)
+       log_write(0, LOG_MAIN,
+            "mime_regex acl condition warning - could not decode MIME part to file.");
+       return DEFER;
+-    };
+-  };
+-
++    }
++  }
+ 
+   /* open file */
+-  f = fopen(CS mime_decoded_filename, "rb");
+-  if (f == NULL) {
+-    /* open failed */
++  if (!(f = fopen(CS mime_decoded_filename, "rb"))) {
+     log_write(0, LOG_MAIN,
+-         "mime_regex acl condition warning - can't open '%s' for reading.", mime_decoded_filename);
++         "mime_regex acl condition warning - can't open '%s' for reading.",
++	 mime_decoded_filename);
+     return DEFER;
+-  };
++  }
+ 
+   /* get 32k memory */
+   mime_subject = (uschar *)store_get(32767);
+ 
+-  /* read max 32k chars from file */
+   mime_subject_len = fread(mime_subject, 1, 32766, f);
+ 
+-  re_list_item = re_list_head;
+-  do {
+-    /* try matcher on the mmapped file */
+-    debug_printf("Matching '%s'\n", re_list_item->pcre_text);
+-    if (pcre_exec(re_list_item->re, NULL, CS mime_subject,
+-                  mime_subject_len, 0, 0, NULL, 0) >= 0) {
+-      Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023);
+-      regex_match_string = regex_match_string_buffer;
+-      (void)fclose(f);
+-      return OK;
+-    };
+-    re_list_item = re_list_item->next;
+-  } while (re_list_item != NULL);
+-
++  ret = matcher(re_list_head, mime_subject, mime_subject_len);
+   (void)fclose(f);
+-
+-  /* no matches ... */
+-  return FAIL;
++  return ret;
+ }
+ 
+ #endif /* WITH_CONTENT_SCAN */
+
+commit 895fbaf26d3450d4eeacbad8fe04c328a77645f0
+Author: Jeremy Harris <jgh146exb at wizmail.org>
+Date:   Wed Sep 9 16:03:38 2015 +0100
+
+    DSN: Under EXPERIMENTAL_DSN_INFO add extras to bounce messages.  Bug 1686
+
+diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults
+index 596e651..6af3b4d 100644
+--- a/src/src/config.h.defaults
++++ b/src/src/config.h.defaults
+@@ -172,6 +172,7 @@ it's a default value. */
+ #define EXPERIMENTAL_BRIGHTMAIL
+ #define EXPERIMENTAL_DANE
+ #define EXPERIMENTAL_DCC
++#define EXPERIMENTAL_DSN_INFO
+ #define EXPERIMENTAL_DMARC
+ #define EXPERIMENTAL_EVENT
+ #define EXPERIMENTAL_INTERNATIONAL
+diff --git a/src/src/deliver.c b/src/src/deliver.c
+index b5aa9b9..3f22dc9 100644
+--- a/src/src/deliver.c
++++ b/src/src/deliver.c
+@@ -3223,41 +3223,56 @@ while (!done)
+       break;
+       }
+ 
+-    addr->transport_return = *ptr++;
+-    addr->special_action = *ptr++;
+-    memcpy(&(addr->basic_errno), ptr, sizeof(addr->basic_errno));
+-    ptr += sizeof(addr->basic_errno);
+-    memcpy(&(addr->more_errno), ptr, sizeof(addr->more_errno));
+-    ptr += sizeof(addr->more_errno);
+-    memcpy(&(addr->flags), ptr, sizeof(addr->flags));
+-    ptr += sizeof(addr->flags);
+-    addr->message = (*ptr)? string_copy(ptr) : NULL;
+-    while(*ptr++);
+-    addr->user_message = (*ptr)? string_copy(ptr) : NULL;
+-    while(*ptr++);
+-
+-    /* Always two strings for host information, followed by the port number and DNSSEC mark */
+-
+-    if (*ptr != 0)
++    switch (subid)
+       {
+-      h = store_get(sizeof(host_item));
+-      h->name = string_copy(ptr);
+-      while (*ptr++);
+-      h->address = string_copy(ptr);
+-      while(*ptr++);
+-      memcpy(&(h->port), ptr, sizeof(h->port));
+-      ptr += sizeof(h->port);
+-      h->dnssec = *ptr == '2' ? DS_YES
+-		: *ptr == '1' ? DS_NO
+-		: DS_UNK;
+-      ptr++;
+-      addr->host_used = h;
+-      }
+-    else ptr++;
++#ifdef EXPERIMENTAL_DSN_INFO
++      case '1':	/* must arrive before A0, and applies to that addr */
++      		/* Two strings: smtp_greeting and helo_response */
++	addr->smtp_greeting = string_copy(ptr);
++	while(*ptr++);
++	addr->helo_response = string_copy(ptr);
++	while(*ptr++);
++	break;
++#endif
+ 
+-    /* Finished with this address */
++      case '0':
++	addr->transport_return = *ptr++;
++	addr->special_action = *ptr++;
++	memcpy(&(addr->basic_errno), ptr, sizeof(addr->basic_errno));
++	ptr += sizeof(addr->basic_errno);
++	memcpy(&(addr->more_errno), ptr, sizeof(addr->more_errno));
++	ptr += sizeof(addr->more_errno);
++	memcpy(&(addr->flags), ptr, sizeof(addr->flags));
++	ptr += sizeof(addr->flags);
++	addr->message = (*ptr)? string_copy(ptr) : NULL;
++	while(*ptr++);
++	addr->user_message = (*ptr)? string_copy(ptr) : NULL;
++	while(*ptr++);
+ 
+-    addr = addr->next;
++	/* Always two strings for host information, followed by the port number and DNSSEC mark */
++
++	if (*ptr != 0)
++	  {
++	  h = store_get(sizeof(host_item));
++	  h->name = string_copy(ptr);
++	  while (*ptr++);
++	  h->address = string_copy(ptr);
++	  while(*ptr++);
++	  memcpy(&(h->port), ptr, sizeof(h->port));
++	  ptr += sizeof(h->port);
++	  h->dnssec = *ptr == '2' ? DS_YES
++		    : *ptr == '1' ? DS_NO
++		    : DS_UNK;
++	  ptr++;
++	  addr->host_used = h;
++	  }
++	else ptr++;
++
++	/* Finished with this address */
++
++	addr = addr->next;
++	break;
++      }
+     break;
+ 
+     /* Local interface address/port */
+@@ -4423,7 +4438,6 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
+ 
+       for (r = addr->retries; r != NULL; r = r->next)
+         {
+-        uschar *ptr;
+         sprintf(CS big_buffer, "%c%.500s", r->flags, r->key);
+         ptr = big_buffer + Ustrlen(big_buffer+2) + 3;
+         memcpy(ptr, &(r->basic_errno), sizeof(r->basic_errno));
+@@ -4438,11 +4452,31 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
+         rmt_dlv_checked_write(fd, 'R', '0', big_buffer, ptr - big_buffer);
+         }
+ 
+-      /* The rest of the information goes in an 'A' item. */
++#ifdef EXPERIMENTAL_DSN_INFO
++/*um, are they really per-addr?  Other per-conn stuff is not (auth, tls).  But host_used is! */
++      if (addr->smtp_greeting)
++	{
++	ptr = big_buffer;
++	DEBUG(D_deliver) debug_printf("smtp_greeting '%s'\n", addr->smtp_greeting);
++        sprintf(CS ptr, "%.128s", addr->smtp_greeting);
++        while(*ptr++);
++	if (addr->helo_response)
++	  {
++	  DEBUG(D_deliver) debug_printf("helo_response '%s'\n", addr->helo_response);
++	  sprintf(CS ptr, "%.128s", addr->helo_response);
++	  while(*ptr++);
++	  }
++	else
++	  *ptr++ = '\0';
++        rmt_dlv_checked_write(fd, 'A', '1', big_buffer, ptr - big_buffer);
++	}
++#endif
++
++      /* The rest of the information goes in an 'A0' item. */
+ 
+-      ptr = big_buffer + 2;
+       sprintf(CS big_buffer, "%c%c", addr->transport_return,
+         addr->special_action);
++      ptr = big_buffer + 2;
+       memcpy(ptr, &(addr->basic_errno), sizeof(addr->basic_errno));
+       ptr += sizeof(addr->basic_errno);
+       memcpy(ptr, &(addr->more_errno), sizeof(addr->more_errno));
+@@ -4480,7 +4514,11 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
+       }
+ 
+     /* Local interface address/port */
++#ifdef EXPERIMENTAL_DSN_INFO
++    if (sending_ip_address)
++#else
+     if (LOGGING(incoming_interface) && sending_ip_address)
++#endif
+       {
+       uschar * ptr = big_buffer;
+       sprintf(CS ptr, "%.128s", sending_ip_address);
+@@ -7209,16 +7247,32 @@ wording. */
+  
+       for (addr = handled_addr; addr; addr = addr->next)
+         {
++	host_item * hu;
+         fprintf(f, "Action: failed\n"
+ 	    "Final-Recipient: rfc822;%s\n"
+ 	    "Status: 5.0.0\n",
+ 	    addr->address);
+-        if (addr->host_used && addr->host_used->name)
+-          {
+-          fprintf(f, "Remote-MTA: dns; %s\n",
+-	    addr->host_used->name);
+-          print_dsn_diagnostic_code(addr, f);
+-          }
++        if ((hu = addr->host_used) && hu->name)
++	  {
++	  const uschar * s;
++	  fprintf(f, "Remote-MTA: dns; %s\n",
++	    hu->name);
++#ifdef EXPERIMENTAL_DSN_INFO
++	  if (hu->address)
++	    {
++	    uschar * p = hu->port == 25
++	      ? US"" : string_sprintf(":%d", hu->port);
++	    fprintf(f, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p);
++	    }
++	  if ((s = addr->smtp_greeting) && *s)
++	    fprintf(f, "X-Remote-MTA-smtp-greeting: X-str; %s\n", s);
++	  if ((s = addr->helo_response) && *s)
++	    fprintf(f, "X-Remote-MTA-helo-response: X-str; %s\n", s);
++	  if ((s = addr->message) && *s)
++	    fprintf(f, "X-Exim-Diagnostic: X-str; %s\n", s);
++#endif
++	  print_dsn_diagnostic_code(addr, f);
++	  }
+ 	fputc('\n', f);
+         }
+ 
+diff --git a/src/src/exim.c b/src/src/exim.c
+index 999b94c..084d649 100644
+--- a/src/src/exim.c
++++ b/src/src/exim.c
+@@ -847,6 +847,12 @@ fprintf(f, "Support for:");
+ #ifdef EXPERIMENTAL_DMARC
+   fprintf(f, " Experimental_DMARC");
+ #endif
++#ifdef EXPERIMENTAL_DSN_INFO
++  fprintf(f, " Experimental_DSN_info");
++#endif
++#ifdef EXPERIMENTAL_INTERNATIONAL
++  fprintf(f, " Experimental_International");
++#endif
+ #ifdef EXPERIMENTAL_PROXY
+   fprintf(f, " Experimental_Proxy");
+ #endif
+@@ -859,9 +865,6 @@ fprintf(f, "Support for:");
+ #ifdef EXPERIMENTAL_SOCKS
+   fprintf(f, " Experimental_SOCKS");
+ #endif
+-#ifdef EXPERIMENTAL_INTERNATIONAL
+-  fprintf(f, " Experimental_International");
+-#endif
+ fprintf(f, "\n");
+ 
+ fprintf(f, "Lookups (built-in):");
+diff --git a/src/src/globals.c b/src/src/globals.c
+index 8445f00..f3b6791 100644
+--- a/src/src/globals.c
++++ b/src/src/globals.c
+@@ -354,13 +354,17 @@ address_item address_defaults = {
+   NULL,                 /* return_filename */
+   NULL,                 /* self_hostname */
+   NULL,                 /* shadow_message */
+-  #ifdef SUPPORT_TLS
++#ifdef SUPPORT_TLS
+   NULL,                 /* cipher */
+   NULL,			/* ourcert */
+   NULL,			/* peercert */
+   NULL,                 /* peerdn */
+   OCSP_NOT_REQ,         /* ocsp */
+-  #endif
++#endif
++#ifdef EXPERIMENTAL_DSN_INFO
++  NULL,			/* smtp_greeting */
++  NULL,			/* helo_response */
++#endif
+   NULL,			/* authenticator */
+   NULL,			/* auth_id */
+   NULL,			/* auth_sndr */
+diff --git a/src/src/structs.h b/src/src/structs.h
+index 438b521..db9e843 100644
+--- a/src/src/structs.h
++++ b/src/src/structs.h
+@@ -561,13 +561,18 @@ typedef struct address_item {
+   uschar *self_hostname;          /* after self=pass */
+   uschar *shadow_message;         /* info about shadow transporting */
+ 
+-  #ifdef SUPPORT_TLS
++#ifdef SUPPORT_TLS
+   uschar *cipher;                 /* Cipher used for transport */
+   void   *ourcert;                /* Certificate offered to peer, binary */
+   void   *peercert;               /* Certificate from peer, binary */
+   uschar *peerdn;                 /* DN of server's certificate */
+   int    ocsp;			  /* OCSP status of peer cert */
+-  #endif
++#endif
++
++#ifdef EXPERIMENTAL_DSN_INFO
++  const uschar *smtp_greeting;	  /* peer self-identification */
++  const uschar *helo_response;	  /* peer message */
++#endif
+ 
+   uschar *authenticator;	  /* auth driver name used by transport */
+   uschar *auth_id;		  /* auth "login" name used by transport */
+diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
+index c93f2ef..ac40460 100644
+--- a/src/src/transports/smtp.c
++++ b/src/src/transports/smtp.c
+@@ -440,6 +440,8 @@ Arguments:
+   rc             to put in each address's transport_return field
+   pass_message   if TRUE, set the "pass message" flag in the address
+   host           if set, mark addrs as having used this host
++  smtp_greeting  from peer
++  helo_response  from peer
+ 
+ If errno_value has the special value ERRNO_CONNECTTIMEOUT, ETIMEDOUT is put in
+ the errno field, and RTEF_CTOUT is ORed into the more_errno field, to indicate
+@@ -450,7 +452,11 @@ Returns:       nothing
+ 
+ static void
+ set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc,
+-  BOOL pass_message, host_item * host)
++  BOOL pass_message, host_item * host
++#ifdef EXPERIMENTAL_DSN_INFO
++  , const uschar * smtp_greeting, const uschar * helo_response
++#endif
++  )
+ {
+ address_item *addr;
+ int orvalue = 0;
+@@ -459,7 +465,7 @@ if (errno_value == ERRNO_CONNECTTIMEOUT)
+   errno_value = ETIMEDOUT;
+   orvalue = RTEF_CTOUT;
+   }
+-for (addr = addrlist; addr != NULL; addr = addr->next)
++for (addr = addrlist; addr; addr = addr->next)
+   if (addr->transport_return >= PENDING)
+     {
+     addr->basic_errno = errno_value;
+@@ -471,10 +477,31 @@ for (addr = addrlist; addr != NULL; addr = addr->next)
+       }
+     addr->transport_return = rc;
+     if (host)
++      {
+       addr->host_used = host;
++#ifdef EXPERIMENTAL_DSN_INFO
++      if (smtp_greeting)
++	{uschar * s = Ustrchr(smtp_greeting, '\n'); if (s) *s = '\0';}
++      addr->smtp_greeting = smtp_greeting;
++
++      if (helo_response)
++	{uschar * s = Ustrchr(helo_response, '\n'); if (s) *s = '\0';}
++      addr->helo_response = helo_response;
++#endif
++      }
+     }
+ }
+ 
++static void
++set_errno_nohost(address_item *addrlist, int errno_value, uschar *msg, int rc,
++  BOOL pass_message)
++{
++set_errno(addrlist, errno_value, msg, rc, pass_message, NULL
++#ifdef EXPERIMENTAL_DSN_INFO
++	  , NULL, NULL
++#endif
++	  );
++}
+ 
+ 
+ /*************************************************
+@@ -847,7 +874,7 @@ while (count-- > 0)
+     {
+     uschar *message = string_sprintf("SMTP timeout after RCPT TO:<%s>",
+ 			  transport_rcpt_address(addr, include_affixes));
+-    set_errno(addrlist, ETIMEDOUT, message, DEFER, FALSE, NULL);
++    set_errno_nohost(addrlist, ETIMEDOUT, message, DEFER, FALSE);
+     retry_add_item(addr, addr->address_retry_key, 0);
+     update_waiting = FALSE;
+     return -1;
+@@ -1096,8 +1123,8 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
+ 	  /* Internal problem, message in buffer. */
+ 
+ 	  case ERROR:
+-	  set_errno(addrlist, ERRNO_AUTHPROB, string_copy(buffer),
+-		    DEFER, FALSE, NULL);
++	  set_errno_nohost(addrlist, ERRNO_AUTHPROB, string_copy(buffer),
++		    DEFER, FALSE);
+ 	  return ERROR;
+ 	  }
+ 
+@@ -1111,9 +1138,9 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
+ 
+ if (require_auth == OK && !smtp_authenticated)
+   {
+-  set_errno(addrlist, ERRNO_AUTHFAIL,
++  set_errno_nohost(addrlist, ERRNO_AUTHFAIL,
+     string_sprintf("authentication required but %s", fail_reason), DEFER,
+-    FALSE, NULL);
++    FALSE);
+   return DEFER;
+   }
+ 
+@@ -1152,7 +1179,7 @@ if (ob->authenticated_sender != NULL)
+       {
+       uschar *message = string_sprintf("failed to expand "
+         "authenticated_sender: %s", expand_string_message);
+-      set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL);
++      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
+       return TRUE;
+       }
+     }
+@@ -1381,6 +1408,10 @@ smtp_outblock outblock;
+ int max_rcpt = tblock->max_addresses;
+ uschar *igquotstr = US"";
+ 
++#ifdef EXPERIMENTAL_DSN_INFO
++uschar *smtp_greeting = NULL;
++uschar *helo_response = NULL;
++#endif
+ uschar *helo_data = NULL;
+ 
+ uschar *message = NULL;
+@@ -1432,8 +1463,8 @@ tls_modify_variables(&tls_out);
+ #ifndef SUPPORT_TLS
+ if (smtps)
+   {
+-  set_errno(addrlist, ERRNO_TLSFAILURE, US"TLS support not available",
+-	    DEFER, FALSE, NULL);
++  set_errno_nohost(addrlist, ERRNO_TLSFAILURE, US"TLS support not available",
++	    DEFER, FALSE);
+   return ERROR;
+   }
+ #endif
+@@ -1450,8 +1481,8 @@ if (continue_hostname == NULL)
+ 
+   if (inblock.sock < 0)
+     {
+-    set_errno(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno,
+-      NULL, DEFER, FALSE, NULL);
++    set_errno_nohost(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno,
++      NULL, DEFER, FALSE);
+     return DEFER;
+     }
+ 
+@@ -1469,18 +1500,18 @@ if (continue_hostname == NULL)
+ 	&& dane_required	/* do not error on only dane-requested */
+ 	)
+ 	{
+-	set_errno(addrlist, ERRNO_DNSDEFER,
++	set_errno_nohost(addrlist, ERRNO_DNSDEFER,
+ 	  string_sprintf("DANE error: tlsa lookup %s",
+ 	    rc == DEFER ? "DEFER" : "FAIL"),
+-	  rc, FALSE, NULL);
++	  rc, FALSE);
+ 	return rc;
+ 	}
+       }
+     else if (dane_required)
+       {
+-      set_errno(addrlist, ERRNO_DNSDEFER,
++      set_errno_nohost(addrlist, ERRNO_DNSDEFER,
+ 	string_sprintf("DANE error: %s lookup not DNSSEC", host->name),
+-	FAIL, FALSE, NULL);
++	FAIL, FALSE);
+       return  FAIL;
+       }
+ 
+@@ -1501,7 +1532,7 @@ if (continue_hostname == NULL)
+     if ((helo_data = string_domain_utf8_to_alabel(helo_data, &errstr)), errstr)
+       {
+       errstr = string_sprintf("failed to expand helo_data: %s", errstr);
+-      set_errno(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, NULL);
++      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE);
+       yield = DEFER;
+       goto SEND_QUIT;
+       }
+@@ -1514,8 +1545,12 @@ if (continue_hostname == NULL)
+ 
+   if (!smtps)
+     {
+-    if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
+-      ob->command_timeout)) goto RESPONSE_FAILED;
++    BOOL good_response = smtp_read_response(&inblock, buffer, sizeof(buffer),
++      '2', ob->command_timeout);
++#ifdef EXPERIMENTAL_DSN_INFO
++    smtp_greeting = string_copy(buffer);
++#endif
++    if (!good_response) goto RESPONSE_FAILED;
+ 
+ #ifdef EXPERIMENTAL_EVENT
+       {
+@@ -1525,9 +1560,9 @@ if (continue_hostname == NULL)
+       s = event_raise(tblock->event_action, US"smtp:connect", buffer);
+       if (s)
+ 	{
+-	set_errno(addrlist, ERRNO_EXPANDFAIL,
++	set_errno_nohost(addrlist, ERRNO_EXPANDFAIL,
+ 	  string_sprintf("deferred by smtp:connect event expansion: %s", s),
+-	  DEFER, FALSE, NULL);
++	  DEFER, FALSE);
+ 	yield = DEFER;
+ 	goto SEND_QUIT;
+ 	}
+@@ -1541,7 +1576,7 @@ if (continue_hostname == NULL)
+       {
+       uschar *message = string_sprintf("failed to expand helo_data: %s",
+         expand_string_message);
+-      set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL);
++      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
+       yield = DEFER;
+       goto SEND_QUIT;
+       }
+@@ -1606,9 +1641,18 @@ goto SEND_QUIT;
+     if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
+            ob->command_timeout))
+       {
+-      if (errno != 0 || buffer[0] == 0 || lmtp) goto RESPONSE_FAILED;
++      if (errno != 0 || buffer[0] == 0 || lmtp)
++	{
++#ifdef EXPERIMENTAL_DSN_INFO
++	helo_response = string_copy(buffer);
++#endif
++	goto RESPONSE_FAILED;
++	}
+       esmtp = FALSE;
+       }
++#ifdef EXPERIMENTAL_DSN_INFO
++    helo_response = string_copy(buffer);
++#endif
+     }
+   else
+     {
+@@ -1618,10 +1662,16 @@ goto SEND_QUIT;
+ 
+   if (!esmtp)
+     {
++    BOOL good_response;
++
+     if (smtp_write_command(&outblock, FALSE, "HELO %s\r\n", helo_data) < 0)
+       goto SEND_FAILED;
+-    if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
+-      ob->command_timeout)) goto RESPONSE_FAILED;
++    good_response = smtp_read_response(&inblock, buffer, sizeof(buffer),
++      '2', ob->command_timeout);
++#ifdef EXPERIMENTAL_DSN_INFO
++    helo_response = string_copy(buffer);
++#endif
++    if (!good_response) goto RESPONSE_FAILED;
+     }
+ 
+   /* Set IGNOREQUOTA if the response to LHLO specifies support and the
+@@ -1671,6 +1721,11 @@ error messages. Note that smtp_use_size and smtp_use_pipelining will have been
+ set from the command line if they were set in the process that passed the
+ connection on. */
+ 
++/*XXX continue case needs to propagate DSN_INFO, prob. in deliver.c
++as the contine goes via transport_pass_socket() and doublefork and exec.
++It does not wait.  Unclear how we keep separate host's responses
++separate - we could match up by host ip+port as a bodge. */
++
+ else
+   {
+   inblock.sock = outblock.sock = fileno(stdin);
+@@ -1749,7 +1804,7 @@ if (  tls_offered
+ 
+     /* TLS session is set up */
+ 
+-    for (addr = addrlist; addr != NULL; addr = addr->next)
++    for (addr = addrlist; addr; addr = addr->next)
+       if (addr->transport_return == PENDING_DEFER)
+         {
+         addr->cipher = tls_out.cipher;
+@@ -1774,6 +1829,8 @@ start of the Exim process (in exim.c). */
+ if (tls_out.active >= 0)
+   {
+   char *greeting_cmd;
++  BOOL good_response;
++
+   if (helo_data == NULL)
+     {
+     helo_data = expand_string(ob->helo_data);
+@@ -1781,7 +1838,7 @@ if (tls_out.active >= 0)
+       {
+       uschar *message = string_sprintf("failed to expand helo_data: %s",
+         expand_string_message);
+-      set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL);
++      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
+       yield = DEFER;
+       goto SEND_QUIT;
+       }
+@@ -1790,8 +1847,12 @@ if (tls_out.active >= 0)
+   /* For SMTPS we need to wait for the initial OK response. */
+   if (smtps)
+     {
+-    if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
+-      ob->command_timeout)) goto RESPONSE_FAILED;
++    good_response = smtp_read_response(&inblock, buffer, sizeof(buffer),
++      '2', ob->command_timeout);
++#ifdef EXPERIMENTAL_DSN_INFO
++    smtp_greeting = string_copy(buffer);
++#endif
++    if (!good_response) goto RESPONSE_FAILED;
+     }
+ 
+   if (esmtp)
+@@ -1806,9 +1867,12 @@ if (tls_out.active >= 0)
+   if (smtp_write_command(&outblock, FALSE, "%s %s\r\n",
+         lmtp? "LHLO" : greeting_cmd, helo_data) < 0)
+     goto SEND_FAILED;
+-  if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
+-       ob->command_timeout))
+-    goto RESPONSE_FAILED;
++  good_response = smtp_read_response(&inblock, buffer, sizeof(buffer),
++    '2', ob->command_timeout);
++#ifdef EXPERIMENTAL_DSN_INFO
++  helo_response = string_copy(buffer);
++#endif
++  if (!good_response) goto RESPONSE_FAILED;
+   }
+ 
+ /* If the host is required to use a secure channel, ensure that we
+@@ -1935,8 +1999,8 @@ if (tblock->filter_command != NULL)
+ 
+   if (!rc)
+     {
+-    set_errno(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER,
+-      FALSE, NULL);
++    set_errno_nohost(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER,
++      FALSE);
+     yield = ERROR;
+     goto SEND_QUIT;
+     }
+@@ -2065,7 +2129,7 @@ pending_MAIL = TRUE;     /* The block starts with MAIL */
+     {
+     if (s = string_address_utf8_to_alabel(return_path, &errstr), errstr)
+       {
+-      set_errno(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, NULL);
++      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE);
+       yield = ERROR;
+       goto SEND_QUIT;
+       }
+@@ -2217,8 +2281,8 @@ if (mua_wrapper)
+     if (badaddr->transport_return != PENDING_OK)
+       {
+       /*XXX could we find a better errno than 0 here? */
+-      set_errno(addrlist, 0, badaddr->message, FAIL,
+-	testflag(badaddr, af_pass_message), NULL);
++      set_errno_nohost(addrlist, 0, badaddr->message, FAIL,
++	testflag(badaddr, af_pass_message));
+       ok = FALSE;
+       break;
+       }
+@@ -2475,7 +2539,7 @@ if (!ok) ok = TRUE; else
+         else
+           sprintf(CS buffer, "%.500s\n", addr->unique);
+ 
+-        DEBUG(D_deliver) debug_printf("journalling %s", buffer);
++        DEBUG(D_deliver) debug_printf("journalling %s\n", buffer);
+         len = Ustrlen(CS buffer);
+         if (write(journal_fd, buffer, len) != len)
+           log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
+@@ -2512,7 +2576,7 @@ if (!ok) ok = TRUE; else
+           else
+             sprintf(CS buffer, "%.500s\n", addr->unique);
+ 
+-          DEBUG(D_deliver) debug_printf("journalling(PRDR) %s", buffer);
++          DEBUG(D_deliver) debug_printf("journalling(PRDR) %s\n", buffer);
+           len = Ustrlen(CS buffer);
+           if (write(journal_fd, buffer, len) != len)
+             log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
+@@ -2542,22 +2606,27 @@ the problem is not related to this specific message. */
+ 
+ if (!ok)
+   {
+-  int code;
++  int code, set_rc;
++  uschar * set_message;
+ 
+   RESPONSE_FAILED:
+-  save_errno = errno;
+-  message = NULL;
+-  send_quit = check_response(host, &save_errno, addrlist->more_errno,
+-    buffer, &code, &message, &pass_message);
+-  goto FAILED;
++    {
++    save_errno = errno;
++    message = NULL;
++    send_quit = check_response(host, &save_errno, addrlist->more_errno,
++      buffer, &code, &message, &pass_message);
++    goto FAILED;
++    }
+ 
+   SEND_FAILED:
+-  save_errno = errno;
+-  code = '4';
+-  message = US string_sprintf("send() to %s [%s] failed: %s",
+-    host->name, host->address, strerror(save_errno));
+-  send_quit = FALSE;
+-  goto FAILED;
++    {
++    save_errno = errno;
++    code = '4';
++    message = US string_sprintf("send() to %s [%s] failed: %s",
++      host->name, host->address, strerror(save_errno));
++    send_quit = FALSE;
++    goto FAILED;
++    }
+ 
+   /* This label is jumped to directly when a TLS negotiation has failed,
+   or was not done for a host for which it is required. Values will be set
+@@ -2578,16 +2647,14 @@ if (!ok)
+ 
+   FAILED:
+   ok = FALSE;                /* For when reached by GOTO */
++  set_message = message;
+ 
+   if (setting_up)
+     {
+     if (code == '5')
+-      set_errno(addrlist, save_errno, message, FAIL, pass_message, host);
++      set_rc = FAIL;
+     else
+-      {
+-      set_errno(addrlist, save_errno, message, DEFER, pass_message, host);
+-      yield = DEFER;
+-      }
++      yield = set_rc = DEFER;
+     }
+ 
+   /* We want to handle timeouts after MAIL or "." and loss of connection after
+@@ -2646,14 +2713,15 @@ if (!ok)
+     if (message_error)
+       {
+       if (mua_wrapper) code = '5';  /* Force hard failure in wrapper mode */
+-      set_errno(addrlist, save_errno, message, (code == '5')? FAIL : DEFER,
+-        pass_message, host);
+ 
+       /* If there's an errno, the message contains just the identity of
+       the host. */
+ 
+-      if (code != '5')     /* Anything other than 5 is treated as temporary */
++      if (code == '5')
++	set_rc = FAIL;
++      else		/* Anything other than 5 is treated as temporary */
+         {
++	set_rc = DEFER;
+         if (save_errno > 0)
+           message = US string_sprintf("%s: %s", message, strerror(save_errno));
+         if (host->next != NULL) log_write(0, LOG_MAIN, "%s", message);
+@@ -2670,11 +2738,17 @@ if (!ok)
+ 
+     else
+       {
++      set_rc = DEFER;
+       yield = (save_errno == ERRNO_CHHEADER_FAIL ||
+                save_errno == ERRNO_FILTER_FAIL)? ERROR : DEFER;
+-      set_errno(addrlist, save_errno, message, DEFER, pass_message, host);
+       }
+     }
++
++  set_errno(addrlist, save_errno, set_message, set_rc, pass_message, host
++#ifdef EXPERIMENTAL_DSN_INFO
++	    , smtp_greeting, helo_response
++#endif
++	    );
+   }
+ 
+ 
+@@ -2787,6 +2861,9 @@ if (completed_address && ok && send_quit)
+       /* If the socket is successfully passed, we musn't send QUIT (or
+       indeed anything!) from here. */
+ 
++/*XXX DSN_INFO: assume likely to do new HELO; but for greet we'll want to
++propagate it from the initial
++*/
+       if (ok && transport_pass_socket(tblock->name, host->name, host->address,
+             new_message_id, inblock.sock))
+         {
+@@ -2796,7 +2873,11 @@ if (completed_address && ok && send_quit)
+ 
+     /* If RSET failed and there are addresses left, they get deferred. */
+ 
+-    else set_errno(first_addr, errno, msg, DEFER, FALSE, host);
++    else set_errno(first_addr, errno, msg, DEFER, FALSE, host
++#ifdef EXPERIMENTAL_DSN_INFO
++		  , smtp_greeting, helo_response
++#endif
++		  );
+     }
+   }
+ 
+@@ -2938,6 +3019,10 @@ for (addr = addrlist; addr != NULL; addr = addr->next)
+     addr->peerdn = NULL;
+     addr->ocsp = OCSP_NOT_REQ;
+ #endif
++#ifdef EXPERIMENTAL_DSN_INFO
++    addr->smtp_greeting = NULL;
++    addr->helo_response = NULL;
++#endif
+     }
+ return first_addr;
+ }
+@@ -3479,7 +3564,7 @@ for (cutoff_retry = 0; expired &&
+     if (dont_deliver)
+       {
+       host_item *host2;
+-      set_errno(addrlist, 0, NULL, OK, FALSE, NULL);
++      set_errno_nohost(addrlist, 0, NULL, OK, FALSE);
+       for (addr = addrlist; addr != NULL; addr = addr->next)
+         {
+         addr->host_used = host;
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/exim.git/commitdiff/4022a9ecb0e07f48081d5a0bdcd7e81bbcb71728



More information about the pld-cvs-commit mailing list