[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