]> git.pld-linux.org Git - packages/exim.git/blob - exim-git.patch
- rel 2; include git patch (contains fix for exim crash while handling hashed queue)
[packages/exim.git] / exim-git.patch
1 commit aa7751be078fe9a20efa2cf2a8856fadb98f4178
2 Author: Jeremy Harris <jgh146exb@wizmail.org>
3 Date:   Sun Jun 28 15:14:02 2015 +0100
4
5     Compiler quietening
6
7 diff --git a/src/src/dns.c b/src/src/dns.c
8 index 64958d9..a239bec 100644
9 --- a/src/src/dns.c
10 +++ b/src/src/dns.c
11 @@ -40,7 +40,6 @@ fakens_search(const uschar *domain, int type, uschar *answerptr, int size)
12  {
13  int len = Ustrlen(domain);
14  int asize = size;                  /* Locally modified */
15 -uschar *endname;
16  uschar name[256];
17  uschar utilname[256];
18  uschar *aptr = answerptr;          /* Locally modified */
19 @@ -51,7 +50,6 @@ struct stat statbuf;
20  if (domain[len - 1] == '.') len--;
21  Ustrncpy(name, domain, len);
22  name[len] = 0;
23 -endname = name + len;
24  
25  /* Look for the fakens utility, and if it exists, call it. */
26  
27 @@ -86,7 +84,7 @@ if (stat(CS utilname, &statbuf) >= 0)
28      asize -= rc;      /* may need to be passed on to res_search(). */
29      }
30  
31 -  /* If we ran out of output buffer before exhasting the return,
32 +  /* If we ran out of output buffer before exhausting the return,
33    carry on reading and counting it. */
34  
35    if (asize == 0)
36 diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
37 index cf0a5d6..711ee86 100644
38 --- a/src/src/smtp_in.c
39 +++ b/src/src/smtp_in.c
40 @@ -3293,7 +3293,7 @@ while (done <= 0)
41    pid_t pid;
42    int start, end, sender_domain, recipient_domain;
43    int ptr, size, rc;
44 -  int c, i;
45 +  int c;
46    auth_instance *au;
47    uschar *orcpt = NULL;
48    int flags;
49 diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
50 index e1dcd77..d8377fd 100644
51 --- a/src/src/tls-openssl.c
52 +++ b/src/src/tls-openssl.c
53 @@ -708,7 +708,7 @@ if (  (nid = OBJ_sn2nid       (CCS exp_curve)) == NID_undef
54  
55  if (!(ecdh = EC_KEY_new_by_curve_name(nid)))
56    {
57 -  tls_error("Unable to create ec curve", host, NULL);
58 +  tls_error(US"Unable to create ec curve", host, NULL);
59    return FALSE;
60    }
61  
62 diff --git a/src/src/utf8.c b/src/src/utf8.c
63 index a0ec003..8a7cf38 100644
64 --- a/src/src/utf8.c
65 +++ b/src/src/utf8.c
66 @@ -127,7 +127,7 @@ if ((rc = punycode_decode(p_len, CCS alabel+4, &p_len, p, NULL)) != PUNYCODE_SUC
67    return NULL;
68    }
69  
70 -s = stringprep_ucs4_to_utf8(p, p_len, NULL, &p_len);
71 +s = US stringprep_ucs4_to_utf8(p, p_len, NULL, &p_len);
72  res = string_copyn(s, p_len);
73  free(s);
74  return res;
75
76 commit 5b881b5a8e0d7bc540f4b63cc9559d2cb1775965
77 Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
78 Date:   Thu Jul 30 09:43:51 2015 +0200
79
80     Docs: Add a note about the maximum spam bar length
81
82 diff --git a/src/src/spam.h b/src/src/spam.h
83 index 05ab655..2fe7380 100644
84 --- a/src/src/spam.h
85 +++ b/src/src/spam.h
86 @@ -12,7 +12,8 @@
87  /* timeout for reading and writing spamd */
88  #define SPAMD_TIMEOUT 120
89  
90 -/* maximum length of the spam bar */
91 +/* maximum length of the spam bar, please update the
92 + * spec, the max length is mentioned there */
93  #define MAX_SPAM_BAR_CHARS 50
94  
95  /* SHUT_WR seems to be undefined on Unixware ? */
96
97 commit 98716abe2b636d275e866f3ad6374cb70bf6e504
98 Author: Jeremy Harris <jgh146exb@wizmail.org>
99 Date:   Sun Aug 2 13:44:31 2015 +0100
100
101     Testsuite: Add testcase for OCSP-nonaware client, to supporting server.  Bug 1664
102     
103     The logfile here is for (I hope) the passing case, though the fixed GnuTLS library
104     is not yet available.  Also due to the bug, client-gnutls is not usable for the
105     test; client-openssl must be used - meaning that a GnuTLS-only system cannot run
106     the testcase:
107     
108       OCSP-GnuTLS/5650 OCSP stapling, server
109       ** Command 15 ("client-ssl", starting at line 98)
110       ** Return code 127 (expected 0)
111
112 diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
113 index 61ed0e8..e2ac17c 100644
114 --- a/src/src/tls-gnu.c
115 +++ b/src/src/tls-gnu.c
116 @@ -842,7 +842,7 @@ if (  !host /* server */
117    gnutls_certificate_set_ocsp_status_request_function(state->x509_cred,
118      server_ocsp_stapling_cb, state->exp_tls_ocsp_file);
119  
120 -  DEBUG(D_tls) debug_printf("Set OCSP response file %s\n", &state->exp_tls_ocsp_file);
121 +  DEBUG(D_tls) debug_printf("OCSP response file = %s\n", state->exp_tls_ocsp_file);
122    }
123  #endif
124  
125
126 commit 9196d5bf543d75a81ae0825a352920d27241c325
127 Author: Jeremy Harris <jgh146exb@wizmail.org>
128 Date:   Sun Aug 2 13:53:15 2015 +0100
129
130     GnuTLS: avoid using OCSP on buggy library versions.  Bug 1664
131
132 diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
133 index e2ac17c..8aabc5c 100644
134 --- a/src/src/tls-gnu.c
135 +++ b/src/src/tls-gnu.c
136 @@ -176,6 +176,8 @@ static const char * const exim_default_gnutls_priority = "NORMAL";
137  
138  static BOOL exim_gnutls_base_init_done = FALSE;
139  
140 +static BOOL gnutls_buggy_ocsp = FALSE;
141 +
142  
143  /* ------------------------------------------------------------------------ */
144  /* macros */
145 @@ -831,18 +833,25 @@ if (  !host       /* server */
146     && tls_ocsp_file
147     )
148    {
149 -  if (!expand_check(tls_ocsp_file, US"tls_ocsp_file",
150 -       &state->exp_tls_ocsp_file))
151 -    return DEFER;
152 +  if (gnutls_buggy_ocsp)
153 +    {
154 +    DEBUG(D_tls) debug_printf("GnuTLS library is buggy for OCSP; avoiding\n");
155 +    }
156 +  else
157 +    {
158 +    if (!expand_check(tls_ocsp_file, US"tls_ocsp_file",
159 +         &state->exp_tls_ocsp_file))
160 +      return DEFER;
161  
162 -  /* Use the full callback method for stapling just to get observability.
163 -  More efficient would be to read the file once only, if it never changed
164 -  (due to SNI). Would need restart on file update, or watch datestamp.  */
165 +    /* Use the full callback method for stapling just to get observability.
166 +    More efficient would be to read the file once only, if it never changed
167 +    (due to SNI). Would need restart on file update, or watch datestamp.  */
168  
169 -  gnutls_certificate_set_ocsp_status_request_function(state->x509_cred,
170 -    server_ocsp_stapling_cb, state->exp_tls_ocsp_file);
171 +    gnutls_certificate_set_ocsp_status_request_function(state->x509_cred,
172 +      server_ocsp_stapling_cb, state->exp_tls_ocsp_file);
173  
174 -  DEBUG(D_tls) debug_printf("OCSP response file = %s\n", state->exp_tls_ocsp_file);
175 +    DEBUG(D_tls) debug_printf("OCSP response file = %s\n", state->exp_tls_ocsp_file);
176 +    }
177    }
178  #endif
179  
180 @@ -1011,6 +1020,35 @@ return OK;
181  *            Initialize for GnuTLS               *
182  *************************************************/
183  
184 +
185 +static BOOL
186 +tls_is_buggy_ocsp(void)
187 +{
188 +const uschar * s;
189 +uschar maj, mid, mic;
190 +
191 +s = CUS gnutls_check_version(NULL);
192 +maj = atoi(CCS s);
193 +if (maj == 3)
194 +  {
195 +  while (*s && *s != '.') s++;
196 +  mid = atoi(CCS ++s);
197 +  if (mid <= 2)
198 +    return TRUE;
199 +  else if (mid >= 5)
200 +    return FALSE;
201 +  else
202 +    {
203 +    while (*s && *s != '.') s++;
204 +    mic = atoi(CCS ++s);
205 +    return mic <= (mid == 3 ? 16 : 3);
206 +    }
207 +  }
208 +return FALSE;
209 +}
210 +
211 +
212 +
213  /* Called from both server and client code. In the case of a server, errors
214  before actual TLS negotiation return DEFER.
215  
216 @@ -1074,6 +1112,9 @@ if (!exim_gnutls_base_init_done)
217      }
218  #endif
219  
220 +  if ((gnutls_buggy_ocsp = tls_is_buggy_ocsp()))
221 +    log_write(0, LOG_MAIN, "OCSP unusable with this GnuTLS library version");
222 +
223    exim_gnutls_base_init_done = TRUE;
224    }
225  
226
227 commit 63f0dbe0ca0aba7be3bc8807f45c8703a1cfafe1
228 Author: Jeremy Harris <jgh146exb@wizmail.org>
229 Date:   Thu Aug 6 21:38:33 2015 +0100
230
231     OpenSSL: fix complile on pre-EC-capable library versions
232
233 diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
234 index d8377fd..b1dccb8 100644
235 --- a/src/src/tls-openssl.c
236 +++ b/src/src/tls-openssl.c
237 @@ -659,15 +659,15 @@ Returns:    TRUE if OK (nothing to set up, or setup worked)
238  static BOOL
239  init_ecdh(SSL_CTX * sctx, host_item * host)
240  {
241 +#ifdef OPENSSL_NO_ECDH
242 +return TRUE;
243 +#else
244 +
245  EC_KEY * ecdh;
246  uschar * exp_curve;
247  int nid;
248  BOOL rv;
249  
250 -#ifdef OPENSSL_NO_ECDH
251 -return TRUE;
252 -#else
253 -
254  if (host)      /* No ECDH setup for clients, only for servers */
255    return TRUE;
256  
257
258 commit 755762fd4c420cabbcba4a9c79947e926fa82219
259 Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
260 Date:   Sun Aug 9 23:29:44 2015 +0200
261
262     Compiler quietening
263
264 diff --git a/src/src/malware.c b/src/src/malware.c
265 index 141c6ea..96af1e8 100644
266 --- a/src/src/malware.c
267 +++ b/src/src/malware.c
268 @@ -886,7 +886,7 @@ if (!malware_ok)
269               string_sprintf("unable to read result (%s)", strerror(errno)),
270               sock);
271  
272 -         for (p[bread] = '\0'; q = Ustrchr(p, '\n'); p = q+1)
273 +         for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1)
274             {
275             *q = '\0';
276  
277 @@ -1880,6 +1880,9 @@ if (!malware_ok)
278  
279               /* here for any unexpected response from the scanner */
280               goto endloop;
281 +
282 +           case AVA_DONE:      log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
283 +                           __FILE__, __LINE__, __FUNCTION__);
284             }
285           }
286         }
287 diff --git a/src/src/spam.c b/src/src/spam.c
288 index ca8d207..457aa3a 100644
289 --- a/src/src/spam.c
290 +++ b/src/src/spam.c
291 @@ -297,7 +297,7 @@ start = time(NULL);
292      sd = (spamd_address_container *)store_get(sizeof(spamd_address_container));
293  
294      for (sublist = address, args = 0, spamd_param_init(sd);
295 -        s = string_nextinlist(&sublist, &sublist_sep, NULL, 0);
296 +        (s = string_nextinlist(&sublist, &sublist_sep, NULL, 0));
297          args++
298          )
299        {
300 diff --git a/src/src/tlscert-openssl.c b/src/src/tlscert-openssl.c
301 index 72808a7..19db040 100644
302 --- a/src/src/tlscert-openssl.c
303 +++ b/src/src/tlscert-openssl.c
304 @@ -127,7 +127,7 @@ else
305    {
306    struct tm tm;
307    struct tm * tm_p = &tm;
308 -  BOOL mod_tz;
309 +  BOOL mod_tz = TRUE;
310    uschar * tz = to_tz(US"GMT0");    /* need to call strptime with baseline TZ */
311  
312    /* Parse OpenSSL ASN1_TIME_print output.  A shame there seems to
313 @@ -164,7 +164,7 @@ else
314        }
315      }
316  
317 -  if (mod_tz);
318 +  if (mod_tz)
319      restore_tz(tz);
320    }
321  BIO_free(bp);
322
323 commit 4bd6107db73131e1b48f1902833fd7c637c08bda
324 Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
325 Date:   Mon Aug 10 00:39:36 2015 +0200
326
327     Really re-select() when interrupted.
328
329 diff --git a/src/src/ip.c b/src/src/ip.c
330 index ead7299..cb54f16 100644
331 --- a/src/src/ip.c
332 +++ b/src/src/ip.c
333 @@ -499,7 +499,7 @@ do
334  
335    /* If the socket is ready, break out of the loop. */
336    }
337 -while (!FD_ISSET(fd, &select_inset));
338 +while (rc < 0 || !FD_ISSET(fd, &select_inset));
339  return TRUE;
340  }
341  
342
343 commit 505d976aa23de4294751162dee6466e335c96fbf
344 Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
345 Date:   Tue Aug 11 09:13:11 2015 +0200
346
347     Build: Make test_{os,parse,dbfn,string} work
348
349 diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base
350 index 1d5a5f6..95110e6 100644
351 --- a/src/OS/Makefile-Base
352 +++ b/src/OS/Makefile-Base
353 @@ -743,11 +743,11 @@ sa-os.o:         $(HDRS) os.c
354  # These are the test targets themselves
355  
356  test_dbfn:   config.h dbfn.c dummies.o sa-globals.o sa-os.o store.o \
357 -              string.o tod.o version.o
358 +              string.o tod.o version.o utf8.o
359         $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE dbfn.c
360         $(LNCC) -o test_dbfn $(LFLAGS) dbfn.o \
361           dummies.o sa-globals.o sa-os.o store.o string.o \
362 -         tod.o version.o $(LIBS) $(DBMLIB)
363 +         tod.o version.o utf8.o $(LIBS) $(DBMLIB) $(LDFLAGS)
364         rm -f dbfn.o
365  
366  test_host:   config.h child.c host.c dns.c dummies.c sa-globals.o os.o \
367 @@ -761,23 +761,24 @@ test_host:   config.h child.c host.c dns.c dummies.c sa-globals.o os.o \
368           tod.o tree.o $(LIBS) $(LIBRESOLV)
369         rm -f child.o dummies.o host.o dns.o
370  
371 -test_os:     os.h os.c dummies.o sa-globals.o store.o string.o tod.o
372 +test_os:     os.h os.c dummies.o sa-globals.o store.o string.o tod.o utf8.o
373         $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE os.c
374         $(LNCC) -o test_os $(LFLAGS) os.o dummies.o \
375 -         sa-globals.o store.o string.o tod.o $(LIBS)
376 +         sa-globals.o store.o string.o tod.o utf8.o $(LIBS) $(LDFLAGS)
377         rm -f os.o
378  
379  test_parse:  config.h parse.c dummies.o sa-globals.o \
380 -            store.o string.o tod.o version.o
381 +            store.o string.o tod.o version.o utf8.o
382         $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE parse.c
383         $(LNCC) -o test_parse $(LFLAGS) parse.o \
384 -         dummies.o sa-globals.o store.o string.o tod.o version.o
385 +         dummies.o sa-globals.o store.o string.o tod.o version.o \
386 +         utf8.o $(LDFLAGS)
387         rm -f parse.o
388  
389 -test_string: config.h string.c dummies.o sa-globals.o store.o tod.o
390 +test_string: config.h string.c dummies.o sa-globals.o store.o tod.o utf8.o
391         $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE string.c
392         $(LNCC) -o test_string $(LFLAGS) -DSTAND_ALONE string.o \
393 -         dummies.o sa-globals.o store.o tod.o $(LIBS)
394 +         dummies.o sa-globals.o store.o tod.o utf8.o $(LIBS) $(LDFLAGS)
395         rm -f string.o
396  
397  # End
398 diff --git a/src/src/host.c b/src/src/host.c
399 index 94126d4..31c2bbf 100644
400 --- a/src/src/host.c
401 +++ b/src/src/host.c
402 @@ -3212,7 +3212,7 @@ while (Ufgets(buffer, 256, stdin) != NULL)
403    else
404      {
405      int flags = whichrrs;
406 -    dnssec d;
407 +    dnssec_domains d;
408  
409      h.name = buffer;
410      h.next = NULL;
411
412 commit 2ef7ed082481b2dccd3c2e0eae849b24bf0b172a
413 Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
414 Date:   Tue Aug 11 17:36:29 2015 +0200
415
416     Fix ESMTP MAIL command option processing
417     
418     If the address containes spaces, the option processing
419     was confused.
420
421 diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
422 index 711ee86..effc636 100644
423 --- a/src/src/smtp_in.c
424 +++ b/src/src/smtp_in.c
425 @@ -9,6 +9,7 @@
426  
427  
428  #include "exim.h"
429 +#include <assert.h>
430  
431  
432  /* Initialize for TCP wrappers if so configured. It appears that the macro
433 @@ -232,6 +233,7 @@ static uschar *protocols[] = {
434  
435  /* Sanity check and validate optional args to MAIL FROM: envelope */
436  enum {
437 +  ENV_MAIL_OPT_NULL,
438    ENV_MAIL_OPT_SIZE, ENV_MAIL_OPT_BODY, ENV_MAIL_OPT_AUTH,
439  #ifndef DISABLE_PRDR
440    ENV_MAIL_OPT_PRDR,
441 @@ -240,7 +242,6 @@ enum {
442  #ifdef EXPERIMENTAL_INTERNATIONAL
443    ENV_MAIL_OPT_UTF8,
444  #endif
445 -  ENV_MAIL_OPT_NULL
446    };
447  typedef struct {
448    uschar *   name;  /* option requested during MAIL cmd */
449 @@ -260,7 +261,8 @@ static env_mail_type_t env_mail_type_list[] = {
450  #ifdef EXPERIMENTAL_INTERNATIONAL
451      { US"SMTPUTF8",ENV_MAIL_OPT_UTF8,  FALSE },                /* rfc6531 */
452  #endif
453 -    { US"NULL",   ENV_MAIL_OPT_NULL,   FALSE }
454 +    /* keep this the last entry */
455 +    { US"NULL",   ENV_MAIL_OPT_NULL,   FALSE },
456    };
457  
458  /* When reading SMTP from a remote host, we have to use our own versions of the
459 @@ -3887,7 +3889,7 @@ while (done <= 0)
460        if (!extract_option(&name, &value)) break;
461  
462        for (mail_args = env_mail_type_list;
463 -           (char *)mail_args < (char *)env_mail_type_list + sizeof(env_mail_type_list);
464 +           mail_args->value != ENV_MAIL_OPT_NULL;
465             mail_args++
466            )
467          if (strcmpic(name, mail_args->name) == 0)
468 @@ -4066,15 +4068,17 @@ while (done <= 0)
469             }
470           break;
471  #endif
472 -        /* Unknown option. Stick back the terminator characters and break
473 +        /* No valid option. Stick back the terminator characters and break
474          the loop.  Do the name-terminator second as extract_option sets
475 -       value==name when it found no equal-sign.
476 -       An error for a malformed address will occur. */
477 -        default:
478 +        value==name when it found no equal-sign.
479 +        An error for a malformed address will occur. */
480 +        case ENV_MAIL_OPT_NULL:
481            value[-1] = '=';
482            name[-1] = ' ';
483            arg_error = TRUE;
484            break;
485 +
486 +        default:  assert(0);
487          }
488        /* Break out of for loop if switch() had bad argument or
489           when start of the email address is reached */
490
491 commit 4fb7df6d044a39151e72346ac0d67ac09686f704
492 Author: Jeremy Harris <jgh146exb@wizmail.org>
493 Date:   Tue Aug 11 22:54:53 2015 +0100
494
495     GnuTLS: avoid whining about OCSP when not requested by config
496
497 diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
498 index 8aabc5c..fe18094 100644
499 --- a/src/src/tls-gnu.c
500 +++ b/src/src/tls-gnu.c
501 @@ -176,7 +176,9 @@ static const char * const exim_default_gnutls_priority = "NORMAL";
502  
503  static BOOL exim_gnutls_base_init_done = FALSE;
504  
505 +#ifndef DISABLE_OCSP
506  static BOOL gnutls_buggy_ocsp = FALSE;
507 +#endif
508  
509  
510  /* ------------------------------------------------------------------------ */
511 @@ -1021,6 +1023,8 @@ return OK;
512  *************************************************/
513  
514  
515 +#ifndef DISABLE_OCSP
516 +
517  static BOOL
518  tls_is_buggy_ocsp(void)
519  {
520 @@ -1047,6 +1051,7 @@ if (maj == 3)
521  return FALSE;
522  }
523  
524 +#endif
525  
526  
527  /* Called from both server and client code. In the case of a server, errors
528 @@ -1112,8 +1117,10 @@ if (!exim_gnutls_base_init_done)
529      }
530  #endif
531  
532 -  if ((gnutls_buggy_ocsp = tls_is_buggy_ocsp()))
533 +#ifndef DISABLE_OCSP
534 +  if (tls_ocsp_file && (gnutls_buggy_ocsp = tls_is_buggy_ocsp()))
535      log_write(0, LOG_MAIN, "OCSP unusable with this GnuTLS library version");
536 +#endif
537  
538    exim_gnutls_base_init_done = TRUE;
539    }
540
541 commit c528cec4dbfdb6e367a6ac0ed72e2e768a9c4392
542 Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
543 Date:   Wed Aug 12 23:45:44 2015 +0200
544
545     Adjust the timeout after interrupted select()
546
547 diff --git a/src/src/ip.c b/src/src/ip.c
548 index cb54f16..7991a58 100644
549 --- a/src/src/ip.c
550 +++ b/src/src/ip.c
551 @@ -451,8 +451,8 @@ BOOL
552  fd_ready(int fd, int timeout)
553  {
554  fd_set select_inset;
555 -struct timeval tv;
556  time_t start_recv = time(NULL);
557 +int time_left = timeout;
558  int rc;
559  
560  if (timeout <= 0)
561 @@ -464,10 +464,9 @@ if (timeout <= 0)
562  
563  do
564    {
565 +  struct timeval tv = { time_left, 0 };
566    FD_ZERO (&select_inset);
567    FD_SET (fd, &select_inset);
568 -  tv.tv_sec = timeout;
569 -  tv.tv_usec = 0;
570  
571    /*DEBUG(D_transport) debug_printf("waiting for data on fd\n");*/
572    rc = select(fd + 1, (SELECT_ARG2_TYPE *)&select_inset, NULL, NULL, &tv);
573 @@ -479,25 +478,24 @@ do
574    Aug 2004: Somebody set up a cron job that ran exiwhat every 2 minutes, making
575    the interrupt not at all rare. Since the timeout is typically more than 2
576    minutes, the effect was to block the timeout completely. To prevent this
577 -  happening again, we do an explicit time test. */
578 +  happening again, we do an explicit time test and adjust the timeout
579 +  accordingly */
580  
581    if (rc < 0 && errno == EINTR)
582      {
583      DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n");
584 -    if (time(NULL) - start_recv < timeout) continue;
585 -    DEBUG(D_transport) debug_printf("total wait time exceeds timeout\n");
586 +    /* Watch out, 'continue' jumps to the condition, not to the loops top */
587 +    if (time_left = timeout - (time(NULL) - start_recv)) continue;
588      }
589  
590 -  /* Handle a timeout, and treat any other select error as a timeout, including
591 -  an EINTR when we have been in this loop for longer than timeout. */
592 -
593    if (rc <= 0)
594      {
595      errno = ETIMEDOUT;
596      return FALSE;
597      }
598  
599 -  /* If the socket is ready, break out of the loop. */
600 +  /* Checking the FD_ISSET is not enough, if we're interrupted, the
601 +  select_inset may still contain the 'input'. */
602    }
603  while (rc < 0 || !FD_ISSET(fd, &select_inset));
604  return TRUE;
605
606 commit 85ff3cf9f3ab78c4dfa9f9ff34d27e6fe8f73c39
607 Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
608 Date:   Thu Aug 13 00:20:12 2015 +0200
609
610     Fix timeout adjustment in c528cec4
611
612 diff --git a/src/src/ip.c b/src/src/ip.c
613 index 7991a58..2d71705 100644
614 --- a/src/src/ip.c
615 +++ b/src/src/ip.c
616 @@ -455,7 +455,7 @@ time_t start_recv = time(NULL);
617  int time_left = timeout;
618  int rc;
619  
620 -if (timeout <= 0)
621 +if (time_left <= 0)
622    {
623    errno = ETIMEDOUT;
624    return FALSE;
625 @@ -484,8 +484,10 @@ do
626    if (rc < 0 && errno == EINTR)
627      {
628      DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n");
629 +
630      /* Watch out, 'continue' jumps to the condition, not to the loops top */
631 -    if (time_left = timeout - (time(NULL) - start_recv)) continue;
632 +    time_left = timeout - (time(NULL) - start_recv);
633 +    if (time_left > 0) continue;
634      }
635  
636    if (rc <= 0)
637
638 commit 6c6d6e483411af2c087ff258f4041d38eb65e775
639 Author: Tony Finch <dot@dotat.at>
640 Date:   Thu Aug 13 15:16:48 2015 +0100
641
642     Overhaul the debug_selector and log_selector machinery to support variable-length bit vectors. No functional change.
643
644 diff --git a/src/src/acl.c b/src/src/acl.c
645 index 91ee571..f2e0ef2 100644
646 --- a/src/src/acl.c
647 +++ b/src/src/acl.c
648 @@ -4287,7 +4287,7 @@ while (acl != NULL)
649      case ACL_WARN:
650      if (cond == OK)
651        acl_warn(where, *user_msgptr, *log_msgptr);
652 -    else if (cond == DEFER && (log_extra_selector & LX_acl_warn_skipped) != 0)
653 +    else if (cond == DEFER && LOGGING(acl_warn_skipped))
654        log_write(0, LOG_MAIN, "%s Warning: ACL \"warn\" statement skipped: "
655          "condition test deferred%s%s", host_and_ident(TRUE),
656          (*log_msgptr == NULL)? US"" : US": ",
657 diff --git a/src/src/daemon.c b/src/src/daemon.c
658 index 894a96f..2d10387 100644
659 --- a/src/src/daemon.c
660 +++ b/src/src/daemon.c
661 @@ -145,7 +145,7 @@ int dup_accept_socket = -1;
662  int max_for_this_host = 0;
663  int wfsize = 0;
664  int wfptr = 0;
665 -int use_log_write_selector = log_write_selector;
666 +int save_log_selector = *log_selector;
667  uschar *whofrom = NULL;
668  
669  void *reset_point = store_get(0);
670 @@ -206,11 +206,11 @@ memory is reclaimed. */
671  
672  whofrom = string_append(whofrom, &wfsize, &wfptr, 3, "[", sender_host_address, "]");
673  
674 -if ((log_extra_selector & LX_incoming_port) != 0)
675 +if (LOGGING(incoming_port))
676    whofrom = string_append(whofrom, &wfsize, &wfptr, 2, ":", string_sprintf("%d",
677      sender_host_port));
678  
679 -if ((log_extra_selector & LX_incoming_interface) != 0)
680 +if (LOGGING(incoming_interface))
681    whofrom = string_append(whofrom, &wfsize, &wfptr, 4, " I=[",
682      interface_address, "]:", string_sprintf("%d", interface_port));
683  
684 @@ -338,11 +338,11 @@ the generalized logging code each time when the selector is false. If the
685  selector is set, check whether the host is on the list for logging. If not,
686  arrange to unset the selector in the subprocess. */
687  
688 -if ((log_write_selector & L_smtp_connection) != 0)
689 +if (LOGGING(smtp_connection))
690    {
691    uschar *list = hosts_connection_nolog;
692    if (list != NULL && verify_check_host(&list) == OK)
693 -    use_log_write_selector &= ~L_smtp_connection;
694 +    save_log_selector &= ~L_smtp_connection;
695    else
696      log_write(L_smtp_connection, LOG_MAIN, "SMTP connection from %s "
697        "(TCP/IP connection count = %d)", whofrom, smtp_accept_count + 1);
698 @@ -372,7 +372,7 @@ if (pid == 0)
699  
700    /* May have been modified for the subprocess */
701  
702 -  log_write_selector = use_log_write_selector;
703 +  *log_selector = save_log_selector;
704  
705    /* Get the local interface address into permanent store */
706  
707 diff --git a/src/src/deliver.c b/src/src/deliver.c
708 index 78f8f4b..c796de0 100644
709 --- a/src/src/deliver.c
710 +++ b/src/src/deliver.c
711 @@ -682,7 +682,7 @@ d_hostlog(uschar * s, int * sizep, int * ptrp, address_item * addr)
712  {
713    s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name,
714      US" [", addr->host_used->address, US"]");
715 -  if ((log_extra_selector & LX_outgoing_port) != 0)
716 +  if (LOGGING(outgoing_port))
717      s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
718        addr->host_used->port));
719    return s;
720 @@ -692,10 +692,9 @@ d_hostlog(uschar * s, int * sizep, int * ptrp, address_item * addr)
721  static uschar *
722  d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr)
723  {
724 -  if ((log_extra_selector & LX_tls_cipher) != 0 && addr->cipher != NULL)
725 +  if (LOGGING(tls_cipher) && addr->cipher != NULL)
726      s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher);
727 -  if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
728 -       addr->cipher != NULL)
729 +  if (LOGGING(tls_certificate_verified) && addr->cipher != NULL)
730      s = string_append(s, sizep, ptrp, 2, US" CV=",
731        testflag(addr, af_cert_verified)
732        ?
733 @@ -706,7 +705,7 @@ d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr)
734  #endif
735          "yes"
736        : "no");
737 -  if ((log_extra_selector & LX_tls_peerdn) != 0 && addr->peerdn != NULL)
738 +  if (LOGGING(tls_peerdn) && addr->peerdn != NULL)
739      s = string_append(s, sizep, ptrp, 3, US" DN=\"",
740        string_printing(addr->peerdn), US"\"");
741    return s;
742 @@ -808,7 +807,7 @@ pointer to a single host item in their host list, for use by the transport. */
743  
744  s = reset_point = store_get(size);
745  
746 -log_address = string_log_address(addr, (log_write_selector & L_all_parents) != 0, TRUE);
747 +log_address = string_log_address(addr, LOGGING(all_parents), TRUE);
748  if (msg)
749    s = string_append(s, &size, &ptr, 3, host_and_ident(TRUE), US" ", log_address);
750  else
751 @@ -817,11 +816,11 @@ else
752    s = string_append(s, &size, &ptr, 2, US"> ", log_address);
753    }
754  
755 -if (log_extra_selector & LX_incoming_interface  &&  sending_ip_address)
756 +if (LOGGING(incoming_interface) && sending_ip_address)
757    s = string_append(s, &size, &ptr, 3, US" I=[", sending_ip_address, US"]");
758    /* for the port:  string_sprintf("%d", sending_port) */
759  
760 -if ((log_extra_selector & LX_sender_on_delivery) != 0  ||  msg)
761 +if (LOGGING(sender_on_delivery) || msg)
762    s = string_append(s, &size, &ptr, 3, US" F=<",
763  #ifdef EXPERIMENTAL_INTERNATIONAL
764      testflag(addr, af_utf8_downcvt)
765 @@ -841,8 +840,7 @@ delivery; indeed, I did for some time, until this statement crashed. The case
766  when it is not set is for a delivery to /dev/null which is optimised by not
767  being run at all. */
768  
769 -if (used_return_path != NULL &&
770 -      (log_extra_selector & LX_return_path_on_delivery) != 0)
771 +if (used_return_path != NULL && LOGGING(return_path_on_delivery))
772    s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">");
773  
774  if (msg)
775 @@ -854,7 +852,7 @@ if (addr->router != NULL)
776  
777  s = string_append(s, &size, &ptr, 2, US" T=", addr->transport->name);
778  
779 -if ((log_extra_selector & LX_delivery_size) != 0)
780 +if (LOGGING(delivery_size))
781    s = string_append(s, &size, &ptr, 2, US" S=",
782      string_sprintf("%d", transport_count));
783  
784 @@ -901,7 +899,7 @@ else
785      if (addr->auth_id)
786        {
787        s = string_append(s, &size, &ptr, 2, US":", addr->auth_id);
788 -      if (log_extra_selector & LX_smtp_mailauth  &&  addr->auth_sndr)
789 +      if (LOGGING(smtp_mailauth) && addr->auth_sndr)
790          s = string_append(s, &size, &ptr, 2, US":", addr->auth_sndr);
791        }
792      }
793 @@ -914,8 +912,7 @@ else
794  
795  /* confirmation message (SMTP (host_used) and LMTP (driver_name)) */
796  
797 -if (log_extra_selector & LX_smtp_confirmation &&
798 -    addr->message &&
799 +if (LOGGING(smtp_confirmation) && addr->message &&
800      (addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0))
801    {
802    unsigned i;
803 @@ -935,11 +932,11 @@ if (log_extra_selector & LX_smtp_confirmation &&
804  
805  /* Time on queue and actual time taken to deliver */
806  
807 -if ((log_extra_selector & LX_queue_time) != 0)
808 +if (LOGGING(queue_time))
809    s = string_append(s, &size, &ptr, 2, US" QT=",
810      readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) );
811  
812 -if ((log_extra_selector & LX_deliver_time) != 0)
813 +if (LOGGING(deliver_time))
814    s = string_append(s, &size, &ptr, 2, US" DT=",
815      readconf_printtime(addr->more_errno));
816  
817 @@ -1230,8 +1227,7 @@ else if (result == DEFER || result == PANIC)
818      /* Create the address string for logging. Must not do this earlier, because
819      an OK result may be changed to FAIL when a pipe returns text. */
820  
821 -    log_address = string_log_address(addr,
822 -      (log_write_selector & L_all_parents) != 0, result == OK);
823 +    log_address = string_log_address(addr, LOGGING(all_parents), result == OK);
824  
825      s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address));
826  
827 @@ -1342,18 +1338,16 @@ else
828    /* Create the address string for logging. Must not do this earlier, because
829    an OK result may be changed to FAIL when a pipe returns text. */
830  
831 -  log_address = string_log_address(addr,
832 -    (log_write_selector & L_all_parents) != 0, result == OK);
833 +  log_address = string_log_address(addr, LOGGING(all_parents), result == OK);
834  
835    s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address));
836  
837 -  if ((log_extra_selector & LX_sender_on_delivery) != 0)
838 +  if (LOGGING(sender_on_delivery))
839      s = string_append(s, &size, &ptr, 3, US" F=<", sender_address, US">");
840  
841    /* Return path may not be set if no delivery actually happened */
842  
843 -  if (used_return_path != NULL &&
844 -      (log_extra_selector & LX_return_path_on_delivery) != 0)
845 +  if (used_return_path != NULL && LOGGING(return_path_on_delivery))
846      s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">");
847  
848    if (addr->router != NULL)
849 @@ -4449,7 +4443,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
850        }
851  
852      /* Local interface address/port */
853 -    if (log_extra_selector & LX_incoming_interface  &&  sending_ip_address)
854 +    if (LOGGING(incoming_interface) && sending_ip_address)
855        {
856        uschar * ptr = big_buffer;
857        sprintf(CS ptr, "%.128s", sending_ip_address);
858 @@ -7365,7 +7359,7 @@ if (addr_defer == NULL)
859  
860    /* Log the end of this message, with queue time if requested. */
861  
862 -  if ((log_extra_selector & LX_queue_time_overall) != 0)
863 +  if (LOGGING(queue_time_overall))
864      log_write(0, LOG_MAIN, "Completed QT=%s",
865        readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) );
866    else
867 diff --git a/src/src/exim.c b/src/src/exim.c
868 index f9d57ab..d7cb5d8 100644
869 --- a/src/src/exim.c
870 +++ b/src/src/exim.c
871 @@ -1639,6 +1639,10 @@ if (log_buffer == NULL)
872    exit(EXIT_FAILURE);
873    }
874  
875 +/* Initialize the default log options. */
876 +
877 +bits_set(log_selector, log_selector_size, log_default);
878 +
879  /* Set log_stderr to stderr, provided that stderr exists. This gets reset to
880  NULL when the daemon is run and the file is closed. We have to use this
881  indirection, because some systems don't allow writing to the variable "stderr".
882 @@ -2451,8 +2455,8 @@ for (i = 1; i < argc; i++)
883          argrest++;
884          }
885        if (*argrest != 0)
886 -        decode_bits(&selector, NULL, D_memory, 0, argrest, debug_options,
887 -          debug_options_count, US"debug", 0);
888 +        decode_bits(&selector, 1, debug_notall, argrest,
889 +          debug_options, debug_options_count, US"debug", 0);
890        debug_selector = selector;
891        }
892      break;
893 @@ -3787,14 +3791,17 @@ else
894  
895  /* Handle the decoding of logging options. */
896  
897 -decode_bits(&log_write_selector, &log_extra_selector, 0, 0,
898 +decode_bits(log_selector, log_selector_size, log_notall,
899    log_selector_string, log_options, log_options_count, US"log", 0);
900  
901  DEBUG(D_any)
902    {
903 +  int i;
904    debug_printf("configuration file is %s\n", config_main_filename);
905 -  debug_printf("log selectors = %08x %08x\n", log_write_selector,
906 -    log_extra_selector);
907 +  debug_printf("log selectors =");
908 +  for (i = 0; i < log_selector_size; i++)
909 +    debug_printf(" %08x", log_selector[i]);
910 +  debug_printf("\n");
911    }
912  
913  /* If domain literals are not allowed, check the sender address that was
914 @@ -4001,7 +4008,7 @@ a debugging feature for finding out what arguments certain MUAs actually use.
915  Don't attempt it if logging is disabled, or if listing variables or if
916  verifying/testing addresses or expansions. */
917  
918 -if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0)
919 +if (((debug_selector & D_any) != 0 || LOGGING(arguments))
920        && really_exim && !list_options && !checking)
921    {
922    int i;
923 @@ -4036,7 +4043,7 @@ if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0)
924      while (*p) p++;
925      }
926  
927 -  if ((log_extra_selector & LX_arguments) != 0)
928 +  if (LOGGING(arguments))
929      log_write(0, LOG_MAIN, "%s", big_buffer);
930    else
931      debug_printf("%s\n", big_buffer);
932 @@ -5021,7 +5028,7 @@ if (host_checking)
933        sender_host_address);
934  
935    if (verify_check_host(&hosts_connection_nolog) == OK)
936 -    log_write_selector &= ~L_smtp_connection;
937 +    BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection);
938    log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info());
939  
940    /* NOTE: We do *not* call smtp_log_no_mail() if smtp_start_session() fails,
941 @@ -5195,7 +5202,7 @@ if (smtp_input)
942    smtp_in = stdin;
943    smtp_out = stdout;
944    if (verify_check_host(&hosts_connection_nolog) == OK)
945 -    log_write_selector &= ~L_smtp_connection;
946 +    BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection);
947    log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info());
948    if (!smtp_start_session())
949      {
950 diff --git a/src/src/functions.h b/src/src/functions.h
951 index 0257904..4af0017 100644
952 --- a/src/src/functions.h
953 +++ b/src/src/functions.h
954 @@ -99,6 +99,9 @@ extern int     auth_get_no64_data(uschar **, uschar *);
955  extern uschar *auth_xtextencode(uschar *, int);
956  extern int     auth_xtextdecode(uschar *, uschar **);
957  
958 +extern void    bits_clear(unsigned int *, size_t, int *);
959 +extern void    bits_set(unsigned int *, size_t, int *);
960 +
961  extern void    cancel_cutthrough_connection(const char *);
962  extern int     check_host(void *, const uschar *, const uschar **, uschar **);
963  extern uschar **child_exec_exim(int, BOOL, int *, BOOL, int, ...);
964 @@ -123,8 +126,8 @@ extern void    debug_print_ids(uschar *);
965  extern void    debug_print_string(uschar *);
966  extern void    debug_print_tree(tree_node *);
967  extern void    debug_vprintf(const char *, va_list);
968 -extern void    decode_bits(unsigned int *, unsigned int *,
969 -                  int, int, uschar *, bit_table *, int, uschar *, int);
970 +extern void    decode_bits(unsigned int *, size_t, int *,
971 +                  uschar *, bit_table *, int, uschar *, int);
972  extern address_item *deliver_make_addr(uschar *, BOOL);
973  extern void    deliver_init(void);
974  extern void    delivery_log(int, address_item *, int, uschar *);
975 diff --git a/src/src/globals.c b/src/src/globals.c
976 index 66baffe..1344b5a 100644
977 --- a/src/src/globals.c
978 +++ b/src/src/globals.c
979 @@ -536,40 +536,45 @@ uschar *dccifd_options         = US"header";
980  BOOL    debug_daemon           = FALSE;
981  int     debug_fd               = -1;
982  FILE   *debug_file             = NULL;
983 -bit_table debug_options[]      = {
984 -  { US"acl",            D_acl },
985 -  { US"all",            D_all },
986 -  { US"auth",           D_auth },
987 -  { US"deliver",        D_deliver },
988 -  { US"dns",            D_dns },
989 -  { US"dnsbl",          D_dnsbl },
990 -  { US"exec",           D_exec },
991 -  { US"expand",         D_expand },
992 -  { US"filter",         D_filter },
993 -  { US"hints_lookup",   D_hints_lookup },
994 -  { US"host_lookup",    D_host_lookup },
995 -  { US"ident",          D_ident },
996 -  { US"interface",      D_interface },
997 -  { US"lists",          D_lists },
998 -  { US"load",           D_load },
999 -  { US"local_scan",     D_local_scan },
1000 -  { US"lookup",         D_lookup },
1001 -  { US"memory",         D_memory },
1002 -  { US"pid",            D_pid },
1003 -  { US"process_info",   D_process_info },
1004 -  { US"queue_run",      D_queue_run },
1005 -  { US"receive",        D_receive },
1006 -  { US"resolver",       D_resolver },
1007 -  { US"retry",          D_retry },
1008 -  { US"rewrite",        D_rewrite },
1009 -  { US"route",          D_route },
1010 -  { US"timestamp",      D_timestamp },
1011 -  { US"tls",            D_tls },
1012 -  { US"transport",      D_transport },
1013 -  { US"uid",            D_uid },
1014 -  { US"verify",         D_verify }
1015 +int     debug_notall[]         = {
1016 +  Di_memory,
1017 +  -1
1018  };
1019 -int     debug_options_count    = sizeof(debug_options)/sizeof(bit_table);
1020 +bit_table debug_options[]      = { /* must be in alphabetical order */
1021 +  BIT_TABLE(D, acl),
1022 +  BIT_TABLE(D, all),
1023 +  BIT_TABLE(D, auth),
1024 +  BIT_TABLE(D, deliver),
1025 +  BIT_TABLE(D, dns),
1026 +  BIT_TABLE(D, dnsbl),
1027 +  BIT_TABLE(D, exec),
1028 +  BIT_TABLE(D, expand),
1029 +  BIT_TABLE(D, filter),
1030 +  BIT_TABLE(D, hints_lookup),
1031 +  BIT_TABLE(D, host_lookup),
1032 +  BIT_TABLE(D, ident),
1033 +  BIT_TABLE(D, interface),
1034 +  BIT_TABLE(D, lists),
1035 +  BIT_TABLE(D, load),
1036 +  BIT_TABLE(D, local_scan),
1037 +  BIT_TABLE(D, lookup),
1038 +  BIT_TABLE(D, memory),
1039 +  BIT_TABLE(D, pid),
1040 +  BIT_TABLE(D, process_info),
1041 +  BIT_TABLE(D, queue_run),
1042 +  BIT_TABLE(D, receive),
1043 +  BIT_TABLE(D, resolver),
1044 +  BIT_TABLE(D, retry),
1045 +  BIT_TABLE(D, rewrite),
1046 +  BIT_TABLE(D, route),
1047 +  BIT_TABLE(D, timestamp),
1048 +  BIT_TABLE(D, tls),
1049 +  BIT_TABLE(D, transport),
1050 +  BIT_TABLE(D, uid),
1051 +  BIT_TABLE(D, verify),
1052 +};
1053 +int     debug_options_count    = nelem(debug_options);
1054 +
1055  unsigned int debug_selector    = 0;
1056  int     delay_warning[DELAY_WARNING_SIZE] = { DELAY_WARNING_SIZE, 1, 24*60*60 };
1057  uschar *delay_warning_condition=
1058 @@ -813,78 +818,91 @@ uid_t   local_user_uid         = (uid_t)(-1);
1059  tree_node *localpartlist_anchor= NULL;
1060  int     localpartlist_count    = 0;
1061  uschar *log_buffer             = NULL;
1062 -unsigned int log_extra_selector = LX_default;
1063 +
1064 +int     log_default[]          = { /* for initializing log_selector */
1065 +  Li_acl_warn_skipped,
1066 +  Li_connection_reject,
1067 +  Li_delay_delivery,
1068 +  Li_dnslist_defer,
1069 +  Li_etrn,
1070 +  Li_host_lookup_failed,
1071 +  Li_lost_incoming_connection,
1072 +  Li_queue_run,
1073 +  Li_rejected_header,
1074 +  Li_retry_defer,
1075 +  Li_sender_verify_fail,
1076 +  Li_size_reject,
1077 +  Li_skip_delivery,
1078 +  Li_smtp_confirmation,
1079 +  Li_tls_certificate_verified,
1080 +  Li_tls_cipher,
1081 +  -1
1082 +};
1083 +
1084  uschar *log_file_path          = US LOG_FILE_PATH
1085                             "\0<--------------Space to patch log_file_path->";
1086  
1087 -/* Those log options with L_xxx identifiers have values less than 0x800000 and
1088 -are the ones that get put into log_write_selector. They can be used in calls to
1089 -log_write() to test for the bit. The options with LX_xxx identifiers have
1090 -values greater than 0x80000000 and are put into log_extra_selector (without the
1091 -top bit). They are never used in calls to log_write(), but are tested
1092 -independently. This separation became necessary when the number of log
1093 -selectors was getting close to filling a 32-bit word. */
1094 -
1095 -/* Note that this list must be in alphabetical order. */
1096 -
1097 -bit_table log_options[]        = {
1098 -  { US"8bitmime",                     LX_8bitmime },
1099 -  { US"acl_warn_skipped",             LX_acl_warn_skipped },
1100 -  { US"address_rewrite",              L_address_rewrite },
1101 -  { US"all",                          L_all },
1102 -  { US"all_parents",                  L_all_parents },
1103 -  { US"arguments",                    LX_arguments },
1104 -  { US"connection_reject",            L_connection_reject },
1105 -  { US"delay_delivery",               L_delay_delivery },
1106 -  { US"deliver_time",                 LX_deliver_time },
1107 -  { US"delivery_size",                LX_delivery_size },
1108 -  { US"dnslist_defer",                L_dnslist_defer },
1109 -  { US"etrn",                         L_etrn },
1110 -  { US"host_lookup_failed",           L_host_lookup_failed },
1111 -  { US"ident_timeout",                LX_ident_timeout },
1112 -  { US"incoming_interface",           LX_incoming_interface },
1113 -  { US"incoming_port",                LX_incoming_port },
1114 -  { US"lost_incoming_connection",     L_lost_incoming_connection },
1115 -  { US"outgoing_port",                LX_outgoing_port },
1116 -  { US"pid",                          LX_pid },
1117 +int     log_notall[]           = {
1118 +  -1
1119 +};
1120 +bit_table log_options[]        = { /* must be in alphabetical order */
1121 +  BIT_TABLE(L, 8bitmime),
1122 +  BIT_TABLE(L, acl_warn_skipped),
1123 +  BIT_TABLE(L, address_rewrite),
1124 +  BIT_TABLE(L, all),
1125 +  BIT_TABLE(L, all_parents),
1126 +  BIT_TABLE(L, arguments),
1127 +  BIT_TABLE(L, connection_reject),
1128 +  BIT_TABLE(L, delay_delivery),
1129 +  BIT_TABLE(L, deliver_time),
1130 +  BIT_TABLE(L, delivery_size),
1131 +  BIT_TABLE(L, dnslist_defer),
1132 +  BIT_TABLE(L, etrn),
1133 +  BIT_TABLE(L, host_lookup_failed),
1134 +  BIT_TABLE(L, ident_timeout),
1135 +  BIT_TABLE(L, incoming_interface),
1136 +  BIT_TABLE(L, incoming_port),
1137 +  BIT_TABLE(L, lost_incoming_connection),
1138 +  BIT_TABLE(L, outgoing_port),
1139 +  BIT_TABLE(L, pid),
1140  #ifdef EXPERIMENTAL_PROXY
1141 -  { US"proxy",                        LX_proxy },
1142 +  BIT_TABLE(L, proxy),
1143  #endif
1144 -  { US"queue_run",                    L_queue_run },
1145 -  { US"queue_time",                   LX_queue_time },
1146 -  { US"queue_time_overall",           LX_queue_time_overall },
1147 -  { US"received_recipients",          LX_received_recipients },
1148 -  { US"received_sender",              LX_received_sender },
1149 -  { US"rejected_header",              LX_rejected_header },
1150 -  { US"rejected_headers",             LX_rejected_header },
1151 -  { US"retry_defer",                  L_retry_defer },
1152 -  { US"return_path_on_delivery",      LX_return_path_on_delivery },
1153 -  { US"sender_on_delivery",           LX_sender_on_delivery },
1154 -  { US"sender_verify_fail",           LX_sender_verify_fail },
1155 -  { US"size_reject",                  L_size_reject },
1156 -  { US"skip_delivery",                L_skip_delivery },
1157 -  { US"smtp_confirmation",            LX_smtp_confirmation },
1158 -  { US"smtp_connection",              L_smtp_connection },
1159 -  { US"smtp_incomplete_transaction",  L_smtp_incomplete_transaction },
1160 -  { US"smtp_mailauth",                LX_smtp_mailauth },
1161 -  { US"smtp_no_mail",                 LX_smtp_no_mail },
1162 -  { US"smtp_protocol_error",          L_smtp_protocol_error },
1163 -  { US"smtp_syntax_error",            L_smtp_syntax_error },
1164 -  { US"subject",                      LX_subject },
1165 -  { US"tls_certificate_verified",     LX_tls_certificate_verified },
1166 -  { US"tls_cipher",                   LX_tls_cipher },
1167 -  { US"tls_peerdn",                   LX_tls_peerdn },
1168 -  { US"tls_sni",                      LX_tls_sni },
1169 -  { US"unknown_in_list",              LX_unknown_in_list }
1170 +  BIT_TABLE(L, queue_run),
1171 +  BIT_TABLE(L, queue_time),
1172 +  BIT_TABLE(L, queue_time_overall),
1173 +  BIT_TABLE(L, received_recipients),
1174 +  BIT_TABLE(L, received_sender),
1175 +  BIT_TABLE(L, rejected_header),
1176 +  { US"rejected_headers", Li_rejected_header },
1177 +  BIT_TABLE(L, retry_defer),
1178 +  BIT_TABLE(L, return_path_on_delivery),
1179 +  BIT_TABLE(L, sender_on_delivery),
1180 +  BIT_TABLE(L, sender_verify_fail),
1181 +  BIT_TABLE(L, size_reject),
1182 +  BIT_TABLE(L, skip_delivery),
1183 +  BIT_TABLE(L, smtp_confirmation),
1184 +  BIT_TABLE(L, smtp_connection),
1185 +  BIT_TABLE(L, smtp_incomplete_transaction),
1186 +  BIT_TABLE(L, smtp_mailauth),
1187 +  BIT_TABLE(L, smtp_no_mail),
1188 +  BIT_TABLE(L, smtp_protocol_error),
1189 +  BIT_TABLE(L, smtp_syntax_error),
1190 +  BIT_TABLE(L, subject),
1191 +  BIT_TABLE(L, tls_certificate_verified),
1192 +  BIT_TABLE(L, tls_cipher),
1193 +  BIT_TABLE(L, tls_peerdn),
1194 +  BIT_TABLE(L, tls_sni),
1195 +  BIT_TABLE(L, unknown_in_list),
1196  };
1197 +int     log_options_count      = nelem(log_options);
1198  
1199 -int     log_options_count      = sizeof(log_options)/sizeof(bit_table);
1200  int     log_reject_target      = 0;
1201 +unsigned int log_selector[log_selector_size]; /* initialized in main() */
1202  uschar *log_selector_string    = NULL;
1203  FILE   *log_stderr             = NULL;
1204  BOOL    log_testing_mode       = FALSE;
1205  BOOL    log_timezone           = FALSE;
1206 -unsigned int log_write_selector= L_default;
1207  uschar *login_sender_address   = NULL;
1208  uschar *lookup_dnssec_authenticated = NULL;
1209  int     lookup_open_max        = 25;
1210 diff --git a/src/src/globals.h b/src/src/globals.h
1211 index ab03302..978a4cc 100644
1212 --- a/src/src/globals.h
1213 +++ b/src/src/globals.h
1214 @@ -319,6 +319,7 @@ extern uschar *dccifd_options;         /* options for the dccifd daemon */
1215  extern BOOL    debug_daemon;           /* Debug the daemon process only */
1216  extern int     debug_fd;               /* The fd for debug_file */
1217  extern FILE   *debug_file;             /* Where to write debugging info */
1218 +extern int     debug_notall[];         /* Debug options excluded from +all */
1219  extern bit_table debug_options[];      /* Table of debug options */
1220  extern int     debug_options_count;    /* Size of table */
1221  extern int     delay_warning[];        /* Times between warnings */
1222 @@ -531,16 +532,17 @@ extern uid_t   local_user_uid;         /* As it says; may be set in routers */
1223  extern tree_node *localpartlist_anchor;/* Tree of defined localpart lists */
1224  extern int     localpartlist_count;    /* Number defined */
1225  extern uschar *log_buffer;             /* For constructing log entries */
1226 -extern unsigned int log_extra_selector;/* Bit map of logging options other than used by log_write() */
1227 +extern int     log_default[];          /* Initialization list for log_selector */
1228  extern uschar *log_file_path;          /* If unset, use default */
1229 +extern int     log_notall[];           /* Log options excluded from +all */
1230  extern bit_table log_options[];        /* Table of options */
1231  extern int     log_options_count;      /* Size of table */
1232  extern int     log_reject_target;      /* Target log for ACL rejections */
1233 +extern unsigned int log_selector[];    /* Bit map of logging options */
1234  extern uschar *log_selector_string;    /* As supplied in the config */
1235  extern FILE   *log_stderr;             /* Copy of stderr for log use, or NULL */
1236  extern BOOL    log_testing_mode;       /* TRUE in various testing modes */
1237  extern BOOL    log_timezone;           /* TRUE to include the timezone in log lines */
1238 -extern unsigned int log_write_selector;/* Bit map of logging options for log_write() */
1239  extern uschar *login_sender_address;   /* The actual sender address */
1240  extern lookup_info **lookup_list;      /* Array of pointers to available lookups */
1241  extern int     lookup_list_count;      /* Number of entries in the list */
1242 diff --git a/src/src/host.c b/src/src/host.c
1243 index 31c2bbf..5c69c7f 100644
1244 --- a/src/src/host.c
1245 +++ b/src/src/host.c
1246 @@ -544,7 +544,7 @@ use this directly as the first item for Received: because it ain't an RFC 2822
1247  domain. Sigh. */
1248  
1249  address = string_sprintf("[%s]:%d", sender_host_address, sender_host_port);
1250 -if ((log_extra_selector & LX_incoming_port) == 0 || sender_host_port <= 0)
1251 +if (!LOGGING(incoming_port) || sender_host_port <= 0)
1252    *(Ustrrchr(address, ':')) = 0;
1253  
1254  /* If there's no EHLO/HELO data, we can't show it. */
1255 @@ -695,8 +695,7 @@ else
1256    {
1257    uschar *flag = useflag? US"H=" : US"";
1258    uschar *iface = US"";
1259 -  if ((log_extra_selector & LX_incoming_interface) != 0 &&
1260 -       interface_address != NULL)
1261 +  if (LOGGING(incoming_interface) && interface_address != NULL)
1262      iface = string_sprintf(" I=[%s]:%d", interface_address, interface_port);
1263    if (sender_ident == NULL)
1264      (void)string_format(big_buffer, big_buffer_size, "%s%s%s",
1265 diff --git a/src/src/log.c b/src/src/log.c
1266 index 11b3edf..b2d1fcf 100644
1267 --- a/src/src/log.c
1268 +++ b/src/src/log.c
1269 @@ -613,7 +613,7 @@ If a message_id exists, we include it after the timestamp.
1270  
1271  Arguments:
1272    selector  write to main log or LOG_INFO only if this value is zero, or if
1273 -              its bit is set in log_write_selector
1274 +              its bit is set in log_selector[0]
1275    flags     each bit indicates some independent action:
1276                LOG_SENDER      add raw sender to the message
1277                LOG_RECIPIENTS  add raw recipients list to message
1278 @@ -749,15 +749,12 @@ DEBUG(D_any|D_v)
1279    Ustrcpy(ptr, "LOG:");
1280    ptr += 4;
1281  
1282 -  /* Show the options that were passed into the call. These are those whose
1283 -  flag values do not have the 0x80000000 bit in them. Note that this
1284 -  automatically exclude the "all" setting. */
1285 +  /* Show the selector that was passed into the call. */
1286  
1287    for (i = 0; i < log_options_count; i++)
1288      {
1289      unsigned int bit = log_options[i].bit;
1290 -    if ((bit & 0x80000000) != 0) continue;
1291 -    if ((selector & bit) != 0)
1292 +    if (bit < BITWORDSIZE && selector == BIT(bit))
1293        {
1294        *ptr++ = ' ';
1295        Ustrcpy(ptr, log_options[i].name);
1296 @@ -809,7 +806,7 @@ ptr = log_buffer;
1297  sprintf(CS ptr, "%s ", tod_stamp(tod_log));
1298  while(*ptr) ptr++;
1299  
1300 -if ((log_extra_selector & LX_pid) != 0)
1301 +if (LOGGING(pid))
1302    {
1303    sprintf(CS ptr, "[%d] ", (int)getpid());
1304    while (*ptr) ptr++;
1305 @@ -869,7 +866,7 @@ or unless there is no log_stderr (expn called from daemon, for example). */
1306  if (!really_exim || log_testing_mode)
1307    {
1308    if (debug_selector == 0 && log_stderr != NULL &&
1309 -      (selector == 0 || (selector & log_write_selector) != 0))
1310 +      (selector == 0 || (selector & log_selector[0]) != 0))
1311      {
1312      if (host_checking)
1313        fprintf(log_stderr, "LOG: %s", CS(log_buffer + 20));  /* no timestamp */
1314 @@ -887,7 +884,7 @@ has been renamed. Therefore, do a stat() and see if the inode has changed, and
1315  if so, re-open. */
1316  
1317  if ((flags & LOG_MAIN) != 0 &&
1318 -    (selector == 0 || (selector & log_write_selector) != 0))
1319 +    (selector == 0 || (selector & log_selector[0]) != 0))
1320    {
1321    if ((logging_mode & LOG_MODE_SYSLOG) != 0 &&
1322        (syslog_duplication || (flags & (LOG_REJECT|LOG_PANIC)) == 0))
1323 @@ -956,7 +953,7 @@ if ((flags & LOG_REJECT) != 0)
1324    {
1325    header_line *h;
1326  
1327 -  if (header_list != NULL && (log_extra_selector & LX_rejected_header) != 0)
1328 +  if (header_list != NULL && LOGGING(rejected_header))
1329      {
1330      if (recipients_count > 0)
1331        {
1332 @@ -1142,6 +1139,35 @@ syslog_open = FALSE;
1333  
1334  
1335  /*************************************************
1336 +*             Multi-bit set or clear             *
1337 +*************************************************/
1338 +
1339 +/* These functions take a list of bit indexes (terminated by -1) and
1340 +clear or set the corresponding bits in the selector.
1341 +
1342 +Arguments:
1343 +  selector       address of the bit string
1344 +  selsize        number of words in the bit string
1345 +  bits           list of bits to set
1346 +*/
1347 +
1348 +void
1349 +bits_clear(unsigned int *selector, size_t selsize, int *bits)
1350 +{
1351 +for(; *bits != -1; ++bits)
1352 +  BIT_CLEAR(selector, selsize, *bits);
1353 +}
1354 +
1355 +void
1356 +bits_set(unsigned int *selector, size_t selsize, int *bits)
1357 +{
1358 +for(; *bits != -1; ++bits)
1359 +  BIT_SET(selector, selsize, *bits);
1360 +}
1361 +
1362 +
1363 +
1364 +/*************************************************
1365  *         Decode bit settings for log/debug      *
1366  *************************************************/
1367  
1368 @@ -1151,13 +1177,9 @@ also recognizes a numeric setting of the form =<number>, but this is not
1369  intended for user use. It's an easy way for Exim to pass the debug settings
1370  when it is re-exec'ed.
1371  
1372 -The log options are held in two unsigned ints (because there became too many
1373 -for one). The top bit in the table means "put in 2nd selector". This does not
1374 -yet apply to debug options, so the "=" facility sets only the first selector.
1375 -
1376 -The "all" selector, which must be equal to 0xffffffff, is recognized specially.
1377 -It sets all the bits in both selectors. However, there is a facility for then
1378 -unsetting certain bits, because we want to turn off "memory" in the debug case.
1379 +The option table is a list of names and bit indexes. The index -1
1380 +means "set all bits, except for those listed in notall". The notall
1381 +list is terminated by -1.
1382  
1383  The action taken for bad values varies depending upon why we're here.
1384  For log messages, or if the debugging is triggered from config, then we write
1385 @@ -1165,10 +1187,9 @@ to the log on the way out.  For debug setting triggered from the command-line,
1386  we treat it as an unknown option: error message to stderr and die.
1387  
1388  Arguments:
1389 -  selector1      address of the first bit string
1390 -  selector2      address of the second bit string, or NULL
1391 -  notall1        bits to exclude from "all" for selector1
1392 -  notall2        bits to exclude from "all" for selector2
1393 +  selector       address of the bit string
1394 +  selsize        number of words in the bit string
1395 +  notall         list of bits to exclude from "all"
1396    string         the configured string
1397    options        the table of option names
1398    count          size of table
1399 @@ -1179,9 +1200,8 @@ Returns:         nothing on success - bomb out on failure
1400  */
1401  
1402  void
1403 -decode_bits(unsigned int *selector1, unsigned int *selector2, int notall1,
1404 -  int notall2, uschar *string, bit_table *options, int count, uschar *which,
1405 -  int flags)
1406 +decode_bits(unsigned int *selector, size_t selsize, int *notall,
1407 +  uschar *string, bit_table *options, int count, uschar *which, int flags)
1408  {
1409  uschar *errmsg;
1410  if (string == NULL) return;
1411 @@ -1189,7 +1209,8 @@ if (string == NULL) return;
1412  if (*string == '=')
1413    {
1414    char *end;    /* Not uschar */
1415 -  *selector1 = strtoul(CS string+1, &end, 0);
1416 +  memset(selector, 0, sizeof(*selector)*selsize);
1417 +  *selector = strtoul(CS string+1, &end, 0);
1418    if (*end == 0) return;
1419    errmsg = string_sprintf("malformed numeric %s_selector setting: %s", which,
1420      string);
1421 @@ -1232,40 +1253,22 @@ else for(;;)
1422        if (middle->name[len] != 0) c = -1; else
1423          {
1424          unsigned int bit = middle->bit;
1425 -        unsigned int *selector;
1426 -
1427 -        /* The value with all bits set means "force all bits in both selectors"
1428 -        in the case where two are being handled. However, the top bit in the
1429 -        second selector is never set. When setting, some bits can be excluded.
1430 -        */
1431 -
1432 -        if (bit == 0xffffffff)
1433 -          {
1434 -          if (adding)
1435 -            {
1436 -            *selector1 = 0xffffffff ^ notall1;
1437 -            if (selector2 != NULL) *selector2 = 0x7fffffff ^ notall2;
1438 -            }
1439 -          else
1440 -            {
1441 -            *selector1 = 0;
1442 -            if (selector2 != NULL) *selector2 = 0;
1443 -            }
1444 -          }
1445 -
1446 -        /* Otherwise, the 0x80000000 bit means "this value, without the top
1447 -        bit, belongs in the second selector". */
1448  
1449 -        else
1450 -          {
1451 -          if ((bit & 0x80000000) != 0)
1452 -            {
1453 -            selector = selector2;
1454 -            bit &= 0x7fffffff;
1455 -            }
1456 -          else selector = selector1;
1457 -          if (adding) *selector |= bit; else *selector &= ~bit;
1458 -          }
1459 +       if (bit == -1)
1460 +         {
1461 +         if (adding)
1462 +           {
1463 +           memset(selector, -1, sizeof(*selector)*selsize);
1464 +           bits_clear(selector, selsize, notall);
1465 +           }
1466 +         else
1467 +           memset(selector, 0, sizeof(*selector)*selsize);
1468 +         }
1469 +       else if (adding)
1470 +         BIT_SET(selector, selsize, bit);
1471 +       else
1472 +         BIT_CLEAR(selector, selsize, bit);
1473 +
1474          break;  /* Out of loop to match selector name */
1475          }
1476        }
1477 @@ -1335,10 +1338,8 @@ if (tag_name != NULL && (Ustrchr(tag_name, '/') != NULL))
1478  
1479  debug_selector = D_default;
1480  if (opts)
1481 -  {
1482 -  decode_bits(&debug_selector, NULL, D_memory, 0, opts,
1483 +  decode_bits(&debug_selector, 1, debug_notall, opts,
1484        debug_options, debug_options_count, US"debug", DEBUG_FROM_CONFIG);
1485 -  }
1486  
1487  /* When activating from a transport process we may never have logged at all
1488  resulting in certain setup not having been done.  Hack this for now so we
1489 diff --git a/src/src/macros.h b/src/src/macros.h
1490 index 61f9ca6..d63025e 100644
1491 --- a/src/src/macros.h
1492 +++ b/src/src/macros.h
1493 @@ -321,46 +321,84 @@ for having to swallow the rest of an SMTP message is whether the value is
1494  #define END_SIZE       4    /* Reading ended because message too big */
1495  #define END_WERROR     5    /* Write error while reading the message */
1496  
1497 -/* Options bits for debugging; D_v and D_local_scan are also in local_scan.h */
1498 -
1499 -#define D_v                          0x00000001
1500 -#define D_local_scan                 0x00000002
1501 -
1502 -#define D_acl                        0x00000004
1503 -#define D_auth                       0x00000008
1504 -#define D_deliver                    0x00000010
1505 -#define D_dns                        0x00000020
1506 -#define D_dnsbl                      0x00000040
1507 -#define D_exec                       0x00000080
1508 -#define D_expand                     0x00000100
1509 -#define D_filter                     0x00000200
1510 -#define D_hints_lookup               0x00000400
1511 -#define D_host_lookup                0x00000800
1512 -#define D_ident                      0x00001000
1513 -#define D_interface                  0x00002000
1514 -#define D_lists                      0x00004000
1515 -#define D_load                       0x00008000
1516 -#define D_lookup                     0x00010000
1517 -#define D_memory                     0x00020000
1518 -#define D_pid                        0x00040000
1519 -#define D_process_info               0x00080000
1520 -#define D_queue_run                  0x00100000
1521 -#define D_receive                    0x00200000
1522 -#define D_resolver                   0x00400000
1523 -#define D_retry                      0x00800000
1524 -#define D_rewrite                    0x01000000
1525 -#define D_route                      0x02000000
1526 -#define D_timestamp                  0x04000000
1527 -#define D_tls                        0x08000000
1528 -#define D_transport                  0x10000000
1529 -#define D_uid                        0x20000000
1530 -#define D_verify                     0x40000000
1531 -
1532 -/* The D_all value must always have all bits set, as it is recognized specially
1533 -by the function that decodes debug and log selectors. This is to enable it to
1534 -set all the bits in a multi-word selector. Debug doesn't use this yet, but we
1535 -are getting close. In fact, we want to omit "memory" for -d+all, but can't
1536 -handle this here. It is fudged externally. */
1537 +/* Bit masks for debug and log selectors */
1538 +
1539 +/* Assume words are 32 bits wide. Tiny waste of space on 64 bit
1540 +platforms, but this ensures bit vectors always work the same way. */
1541 +#define BITWORDSIZE 32
1542 +
1543 +/* This macro is for single-word bit vectors: the debug selector,
1544 +and the first word of the log selector. */
1545 +#define BIT(n) (1 << (n))
1546 +
1547 +/* And these are for multi-word vectors. */
1548 +#define BITWORD(n) (     (n) / BITWORDSIZE)
1549 +#define BITMASK(n) (1 << (n) % BITWORDSIZE)
1550 +
1551 +#define BIT_CLEAR(s,z,n) ((s)[BITWORD(n)] &= ~BITMASK(n))
1552 +#define BIT_SET(s,z,n)   ((s)[BITWORD(n)] |=  BITMASK(n))
1553 +#define BIT_TEST(s,z,n) (((s)[BITWORD(n)] &   BITMASK(n)) != 0)
1554 +
1555 +/* Used in globals.c for initializing bit_table structures. T will be either
1556 +D or L correspondong to the debug and log selector bits declared below. */
1557 +
1558 +#define BIT_TABLE(T,name) { US #name, T##i_##name }
1559 +
1560 +/* IOTA allows us to keep an implicit sequential count, like a simple enum,
1561 +but we can have sequentially numbered identifiers which are not declared
1562 +sequentially. We use this for more compact declarations of bit indexes and
1563 +masks, alternating between sequential bit index and corresponding mask. */
1564 +
1565 +#define IOTA(iota)      (__LINE__ - iota)
1566 +#define IOTA_INIT(zero) (__LINE__ - zero + 1)
1567 +
1568 +/* Options bits for debugging. DEBUG_BIT() declares both a bit index and the
1569 +corresponding mask. Di_all is a special value recognized by decode_bits().
1570 +
1571 +Exim's code assumes in a number of places that the debug_selector is one
1572 +word, and this is exposed in the local_scan ABI. The D_v and D_local_scan bit
1573 +masks are part of the local_scan API so are #defined in local_scan.h */
1574 +
1575 +#define DEBUG_BIT(name) Di_##name = IOTA(Di_iota), D_##name = BIT(Di_##name)
1576 +
1577 +enum {
1578 +  Di_all        = -1,
1579 +  Di_v          = 0,
1580 +  Di_local_scan = 1,
1581 +
1582 +  Di_iota = IOTA_INIT(2),
1583 +  DEBUG_BIT(acl),
1584 +  DEBUG_BIT(auth),
1585 +  DEBUG_BIT(deliver),
1586 +  DEBUG_BIT(dns),
1587 +  DEBUG_BIT(dnsbl),
1588 +  DEBUG_BIT(exec),
1589 +  DEBUG_BIT(expand),
1590 +  DEBUG_BIT(filter),
1591 +  DEBUG_BIT(hints_lookup),
1592 +  DEBUG_BIT(host_lookup),
1593 +  DEBUG_BIT(ident),
1594 +  DEBUG_BIT(interface),
1595 +  DEBUG_BIT(lists),
1596 +  DEBUG_BIT(load),
1597 +  DEBUG_BIT(lookup),
1598 +  DEBUG_BIT(memory),
1599 +  DEBUG_BIT(pid),
1600 +  DEBUG_BIT(process_info),
1601 +  DEBUG_BIT(queue_run),
1602 +  DEBUG_BIT(receive),
1603 +  DEBUG_BIT(resolver),
1604 +  DEBUG_BIT(retry),
1605 +  DEBUG_BIT(rewrite),
1606 +  DEBUG_BIT(route),
1607 +  DEBUG_BIT(timestamp),
1608 +  DEBUG_BIT(tls),
1609 +  DEBUG_BIT(transport),
1610 +  DEBUG_BIT(uid),
1611 +  DEBUG_BIT(verify),
1612 +};
1613 +
1614 +/* Multi-bit debug masks */
1615  
1616  #define D_all                        0xffffffff
1617  
1618 @@ -380,81 +418,67 @@ handle this here. It is fudged externally. */
1619                                           D_timestamp   | \
1620                                           D_resolver))
1621  
1622 -/* Options bits for logging. Those that will end up in log_write_selector have
1623 -values < 0x80000000. They can be used in calls to log_write(). The others have
1624 -values > 0x80000000 and are put into log_extra_selector (without the top bit).
1625 -These are only ever tested independently. "All" is a magic value that is used
1626 -only in the name table to set all options in both bit maps. */
1627 -
1628 -/* The L_all value must always have all bits set, as it is recognized specially
1629 -by the function that decodes debug and log selectors. This is to enable it to
1630 -set all the bits in a multi-word selector. */
1631 -
1632 -#define L_all                          0xffffffff
1633 -
1634 -#define L_address_rewrite              0x00000001
1635 -#define L_all_parents                  0x00000002
1636 -#define L_connection_reject            0x00000004
1637 -#define L_delay_delivery               0x00000008
1638 -#define L_dnslist_defer                0x00000010
1639 -#define L_etrn                         0x00000020
1640 -#define L_host_lookup_failed           0x00000040
1641 -#define L_lost_incoming_connection     0x00000080
1642 -#define L_queue_run                    0x00000100
1643 -#define L_retry_defer                  0x00000200
1644 -#define L_size_reject                  0x00000400
1645 -#define L_skip_delivery                0x00000800
1646 -#define L_smtp_connection              0x00001000
1647 -#define L_smtp_incomplete_transaction  0x00002000
1648 -#define L_smtp_protocol_error          0x00004000
1649 -#define L_smtp_syntax_error            0x00008000
1650 -
1651 -#define LX_acl_warn_skipped            0x80000001
1652 -#define LX_arguments                   0x80000002
1653 -#define LX_deliver_time                0x80000004
1654 -#define LX_delivery_size               0x80000008
1655 -#define LX_ident_timeout               0x80000010
1656 -#define LX_incoming_interface          0x80000020
1657 -#define LX_incoming_port               0x80000040
1658 -#define LX_outgoing_port               0x80000080
1659 -#define LX_pid                         0x80000100
1660 -#define LX_queue_time                  0x80000200
1661 -#define LX_queue_time_overall          0x80000400
1662 -#define LX_received_sender             0x80000800
1663 -#define LX_received_recipients         0x80001000
1664 -#define LX_rejected_header             0x80002000
1665 -#define LX_return_path_on_delivery     0x80004000
1666 -#define LX_sender_on_delivery          0x80008000
1667 -#define LX_sender_verify_fail          0x80010000
1668 -#define LX_smtp_confirmation           0x80020000
1669 -#define LX_smtp_no_mail                0x80040000
1670 -#define LX_subject                     0x80080000
1671 -#define LX_tls_certificate_verified    0x80100000
1672 -#define LX_tls_cipher                  0x80200000
1673 -#define LX_tls_peerdn                  0x80400000
1674 -#define LX_tls_sni                     0x80800000
1675 -#define LX_unknown_in_list             0x81000000
1676 -#define LX_8bitmime                    0x82000000
1677 -#define LX_smtp_mailauth               0x84000000
1678 -#define LX_proxy                       0x88000000
1679 -
1680 -#define L_default     (L_connection_reject        | \
1681 -                       L_delay_delivery           | \
1682 -                       L_dnslist_defer            | \
1683 -                       L_etrn                     | \
1684 -                       L_host_lookup_failed       | \
1685 -                       L_lost_incoming_connection | \
1686 -                       L_queue_run                | \
1687 -                       L_retry_defer              | \
1688 -                       L_size_reject              | \
1689 -                       L_skip_delivery)
1690 -
1691 -#define LX_default   ((LX_acl_warn_skipped        | \
1692 -                       LX_rejected_header         | \
1693 -                       LX_sender_verify_fail      | \
1694 -                       LX_smtp_confirmation       | \
1695 -                       LX_tls_certificate_verified| \
1696 -                       LX_tls_cipher) & 0x7fffffff)
1697 +/* Options bits for logging. Those that have values < BITWORDSIZE can be used
1698 +in calls to log_write(). The others are put into later words in log_selector
1699 +and are only ever tested independently, so they do not need bit mask
1700 +declarations. The Li_all value is recognized specially by decode_bits(). */
1701 +
1702 +#define LOG_BIT(name) Li_##name = IOTA(Li_iota), L_##name = BIT(Li_##name)
1703 +
1704 +enum {
1705 +  Li_all = -1,
1706 +
1707 +  Li_iota = IOTA_INIT(0),
1708 +  LOG_BIT(address_rewrite),
1709 +  LOG_BIT(all_parents),
1710 +  LOG_BIT(connection_reject),
1711 +  LOG_BIT(delay_delivery),
1712 +  LOG_BIT(dnslist_defer),
1713 +  LOG_BIT(etrn),
1714 +  LOG_BIT(host_lookup_failed),
1715 +  LOG_BIT(lost_incoming_connection),
1716 +  LOG_BIT(queue_run),
1717 +  LOG_BIT(retry_defer),
1718 +  LOG_BIT(size_reject),
1719 +  LOG_BIT(skip_delivery),
1720 +  LOG_BIT(smtp_connection),
1721 +  LOG_BIT(smtp_incomplete_transaction),
1722 +  LOG_BIT(smtp_protocol_error),
1723 +  LOG_BIT(smtp_syntax_error),
1724 +
1725 +  Li_acl_warn_skipped = BITWORDSIZE,
1726 +  Li_arguments,
1727 +  Li_deliver_time,
1728 +  Li_delivery_size,
1729 +  Li_ident_timeout,
1730 +  Li_incoming_interface,
1731 +  Li_incoming_port,
1732 +  Li_outgoing_port,
1733 +  Li_pid,
1734 +  Li_queue_time,
1735 +  Li_queue_time_overall,
1736 +  Li_received_sender,
1737 +  Li_received_recipients,
1738 +  Li_rejected_header,
1739 +  Li_return_path_on_delivery,
1740 +  Li_sender_on_delivery,
1741 +  Li_sender_verify_fail,
1742 +  Li_smtp_confirmation,
1743 +  Li_smtp_no_mail,
1744 +  Li_subject,
1745 +  Li_tls_certificate_verified,
1746 +  Li_tls_cipher,
1747 +  Li_tls_peerdn,
1748 +  Li_tls_sni,
1749 +  Li_unknown_in_list,
1750 +  Li_8bitmime,
1751 +  Li_smtp_mailauth,
1752 +  Li_proxy,
1753 +
1754 +  log_selector_size = BITWORD(Li_proxy) + 1
1755 +};
1756 +
1757 +#define LOGGING(opt) BIT_TEST(log_selector, log_selector_size, Li_##opt)
1758  
1759  /* Private error numbers for delivery failures, set negative so as not
1760  to conflict with system errno values. */
1761 diff --git a/src/src/match.c b/src/src/match.c
1762 index 9e47110..fa42187 100644
1763 --- a/src/src/match.c
1764 +++ b/src/src/match.c
1765 @@ -771,7 +771,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
1766              include_unknown? "yes":"no", error);
1767            if (!include_unknown)
1768              {
1769 -            if ((log_extra_selector & LX_unknown_in_list) != 0)
1770 +            if (LOGGING(unknown_in_list))
1771                log_write(0, LOG_MAIN, "list matching forced to fail: %s", error);
1772              return FAIL;
1773              }
1774 @@ -880,7 +880,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
1775            (void)fclose(f);
1776            if (!include_unknown)
1777              {
1778 -            if ((log_extra_selector & LX_unknown_in_list) != 0)
1779 +            if (LOGGING(unknown_in_list))
1780                log_write(0, LOG_MAIN, "list matching forced to fail: %s", error);
1781              return FAIL;
1782              }
1783 diff --git a/src/src/rda.c b/src/src/rda.c
1784 index 7596466..2afd6dc 100644
1785 --- a/src/src/rda.c
1786 +++ b/src/src/rda.c
1787 @@ -635,7 +635,7 @@ if ((pid = fork()) == 0)
1788      {
1789      DEBUG(D_rewrite) debug_printf("turned off address rewrite logging (not "
1790        "root or exim in this process)\n");
1791 -    log_write_selector &= ~L_address_rewrite;
1792 +    BIT_CLEAR(log_selector, log_selector_size, Li_address_rewrite);
1793      }
1794  
1795    /* Now do the business */
1796 diff --git a/src/src/receive.c b/src/src/receive.c
1797 index 64cf1ae..b430ee2 100644
1798 --- a/src/src/receive.c
1799 +++ b/src/src/receive.c
1800 @@ -1118,8 +1118,7 @@ add_host_info_for_log(uschar *s, int *sizeptr, int *ptrptr)
1801  if (sender_fullhost != NULL)
1802    {
1803    s = string_append(s, sizeptr, ptrptr, 2, US" H=", sender_fullhost);
1804 -  if ((log_extra_selector & LX_incoming_interface) != 0 &&
1805 -       interface_address != NULL)
1806 +  if (LOGGING(incoming_interface) && interface_address != NULL)
1807      {
1808      uschar *ss = string_sprintf(" I=[%s]:%d", interface_address,
1809        interface_port);
1810 @@ -2529,7 +2528,7 @@ if (msgid_header == NULL &&
1811  rewriting. Must copy the count, because later ACLs and the local_scan()
1812  function may mess with the real recipients. */
1813  
1814 -if ((log_extra_selector & LX_received_recipients) != 0)
1815 +if (LOGGING(received_recipients))
1816    {
1817    raw_recipients = store_get(recipients_count * sizeof(uschar *));
1818    for (i = 0; i < recipients_count; i++)
1819 @@ -3573,7 +3572,7 @@ else
1820      goto TEMPREJECT;
1821  
1822      case LOCAL_SCAN_REJECT_NOLOGHDR:
1823 -    log_extra_selector &= ~LX_rejected_header;
1824 +    BIT_CLEAR(log_selector, log_selector_size, Li_rejected_header);
1825      /* Fall through */
1826  
1827      case LOCAL_SCAN_REJECT:
1828 @@ -3582,7 +3581,7 @@ else
1829      break;
1830  
1831      case LOCAL_SCAN_TEMPREJECT_NOLOGHDR:
1832 -    log_extra_selector &= ~LX_rejected_header;
1833 +    BIT_CLEAR(log_selector, log_selector_size, Li_rejected_header);
1834      /* Fall through */
1835  
1836      case LOCAL_SCAN_TEMPREJECT:
1837 @@ -3747,15 +3746,15 @@ if (message_reference != NULL)
1838  s = add_host_info_for_log(s, &size, &sptr);
1839  
1840  #ifdef SUPPORT_TLS
1841 -if (log_extra_selector & LX_tls_cipher && tls_in.cipher)
1842 +if (LOGGING(tls_cipher) && tls_in.cipher)
1843    s = string_append(s, &size, &sptr, 2, US" X=", tls_in.cipher);
1844 -if (log_extra_selector & LX_tls_certificate_verified && tls_in.cipher)
1845 +if (LOGGING(tls_certificate_verified) && tls_in.cipher)
1846    s = string_append(s, &size, &sptr, 2, US" CV=",
1847      tls_in.certificate_verified? "yes":"no");
1848 -if (log_extra_selector & LX_tls_peerdn && tls_in.peerdn)
1849 +if (LOGGING(tls_peerdn) && tls_in.peerdn)
1850    s = string_append(s, &size, &sptr, 3, US" DN=\"",
1851      string_printing(tls_in.peerdn), US"\"");
1852 -if (log_extra_selector & LX_tls_sni && tls_in.sni)
1853 +if (LOGGING(tls_sni) && tls_in.sni)
1854    s = string_append(s, &size, &sptr, 3, US" SNI=\"",
1855      string_printing(tls_in.sni), US"\"");
1856  #endif
1857 @@ -3766,7 +3765,7 @@ if (sender_host_authenticated)
1858    if (authenticated_id != NULL)
1859      {
1860      s = string_append(s, &size, &sptr, 2, US":", authenticated_id);
1861 -    if (log_extra_selector & LX_smtp_mailauth  &&  authenticated_sender != NULL)
1862 +    if (LOGGING(smtp_mailauth) && authenticated_sender != NULL)
1863        s = string_append(s, &size, &sptr, 2, US":", authenticated_sender);
1864      }
1865    }
1866 @@ -3777,7 +3776,7 @@ if (prdr_requested)
1867  #endif
1868  
1869  #ifdef EXPERIMENTAL_PROXY
1870 -if (proxy_session &&  log_extra_selector & LX_proxy)
1871 +if (proxy_session && LOGGING(proxy))
1872    s = string_append(s, &size, &sptr, 2, US" PRX=", proxy_host_address);
1873  #endif
1874  
1875 @@ -3788,7 +3787,7 @@ s = string_append(s, &size, &sptr, 2, US" S=", big_buffer);
1876     0 ... no BODY= used
1877     7 ... 7BIT
1878     8 ... 8BITMIME */
1879 -if (log_extra_selector & LX_8bitmime)
1880 +if (LOGGING(8bitmime))
1881    {
1882    sprintf(CS big_buffer, "%d", body_8bitmime);
1883    s = string_append(s, &size, &sptr, 2, US" M8S=", big_buffer);
1884 @@ -3814,7 +3813,7 @@ if (msgid_header != NULL)
1885  /* If subject logging is turned on, create suitable printing-character
1886  text. By expanding $h_subject: we make use of the MIME decoding. */
1887  
1888 -if ((log_extra_selector & LX_subject) != 0 && subject_header != NULL)
1889 +if (LOGGING(subject) && subject_header != NULL)
1890    {
1891    int i;
1892    uschar *p = big_buffer;
1893 @@ -4003,8 +4002,8 @@ if(!smtp_reply)
1894  #endif
1895    {
1896    log_write(0, LOG_MAIN |
1897 -    (((log_extra_selector & LX_received_recipients) != 0)? LOG_RECIPIENTS : 0) |
1898 -    (((log_extra_selector & LX_received_sender) != 0)? LOG_SENDER : 0),
1899 +    (LOGGING(received_recipients)? LOG_RECIPIENTS : 0) |
1900 +    (LOGGING(received_sender)? LOG_SENDER : 0),
1901      "%s", s);
1902  
1903    /* Log any control actions taken by an ACL or local_scan(). */
1904 diff --git a/src/src/rewrite.c b/src/src/rewrite.c
1905 index 296fe8c..ca7fb6a 100644
1906 --- a/src/src/rewrite.c
1907 +++ b/src/src/rewrite.c
1908 @@ -247,8 +247,7 @@ for (rule = rewrite_rules;
1909  
1910    /* We have a validly rewritten address */
1911  
1912 -  if ((log_write_selector & L_address_rewrite) != 0 ||
1913 -      (debug_selector & D_rewrite) != 0)
1914 +  if (LOGGING(address_rewrite) || (debug_selector & D_rewrite) != 0)
1915      {
1916      int i;
1917      const uschar *where = CUS"?";
1918 diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
1919 index effc636..9982451 100644
1920 --- a/src/src/smtp_in.c
1921 +++ b/src/src/smtp_in.c
1922 @@ -1234,8 +1234,7 @@ if (sender_host_unknown || sender_host_notsocket)
1923  if (is_inetd)
1924    return string_sprintf("SMTP connection from %s (via inetd)", hostname);
1925  
1926 -if ((log_extra_selector & LX_incoming_interface) != 0 &&
1927 -     interface_address != NULL)
1928 +if (LOGGING(incoming_interface) && interface_address != NULL)
1929    return string_sprintf("SMTP connection from %s I=[%s]:%d", hostname,
1930      interface_address, interface_port);
1931  
1932 @@ -1260,16 +1259,15 @@ s_tlslog(uschar * s, int * sizep, int * ptrp)
1933    int size = sizep ? *sizep : 0;
1934    int ptr = ptrp ? *ptrp : 0;
1935  
1936 -  if ((log_extra_selector & LX_tls_cipher) != 0 && tls_in.cipher != NULL)
1937 +  if (LOGGING(tls_cipher) && tls_in.cipher != NULL)
1938      s = string_append(s, &size, &ptr, 2, US" X=", tls_in.cipher);
1939 -  if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
1940 -       tls_in.cipher != NULL)
1941 +  if (LOGGING(tls_certificate_verified) && tls_in.cipher != NULL)
1942      s = string_append(s, &size, &ptr, 2, US" CV=",
1943        tls_in.certificate_verified? "yes":"no");
1944 -  if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_in.peerdn != NULL)
1945 +  if (LOGGING(tls_peerdn) && tls_in.peerdn != NULL)
1946      s = string_append(s, &size, &ptr, 3, US" DN=\"",
1947        string_printing(tls_in.peerdn), US"\"");
1948 -  if ((log_extra_selector & LX_tls_sni) != 0 && tls_in.sni != NULL)
1949 +  if (LOGGING(tls_sni) && tls_in.sni != NULL)
1950      s = string_append(s, &size, &ptr, 3, US" SNI=\"",
1951        string_printing(tls_in.sni), US"\"");
1952  
1953 @@ -1301,7 +1299,7 @@ smtp_log_no_mail(void)
1954  int size, ptr, i;
1955  uschar *s, *sep;
1956  
1957 -if (smtp_mailcmd_count > 0 || (log_extra_selector & LX_smtp_no_mail) == 0)
1958 +if (smtp_mailcmd_count > 0 || !LOGGING(smtp_no_mail))
1959    return;
1960  
1961  s = NULL;
1962 @@ -2510,8 +2508,8 @@ static void
1963  incomplete_transaction_log(uschar *what)
1964  {
1965  if (sender_address == NULL ||                 /* No transaction in progress */
1966 -    (log_write_selector & L_smtp_incomplete_transaction) == 0  /* Not logging */
1967 -  ) return;
1968 +    !LOGGING(smtp_incomplete_transaction))
1969 +  return;
1970  
1971  /* Build list of recipients for logging */
1972  
1973 @@ -2762,7 +2760,7 @@ if (sender_verified_failed != NULL &&
1974  
1975    setflag(sender_verified_failed, af_sverify_told);
1976  
1977 -  if (rc != FAIL || (log_extra_selector & LX_sender_verify_fail) != 0)
1978 +  if (rc != FAIL || LOGGING(sender_verify_fail))
1979      log_write(0, LOG_MAIN|LOG_REJECT, "%s sender verify %s for <%s>%s",
1980        host_and_ident(TRUE),
1981        ((sender_verified_failed->special_action & 255) == DEFER)? "defer":"fail",
1982 diff --git a/src/src/structs.h b/src/src/structs.h
1983 index 6f143d6..438b521 100644
1984 --- a/src/src/structs.h
1985 +++ b/src/src/structs.h
1986 @@ -38,7 +38,7 @@ typedef struct macro_item {
1987  
1988  typedef struct bit_table {
1989    uschar *name;
1990 -  unsigned int bit;
1991 +  int bit;
1992  } bit_table;
1993  
1994  /* Block for holding a uid and gid, possibly unset, and an initgroups flag. */
1995 diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
1996 index b1dccb8..73ac807 100644
1997 --- a/src/src/tls-openssl.c
1998 +++ b/src/src/tls-openssl.c
1999 @@ -1111,8 +1111,7 @@ len = SSL_get_tlsext_status_ocsp_resp(s, &p);
2000  if(!p)
2001   {
2002    /* Expect this when we requested ocsp but got none */
2003 -  if (  cbinfo->u_ocsp.client.verify_required
2004 -     && log_extra_selector & LX_tls_cipher)
2005 +  if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher))
2006      log_write(0, LOG_MAIN, "Received TLS status callback, null content");
2007    else
2008      DEBUG(D_tls) debug_printf(" null\n");
2009 @@ -1122,7 +1121,7 @@ if(!p)
2010  if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
2011   {
2012    tls_out.ocsp = OCSP_FAILED;
2013 -  if (log_extra_selector & LX_tls_cipher)
2014 +  if (LOGGING(tls_cipher))
2015      log_write(0, LOG_MAIN, "Received TLS cert status response, parse error");
2016    else
2017      DEBUG(D_tls) debug_printf(" parse error\n");
2018 @@ -1132,7 +1131,7 @@ if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
2019  if(!(bs = OCSP_response_get1_basic(rsp)))
2020    {
2021    tls_out.ocsp = OCSP_FAILED;
2022 -  if (log_extra_selector & LX_tls_cipher)
2023 +  if (LOGGING(tls_cipher))
2024      log_write(0, LOG_MAIN, "Received TLS cert status response, error parsing response");
2025    else
2026      DEBUG(D_tls) debug_printf(" error parsing response\n");
2027 @@ -1163,7 +1162,7 @@ if(!(bs = OCSP_response_get1_basic(rsp)))
2028               cbinfo->u_ocsp.client.verify_store, 0)) <= 0)
2029        {
2030        tls_out.ocsp = OCSP_FAILED;
2031 -      if (log_extra_selector & LX_tls_cipher)
2032 +      if (LOGGING(tls_cipher))
2033         log_write(0, LOG_MAIN, "Received TLS cert status response, itself unverifiable");
2034        BIO_printf(bp, "OCSP response verify failure\n");
2035        ERR_print_errors(bp);
2036 diff --git a/src/src/transports/lmtp.c b/src/src/transports/lmtp.c
2037 index 0cd89af..1f4d7a6 100644
2038 --- a/src/src/transports/lmtp.c
2039 +++ b/src/src/transports/lmtp.c
2040 @@ -654,7 +654,7 @@ if (send_data)
2041      if (lmtp_read_response(out, buffer, sizeof(buffer), '2', timeout))
2042        {
2043        addr->transport_return = OK;
2044 -      if ((log_extra_selector & LX_smtp_confirmation) != 0)
2045 +      if (LOGGING(smtp_confirmation))
2046          {
2047          const uschar *s = string_printing(buffer);
2048         /* de-const safe here as string_printing known to have alloc'n'copied */
2049 diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
2050 index a952413..609dba3 100644
2051 --- a/src/src/transports/smtp.c
2052 +++ b/src/src/transports/smtp.c
2053 @@ -638,7 +638,7 @@ if (addr->message)
2054    }
2055  else
2056    {
2057 -  if (log_extra_selector & LX_outgoing_port)
2058 +  if (LOGGING(outgoing_port))
2059      message = string_sprintf("%s:%d", message,
2060                 host->port == PORT_NONE ? 25 : host->port);
2061    log_write(0, LOG_MAIN, "%s %s", message, strerror(addr->basic_errno));
2062 @@ -2380,7 +2380,7 @@ if (!ok) ok = TRUE; else
2063  
2064      if (
2065  #ifndef EXPERIMENTAL_EVENT
2066 -          (log_extra_selector & LX_smtp_confirmation) != 0 &&
2067 +          LOGGING(smtp_confirmation) &&
2068  #endif
2069            !lmtp
2070         )
2071 @@ -2435,7 +2435,7 @@ if (!ok) ok = TRUE; else
2072            continue;
2073            }
2074          completed_address = TRUE;   /* NOW we can set this flag */
2075 -        if ((log_extra_selector & LX_smtp_confirmation) != 0)
2076 +        if (LOGGING(smtp_confirmation))
2077            {
2078            const uschar *s = string_printing(buffer);
2079           /* deconst cast ok here as string_printing was checked to have alloc'n'copied */
2080 diff --git a/src/src/verify.c b/src/src/verify.c
2081 index e00e7b9..7992d58 100644
2082 --- a/src/src/verify.c
2083 +++ b/src/src/verify.c
2084 @@ -2916,7 +2916,7 @@ if (ip_bind(sock, host_af, interface_address, 0) < 0)
2085  if (ip_connect(sock, host_af, sender_host_address, port, rfc1413_query_timeout)
2086       < 0)
2087    {
2088 -  if (errno == ETIMEDOUT && (log_extra_selector & LX_ident_timeout) != 0)
2089 +  if (errno == ETIMEDOUT && LOGGING(ident_timeout))
2090      {
2091      log_write(0, LOG_MAIN, "ident connection to %s timed out",
2092        sender_host_address);
2093
2094 commit ac881e2749754fbe167b5f38784dd85b088571cf
2095 Author: Tony Finch <dot@dotat.at>
2096 Date:   Thu Aug 13 15:16:51 2015 +0100
2097
2098     Improve the consistency of logging incoming and outgoing interfaces.
2099     
2100     The I= interface field on outgoing lines is now after the H= remote
2101     host field, same as incoming lines. There is a separate outgoing_interface
2102     log selector which allows you to disable the outgoing I= field.
2103     
2104     (slight massaging by JH)
2105
2106 diff --git a/src/src/deliver.c b/src/src/deliver.c
2107 index c796de0..0e7cea3 100644
2108 --- a/src/src/deliver.c
2109 +++ b/src/src/deliver.c
2110 @@ -676,39 +676,78 @@ while (addr->parent != NULL)
2111  
2112  
2113  
2114 +/*************************************************
2115 +*      Delivery logging support functions        *
2116 +*************************************************/
2117 +
2118 +/* The LOGGING() checks in d_log_interface() are complicated for backwards
2119 +compatibility. When outgoing interface logging was originally added, it was
2120 +conditional on just incoming_interface (which is off by default). The
2121 +outgoing_interface option is on by default to preserve this behaviour, but
2122 +you can enable incoming_interface and disable outgoing_interface to get I=
2123 +fields on incoming lines only.
2124 +
2125 +Arguments:
2126 +  s         The log line buffer
2127 +  sizep     Pointer to the buffer size
2128 +  ptrp      Pointer to current index into buffer
2129 +  addr      The address to be logged
2130 +
2131 +Returns:    New value for s
2132 +*/
2133  
2134  static uschar *
2135 -d_hostlog(uschar * s, int * sizep, int * ptrp, address_item * addr)
2136 +d_log_interface(uschar *s, int *sizep, int *ptrp)
2137  {
2138 -  s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name,
2139 -    US" [", addr->host_used->address, US"]");
2140 +if (LOGGING(incoming_interface) && LOGGING(outgoing_interface)
2141 +    && sending_ip_address != NULL)
2142 +  {
2143 +  s = string_append(s, sizep, ptrp, 2, US" I=[", sending_ip_address);
2144    if (LOGGING(outgoing_port))
2145 -    s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
2146 -      addr->host_used->port));
2147 -  return s;
2148 +    s = string_append(s, sizep, ptrp, 2, US"]:",
2149 +      string_sprintf("%d", sending_port));
2150 +  else
2151 +    s = string_cat(s, sizep, ptrp, "]", 1);
2152 +  }
2153 +return s;
2154  }
2155  
2156 +
2157 +
2158 +static uschar *
2159 +d_hostlog(uschar *s, int *sizep, int *ptrp, address_item *addr)
2160 +{
2161 +s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name,
2162 +  US" [", addr->host_used->address, US"]");
2163 +if (LOGGING(outgoing_port))
2164 +  s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
2165 +    addr->host_used->port));
2166 +return d_log_interface(s, sizep, ptrp);
2167 +}
2168 +
2169 +
2170 +
2171  #ifdef SUPPORT_TLS
2172  static uschar *
2173  d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr)
2174  {
2175 -  if (LOGGING(tls_cipher) && addr->cipher != NULL)
2176 -    s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher);
2177 -  if (LOGGING(tls_certificate_verified) && addr->cipher != NULL)
2178 -    s = string_append(s, sizep, ptrp, 2, US" CV=",
2179 -      testflag(addr, af_cert_verified)
2180 -      ?
2181 +if (LOGGING(tls_cipher) && addr->cipher != NULL)
2182 +  s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher);
2183 +if (LOGGING(tls_certificate_verified) && addr->cipher != NULL)
2184 +  s = string_append(s, sizep, ptrp, 2, US" CV=",
2185 +    testflag(addr, af_cert_verified)
2186 +    ?
2187  #ifdef EXPERIMENTAL_DANE
2188 -        testflag(addr, af_dane_verified)
2189 -      ? "dane"
2190 -      :
2191 +      testflag(addr, af_dane_verified)
2192 +    ? "dane"
2193 +    :
2194  #endif
2195 -        "yes"
2196 -      : "no");
2197 -  if (LOGGING(tls_peerdn) && addr->peerdn != NULL)
2198 -    s = string_append(s, sizep, ptrp, 3, US" DN=\"",
2199 -      string_printing(addr->peerdn), US"\"");
2200 -  return s;
2201 +      "yes"
2202 +    : "no");
2203 +if (LOGGING(tls_peerdn) && addr->peerdn != NULL)
2204 +  s = string_append(s, sizep, ptrp, 3, US" DN=\"",
2205 +    string_printing(addr->peerdn), US"\"");
2206 +return s;
2207  }
2208  #endif
2209  
2210 @@ -816,10 +855,6 @@ else
2211    s = string_append(s, &size, &ptr, 2, US"> ", log_address);
2212    }
2213  
2214 -if (LOGGING(incoming_interface) && sending_ip_address)
2215 -  s = string_append(s, &size, &ptr, 3, US" I=[", sending_ip_address, US"]");
2216 -  /* for the port:  string_sprintf("%d", sending_port) */
2217 -
2218  if (LOGGING(sender_on_delivery) || msg)
2219    s = string_append(s, &size, &ptr, 3, US" F=<",
2220  #ifdef EXPERIMENTAL_INTERNATIONAL
2221 @@ -862,6 +897,7 @@ if (addr->transport->info->local)
2222    {
2223    if (addr->host_list)
2224      s = string_append(s, &size, &ptr, 2, US" H=", addr->host_list->name);
2225 +  s = d_log_interface(s, &size, &ptr);
2226    if (addr->shadow_message != NULL)
2227      s = string_cat(s, &size, &ptr, addr->shadow_message,
2228        Ustrlen(addr->shadow_message));
2229 diff --git a/src/src/globals.c b/src/src/globals.c
2230 index 1344b5a..4188b4d 100644
2231 --- a/src/src/globals.c
2232 +++ b/src/src/globals.c
2233 @@ -827,6 +827,7 @@ int     log_default[]          = { /* for initializing log_selector */
2234    Li_etrn,
2235    Li_host_lookup_failed,
2236    Li_lost_incoming_connection,
2237 +  Li_outgoing_interface, /* see d_log_interface in deliver.c */
2238    Li_queue_run,
2239    Li_rejected_header,
2240    Li_retry_defer,
2241 @@ -863,6 +864,7 @@ bit_table log_options[]        = { /* must be in alphabetical order */
2242    BIT_TABLE(L, incoming_interface),
2243    BIT_TABLE(L, incoming_port),
2244    BIT_TABLE(L, lost_incoming_connection),
2245 +  BIT_TABLE(L, outgoing_interface),
2246    BIT_TABLE(L, outgoing_port),
2247    BIT_TABLE(L, pid),
2248  #ifdef EXPERIMENTAL_PROXY
2249 diff --git a/src/src/log.c b/src/src/log.c
2250 index b2d1fcf..558c000 100644
2251 --- a/src/src/log.c
2252 +++ b/src/src/log.c
2253 @@ -753,8 +753,8 @@ DEBUG(D_any|D_v)
2254  
2255    for (i = 0; i < log_options_count; i++)
2256      {
2257 -    unsigned int bit = log_options[i].bit;
2258 -    if (bit < BITWORDSIZE && selector == BIT(bit))
2259 +    unsigned int bitnum = log_options[i].bit;
2260 +    if (bitnum < BITWORDSIZE && selector == BIT(bitnum))
2261        {
2262        *ptr++ = ' ';
2263        Ustrcpy(ptr, log_options[i].name);
2264 diff --git a/src/src/macros.h b/src/src/macros.h
2265 index d63025e..0ce24f8 100644
2266 --- a/src/src/macros.h
2267 +++ b/src/src/macros.h
2268 @@ -474,8 +474,9 @@ enum {
2269    Li_8bitmime,
2270    Li_smtp_mailauth,
2271    Li_proxy,
2272 +  Li_outgoing_interface,
2273  
2274 -  log_selector_size = BITWORD(Li_proxy) + 1
2275 +  log_selector_size = BITWORD(Li_outgoing_interface) + 1
2276  };
2277  
2278  #define LOGGING(opt) BIT_TEST(log_selector, log_selector_size, Li_##opt)
2279
2280 commit 6b51df8340eacc95e3def9a4376506610e91996c
2281 Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
2282 Date:   Wed Aug 19 15:22:41 2015 +0200
2283
2284     Fix post-transport-crash.
2285     
2286     The crash probably was introduced in a39bd74d3e94 and
2287     needs 'split_spool_directory=yes' to expose.
2288     
2289     Thanks to Wolfgang Breyha, who found the same fix.
2290
2291 diff --git a/src/src/transport.c b/src/src/transport.c
2292 index fa6f869..a6ad3ed 100644
2293 --- a/src/src/transport.c
2294 +++ b/src/src/transport.c
2295 @@ -1752,7 +1752,7 @@ while (1)
2296      {
2297      if (split_spool_directory)
2298         sprintf(CS spool_file, "%s%c/%s-D",
2299 -                     spool_dir, new_message_id[5], msgq[i].message_id);
2300 +                     spool_dir, msgq[i].message_id[5], msgq[i].message_id);
2301      else
2302         sprintf(CS spool_file, "%s%s-D", spool_dir, msgq[i].message_id);
2303  
2304
2305 commit b20b82a0b4169cb23380a373ed2a898b0cb337d2
2306 Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
2307 Date:   Fri Aug 21 12:26:50 2015 +0200
2308
2309     Add a .ctags file to src
2310
2311 diff --git a/src/.ctags b/src/.ctags
2312 new file mode 100644
2313 index 0000000..c764086
2314 --- /dev/null
2315 +++ b/src/.ctags
2316 @@ -0,0 +1,2 @@
2317 +--recurse
2318 +--exclude=build-*
2319
2320 commit dadff1d47e54962b0fdf98e8ce5cef42b6cb7fb5
2321 Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
2322 Date:   Thu Aug 20 13:58:06 2015 +0200
2323
2324     Fix post-transport-crash: safeguard for missing spool BUG 1671
2325     
2326     Based on a proposal from Wolfgang Breyha.
2327
2328 diff --git a/src/src/deliver.c b/src/src/deliver.c
2329 index 0e7cea3..b5aa9b9 100644
2330 --- a/src/src/deliver.c
2331 +++ b/src/src/deliver.c
2332 @@ -9,6 +9,7 @@
2333  
2334  
2335  #include "exim.h"
2336 +#include <assert.h>
2337  
2338  
2339  /* Data block for keeping track of subprocesses for parallel remote
2340 @@ -7934,17 +7935,36 @@ if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
2341  uschar *
2342  deliver_get_sender_address (uschar * id)
2343  {
2344 +int rc;
2345 +uschar * new_sender_address,
2346 +       * save_sender_address;
2347 +
2348  if (!spool_open_datafile(id))
2349    return NULL;
2350  
2351 +/* Save and restore the global sender_address.  I'm not sure if we should
2352 +not save/restore all the other global variables too, because
2353 +spool_read_header() may change all of them. But OTOH, when this
2354 +deliver_get_sender_address() gets called, the current message is done
2355 +already and nobody needs the globals anymore. (HS12, 2015-08-21) */
2356 +
2357  sprintf(CS spoolname, "%s-H", id);
2358 -if (spool_read_header(spoolname, TRUE, TRUE) != spool_read_OK)
2359 +save_sender_address = sender_address;
2360 +
2361 +rc = spool_read_header(spoolname, TRUE, TRUE);
2362 +
2363 +new_sender_address = sender_address;
2364 +sender_address = save_sender_address;
2365 +
2366 +if (rc != spool_read_OK)
2367    return NULL;
2368  
2369 +assert(new_sender_address);
2370 +
2371  (void)close(deliver_datafile);
2372  deliver_datafile = -1;
2373  
2374 -return sender_address;
2375 +return new_sender_address;
2376  }
2377  
2378  /* vi: aw ai sw=2
2379 diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
2380 index 609dba3..c93f2ef 100644
2381 --- a/src/src/transports/smtp.c
2382 +++ b/src/src/transports/smtp.c
2383 @@ -1274,14 +1274,19 @@ we will veto this new message.  */
2384  static BOOL
2385  smtp_are_same_identities(uschar * message_id, smtp_compare_t * s_compare)
2386  {
2387 -uschar * save_sender_address = sender_address;
2388 -uschar * current_local_identity =
2389 +
2390 +uschar * message_local_identity,
2391 +       * current_local_identity,
2392 +       * new_sender_address;
2393 +
2394 +current_local_identity =
2395    smtp_local_identity(s_compare->current_sender_address, s_compare->tblock);
2396 -uschar * new_sender_address = deliver_get_sender_address(message_id);
2397 -uschar * message_local_identity =
2398 -  smtp_local_identity(new_sender_address, s_compare->tblock);
2399  
2400 -sender_address = save_sender_address;
2401 +if (!(new_sender_address = deliver_get_sender_address(message_id)))
2402 +    return 0;
2403 +
2404 +message_local_identity =
2405 +  smtp_local_identity(new_sender_address, s_compare->tblock);
2406  
2407  return Ustrcmp(current_local_identity, message_local_identity) == 0;
2408  }
2409
2410 commit 3703d8187af01d13ca71f7918c7ef78529bb784d
2411 Author: Jeremy Harris <jgh146exb@wizmail.org>
2412 Date:   Fri Aug 21 18:08:39 2015 +0100
2413
2414     Remember the fail reason for verify=headers_syntax.  Bug 264
2415
2416 diff --git a/src/src/acl.c b/src/src/acl.c
2417 index f2e0ef2..064ee6c 100644
2418 --- a/src/src/acl.c
2419 +++ b/src/src/acl.c
2420 @@ -1803,27 +1803,27 @@ switch(vp->value)
2421      test whether it was successful or not. (This is for optional verification; for
2422      mandatory verification, the connection doesn't last this long.) */
2423  
2424 -      if (tls_in.certificate_verified) return OK;
2425 -      *user_msgptr = US"no verified certificate";
2426 -      return FAIL;
2427 +    if (tls_in.certificate_verified) return OK;
2428 +    *user_msgptr = US"no verified certificate";
2429 +    return FAIL;
2430  
2431    case VERIFY_HELO:
2432      /* We can test the result of optional HELO verification that might have
2433      occurred earlier. If not, we can attempt the verification now. */
2434  
2435 -      if (!helo_verified && !helo_verify_failed) smtp_verify_helo();
2436 -      return helo_verified? OK : FAIL;
2437 +    if (!helo_verified && !helo_verify_failed) smtp_verify_helo();
2438 +    return helo_verified? OK : FAIL;
2439  
2440    case VERIFY_CSA:
2441      /* Do Client SMTP Authorization checks in a separate function, and turn the
2442      result code into user-friendly strings. */
2443  
2444 -      rc = acl_verify_csa(list);
2445 -      *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s",
2446 +    rc = acl_verify_csa(list);
2447 +    *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s",
2448                                                csa_reason_string[rc]);
2449 -      csa_status = csa_status_string[rc];
2450 -      DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status);
2451 -      return csa_return_code[rc];
2452 +    csa_status = csa_status_string[rc];
2453 +    DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status);
2454 +    return csa_return_code[rc];
2455  
2456    case VERIFY_HDR_SYNTAX:
2457      /* Check that all relevant header lines have the correct syntax. If there is
2458 @@ -1832,8 +1832,11 @@ switch(vp->value)
2459      always). */
2460  
2461      rc = verify_check_headers(log_msgptr);
2462 -    if (rc != OK && smtp_return_error_details && *log_msgptr != NULL)
2463 -      *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
2464 +    if (rc != OK && *log_msgptr)
2465 +      if (smtp_return_error_details)
2466 +       *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
2467 +      else
2468 +       acl_verify_message = *log_msgptr;
2469      return rc;
2470  
2471    case VERIFY_HDR_NAMES_ASCII:
2472 @@ -3788,7 +3791,8 @@ for (; cb != NULL; cb = cb->next)
2473  
2474      case ACLC_VERIFY:
2475      rc = acl_verify(where, addr, arg, user_msgptr, log_msgptr, basic_errno);
2476 -    acl_verify_message = *user_msgptr;
2477 +    if (*user_msgptr)
2478 +      acl_verify_message = *user_msgptr;
2479      if (verb == ACL_WARN) *user_msgptr = NULL;
2480      break;
2481  
2482
2483 commit c8899c20aa08c9ae6a4c291aad23ba90512bebe4
2484 Author: Jeremy Harris <jgh146exb@wizmail.org>
2485 Date:   Tue Aug 25 10:36:27 2015 +0100
2486
2487     Close logs after daemon-process exceptional write.  Bug 728
2488
2489 diff --git a/src/src/daemon.c b/src/src/daemon.c
2490 index 2d10387..e1ff9a1 100644
2491 --- a/src/src/daemon.c
2492 +++ b/src/src/daemon.c
2493 @@ -735,6 +735,7 @@ else (void)close(dup_accept_socket);
2494  /* Release any store used in this process, including the store used for holding
2495  the incoming host address and an expanded active_hostname. */
2496  
2497 +log_close_all();
2498  store_reset(reset_point);
2499  sender_host_address = NULL;
2500  }
2501
2502 commit f38917cc94ab337c15ff70c254dd564ee2dcafe7
2503 Author: Jeremy Harris <jgh146exb@wizmail.org>
2504 Date:   Tue Sep 8 23:05:20 2015 +0100
2505
2506         Capture substrings in ACL regex= .  Bug 425.
2507
2508 diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults
2509 index c33e098..596e651 100644
2510 --- a/src/src/config.h.defaults
2511 +++ b/src/src/config.h.defaults
2512 @@ -116,6 +116,8 @@ it's a default value. */
2513  #define RADIUS_CONFIG_FILE
2514  #define RADIUS_LIB_TYPE
2515  
2516 +#define REGEX_VARS                 9
2517 +
2518  #define ROUTER_ACCEPT
2519  #define ROUTER_DNSLOOKUP
2520  #define ROUTER_IPLITERAL
2521 diff --git a/src/src/exim.c b/src/src/exim.c
2522 index d7cb5d8..999b94c 100644
2523 --- a/src/src/exim.c
2524 +++ b/src/src/exim.c
2525 @@ -1753,6 +1753,8 @@ regex_whitelisted_macro =
2526    regex_must_compile(US"^[A-Za-z0-9_/.-]*$", FALSE, TRUE);
2527  #endif
2528  
2529 +for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL;
2530 +
2531  
2532  /* If the program is called as "mailq" treat it as equivalent to "exim -bp";
2533  this seems to be a generally accepted convention, since one finds symbolic
2534 diff --git a/src/src/expand.c b/src/src/expand.c
2535 index 89e0ac7..1bff521 100644
2536 --- a/src/src/expand.c
2537 +++ b/src/src/expand.c
2538 @@ -1726,7 +1726,14 @@ if (Ustrncmp(name, "auth", 4) == 0)
2539    uschar *endptr;
2540    int n = Ustrtoul(name + 4, &endptr, 10);
2541    if (*endptr == 0 && n != 0 && n <= AUTH_VARS)
2542 -    return (auth_vars[n-1] == NULL)? US"" : auth_vars[n-1];
2543 +    return !auth_vars[n-1] ? US"" : auth_vars[n-1];
2544 +  }
2545 +else if (Ustrncmp(name, "regex", 5) == 0)
2546 +  {
2547 +  uschar *endptr;
2548 +  int n = Ustrtoul(name + 5, &endptr, 10);
2549 +  if (*endptr == 0 && n != 0 && n <= REGEX_VARS)
2550 +    return !regex_vars[n-1] ? US"" : regex_vars[n-1];
2551    }
2552  
2553  /* For all other variables, search the table */
2554 diff --git a/src/src/globals.c b/src/src/globals.c
2555 index 4188b4d..8445f00 100644
2556 --- a/src/src/globals.c
2557 +++ b/src/src/globals.c
2558 @@ -1090,8 +1090,9 @@ const pcre *regex_From         = NULL;
2559  const pcre *regex_IGNOREQUOTA  = NULL;
2560  const pcre *regex_PIPELINING   = NULL;
2561  const pcre *regex_SIZE         = NULL;
2562 -const pcre *regex_smtp_code    = NULL;
2563  const pcre *regex_ismsgid      = NULL;
2564 +const pcre *regex_smtp_code    = NULL;
2565 +uschar *regex_vars[REGEX_VARS];
2566  #ifdef WHITELIST_D_MACROS
2567  const pcre *regex_whitelisted_macro = NULL;
2568  #endif
2569 diff --git a/src/src/globals.h b/src/src/globals.h
2570 index 978a4cc..3c69e43 100644
2571 --- a/src/src/globals.h
2572 +++ b/src/src/globals.h
2573 @@ -717,8 +717,9 @@ extern const pcre  *regex_From;        /* For recognizing "From_" lines */
2574  extern const pcre  *regex_IGNOREQUOTA; /* For recognizing IGNOREQUOTA (LMTP) */
2575  extern const pcre  *regex_PIPELINING;  /* For recognizing PIPELINING */
2576  extern const pcre  *regex_SIZE;        /* For recognizing SIZE settings */
2577 -extern const pcre  *regex_smtp_code;   /* For recognizing SMTP codes */
2578  extern const pcre  *regex_ismsgid;     /* Compiled r.e. for message it */
2579 +extern const pcre  *regex_smtp_code;   /* For recognizing SMTP codes */
2580 +extern uschar *regex_vars[];           /* $regexN variables */
2581  #ifdef WHITELIST_D_MACROS
2582  extern const pcre  *regex_whitelisted_macro; /* For -D macro values */
2583  #endif
2584 diff --git a/src/src/regex.c b/src/src/regex.c
2585 index ed73b6e..93422fa 100644
2586 --- a/src/src/regex.c
2587 +++ b/src/src/regex.c
2588 @@ -25,109 +25,120 @@ uschar regex_match_string_buffer[1024];
2589  extern FILE *mime_stream;
2590  extern uschar *mime_current_boundary;
2591  
2592 -int
2593 -regex(const uschar **listptr)
2594 +static pcre_list *
2595 +compile(const uschar * list)
2596  {
2597    int sep = 0;
2598 -  const uschar *list = *listptr;
2599    uschar *regex_string;
2600    uschar regex_string_buffer[1024];
2601 -  unsigned long mbox_size;
2602 -  FILE *mbox_file;
2603 -  pcre *re;
2604 -  pcre_list *re_list_head = NULL;
2605 -  pcre_list *re_list_item;
2606    const char *pcre_error;
2607    int pcre_erroffset;
2608 +  pcre_list *re_list_head = NULL;
2609 +  pcre_list *ri;
2610 +
2611 +  /* precompile our regexes */
2612 +  while ((regex_string = string_nextinlist(&list, &sep,
2613 +                                           regex_string_buffer,
2614 +                                           sizeof(regex_string_buffer))) != NULL) {
2615 +    pcre *re;
2616 +
2617 +    /* parse option */
2618 +    if ( (strcmpic(regex_string,US"false") == 0) ||
2619 +         (Ustrcmp(regex_string,"0") == 0) )
2620 +      continue;                                /* explicitly no matching */
2621 +
2622 +    /* compile our regular expression */
2623 +    if (!(re = pcre_compile( CS regex_string,
2624 +                       0, &pcre_error, &pcre_erroffset, NULL ))) {
2625 +      log_write(0, LOG_MAIN,
2626 +           "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.",
2627 +          regex_string, pcre_error, pcre_erroffset);
2628 +      continue;
2629 +    }
2630 +
2631 +    ri = store_get(sizeof(pcre_list));
2632 +    ri->re = re;
2633 +    ri->pcre_text = string_copy(regex_string);
2634 +    ri->next = re_list_head;
2635 +    re_list_head = ri;
2636 +  }
2637 +  return re_list_head;
2638 +}
2639 +
2640 +static int
2641 +matcher(pcre_list * re_list_head, uschar * linebuffer, int len)
2642 +{
2643 +  pcre_list * ri;
2644 +
2645 +  for(ri = re_list_head; ri; ri = ri->next)
2646 +    {
2647 +    int ovec[3*(REGEX_VARS+1)];
2648 +    int n, nn;
2649 +
2650 +    /* try matcher on the line */
2651 +    n = pcre_exec(ri->re, NULL,
2652 +        CS linebuffer, len, 0, 0,
2653 +        ovec, nelem(ovec));
2654 +    if (n > 0)
2655 +      {
2656 +      Ustrncpy(regex_match_string_buffer, ri->pcre_text, 1023);
2657 +      regex_match_string = regex_match_string_buffer;
2658 +
2659 +      for (nn = 1; nn < n; nn++)
2660 +       regex_vars[nn-1] =
2661 +         string_copyn(linebuffer + ovec[nn*2], ovec[nn*2+1] - ovec[nn*2]);
2662 +
2663 +      return OK;
2664 +      }
2665 +    }
2666 +  return FAIL;
2667 +}
2668 +
2669 +int
2670 +regex(const uschar **listptr)
2671 +{
2672 +  unsigned long mbox_size;
2673 +  FILE *mbox_file;
2674 +  pcre_list *re_list_head;
2675    uschar *linebuffer;
2676    long f_pos = 0;
2677 +  int ret = FAIL;
2678  
2679    /* reset expansion variable */
2680    regex_match_string = NULL;
2681  
2682 -  if (mime_stream == NULL) {
2683 -    /* We are in the DATA ACL */
2684 +  if (mime_stream == NULL) {                   /* We are in the DATA ACL */
2685      mbox_file = spool_mbox(&mbox_size, NULL);
2686 -    if (mbox_file == NULL) {
2687 -      /* error while spooling */
2688 +    if (mbox_file == NULL) {                   /* error while spooling */
2689        log_write(0, LOG_MAIN|LOG_PANIC,
2690               "regex acl condition: error while creating mbox spool file");
2691        return DEFER;
2692 -    };
2693 +    }
2694    }
2695    else {
2696      f_pos = ftell(mime_stream);
2697      mbox_file = mime_stream;
2698 -  };
2699 +  }
2700  
2701    /* precompile our regexes */
2702 -  while ((regex_string = string_nextinlist(&list, &sep,
2703 -                                           regex_string_buffer,
2704 -                                           sizeof(regex_string_buffer))) != NULL) {
2705 -
2706 -    /* parse option */
2707 -    if ( (strcmpic(regex_string,US"false") == 0) ||
2708 -         (Ustrcmp(regex_string,"0") == 0) ) {
2709 -      /* explicitly no matching */
2710 -      continue;
2711 -    };
2712 -
2713 -    /* compile our regular expression */
2714 -    re = pcre_compile( CS regex_string,
2715 -                       0,
2716 -                       &pcre_error,
2717 -                       &pcre_erroffset,
2718 -                       NULL );
2719 -
2720 -    if (re == NULL) {
2721 -      log_write(0, LOG_MAIN,
2722 -           "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset);
2723 -      continue;
2724 -    }
2725 -    else {
2726 -      re_list_item = store_get(sizeof(pcre_list));
2727 -      re_list_item->re = re;
2728 -      re_list_item->pcre_text = string_copy(regex_string);
2729 -      re_list_item->next = re_list_head;
2730 -      re_list_head = re_list_item;
2731 -    };
2732 -  };
2733 -
2734 -  /* no regexes -> nothing to do */
2735 -  if (re_list_head == NULL) {
2736 -    return FAIL;
2737 -  };
2738 +  if (!(re_list_head = compile(*listptr)))
2739 +    return FAIL;                       /* no regexes -> nothing to do */
2740  
2741    /* match each line against all regexes */
2742    linebuffer = store_get(32767);
2743    while (fgets(CS linebuffer, 32767, mbox_file) != NULL) {
2744 -    if ( (mime_stream != NULL) && (mime_current_boundary != NULL) ) {
2745 -      /* check boundary */
2746 -      if (Ustrncmp(linebuffer,"--",2) == 0) {
2747 -        if (Ustrncmp((linebuffer+2),mime_current_boundary,Ustrlen(mime_current_boundary)) == 0)
2748 -          /* found boundary */
2749 -          break;
2750 -      };
2751 -    };
2752 -    re_list_item = re_list_head;
2753 -    do {
2754 -      /* try matcher on the line */
2755 -      if (pcre_exec(re_list_item->re, NULL, CS linebuffer,
2756 -      (int)Ustrlen(linebuffer), 0, 0, NULL, 0) >= 0) {
2757 -        Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023);
2758 -        regex_match_string = regex_match_string_buffer;
2759 -        if (mime_stream == NULL)
2760 -          (void)fclose(mbox_file);
2761 -        else {
2762 -          clearerr(mime_stream);
2763 -          fseek(mime_stream,f_pos,SEEK_SET);
2764 -        };
2765 -        return OK;
2766 -      };
2767 -      re_list_item = re_list_item->next;
2768 -    } while (re_list_item != NULL);
2769 -  };
2770  
2771 +    if (  mime_stream && mime_current_boundary         /* check boundary */
2772 +       && Ustrncmp(linebuffer,"--",2) == 0
2773 +       && Ustrncmp((linebuffer+2),mime_current_boundary,Ustrlen(mime_current_boundary)) == 0)
2774 +       break;                                          /* found boundary */
2775 +
2776 +    if ((ret = matcher(re_list_head, linebuffer, (int)Ustrlen(linebuffer))) == OK)
2777 +      goto done;
2778 +  }
2779 +  /* no matches ... */
2780 +
2781 +done:
2782    if (mime_stream == NULL)
2783      (void)fclose(mbox_file);
2784    else {
2785 @@ -135,67 +146,25 @@ regex(const uschar **listptr)
2786      fseek(mime_stream,f_pos,SEEK_SET);
2787    };
2788  
2789 -  /* no matches ... */
2790 -  return FAIL;
2791 +  return ret;
2792  }
2793  
2794  
2795  int
2796  mime_regex(const uschar **listptr)
2797  {
2798 -  int sep = 0;
2799 -  const uschar *list = *listptr;
2800 -  uschar *regex_string;
2801 -  uschar regex_string_buffer[1024];
2802 -  pcre *re;
2803    pcre_list *re_list_head = NULL;
2804 -  pcre_list *re_list_item;
2805 -  const char *pcre_error;
2806 -  int pcre_erroffset;
2807    FILE *f;
2808    uschar *mime_subject = NULL;
2809    int mime_subject_len = 0;
2810 +  int ret;
2811  
2812    /* reset expansion variable */
2813    regex_match_string = NULL;
2814  
2815    /* precompile our regexes */
2816 -  while ((regex_string = string_nextinlist(&list, &sep,
2817 -                                           regex_string_buffer,
2818 -                                           sizeof(regex_string_buffer))) != NULL) {
2819 -
2820 -    /* parse option */
2821 -    if ( (strcmpic(regex_string,US"false") == 0) ||
2822 -         (Ustrcmp(regex_string,"0") == 0) ) {
2823 -      /* explicitly no matching */
2824 -      continue;
2825 -    };
2826 -
2827 -    /* compile our regular expression */
2828 -    re = pcre_compile( CS regex_string,
2829 -                       0,
2830 -                       &pcre_error,
2831 -                       &pcre_erroffset,
2832 -                       NULL );
2833 -
2834 -    if (re == NULL) {
2835 -      log_write(0, LOG_MAIN,
2836 -           "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset);
2837 -      continue;
2838 -    }
2839 -    else {
2840 -      re_list_item = store_get(sizeof(pcre_list));
2841 -      re_list_item->re = re;
2842 -      re_list_item->pcre_text = string_copy(regex_string);
2843 -      re_list_item->next = re_list_head;
2844 -      re_list_head = re_list_item;
2845 -    };
2846 -  };
2847 -
2848 -  /* no regexes -> nothing to do */
2849 -  if (re_list_head == NULL) {
2850 -    return FAIL;
2851 -  };
2852 +  if (!(re_list_head = compile(*listptr)))
2853 +    return FAIL;                       /* no regexes -> nothing to do */
2854  
2855    /* check if the file is already decoded */
2856    if (mime_decoded_filename == NULL) {
2857 @@ -207,43 +176,25 @@ mime_regex(const uschar **listptr)
2858        log_write(0, LOG_MAIN,
2859             "mime_regex acl condition warning - could not decode MIME part to file.");
2860        return DEFER;
2861 -    };
2862 -  };
2863 -
2864 +    }
2865 +  }
2866  
2867    /* open file */
2868 -  f = fopen(CS mime_decoded_filename, "rb");
2869 -  if (f == NULL) {
2870 -    /* open failed */
2871 +  if (!(f = fopen(CS mime_decoded_filename, "rb"))) {
2872      log_write(0, LOG_MAIN,
2873 -         "mime_regex acl condition warning - can't open '%s' for reading.", mime_decoded_filename);
2874 +         "mime_regex acl condition warning - can't open '%s' for reading.",
2875 +        mime_decoded_filename);
2876      return DEFER;
2877 -  };
2878 +  }
2879  
2880    /* get 32k memory */
2881    mime_subject = (uschar *)store_get(32767);
2882  
2883 -  /* read max 32k chars from file */
2884    mime_subject_len = fread(mime_subject, 1, 32766, f);
2885  
2886 -  re_list_item = re_list_head;
2887 -  do {
2888 -    /* try matcher on the mmapped file */
2889 -    debug_printf("Matching '%s'\n", re_list_item->pcre_text);
2890 -    if (pcre_exec(re_list_item->re, NULL, CS mime_subject,
2891 -                  mime_subject_len, 0, 0, NULL, 0) >= 0) {
2892 -      Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023);
2893 -      regex_match_string = regex_match_string_buffer;
2894 -      (void)fclose(f);
2895 -      return OK;
2896 -    };
2897 -    re_list_item = re_list_item->next;
2898 -  } while (re_list_item != NULL);
2899 -
2900 +  ret = matcher(re_list_head, mime_subject, mime_subject_len);
2901    (void)fclose(f);
2902 -
2903 -  /* no matches ... */
2904 -  return FAIL;
2905 +  return ret;
2906  }
2907  
2908  #endif /* WITH_CONTENT_SCAN */
2909
2910 commit 895fbaf26d3450d4eeacbad8fe04c328a77645f0
2911 Author: Jeremy Harris <jgh146exb@wizmail.org>
2912 Date:   Wed Sep 9 16:03:38 2015 +0100
2913
2914     DSN: Under EXPERIMENTAL_DSN_INFO add extras to bounce messages.  Bug 1686
2915
2916 diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults
2917 index 596e651..6af3b4d 100644
2918 --- a/src/src/config.h.defaults
2919 +++ b/src/src/config.h.defaults
2920 @@ -172,6 +172,7 @@ it's a default value. */
2921  #define EXPERIMENTAL_BRIGHTMAIL
2922  #define EXPERIMENTAL_DANE
2923  #define EXPERIMENTAL_DCC
2924 +#define EXPERIMENTAL_DSN_INFO
2925  #define EXPERIMENTAL_DMARC
2926  #define EXPERIMENTAL_EVENT
2927  #define EXPERIMENTAL_INTERNATIONAL
2928 diff --git a/src/src/deliver.c b/src/src/deliver.c
2929 index b5aa9b9..3f22dc9 100644
2930 --- a/src/src/deliver.c
2931 +++ b/src/src/deliver.c
2932 @@ -3223,41 +3223,56 @@ while (!done)
2933        break;
2934        }
2935  
2936 -    addr->transport_return = *ptr++;
2937 -    addr->special_action = *ptr++;
2938 -    memcpy(&(addr->basic_errno), ptr, sizeof(addr->basic_errno));
2939 -    ptr += sizeof(addr->basic_errno);
2940 -    memcpy(&(addr->more_errno), ptr, sizeof(addr->more_errno));
2941 -    ptr += sizeof(addr->more_errno);
2942 -    memcpy(&(addr->flags), ptr, sizeof(addr->flags));
2943 -    ptr += sizeof(addr->flags);
2944 -    addr->message = (*ptr)? string_copy(ptr) : NULL;
2945 -    while(*ptr++);
2946 -    addr->user_message = (*ptr)? string_copy(ptr) : NULL;
2947 -    while(*ptr++);
2948 -
2949 -    /* Always two strings for host information, followed by the port number and DNSSEC mark */
2950 -
2951 -    if (*ptr != 0)
2952 +    switch (subid)
2953        {
2954 -      h = store_get(sizeof(host_item));
2955 -      h->name = string_copy(ptr);
2956 -      while (*ptr++);
2957 -      h->address = string_copy(ptr);
2958 -      while(*ptr++);
2959 -      memcpy(&(h->port), ptr, sizeof(h->port));
2960 -      ptr += sizeof(h->port);
2961 -      h->dnssec = *ptr == '2' ? DS_YES
2962 -               : *ptr == '1' ? DS_NO
2963 -               : DS_UNK;
2964 -      ptr++;
2965 -      addr->host_used = h;
2966 -      }
2967 -    else ptr++;
2968 +#ifdef EXPERIMENTAL_DSN_INFO
2969 +      case '1':        /* must arrive before A0, and applies to that addr */
2970 +               /* Two strings: smtp_greeting and helo_response */
2971 +       addr->smtp_greeting = string_copy(ptr);
2972 +       while(*ptr++);
2973 +       addr->helo_response = string_copy(ptr);
2974 +       while(*ptr++);
2975 +       break;
2976 +#endif
2977  
2978 -    /* Finished with this address */
2979 +      case '0':
2980 +       addr->transport_return = *ptr++;
2981 +       addr->special_action = *ptr++;
2982 +       memcpy(&(addr->basic_errno), ptr, sizeof(addr->basic_errno));
2983 +       ptr += sizeof(addr->basic_errno);
2984 +       memcpy(&(addr->more_errno), ptr, sizeof(addr->more_errno));
2985 +       ptr += sizeof(addr->more_errno);
2986 +       memcpy(&(addr->flags), ptr, sizeof(addr->flags));
2987 +       ptr += sizeof(addr->flags);
2988 +       addr->message = (*ptr)? string_copy(ptr) : NULL;
2989 +       while(*ptr++);
2990 +       addr->user_message = (*ptr)? string_copy(ptr) : NULL;
2991 +       while(*ptr++);
2992  
2993 -    addr = addr->next;
2994 +       /* Always two strings for host information, followed by the port number and DNSSEC mark */
2995 +
2996 +       if (*ptr != 0)
2997 +         {
2998 +         h = store_get(sizeof(host_item));
2999 +         h->name = string_copy(ptr);
3000 +         while (*ptr++);
3001 +         h->address = string_copy(ptr);
3002 +         while(*ptr++);
3003 +         memcpy(&(h->port), ptr, sizeof(h->port));
3004 +         ptr += sizeof(h->port);
3005 +         h->dnssec = *ptr == '2' ? DS_YES
3006 +                   : *ptr == '1' ? DS_NO
3007 +                   : DS_UNK;
3008 +         ptr++;
3009 +         addr->host_used = h;
3010 +         }
3011 +       else ptr++;
3012 +
3013 +       /* Finished with this address */
3014 +
3015 +       addr = addr->next;
3016 +       break;
3017 +      }
3018      break;
3019  
3020      /* Local interface address/port */
3021 @@ -4423,7 +4438,6 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
3022  
3023        for (r = addr->retries; r != NULL; r = r->next)
3024          {
3025 -        uschar *ptr;
3026          sprintf(CS big_buffer, "%c%.500s", r->flags, r->key);
3027          ptr = big_buffer + Ustrlen(big_buffer+2) + 3;
3028          memcpy(ptr, &(r->basic_errno), sizeof(r->basic_errno));
3029 @@ -4438,11 +4452,31 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
3030          rmt_dlv_checked_write(fd, 'R', '0', big_buffer, ptr - big_buffer);
3031          }
3032  
3033 -      /* The rest of the information goes in an 'A' item. */
3034 +#ifdef EXPERIMENTAL_DSN_INFO
3035 +/*um, are they really per-addr?  Other per-conn stuff is not (auth, tls).  But host_used is! */
3036 +      if (addr->smtp_greeting)
3037 +       {
3038 +       ptr = big_buffer;
3039 +       DEBUG(D_deliver) debug_printf("smtp_greeting '%s'\n", addr->smtp_greeting);
3040 +        sprintf(CS ptr, "%.128s", addr->smtp_greeting);
3041 +        while(*ptr++);
3042 +       if (addr->helo_response)
3043 +         {
3044 +         DEBUG(D_deliver) debug_printf("helo_response '%s'\n", addr->helo_response);
3045 +         sprintf(CS ptr, "%.128s", addr->helo_response);
3046 +         while(*ptr++);
3047 +         }
3048 +       else
3049 +         *ptr++ = '\0';
3050 +        rmt_dlv_checked_write(fd, 'A', '1', big_buffer, ptr - big_buffer);
3051 +       }
3052 +#endif
3053 +
3054 +      /* The rest of the information goes in an 'A0' item. */
3055  
3056 -      ptr = big_buffer + 2;
3057        sprintf(CS big_buffer, "%c%c", addr->transport_return,
3058          addr->special_action);
3059 +      ptr = big_buffer + 2;
3060        memcpy(ptr, &(addr->basic_errno), sizeof(addr->basic_errno));
3061        ptr += sizeof(addr->basic_errno);
3062        memcpy(ptr, &(addr->more_errno), sizeof(addr->more_errno));
3063 @@ -4480,7 +4514,11 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
3064        }
3065  
3066      /* Local interface address/port */
3067 +#ifdef EXPERIMENTAL_DSN_INFO
3068 +    if (sending_ip_address)
3069 +#else
3070      if (LOGGING(incoming_interface) && sending_ip_address)
3071 +#endif
3072        {
3073        uschar * ptr = big_buffer;
3074        sprintf(CS ptr, "%.128s", sending_ip_address);
3075 @@ -7209,16 +7247,32 @@ wording. */
3076   
3077        for (addr = handled_addr; addr; addr = addr->next)
3078          {
3079 +       host_item * hu;
3080          fprintf(f, "Action: failed\n"
3081             "Final-Recipient: rfc822;%s\n"
3082             "Status: 5.0.0\n",
3083             addr->address);
3084 -        if (addr->host_used && addr->host_used->name)
3085 -          {
3086 -          fprintf(f, "Remote-MTA: dns; %s\n",
3087 -           addr->host_used->name);
3088 -          print_dsn_diagnostic_code(addr, f);
3089 -          }
3090 +        if ((hu = addr->host_used) && hu->name)
3091 +         {
3092 +         const uschar * s;
3093 +         fprintf(f, "Remote-MTA: dns; %s\n",
3094 +           hu->name);
3095 +#ifdef EXPERIMENTAL_DSN_INFO
3096 +         if (hu->address)
3097 +           {
3098 +           uschar * p = hu->port == 25
3099 +             ? US"" : string_sprintf(":%d", hu->port);
3100 +           fprintf(f, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p);
3101 +           }
3102 +         if ((s = addr->smtp_greeting) && *s)
3103 +           fprintf(f, "X-Remote-MTA-smtp-greeting: X-str; %s\n", s);
3104 +         if ((s = addr->helo_response) && *s)
3105 +           fprintf(f, "X-Remote-MTA-helo-response: X-str; %s\n", s);
3106 +         if ((s = addr->message) && *s)
3107 +           fprintf(f, "X-Exim-Diagnostic: X-str; %s\n", s);
3108 +#endif
3109 +         print_dsn_diagnostic_code(addr, f);
3110 +         }
3111         fputc('\n', f);
3112          }
3113  
3114 diff --git a/src/src/exim.c b/src/src/exim.c
3115 index 999b94c..084d649 100644
3116 --- a/src/src/exim.c
3117 +++ b/src/src/exim.c
3118 @@ -847,6 +847,12 @@ fprintf(f, "Support for:");
3119  #ifdef EXPERIMENTAL_DMARC
3120    fprintf(f, " Experimental_DMARC");
3121  #endif
3122 +#ifdef EXPERIMENTAL_DSN_INFO
3123 +  fprintf(f, " Experimental_DSN_info");
3124 +#endif
3125 +#ifdef EXPERIMENTAL_INTERNATIONAL
3126 +  fprintf(f, " Experimental_International");
3127 +#endif
3128  #ifdef EXPERIMENTAL_PROXY
3129    fprintf(f, " Experimental_Proxy");
3130  #endif
3131 @@ -859,9 +865,6 @@ fprintf(f, "Support for:");
3132  #ifdef EXPERIMENTAL_SOCKS
3133    fprintf(f, " Experimental_SOCKS");
3134  #endif
3135 -#ifdef EXPERIMENTAL_INTERNATIONAL
3136 -  fprintf(f, " Experimental_International");
3137 -#endif
3138  fprintf(f, "\n");
3139  
3140  fprintf(f, "Lookups (built-in):");
3141 diff --git a/src/src/globals.c b/src/src/globals.c
3142 index 8445f00..f3b6791 100644
3143 --- a/src/src/globals.c
3144 +++ b/src/src/globals.c
3145 @@ -354,13 +354,17 @@ address_item address_defaults = {
3146    NULL,                 /* return_filename */
3147    NULL,                 /* self_hostname */
3148    NULL,                 /* shadow_message */
3149 -  #ifdef SUPPORT_TLS
3150 +#ifdef SUPPORT_TLS
3151    NULL,                 /* cipher */
3152    NULL,                        /* ourcert */
3153    NULL,                        /* peercert */
3154    NULL,                 /* peerdn */
3155    OCSP_NOT_REQ,         /* ocsp */
3156 -  #endif
3157 +#endif
3158 +#ifdef EXPERIMENTAL_DSN_INFO
3159 +  NULL,                        /* smtp_greeting */
3160 +  NULL,                        /* helo_response */
3161 +#endif
3162    NULL,                        /* authenticator */
3163    NULL,                        /* auth_id */
3164    NULL,                        /* auth_sndr */
3165 diff --git a/src/src/structs.h b/src/src/structs.h
3166 index 438b521..db9e843 100644
3167 --- a/src/src/structs.h
3168 +++ b/src/src/structs.h
3169 @@ -561,13 +561,18 @@ typedef struct address_item {
3170    uschar *self_hostname;          /* after self=pass */
3171    uschar *shadow_message;         /* info about shadow transporting */
3172  
3173 -  #ifdef SUPPORT_TLS
3174 +#ifdef SUPPORT_TLS
3175    uschar *cipher;                 /* Cipher used for transport */
3176    void   *ourcert;                /* Certificate offered to peer, binary */
3177    void   *peercert;               /* Certificate from peer, binary */
3178    uschar *peerdn;                 /* DN of server's certificate */
3179    int    ocsp;                   /* OCSP status of peer cert */
3180 -  #endif
3181 +#endif
3182 +
3183 +#ifdef EXPERIMENTAL_DSN_INFO
3184 +  const uschar *smtp_greeting;   /* peer self-identification */
3185 +  const uschar *helo_response;   /* peer message */
3186 +#endif
3187  
3188    uschar *authenticator;         /* auth driver name used by transport */
3189    uschar *auth_id;               /* auth "login" name used by transport */
3190 diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
3191 index c93f2ef..ac40460 100644
3192 --- a/src/src/transports/smtp.c
3193 +++ b/src/src/transports/smtp.c
3194 @@ -440,6 +440,8 @@ Arguments:
3195    rc             to put in each address's transport_return field
3196    pass_message   if TRUE, set the "pass message" flag in the address
3197    host           if set, mark addrs as having used this host
3198 +  smtp_greeting  from peer
3199 +  helo_response  from peer
3200  
3201  If errno_value has the special value ERRNO_CONNECTTIMEOUT, ETIMEDOUT is put in
3202  the errno field, and RTEF_CTOUT is ORed into the more_errno field, to indicate
3203 @@ -450,7 +452,11 @@ Returns:       nothing
3204  
3205  static void
3206  set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc,
3207 -  BOOL pass_message, host_item * host)
3208 +  BOOL pass_message, host_item * host
3209 +#ifdef EXPERIMENTAL_DSN_INFO
3210 +  , const uschar * smtp_greeting, const uschar * helo_response
3211 +#endif
3212 +  )
3213  {
3214  address_item *addr;
3215  int orvalue = 0;
3216 @@ -459,7 +465,7 @@ if (errno_value == ERRNO_CONNECTTIMEOUT)
3217    errno_value = ETIMEDOUT;
3218    orvalue = RTEF_CTOUT;
3219    }
3220 -for (addr = addrlist; addr != NULL; addr = addr->next)
3221 +for (addr = addrlist; addr; addr = addr->next)
3222    if (addr->transport_return >= PENDING)
3223      {
3224      addr->basic_errno = errno_value;
3225 @@ -471,10 +477,31 @@ for (addr = addrlist; addr != NULL; addr = addr->next)
3226        }
3227      addr->transport_return = rc;
3228      if (host)
3229 +      {
3230        addr->host_used = host;
3231 +#ifdef EXPERIMENTAL_DSN_INFO
3232 +      if (smtp_greeting)
3233 +       {uschar * s = Ustrchr(smtp_greeting, '\n'); if (s) *s = '\0';}
3234 +      addr->smtp_greeting = smtp_greeting;
3235 +
3236 +      if (helo_response)
3237 +       {uschar * s = Ustrchr(helo_response, '\n'); if (s) *s = '\0';}
3238 +      addr->helo_response = helo_response;
3239 +#endif
3240 +      }
3241      }
3242  }
3243  
3244 +static void
3245 +set_errno_nohost(address_item *addrlist, int errno_value, uschar *msg, int rc,
3246 +  BOOL pass_message)
3247 +{
3248 +set_errno(addrlist, errno_value, msg, rc, pass_message, NULL
3249 +#ifdef EXPERIMENTAL_DSN_INFO
3250 +         , NULL, NULL
3251 +#endif
3252 +         );
3253 +}
3254  
3255  
3256  /*************************************************
3257 @@ -847,7 +874,7 @@ while (count-- > 0)
3258      {
3259      uschar *message = string_sprintf("SMTP timeout after RCPT TO:<%s>",
3260                           transport_rcpt_address(addr, include_affixes));
3261 -    set_errno(addrlist, ETIMEDOUT, message, DEFER, FALSE, NULL);
3262 +    set_errno_nohost(addrlist, ETIMEDOUT, message, DEFER, FALSE);
3263      retry_add_item(addr, addr->address_retry_key, 0);
3264      update_waiting = FALSE;
3265      return -1;
3266 @@ -1096,8 +1123,8 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
3267           /* Internal problem, message in buffer. */
3268  
3269           case ERROR:
3270 -         set_errno(addrlist, ERRNO_AUTHPROB, string_copy(buffer),
3271 -                   DEFER, FALSE, NULL);
3272 +         set_errno_nohost(addrlist, ERRNO_AUTHPROB, string_copy(buffer),
3273 +                   DEFER, FALSE);
3274           return ERROR;
3275           }
3276  
3277 @@ -1111,9 +1138,9 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
3278  
3279  if (require_auth == OK && !smtp_authenticated)
3280    {
3281 -  set_errno(addrlist, ERRNO_AUTHFAIL,
3282 +  set_errno_nohost(addrlist, ERRNO_AUTHFAIL,
3283      string_sprintf("authentication required but %s", fail_reason), DEFER,
3284 -    FALSE, NULL);
3285 +    FALSE);
3286    return DEFER;
3287    }
3288  
3289 @@ -1152,7 +1179,7 @@ if (ob->authenticated_sender != NULL)
3290        {
3291        uschar *message = string_sprintf("failed to expand "
3292          "authenticated_sender: %s", expand_string_message);
3293 -      set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL);
3294 +      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
3295        return TRUE;
3296        }
3297      }
3298 @@ -1381,6 +1408,10 @@ smtp_outblock outblock;
3299  int max_rcpt = tblock->max_addresses;
3300  uschar *igquotstr = US"";
3301  
3302 +#ifdef EXPERIMENTAL_DSN_INFO
3303 +uschar *smtp_greeting = NULL;
3304 +uschar *helo_response = NULL;
3305 +#endif
3306  uschar *helo_data = NULL;
3307  
3308  uschar *message = NULL;
3309 @@ -1432,8 +1463,8 @@ tls_modify_variables(&tls_out);
3310  #ifndef SUPPORT_TLS
3311  if (smtps)
3312    {
3313 -  set_errno(addrlist, ERRNO_TLSFAILURE, US"TLS support not available",
3314 -           DEFER, FALSE, NULL);
3315 +  set_errno_nohost(addrlist, ERRNO_TLSFAILURE, US"TLS support not available",
3316 +           DEFER, FALSE);
3317    return ERROR;
3318    }
3319  #endif
3320 @@ -1450,8 +1481,8 @@ if (continue_hostname == NULL)
3321  
3322    if (inblock.sock < 0)
3323      {
3324 -    set_errno(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno,
3325 -      NULL, DEFER, FALSE, NULL);
3326 +    set_errno_nohost(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno,
3327 +      NULL, DEFER, FALSE);
3328      return DEFER;
3329      }
3330  
3331 @@ -1469,18 +1500,18 @@ if (continue_hostname == NULL)
3332         && dane_required        /* do not error on only dane-requested */
3333         )
3334         {
3335 -       set_errno(addrlist, ERRNO_DNSDEFER,
3336 +       set_errno_nohost(addrlist, ERRNO_DNSDEFER,
3337           string_sprintf("DANE error: tlsa lookup %s",
3338             rc == DEFER ? "DEFER" : "FAIL"),
3339 -         rc, FALSE, NULL);
3340 +         rc, FALSE);
3341         return rc;
3342         }
3343        }
3344      else if (dane_required)
3345        {
3346 -      set_errno(addrlist, ERRNO_DNSDEFER,
3347 +      set_errno_nohost(addrlist, ERRNO_DNSDEFER,
3348         string_sprintf("DANE error: %s lookup not DNSSEC", host->name),
3349 -       FAIL, FALSE, NULL);
3350 +       FAIL, FALSE);
3351        return  FAIL;
3352        }
3353  
3354 @@ -1501,7 +1532,7 @@ if (continue_hostname == NULL)
3355      if ((helo_data = string_domain_utf8_to_alabel(helo_data, &errstr)), errstr)
3356        {
3357        errstr = string_sprintf("failed to expand helo_data: %s", errstr);
3358 -      set_errno(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, NULL);
3359 +      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE);
3360        yield = DEFER;
3361        goto SEND_QUIT;
3362        }
3363 @@ -1514,8 +1545,12 @@ if (continue_hostname == NULL)
3364  
3365    if (!smtps)
3366      {
3367 -    if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
3368 -      ob->command_timeout)) goto RESPONSE_FAILED;
3369 +    BOOL good_response = smtp_read_response(&inblock, buffer, sizeof(buffer),
3370 +      '2', ob->command_timeout);
3371 +#ifdef EXPERIMENTAL_DSN_INFO
3372 +    smtp_greeting = string_copy(buffer);
3373 +#endif
3374 +    if (!good_response) goto RESPONSE_FAILED;
3375  
3376  #ifdef EXPERIMENTAL_EVENT
3377        {
3378 @@ -1525,9 +1560,9 @@ if (continue_hostname == NULL)
3379        s = event_raise(tblock->event_action, US"smtp:connect", buffer);
3380        if (s)
3381         {
3382 -       set_errno(addrlist, ERRNO_EXPANDFAIL,
3383 +       set_errno_nohost(addrlist, ERRNO_EXPANDFAIL,
3384           string_sprintf("deferred by smtp:connect event expansion: %s", s),
3385 -         DEFER, FALSE, NULL);
3386 +         DEFER, FALSE);
3387         yield = DEFER;
3388         goto SEND_QUIT;
3389         }
3390 @@ -1541,7 +1576,7 @@ if (continue_hostname == NULL)
3391        {
3392        uschar *message = string_sprintf("failed to expand helo_data: %s",
3393          expand_string_message);
3394 -      set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL);
3395 +      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
3396        yield = DEFER;
3397        goto SEND_QUIT;
3398        }
3399 @@ -1606,9 +1641,18 @@ goto SEND_QUIT;
3400      if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
3401             ob->command_timeout))
3402        {
3403 -      if (errno != 0 || buffer[0] == 0 || lmtp) goto RESPONSE_FAILED;
3404 +      if (errno != 0 || buffer[0] == 0 || lmtp)
3405 +       {
3406 +#ifdef EXPERIMENTAL_DSN_INFO
3407 +       helo_response = string_copy(buffer);
3408 +#endif
3409 +       goto RESPONSE_FAILED;
3410 +       }
3411        esmtp = FALSE;
3412        }
3413 +#ifdef EXPERIMENTAL_DSN_INFO
3414 +    helo_response = string_copy(buffer);
3415 +#endif
3416      }
3417    else
3418      {
3419 @@ -1618,10 +1662,16 @@ goto SEND_QUIT;
3420  
3421    if (!esmtp)
3422      {
3423 +    BOOL good_response;
3424 +
3425      if (smtp_write_command(&outblock, FALSE, "HELO %s\r\n", helo_data) < 0)
3426        goto SEND_FAILED;
3427 -    if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
3428 -      ob->command_timeout)) goto RESPONSE_FAILED;
3429 +    good_response = smtp_read_response(&inblock, buffer, sizeof(buffer),
3430 +      '2', ob->command_timeout);
3431 +#ifdef EXPERIMENTAL_DSN_INFO
3432 +    helo_response = string_copy(buffer);
3433 +#endif
3434 +    if (!good_response) goto RESPONSE_FAILED;
3435      }
3436  
3437    /* Set IGNOREQUOTA if the response to LHLO specifies support and the
3438 @@ -1671,6 +1721,11 @@ error messages. Note that smtp_use_size and smtp_use_pipelining will have been
3439  set from the command line if they were set in the process that passed the
3440  connection on. */
3441  
3442 +/*XXX continue case needs to propagate DSN_INFO, prob. in deliver.c
3443 +as the contine goes via transport_pass_socket() and doublefork and exec.
3444 +It does not wait.  Unclear how we keep separate host's responses
3445 +separate - we could match up by host ip+port as a bodge. */
3446 +
3447  else
3448    {
3449    inblock.sock = outblock.sock = fileno(stdin);
3450 @@ -1749,7 +1804,7 @@ if (  tls_offered
3451  
3452      /* TLS session is set up */
3453  
3454 -    for (addr = addrlist; addr != NULL; addr = addr->next)
3455 +    for (addr = addrlist; addr; addr = addr->next)
3456        if (addr->transport_return == PENDING_DEFER)
3457          {
3458          addr->cipher = tls_out.cipher;
3459 @@ -1774,6 +1829,8 @@ start of the Exim process (in exim.c). */
3460  if (tls_out.active >= 0)
3461    {
3462    char *greeting_cmd;
3463 +  BOOL good_response;
3464 +
3465    if (helo_data == NULL)
3466      {
3467      helo_data = expand_string(ob->helo_data);
3468 @@ -1781,7 +1838,7 @@ if (tls_out.active >= 0)
3469        {
3470        uschar *message = string_sprintf("failed to expand helo_data: %s",
3471          expand_string_message);
3472 -      set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL);
3473 +      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
3474        yield = DEFER;
3475        goto SEND_QUIT;
3476        }
3477 @@ -1790,8 +1847,12 @@ if (tls_out.active >= 0)
3478    /* For SMTPS we need to wait for the initial OK response. */
3479    if (smtps)
3480      {
3481 -    if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
3482 -      ob->command_timeout)) goto RESPONSE_FAILED;
3483 +    good_response = smtp_read_response(&inblock, buffer, sizeof(buffer),
3484 +      '2', ob->command_timeout);
3485 +#ifdef EXPERIMENTAL_DSN_INFO
3486 +    smtp_greeting = string_copy(buffer);
3487 +#endif
3488 +    if (!good_response) goto RESPONSE_FAILED;
3489      }
3490  
3491    if (esmtp)
3492 @@ -1806,9 +1867,12 @@ if (tls_out.active >= 0)
3493    if (smtp_write_command(&outblock, FALSE, "%s %s\r\n",
3494          lmtp? "LHLO" : greeting_cmd, helo_data) < 0)
3495      goto SEND_FAILED;
3496 -  if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
3497 -       ob->command_timeout))
3498 -    goto RESPONSE_FAILED;
3499 +  good_response = smtp_read_response(&inblock, buffer, sizeof(buffer),
3500 +    '2', ob->command_timeout);
3501 +#ifdef EXPERIMENTAL_DSN_INFO
3502 +  helo_response = string_copy(buffer);
3503 +#endif
3504 +  if (!good_response) goto RESPONSE_FAILED;
3505    }
3506  
3507  /* If the host is required to use a secure channel, ensure that we
3508 @@ -1935,8 +1999,8 @@ if (tblock->filter_command != NULL)
3509  
3510    if (!rc)
3511      {
3512 -    set_errno(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER,
3513 -      FALSE, NULL);
3514 +    set_errno_nohost(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER,
3515 +      FALSE);
3516      yield = ERROR;
3517      goto SEND_QUIT;
3518      }
3519 @@ -2065,7 +2129,7 @@ pending_MAIL = TRUE;     /* The block starts with MAIL */
3520      {
3521      if (s = string_address_utf8_to_alabel(return_path, &errstr), errstr)
3522        {
3523 -      set_errno(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, NULL);
3524 +      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE);
3525        yield = ERROR;
3526        goto SEND_QUIT;
3527        }
3528 @@ -2217,8 +2281,8 @@ if (mua_wrapper)
3529      if (badaddr->transport_return != PENDING_OK)
3530        {
3531        /*XXX could we find a better errno than 0 here? */
3532 -      set_errno(addrlist, 0, badaddr->message, FAIL,
3533 -       testflag(badaddr, af_pass_message), NULL);
3534 +      set_errno_nohost(addrlist, 0, badaddr->message, FAIL,
3535 +       testflag(badaddr, af_pass_message));
3536        ok = FALSE;
3537        break;
3538        }
3539 @@ -2475,7 +2539,7 @@ if (!ok) ok = TRUE; else
3540          else
3541            sprintf(CS buffer, "%.500s\n", addr->unique);
3542  
3543 -        DEBUG(D_deliver) debug_printf("journalling %s", buffer);
3544 +        DEBUG(D_deliver) debug_printf("journalling %s\n", buffer);
3545          len = Ustrlen(CS buffer);
3546          if (write(journal_fd, buffer, len) != len)
3547            log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
3548 @@ -2512,7 +2576,7 @@ if (!ok) ok = TRUE; else
3549            else
3550              sprintf(CS buffer, "%.500s\n", addr->unique);
3551  
3552 -          DEBUG(D_deliver) debug_printf("journalling(PRDR) %s", buffer);
3553 +          DEBUG(D_deliver) debug_printf("journalling(PRDR) %s\n", buffer);
3554            len = Ustrlen(CS buffer);
3555            if (write(journal_fd, buffer, len) != len)
3556              log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
3557 @@ -2542,22 +2606,27 @@ the problem is not related to this specific message. */
3558  
3559  if (!ok)
3560    {
3561 -  int code;
3562 +  int code, set_rc;
3563 +  uschar * set_message;
3564  
3565    RESPONSE_FAILED:
3566 -  save_errno = errno;
3567 -  message = NULL;
3568 -  send_quit = check_response(host, &save_errno, addrlist->more_errno,
3569 -    buffer, &code, &message, &pass_message);
3570 -  goto FAILED;
3571 +    {
3572 +    save_errno = errno;
3573 +    message = NULL;
3574 +    send_quit = check_response(host, &save_errno, addrlist->more_errno,
3575 +      buffer, &code, &message, &pass_message);
3576 +    goto FAILED;
3577 +    }
3578  
3579    SEND_FAILED:
3580 -  save_errno = errno;
3581 -  code = '4';
3582 -  message = US string_sprintf("send() to %s [%s] failed: %s",
3583 -    host->name, host->address, strerror(save_errno));
3584 -  send_quit = FALSE;
3585 -  goto FAILED;
3586 +    {
3587 +    save_errno = errno;
3588 +    code = '4';
3589 +    message = US string_sprintf("send() to %s [%s] failed: %s",
3590 +      host->name, host->address, strerror(save_errno));
3591 +    send_quit = FALSE;
3592 +    goto FAILED;
3593 +    }
3594  
3595    /* This label is jumped to directly when a TLS negotiation has failed,
3596    or was not done for a host for which it is required. Values will be set
3597 @@ -2578,16 +2647,14 @@ if (!ok)
3598  
3599    FAILED:
3600    ok = FALSE;                /* For when reached by GOTO */
3601 +  set_message = message;
3602  
3603    if (setting_up)
3604      {
3605      if (code == '5')
3606 -      set_errno(addrlist, save_errno, message, FAIL, pass_message, host);
3607 +      set_rc = FAIL;
3608      else
3609 -      {
3610 -      set_errno(addrlist, save_errno, message, DEFER, pass_message, host);
3611 -      yield = DEFER;
3612 -      }
3613 +      yield = set_rc = DEFER;
3614      }
3615  
3616    /* We want to handle timeouts after MAIL or "." and loss of connection after
3617 @@ -2646,14 +2713,15 @@ if (!ok)
3618      if (message_error)
3619        {
3620        if (mua_wrapper) code = '5';  /* Force hard failure in wrapper mode */
3621 -      set_errno(addrlist, save_errno, message, (code == '5')? FAIL : DEFER,
3622 -        pass_message, host);
3623  
3624        /* If there's an errno, the message contains just the identity of
3625        the host. */
3626  
3627 -      if (code != '5')     /* Anything other than 5 is treated as temporary */
3628 +      if (code == '5')
3629 +       set_rc = FAIL;
3630 +      else             /* Anything other than 5 is treated as temporary */
3631          {
3632 +       set_rc = DEFER;
3633          if (save_errno > 0)
3634            message = US string_sprintf("%s: %s", message, strerror(save_errno));
3635          if (host->next != NULL) log_write(0, LOG_MAIN, "%s", message);
3636 @@ -2670,11 +2738,17 @@ if (!ok)
3637  
3638      else
3639        {
3640 +      set_rc = DEFER;
3641        yield = (save_errno == ERRNO_CHHEADER_FAIL ||
3642                 save_errno == ERRNO_FILTER_FAIL)? ERROR : DEFER;
3643 -      set_errno(addrlist, save_errno, message, DEFER, pass_message, host);
3644        }
3645      }
3646 +
3647 +  set_errno(addrlist, save_errno, set_message, set_rc, pass_message, host
3648 +#ifdef EXPERIMENTAL_DSN_INFO
3649 +           , smtp_greeting, helo_response
3650 +#endif
3651 +           );
3652    }
3653  
3654  
3655 @@ -2787,6 +2861,9 @@ if (completed_address && ok && send_quit)
3656        /* If the socket is successfully passed, we musn't send QUIT (or
3657        indeed anything!) from here. */
3658  
3659 +/*XXX DSN_INFO: assume likely to do new HELO; but for greet we'll want to
3660 +propagate it from the initial
3661 +*/
3662        if (ok && transport_pass_socket(tblock->name, host->name, host->address,
3663              new_message_id, inblock.sock))
3664          {
3665 @@ -2796,7 +2873,11 @@ if (completed_address && ok && send_quit)
3666  
3667      /* If RSET failed and there are addresses left, they get deferred. */
3668  
3669 -    else set_errno(first_addr, errno, msg, DEFER, FALSE, host);
3670 +    else set_errno(first_addr, errno, msg, DEFER, FALSE, host
3671 +#ifdef EXPERIMENTAL_DSN_INFO
3672 +                 , smtp_greeting, helo_response
3673 +#endif
3674 +                 );
3675      }
3676    }
3677  
3678 @@ -2938,6 +3019,10 @@ for (addr = addrlist; addr != NULL; addr = addr->next)
3679      addr->peerdn = NULL;
3680      addr->ocsp = OCSP_NOT_REQ;
3681  #endif
3682 +#ifdef EXPERIMENTAL_DSN_INFO
3683 +    addr->smtp_greeting = NULL;
3684 +    addr->helo_response = NULL;
3685 +#endif
3686      }
3687  return first_addr;
3688  }
3689 @@ -3479,7 +3564,7 @@ for (cutoff_retry = 0; expired &&
3690      if (dont_deliver)
3691        {
3692        host_item *host2;
3693 -      set_errno(addrlist, 0, NULL, OK, FALSE, NULL);
3694 +      set_errno_nohost(addrlist, 0, NULL, OK, FALSE);
3695        for (addr = addrlist; addr != NULL; addr = addr->next)
3696          {
3697          addr->host_used = host;
This page took 0.449267 seconds and 4 git commands to generate.