From 4022a9ecb0e07f48081d5a0bdcd7e81bbcb71728 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Arkadiusz=20Mi=C5=9Bkiewicz?= Date: Thu, 10 Sep 2015 10:23:35 +0200 Subject: [PATCH 1/1] - 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(-) create mode 100644 exim-git.patch 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 +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) +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 +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 +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 +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) +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) +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) +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) +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 + + + /* 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 +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) +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) +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 +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 =, 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 +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) +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) +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) +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 + + + /* 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 +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 +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 +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 +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; 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 -- 2.44.0