Regex compile cacheing
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 19 Jun 2022 16:15:25 +0000 (17:15 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 19 Jun 2022 16:15:25 +0000 (17:15 +0100)
46 files changed:
doc/doc-txt/ChangeLog
src/OS/Makefile-Base
src/scripts/MakeLinks
src/src/acl.c
src/src/daemon.c
src/src/deliver.c
src/src/dns.c
src/src/drtables.c
src/src/exim.c
src/src/expand.c
src/src/filter.c
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/header.c
src/src/macros.h
src/src/malware.c
src/src/match.c
src/src/queue.c
src/src/readconf.c
src/src/regex.c
src/src/regex_cache.c [new file with mode: 0644]
src/src/rewrite.c
src/src/routers/iplookup.c
src/src/structs.h
src/src/transports/appendfile.c
src/src/transports/smtp.c
src/src/transports/tf_maildir.c
src/src/verify.c
test/confs/0632 [new file with mode: 0644]
test/log/0632 [new file with mode: 0644]
test/scripts/0000-Basic/0632 [new file with mode: 0644]
test/stderr/0002
test/stderr/0183
test/stderr/0386
test/stderr/0388
test/stderr/0398
test/stderr/0432
test/stderr/0471
test/stderr/0544
test/stderr/0632 [new file with mode: 0644]
test/stderr/3400
test/stderr/5005
test/stderr/5006
test/stderr/5410
test/stderr/5420

index a67488798a4c02aa2b846e9dc17a7e802f7af463..0188488e10fba11164b94ac22d7f06392258ff7f 100644 (file)
@@ -12,6 +12,8 @@ JH/02 Option default value updates:
        - queue_fast_ramp (main)        true (was false)
        - remote_max_parallel (main)    4 (was 2)
 
+JH/03 Cache static regex pattern compilations, for use by ACLs.
+
 Exim version 4.96
 -----------------
 
index 99a9f7e7528cbce92f26ef67088d5c7adce75cb2..0c64d45d4ae2a7b403ad99f5bef82ad5a4cc304a 100644 (file)
@@ -488,7 +488,7 @@ OBJ_EXIM = acl.o base64.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \
         filtertest.o globals.o dkim.o dkim_transport.o dnsbl.o hash.o \
         header.o host.o host_address.o ip.o log.o lss.o match.o md5.o moan.o \
         os.o parse.o priv.o queue.o \
-        rda.o readconf.o receive.o retry.o rewrite.o rfc2047.o \
+        rda.o readconf.o receive.o retry.o rewrite.o rfc2047.o regex_cache.o \
         route.o search.o sieve.o smtp_in.o smtp_out.o spool_in.o spool_out.o \
         std-crypto.o store.o string.o tls.o tod.o transport.o tree.o verify.o \
         environment.o macro.o \
@@ -809,6 +809,7 @@ readconf.o:      $(HDRS) readconf.c
 receive.o:       $(HDRS) receive.c
 retry.o:         $(HDRS) retry.c
 rewrite.o:       $(HDRS) rewrite.c
+regex_cache.o:   $(HDRS) regex_cache.c
 rfc2047.o:       $(HDRS) rfc2047.c
 route.o:         $(HDRS) route.c
 search.o:        $(HDRS) search.c
index afc2fab3253042a4024108be1db23a7e91967d0f..471b3a3699e59af85be2e089527687f722024e05 100755 (executable)
@@ -104,7 +104,7 @@ for f in blob.h dbfunctions.h exim.h functions.h globals.h \
   exim_dbmbuild.c exim_dbutil.c exim_lock.c expand.c filter.c filtertest.c \
   globals.c hash.c header.c host.c host_address.c ip.c log.c lss.c match.c md5.c moan.c \
   parse.c perl.c priv.c queue.c rda.c readconf.c receive.c retry.c rewrite.c \
-  rfc2047.c route.c search.c setenv.c environment.c \
+  regex_cache.c rfc2047.c route.c search.c setenv.c environment.c \
   sieve.c smtp_in.c smtp_out.c spool_in.c spool_out.c std-crypto.c store.c \
   string.c tls.c tlscert-gnu.c tlscert-openssl.c tls-cipher-stdname.c \
   tls-gnu.c tls-openssl.c \
index a1694fcdde2e64524fa8aad97644a8b725dc1453..0078aca7dc6cbdac91445ced0646b32c8692c10c 100644 (file)
@@ -3125,8 +3125,9 @@ int sep = -'/';
 
 for (; cb; cb = cb->next)
   {
-  const uschar *arg;
+  const uschar * arg;
   int control_type;
+  BOOL textonly = FALSE;
 
   /* The message and log_message items set up messages to be used in
   case of rejection. They are expanded later. */
@@ -3160,7 +3161,8 @@ for (; cb; cb = cb->next)
 
   if (!conditions[cb->type].expand_at_top)
     arg = cb->arg;
-  else if (!(arg = expand_string(cb->arg)))
+
+  else if (!(arg = expand_string_2(cb->arg, &textonly)))
     {
     if (f.expand_string_forcedfail) continue;
     *log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s",
@@ -3875,14 +3877,14 @@ for (; cb; cb = cb->next)
          return ERROR;
          }
 
-      rc = malware(ss, timeout);
+      rc = malware(ss, textonly, timeout);
       if (rc == DEFER && defer_ok)
        rc = FAIL;      /* FAIL so that the message is passed to the next ACL */
       break;
       }
 
     case ACLC_MIME_REGEX:
-      rc = mime_regex(&arg);
+      rc = mime_regex(&arg, textonly);
       break;
     #endif
 
@@ -3913,7 +3915,7 @@ for (; cb; cb = cb->next)
 
     #ifdef WITH_CONTENT_SCAN
     case ACLC_REGEX:
-      rc = regex(&arg);
+      rc = regex(&arg, textonly);
       break;
     #endif
 
index a5eb707d011615d6935b20aba74787b0a9229e5e..54725e07d9ba4f74e3f442f89aeaa657681bfe84 100644 (file)
@@ -1132,6 +1132,20 @@ exim_exit(EXIT_SUCCESS);
 *      Listener socket for local work prompts   *
 *************************************************/
 
+ssize_t
+daemon_client_sockname(struct sockaddr_un * sup, uschar ** sname)
+{
+#ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+sup->sun_path[0] = 0;  /* Abstract local socket addr - Linux-specific? */
+return offsetof(struct sockaddr_un, sun_path) + 1
+  + snprintf(sup->sun_path+1, sizeof(sup->sun_path)-1, "exim_%d", getpid());
+#else
+*sname = string_sprintf("%s/p_%d", spool_directory, getpid());
+return offsetof(struct sockaddr_un, sun_path)
+  + snprintf(sup->sun_path, sizeof(sup->sun_path), "%s", sname);
+#endif
+}
+
 ssize_t
 daemon_notifier_sockname(struct sockaddr_un * sup)
 {
@@ -1216,7 +1230,11 @@ bad:
 
 static uschar queuerun_msgid[MESSAGE_ID_LENGTH+1];
 
-/* Return TRUE if a sigalrm should be emulated */
+/* The notifier socket has something to read. Pull the message from it, decode
+and do the action.
+
+Return TRUE if a sigalrm should be emulated */
+
 static BOOL
 daemon_notification(void)
 {
@@ -1266,7 +1284,6 @@ for (struct cmsghdr * cp = CMSG_FIRSTHDR(&msg);
     {
     DEBUG(D_queue_run) debug_printf("%s: sender creds pid %d uid %d gid %d\n",
       __FUNCTION__, (int)cr->pid, (int)cr->uid, (int)cr->gid);
-    return FALSE;
     }
 # elif defined(LOCAL_CREDS)                            /* BSD-ish */
   struct sockcred * cr = (struct sockcred *) CMSG_DATA(cp);
@@ -1274,7 +1291,6 @@ for (struct cmsghdr * cp = CMSG_FIRSTHDR(&msg);
     {
     DEBUG(D_queue_run) debug_printf("%s: sender creds pid ??? uid %d gid %d\n",
       __FUNCTION__, (int)cr->sc_uid, (int)cr->sc_gid);
-    return FALSE;
     }
 # endif
   break;
@@ -1305,8 +1321,12 @@ switch (buf[0])
                (const struct sockaddr *)&sa_un, msg.msg_namelen) < 0)
       log_write(0, LOG_MAIN|LOG_PANIC,
        "%s: sendto: %s\n", __FUNCTION__, strerror(errno));
-    return FALSE;
+    break;
     }
+
+  case NOTIFY_REGEX:
+    regex_at_daemon(buf);
+    break;
   }
 return FALSE;
 }
index 8a9a174e3eaaaf9f722fab611db208cd9fc792f3..725d0c872fd84d436538daa3483aaae67b1d8a9a 100644 (file)
@@ -7203,7 +7203,7 @@ local and remote LMTP deliveries. */
 
 if (!regex_IGNOREQUOTA)
   regex_IGNOREQUOTA =
-    regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE);
+    regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", MCS_NOFLAGS, TRUE);
 
 /* Handle local deliveries */
 
index 7d7ee0c0460584022028723c4f09a381908a597a..4071c5822bc1b2c247c8c2df8e0f653d9ef1e27a 100644 (file)
@@ -1324,7 +1324,7 @@ dns_pattern_init(void)
 {
 if (check_dns_names_pattern[0] != 0 && !regex_check_dns_names)
   regex_check_dns_names =
-    regex_must_compile(check_dns_names_pattern, FALSE, TRUE);
+    regex_must_compile(check_dns_names_pattern, MCS_NOFLAGS, TRUE);
 }
 
 /* vi: aw ai sw=2
index 513ef6c4ad202af9fa7a8915a042fa9ab06534a5..b2f2a4b33c4974fd45031cafbdfdaaf623321f63 100644 (file)
@@ -728,8 +728,8 @@ if (!(dd = exim_opendir(LOOKUP_MODULE_DIR)))
   }
 else
   {
-  const pcre2_code *regex_islookupmod = regex_must_compile(
-    US"\\." DYNLIB_FN_EXT "$", FALSE, TRUE);
+  const pcre2_code * regex_islookupmod = regex_must_compile(
+    US"\\." DYNLIB_FN_EXT "$", MCS_NOFLAGS, TRUE);
 
   DEBUG(D_lookup) debug_printf("Loading lookup modules from %s\n", LOOKUP_MODULE_DIR);
   while ((ent = readdir(dd)))
index 052c6bf5c54cc547dcf8738e9d95c9883a19464f..99a4faa8c91cbcf9dec0aeb9674e2094b9d1f5e0 100644 (file)
@@ -83,45 +83,6 @@ enum commandline_info { CMDINFO_NONE=0,
 
 
 
-/*************************************************
-*  Compile regular expression and panic on fail  *
-*************************************************/
-
-/* This function is called when failure to compile a regular expression leads
-to a panic exit. In other cases, pcre_compile() is called directly. In many
-cases where this function is used, the results of the compilation are to be
-placed in long-lived store, so we temporarily reset the store management
-functions that PCRE uses if the use_malloc flag is set.
-
-Argument:
-  pattern     the pattern to compile
-  caseless    TRUE if caseless matching is required
-  use_malloc  TRUE if compile into malloc store
-
-Returns:      pointer to the compiled pattern
-*/
-
-const pcre2_code *
-regex_must_compile(const uschar * pattern, BOOL caseless, BOOL use_malloc)
-{
-size_t offset;
-int options = caseless ? PCRE_COPT|PCRE2_CASELESS : PCRE_COPT;
-const pcre2_code * yield;
-int err;
-
-if (!(yield = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, options,
-  &err, &offset, use_malloc ? pcre_mlc_cmp_ctx : pcre_gen_cmp_ctx)))
-  {
-  uschar errbuf[128];
-  pcre2_get_error_message(err, errbuf, sizeof(errbuf));
-  log_write(0, LOG_MAIN|LOG_PANIC_DIE, "regular expression error: "
-    "%s at offset %ld while compiling %s", errbuf, (long)offset, pattern);
-  }
-
-return yield;
-}
-
-
 static void
 pcre_init(void)
 {
@@ -2019,7 +1980,7 @@ this here, because the -M options check their arguments for syntactic validity
 using mac_ismsgid, which uses this. */
 
 regex_ismsgid =
-  regex_must_compile(US"^(?:[^\\W_]{6}-){2}[^\\W_]{2}$", FALSE, TRUE);
+  regex_must_compile(US"^(?:[^\\W_]{6}-){2}[^\\W_]{2}$", MCS_NOFLAGS, TRUE);
 
 /* Precompile the regular expression that is used for matching an SMTP error
 code, possibly extended, at the start of an error message. Note that the
@@ -2027,14 +1988,14 @@ terminating whitespace character is included. */
 
 regex_smtp_code =
   regex_must_compile(US"^\\d\\d\\d\\s(?:\\d\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\s)?",
-    FALSE, TRUE);
+    MCS_NOFLAGS, TRUE);
 
 #ifdef WHITELIST_D_MACROS
 /* Precompile the regular expression used to filter the content of macros
 given to -D for permissibility. */
 
 regex_whitelisted_macro =
-  regex_must_compile(US"^[A-Za-z0-9_/.-]*$", FALSE, TRUE);
+  regex_must_compile(US"^[A-Za-z0-9_/.-]*$", MCS_NOFLAGS, TRUE);
 #endif
 
 for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL;
@@ -2252,7 +2213,7 @@ on the second character (the one after '-'), to save some effort. */
           -bdf: Ditto, but in the foreground.
        */
        case 'd':
-         f.daemon_listen = TRUE;
+         f.daemon_listen = f.daemon_scion = TRUE;
          if (*argrest == 'f') f.background_daemon = FALSE;
          else if (*argrest) badarg = TRUE;
          break;
@@ -2512,7 +2473,7 @@ on the second character (the one after '-'), to save some effort. */
        case 'w':
          f.inetd_wait_mode = TRUE;
          f.background_daemon = FALSE;
-         f.daemon_listen = TRUE;
+         f.daemon_listen = f.daemon_scion = TRUE;
          if (*argrest)
            if ((inetd_wait_timeout = readconf_readtime(argrest, 0, FALSE)) <= 0)
              exim_fail("exim: bad time value %s: abandoned\n", argv[i]);
@@ -5039,7 +5000,7 @@ for (i = 0;;)
         if (gecos_pattern && gecos_name)
           {
           const pcre2_code *re;
-          re = regex_must_compile(gecos_pattern, FALSE, TRUE); /* Use malloc */
+          re = regex_must_compile(gecos_pattern, MCS_NOFLAGS, TRUE); /* Use malloc */
 
           if (regex_match_and_setup(re, name, 0, -1))
             {
index 9b54ccad1f14eb5cef4958dd86ba265cff1f0960..4d7dc721920d6d88a554dbab8e2e0eb7be6e42ac 100644 (file)
@@ -14,7 +14,7 @@
 
 /* Recursively called function */
 
-static uschar *expand_string_internal(const uschar *, BOOL, const uschar **, BOOL, BOOL, BOOL *);
+static uschar *expand_string_internal(const uschar *, BOOL, const uschar **, BOOL, BOOL, BOOL *, BOOL *);
 static int_eximarith_t expanded_string_integer(const uschar *, BOOL);
 
 #ifdef STAND_ALONE
@@ -1748,9 +1748,7 @@ uschar buf[16];
 int fd;
 ssize_t len;
 const uschar * where;
-#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
 uschar * sname;
-#endif
 
 if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
   {
@@ -1758,17 +1756,9 @@ if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
   return NULL;
   }
 
-#ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
-sa_un.sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */
-len = offsetof(struct sockaddr_un, sun_path) + 1
-  + snprintf(sa_un.sun_path+1, sizeof(sa_un.sun_path)-1, "exim_%d", getpid());
-#else
-sname = string_sprintf("%s/p_%d", spool_directory, getpid());
-len = offsetof(struct sockaddr_un, sun_path)
-  + snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s", sname);
-#endif
+len = daemon_client_sockname(&sa_un, &sname);
 
-if (bind(fd, (const struct sockaddr *)&sa_un, len) < 0)
+if (bind(fd, (const struct sockaddr *)&sa_un, (socklen_t)len) < 0)
   { where = US"bind"; goto bad; }
 
 #ifdef notdef
@@ -2108,7 +2098,9 @@ Arguments:
   check_end  if TRUE, check for final '}'
   name       name of item, for error message
   resetok    if not NULL, pointer to flag - write FALSE if unsafe to reset
-            the store.
+            the store
+  textonly_p if not NULL, pointer to bitmask of which subs were text-only
+            (did not change when expended)
 
 Returns:     0 OK; string pointer updated
              1 curly bracketing error (too few arguments)
@@ -2118,13 +2110,15 @@ Returns:     0 OK; string pointer updated
 
 static int
 read_subs(uschar **sub, int n, int m, const uschar **sptr, BOOL skipping,
-  BOOL check_end, uschar *name, BOOL *resetok)
+  BOOL check_end, uschar *name, BOOL *resetok, unsigned * textonly_p)
 {
-const uschar *s = *sptr;
+const uschar * s = *sptr;
+unsigned textonly_l = 0;
 
 Uskip_whitespace(&s);
 for (int i = 0; i < n; i++)
   {
+  BOOL textonly;
   if (*s != '{')
     {
     if (i < m)
@@ -2136,9 +2130,11 @@ for (int i = 0; i < n; i++)
     sub[i] = NULL;
     break;
     }
-  if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, resetok)))
+  if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, resetok,
+                                               textonly_p ? &textonly : NULL)))
     return 3;
   if (*s++ != '}') return 1;
+  if (textonly_p && textonly) textonly_l |= BIT(i);
   Uskip_whitespace(&s);
   }
 if (check_end && *s++ != '}')
@@ -2153,6 +2149,7 @@ if (check_end && *s++ != '}')
   return 1;
   }
 
+if (textonly_p) *textonly_p = textonly_l;
 *sptr = s;
 return 0;
 }
@@ -2513,11 +2510,11 @@ Returns:   a pointer to the first character after the condition, or
 */
 
 static const uschar *
-eval_condition(const uschar *s, BOOL *resetok, BOOL *yield)
+eval_condition(const uschar * s, BOOL * resetok, BOOL * yield)
 {
 BOOL testfor = TRUE;
 BOOL tempcond, combined_cond;
-BOOL *subcondptr;
+BOOL * subcondptr;
 BOOL sub2_honour_dollar = TRUE;
 BOOL is_forany, is_json, is_jsons;
 int rc, cond_type;
@@ -2525,7 +2522,8 @@ int_eximarith_t num[2];
 struct stat statbuf;
 uschar * opname;
 uschar name[256];
-const uschar *sub[10];
+const uschar * sub[10];
+unsigned sub_textonly = 0;
 
 for (;;)
   if (Uskip_whitespace(&s) == '!') { testfor = !testfor; s++; } else break;
@@ -2619,8 +2617,12 @@ switch(cond_type = identify_operator(&s, &opname))
 
   if (Uskip_whitespace(&s) != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */
 
-  sub[0] = expand_string_internal(s+1, TRUE, &s, yield == NULL, TRUE, resetok);
-  if (!sub[0]) return NULL;
+   {
+    BOOL textonly;
+    sub[0] = expand_string_internal(s+1, TRUE, &s, yield == NULL, TRUE, resetok, &textonly);
+    if (!sub[0]) return NULL;
+    if (textonly) sub_textonly |= BIT(0);
+   }
   /* {-for-text-editors */
   if (*s++ != '}') goto COND_FAILED_CURLY_END;
 
@@ -2718,7 +2720,7 @@ switch(cond_type = identify_operator(&s, &opname))
     if (*s++ != '{') goto COND_FAILED_CURLY_START;     /*}*/
 
     switch(read_subs(sub, nelem(sub), 1,
-      &s, yield == NULL, TRUE, name, resetok))
+      &s, yield == NULL, TRUE, name, resetok, NULL))
       {
       case 1: expand_string_message = US"too few arguments or bracketing "
         "error for acl";
@@ -2770,7 +2772,7 @@ switch(cond_type = identify_operator(&s, &opname))
     Uskip_whitespace(&s);
     if (*s++ != '{') goto COND_FAILED_CURLY_START;     /* }-for-text-editors */
     switch(read_subs(sub, nelem(sub), 2, &s, yield == NULL, TRUE, name,
-                   resetok))
+                   resetok, NULL))
       {
       case 1: expand_string_message = US"too few arguments or bracketing "
        "error for saslauthd";
@@ -2838,9 +2840,11 @@ switch(cond_type = identify_operator(&s, &opname))
 
   for (int i = 0; i < 2; i++)
     {
+    BOOL textonly;
     /* Sometimes, we don't expand substrings; too many insecure configurations
     created using match_address{}{} and friends, where the second param
     includes information from untrustworthy sources. */
+    /*XXX is this moot given taint-tracking? */
     BOOL honour_dollar = TRUE;
     if ((i > 0) && !sub2_honour_dollar)
       honour_dollar = FALSE;
@@ -2853,8 +2857,9 @@ switch(cond_type = identify_operator(&s, &opname))
       return NULL;
       }
     if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, yield == NULL,
-        honour_dollar, resetok)))
+        honour_dollar, resetok, &textonly)))
       return NULL;
+    if (textonly) sub_textonly |= BIT(i);
     DEBUG(D_expand) if (i == 1 && !sub2_honour_dollar && Ustrchr(sub[1], '$'))
       debug_printf_indent("WARNING: the second arg is NOT expanded,"
                        " for security reasons\n");
@@ -2934,19 +2939,11 @@ switch(cond_type = identify_operator(&s, &opname))
 
     case ECOND_MATCH:   /* Regular expression match */
       {
-      const pcre2_code * re;
-      PCRE2_SIZE offset;
-      int err;
-
-      if (!(re = pcre2_compile((PCRE2_SPTR)sub[1], PCRE2_ZERO_TERMINATED,
-                               PCRE_COPT, &err, &offset, pcre_gen_cmp_ctx)))
-       {
-       uschar errbuf[128];
-       pcre2_get_error_message(err, errbuf, sizeof(errbuf));
-       expand_string_message = string_sprintf("regular expression error in "
-         "\"%s\": %s at offset %ld", sub[1], errbuf, (long)offset);
+      const pcre2_code * re = regex_compile(sub[1],
+                 sub_textonly & BIT(1) ? MCS_CACHEABLE : MCS_NOFLAGS,
+                 &expand_string_message, pcre_gen_cmp_ctx);
+      if (!re)
        return NULL;
-       }
 
       tempcond = regex_match_and_setup(re, sub[0], 0, -1);
       break;
@@ -3264,7 +3261,7 @@ switch(cond_type = identify_operator(&s, &opname))
 
     Uskip_whitespace(&s);
     if (*s++ != '{') goto COND_FAILED_CURLY_START;     /* }-for-text-editors */
-    if (!(sub[0] = expand_string_internal(s, TRUE, &s, yield == NULL, TRUE, resetok)))
+    if (!(sub[0] = expand_string_internal(s, TRUE, &s, yield == NULL, TRUE, resetok, NULL)))
       return NULL;
     /* {-for-text-editors */
     if (*s++ != '}') goto COND_FAILED_CURLY_END;
@@ -3352,7 +3349,7 @@ switch(cond_type = identify_operator(&s, &opname))
 
     if (Uskip_whitespace(&s) != '{') goto COND_FAILED_CURLY_START;     /* }-for-text-editors */
     ourname = cond_type == ECOND_BOOL_LAX ? US"bool_lax" : US"bool";
-    switch(read_subs(sub_arg, 1, 1, &s, yield == NULL, FALSE, ourname, resetok))
+    switch(read_subs(sub_arg, 1, 1, &s, yield == NULL, FALSE, ourname, resetok, NULL))
       {
       case 1: expand_string_message = string_sprintf(
                   "too few arguments or bracketing error for %s",
@@ -3420,7 +3417,7 @@ switch(cond_type = identify_operator(&s, &opname))
     uschar cksum[4];
     BOOL boolvalue = FALSE;
 
-    switch(read_subs(sub, 2, 2, CUSS &s, yield == NULL, FALSE, name, resetok))
+    switch(read_subs(sub, 2, 2, CUSS &s, yield == NULL, FALSE, name, resetok, NULL))
       {
       case 1: expand_string_message = US"too few arguments or bracketing "
        "error for inbound_srs";
@@ -3431,7 +3428,7 @@ switch(cond_type = identify_operator(&s, &opname))
     /* Match the given local_part against the SRS-encoded pattern */
 
     re = regex_must_compile(US"^(?i)SRS0=([^=]+)=([A-Z2-7]+)=([^=]*)=(.*)$",
-                           TRUE, FALSE);
+                           MCS_CASELESS | MCS_CACHEABLE, FALSE);
     md = pcre2_match_data_create(4+1, pcre_gen_ctx);
     if (pcre2_match(re, sub[0], PCRE2_ZERO_TERMINATED, 0, PCRE_EOPT,
                    md, pcre_gen_mtc_ctx) < 0)
@@ -3677,7 +3674,7 @@ if (*s++ != '{')
 want this string. Set skipping in the call in the fail case (this will always
 be the case if we were already skipping). */
 
-sub1 = expand_string_internal(s, TRUE, &s, !yes, TRUE, resetok);
+sub1 = expand_string_internal(s, TRUE, &s, !yes, TRUE, resetok, NULL);
 if (sub1 == NULL && (yes || !f.expand_string_forcedfail)) goto FAILED;
 f.expand_string_forcedfail = FALSE;
 if (*s++ != '}')
@@ -3706,7 +3703,7 @@ already skipping. */
 
 if (skip_whitespace(&s) == '{')
   {
-  sub2 = expand_string_internal(s+1, TRUE, &s, yes || skipping, TRUE, resetok);
+  sub2 = expand_string_internal(s+1, TRUE, &s, yes || skipping, TRUE, resetok, NULL);
   if (sub2 == NULL && (!yes || !f.expand_string_forcedfail)) goto FAILED;
   f.expand_string_forcedfail = FALSE;
   if (*s++ != '}')
@@ -4445,6 +4442,7 @@ Arguments:
                  FALSE if it's just another character
   resetok_p     if not NULL, pointer to flag - write FALSE if unsafe to reset
                 the store.
+  textonly_p    if not NULL, pointer to flag - write bool for only-met-text
 
 Returns:         NULL if expansion fails:
                    expand_string_forcedfail is set TRUE if failure was forced
@@ -4454,7 +4452,7 @@ Returns:         NULL if expansion fails:
 
 static uschar *
 expand_string_internal(const uschar *string, BOOL ket_ends, const uschar **left,
-  BOOL skipping, BOOL honour_dollar, BOOL *resetok_p)
+  BOOL skipping, BOOL honour_dollar, BOOL *resetok_p, BOOL * textonly_p)
 {
 rmark reset_point = store_mark();
 gstring * yield = string_get(Ustrlen(string) + 64);
@@ -4462,7 +4460,7 @@ int item_type;
 const uschar * s = string;
 const uschar * save_expand_nstring[EXPAND_MAXN+1];
 int save_expand_nlength[EXPAND_MAXN+1];
-BOOL resetok = TRUE, first = TRUE;
+BOOL resetok = TRUE, first = TRUE, textonly = TRUE;
 
 expand_level++;
 f.expand_string_forcedfail = FALSE;
@@ -4552,6 +4550,7 @@ while (*s)
     s += i;
     continue;
     }
+  textonly = FALSE;
 
   /* No { after the $ - must be a plain name or a number for string
   match variable. There has to be a fudge for variables that are the
@@ -4714,7 +4713,7 @@ while (*s)
       int rc;
 
       switch(read_subs(sub, nelem(sub), 1, &s, skipping, TRUE, name,
-                     &resetok))
+                     &resetok, NULL))
         {
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:
@@ -4750,7 +4749,7 @@ while (*s)
       uschar * sub_arg[1];
 
       switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, skipping, TRUE, name,
-                     &resetok))
+                     &resetok, NULL))
         {
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:
@@ -4837,7 +4836,7 @@ while (*s)
       uschar *encoded;
 
       switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, skipping, TRUE, name,
-                     &resetok))
+                     &resetok, NULL))
         {
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:
@@ -4897,7 +4896,7 @@ while (*s)
 
       if (Uskip_whitespace(&s) == '{')                                 /*}*/
         {
-        key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
+        key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok, NULL);
         if (!key) goto EXPAND_FAILED;                  /*{{*/
         if (*s++ != '}')
          {
@@ -4967,7 +4966,7 @@ while (*s)
        expand_string_message = US"missing '{' for lookup file-or-query arg";
        goto EXPAND_FAILED_CURLY;                                               /*}}*/
        }
-      if (!(filename = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok)))
+      if (!(filename = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok, NULL)))
        goto EXPAND_FAILED;
                                                                                /*{{*/
       if (*s++ != '}')
@@ -5071,7 +5070,7 @@ while (*s)
         }
 
       switch(read_subs(sub_arg, EXIM_PERL_MAX_ARGS + 1, 1, &s, skipping, TRUE,
-           name, &resetok))
+           name, &resetok, NULL))
         {
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:
@@ -5141,7 +5140,7 @@ while (*s)
       {
       uschar * sub_arg[3], * p, * domain;
 
-      switch(read_subs(sub_arg, 3, 2, &s, skipping, TRUE, name, &resetok))
+      switch(read_subs(sub_arg, 3, 2, &s, skipping, TRUE, name, &resetok, NULL))
         {
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:
@@ -5214,15 +5213,16 @@ while (*s)
       prvscheck_address = NULL;
       prvscheck_keynum = NULL;
 
-      switch(read_subs(sub_arg, 1, 1, &s, skipping, FALSE, name, &resetok))
+      switch(read_subs(sub_arg, 1, 1, &s, skipping, FALSE, name, &resetok, NULL))
         {
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:
         case 3: goto EXPAND_FAILED;
         }
 
-      re = regex_must_compile(US"^prvs\\=([0-9])([0-9]{3})([A-F0-9]{6})\\=(.+)\\@(.+)$",
-                              TRUE,FALSE);
+      re = regex_must_compile(
+       US"^prvs\\=([0-9])([0-9]{3})([A-F0-9]{6})\\=(.+)\\@(.+)$",
+       MCS_CASELESS | MCS_CACHEABLE, FALSE);
 
       if (regex_match_and_setup(re,sub_arg[0],0,-1))
         {
@@ -5232,11 +5232,14 @@ while (*s)
         uschar * hash = string_copyn(expand_nstring[3],expand_nlength[3]);
         uschar * domain = string_copyn(expand_nstring[5],expand_nlength[5]);
 
-        DEBUG(D_expand) debug_printf_indent("prvscheck localpart: %s\n", local_part);
-        DEBUG(D_expand) debug_printf_indent("prvscheck key number: %s\n", key_num);
-        DEBUG(D_expand) debug_printf_indent("prvscheck daystamp: %s\n", daystamp);
-        DEBUG(D_expand) debug_printf_indent("prvscheck hash: %s\n", hash);
-        DEBUG(D_expand) debug_printf_indent("prvscheck domain: %s\n", domain);
+        DEBUG(D_expand)
+         {
+         debug_printf_indent("prvscheck localpart: %s\n", local_part);
+         debug_printf_indent("prvscheck key number: %s\n", key_num);
+         debug_printf_indent("prvscheck daystamp: %s\n", daystamp);
+         debug_printf_indent("prvscheck hash: %s\n", hash);
+         debug_printf_indent("prvscheck domain: %s\n", domain);
+         }
 
         /* Set up expansion variables */
         g = string_cat (NULL, local_part);
@@ -5246,7 +5249,7 @@ while (*s)
         prvscheck_keynum = string_copy(key_num);
 
         /* Now expand the second argument */
-        switch(read_subs(sub_arg, 1, 1, &s, skipping, FALSE, name, &resetok))
+        switch(read_subs(sub_arg, 1, 1, &s, skipping, FALSE, name, &resetok, NULL))
           {
           case 1: goto EXPAND_FAILED_CURLY;
           case 2:
@@ -5257,7 +5260,6 @@ while (*s)
 
         p = prvs_hmac_sha1(prvscheck_address, sub_arg[0], prvscheck_keynum,
           daystamp);
-
         if (!p)
           {
           expand_string_message = US"hmac-sha1 conversion failed";
@@ -5300,7 +5302,7 @@ while (*s)
         /* Now expand the final argument. We leave this till now so that
         it can include $prvscheck_result. */
 
-        switch(read_subs(sub_arg, 1, 0, &s, skipping, TRUE, name, &resetok))
+        switch(read_subs(sub_arg, 1, 0, &s, skipping, TRUE, name, &resetok, NULL))
           {
           case 1: goto EXPAND_FAILED_CURLY;
           case 2:
@@ -5321,7 +5323,7 @@ while (*s)
            We need to make sure all subs are expanded first, so as to skip over
            the entire item. */
 
-        switch(read_subs(sub_arg, 2, 1, &s, skipping, TRUE, name, &resetok))
+        switch(read_subs(sub_arg, 2, 1, &s, skipping, TRUE, name, &resetok, NULL))
           {
           case 1: goto EXPAND_FAILED_CURLY;
           case 2:
@@ -5345,7 +5347,7 @@ while (*s)
         goto EXPAND_FAILED;
         }
 
-      switch(read_subs(sub_arg, 2, 1, &s, skipping, TRUE, name, &resetok))
+      switch(read_subs(sub_arg, 2, 1, &s, skipping, TRUE, name, &resetok, NULL))
         {
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:
@@ -5386,7 +5388,7 @@ while (*s)
       /* Read up to 4 arguments, but don't do the end of item check afterwards,
       because there may be a string for expansion on failure. */
 
-      switch(read_subs(sub_arg, 4, 2, &s, skipping, FALSE, name, &resetok))
+      switch(read_subs(sub_arg, 4, 2, &s, skipping, FALSE, name, &resetok, NULL))
         {
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:                             /* Won't occur: no end check */
@@ -5474,7 +5476,7 @@ while (*s)
 
       if (*s == '{')                                                   /*}*/
         {
-        if (!expand_string_internal(s+1, TRUE, &s, TRUE, TRUE, &resetok))
+        if (!expand_string_internal(s+1, TRUE, &s, TRUE, TRUE, &resetok, NULL))
           goto EXPAND_FAILED;                                          /*{*/
         if (*s++ != '}')
          {                                                             /*{*/
@@ -5500,7 +5502,7 @@ while (*s)
     SOCK_FAIL:
       if (*s != '{') goto EXPAND_FAILED;                               /*}*/
       DEBUG(D_any) debug_printf("%s\n", expand_string_message);
-      if (!(arg = expand_string_internal(s+1, TRUE, &s, FALSE, TRUE, &resetok)))
+      if (!(arg = expand_string_internal(s+1, TRUE, &s, FALSE, TRUE, &resetok, NULL)))
         goto EXPAND_FAILED;
       yield = string_cat(yield, arg);                                  /*{*/
       if (*s++ != '}')
@@ -5551,14 +5553,14 @@ while (*s)
       s++;
 
       if (late_expand)         /* this is the default case */
-       {
+       {                                               /*{*/
        int n = Ustrcspn(s, "}");
        arg = skipping ? NULL : string_copyn(s, n);
        s += n;
        }
       else
        {
-       if (!(arg = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok)))
+       if (!(arg = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok, NULL)))
          goto EXPAND_FAILED;
        Uskip_whitespace(&s);
        }
@@ -5667,7 +5669,7 @@ while (*s)
       int o2m;
       uschar * sub[3];
 
-      switch(read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok))
+      switch(read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok, NULL))
         {
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:
@@ -5709,7 +5711,7 @@ while (*s)
 
       sub[2] = NULL;
       switch(read_subs(sub, (item_type == EITEM_LENGTH)? 2:3, 2, &s, skipping,
-             TRUE, name, &resetok))
+             TRUE, name, &resetok, NULL))
         {
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:
@@ -5784,7 +5786,7 @@ while (*s)
       uschar innerkey[MAX_HASHBLOCKLEN];
       uschar outerkey[MAX_HASHBLOCKLEN];
 
-      switch (read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok))
+      switch (read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok, NULL))
         {
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:
@@ -5872,14 +5874,14 @@ while (*s)
       {
       const pcre2_code * re;
       int moffset, moffsetextra, slen;
-      PCRE2_SIZE roffset;
       pcre2_match_data * md;
-      int err, emptyopt;
+      int emptyopt;
       uschar * subject, * sub[3];
       int save_expand_nmax =
         save_expand_strings(save_expand_nstring, save_expand_nlength);
+      unsigned sub_textonly = 0;
 
-      switch(read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok))
+      switch(read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok, &sub_textonly))
         {
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:
@@ -5889,15 +5891,12 @@ while (*s)
 
       /* Compile the regular expression */
 
-      if (!(re = pcre2_compile((PCRE2_SPTR)sub[1], PCRE2_ZERO_TERMINATED,
-                 PCRE_COPT, &err, &roffset, pcre_gen_cmp_ctx)))
-        {
-        uschar errbuf[128];
-       pcre2_get_error_message(err, errbuf, sizeof(errbuf));
-        expand_string_message = string_sprintf("regular expression error in "
-          "\"%s\": %s at offset %ld", sub[1], errbuf, (long)roffset);
+      re = regex_compile(sub[1],
+             sub_textonly & BIT(1) ? MCS_CACHEABLE : MCS_NOFLAGS,
+             &expand_string_message, pcre_gen_cmp_ctx);
+      if (!re)
         goto EXPAND_FAILED;
-        }
+
       md = pcre2_match_data_create(EXPAND_MAXN + 1, pcre_gen_ctx);
 
       /* Now run a loop to do the substitutions as often as necessary. It ends
@@ -6016,7 +6015,7 @@ while (*s)
        {
         for (int j = 5; j > 0 && *s == '{'; j--)                       /*'}'*/
          {
-          if (!expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok))
+          if (!expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok, NULL))
            goto EXPAND_FAILED;                                 /*'{'*/
           if (*s++ != '}')
            {
@@ -6043,7 +6042,7 @@ while (*s)
         {
        if (Uskip_whitespace(&s) == '{')                                /*'}'*/
           {
-          if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok)))
+          if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok, NULL)))
            goto EXPAND_FAILED;                                         /*'{'*/
           if (*s++ != '}')
            {
@@ -6237,7 +6236,7 @@ while (*s)
          goto EXPAND_FAILED_CURLY;
          }
 
-       sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
+       sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok, NULL);
        if (!sub[i])     goto EXPAND_FAILED;                            /*{{*/
        if (*s++ != '}')
          {
@@ -6318,7 +6317,7 @@ while (*s)
     case EITEM_LISTQUOTE:
       {
       uschar * sub[2];
-      switch(read_subs(sub, 2, 2, &s, skipping, TRUE, name, &resetok))
+      switch(read_subs(sub, 2, 2, &s, skipping, TRUE, name, &resetok, NULL))
         {
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:
@@ -6347,7 +6346,7 @@ while (*s)
        expand_string_message = US"missing '{' for field arg of certextract";
        goto EXPAND_FAILED_CURLY;                                       /*}*/
        }
-      sub[0] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
+      sub[0] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok, NULL);
       if (!sub[0])     goto EXPAND_FAILED;                             /*{{*/
       if (*s++ != '}')
         {
@@ -6379,7 +6378,7 @@ while (*s)
          "be a certificate variable";
        goto EXPAND_FAILED;
        }
-      sub[1] = expand_string_internal(s+1, TRUE, &s, skipping, FALSE, &resetok);
+      sub[1] = expand_string_internal(s+1, TRUE, &s, skipping, FALSE, &resetok, NULL);
       if (!sub[1])     goto EXPAND_FAILED;                             /*{{*/
       if (*s++ != '}')
         {
@@ -6434,7 +6433,7 @@ while (*s)
        goto EXPAND_FAILED_CURLY;                                       /*}*/
        }
 
-      if (!(list = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok)))
+      if (!(list = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok, NULL)))
        goto EXPAND_FAILED;                                             /*{{*/
       if (*s++ != '}')
         {
@@ -6452,7 +6451,7 @@ while (*s)
          expand_string_message = US"missing '{' for second arg of reduce";
          goto EXPAND_FAILED_CURLY;                                     /*}*/
          }
-        t = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok);
+        t = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok, NULL);
         if (!t) goto EXPAND_FAILED;
         lookup_value = t;                                              /*{{*/
         if (*s++ != '}')
@@ -6479,7 +6478,7 @@ while (*s)
       the normal internal expansion function. */
 
       if (item_type != EITEM_FILTER)
-        temp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok);
+        temp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok, NULL);
       else
         if ((temp = eval_condition(expr, &resetok, NULL))) s = temp;
 
@@ -6541,7 +6540,7 @@ while (*s)
 
         else
           {
-         uschar * t = expand_string_internal(expr, TRUE, NULL, skipping, TRUE, &resetok);
+         uschar * t = expand_string_internal(expr, TRUE, NULL, skipping, TRUE, &resetok, NULL);
           temp = t;
           if (!temp)
             {
@@ -6632,7 +6631,7 @@ while (*s)
        goto EXPAND_FAILED_CURLY;                                       /*}*/
        }
 
-      srclist = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok);
+      srclist = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok, NULL);
       if (!srclist) goto EXPAND_FAILED;                                        /*{{*/
       if (*s++ != '}')
         {
@@ -6647,7 +6646,7 @@ while (*s)
        goto EXPAND_FAILED_CURLY;                                       /*}*/
        }
 
-      cmp = expand_string_internal(s, TRUE, &s, skipping, FALSE, &resetok);
+      cmp = expand_string_internal(s, TRUE, &s, skipping, FALSE, &resetok, NULL);
       if (!cmp) goto EXPAND_FAILED;                                    /*{{*/
       if (*s++ != '}')
         {
@@ -6682,7 +6681,7 @@ while (*s)
        }
 
       xtract = s;
-      if (!(tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok)))
+      if (!(tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok, NULL)))
        goto EXPAND_FAILED;
       xtract = string_copyn(xtract, s - xtract);
                                                                        /*{{*/
@@ -6710,7 +6709,7 @@ while (*s)
        /* extract field for comparisons */
        iterate_item = srcitem;
        if (  !(srcfield = expand_string_internal(xtract, FALSE, NULL, FALSE,
-                                         TRUE, &resetok))
+                                         TRUE, &resetok, NULL))
           || !*srcfield)
          {
          expand_string_message = string_sprintf(
@@ -6816,7 +6815,7 @@ while (*s)
         }
 
       switch(read_subs(argv, EXPAND_DLFUNC_MAX_ARGS + 2, 2, &s, skipping,
-           TRUE, name, &resetok))
+           TRUE, name, &resetok, NULL))
         {
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:
@@ -6892,7 +6891,7 @@ while (*s)
       if (Uskip_whitespace(&s) != '{')                                 /*}*/
        goto EXPAND_FAILED;
 
-      key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
+      key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok, NULL);
       if (!key) goto EXPAND_FAILED;                                    /*{{*/
       if (*s++ != '}')
         {
@@ -6927,7 +6926,7 @@ while (*s)
       gstring * g = NULL;
       BOOL quoted = FALSE;
 
-      switch (read_subs(sub, 3, 3, CUSS &s, skipping, TRUE, name, &resetok))
+      switch (read_subs(sub, 3, 3, CUSS &s, skipping, TRUE, name, &resetok, NULL))
         {
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:
@@ -7057,7 +7056,7 @@ NOT_ITEM: ;
          {
          const uschar * s1 = s;
          sub = expand_string_internal(s+2, TRUE, &s1, skipping,
-                 FALSE, &resetok);
+                 FALSE, &resetok, NULL);
          if (!sub)       goto EXPAND_FAILED;           /*{*/
          if (*s1 != '}')
            {                                           /*{*/
@@ -7075,7 +7074,7 @@ NOT_ITEM: ;
         /*FALLTHROUGH*/
 #endif
       default:
-       sub = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
+       sub = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok, NULL);
        if (!sub) goto EXPAND_FAILED;
        s++;
        break;
@@ -7172,7 +7171,7 @@ NOT_ITEM: ;
 
       case EOP_EXPAND:
        {
-       uschar *expanded = expand_string_internal(sub, FALSE, NULL, skipping, TRUE, &resetok);
+       uschar *expanded = expand_string_internal(sub, FALSE, NULL, skipping, TRUE, &resetok, NULL);
        if (!expanded)
          {
          expand_string_message =
@@ -8241,7 +8240,7 @@ NOT_ITEM: ;
 terminating brace. */
 
 if (ket_ends && !*s)
-  {
+  {                                                    /*{{*/
   expand_string_message = malformed_header
     ? US"missing } at end of string - could be header name not terminated by colon"
     : US"missing } at end of string";
@@ -8301,6 +8300,7 @@ DEBUG(D_expand)
        "skipping: result is not used\n");
     }
   }
+if (textonly_p) *textonly_p = textonly;
 expand_level--;
 return yield->s;
 
@@ -8349,16 +8349,20 @@ return NULL;
 }
 
 
+
 /* This is the external function call. Do a quick check for any expansion
 metacharacters, and if there are none, just return the input string.
 
-Argument: the string to be expanded
+Arguments
+       the string to be expanded
+       optional pointer for return boolean indicating no-dynamic-expansions
+
 Returns:  the expanded string, or NULL if expansion failed; if failure was
           due to a lookup deferring, search_find_defer will be TRUE
 */
 
 const uschar *
-expand_cstring(const uschar * string)
+expand_string_2(const uschar * string, BOOL * textonly_p)
 {
 if (Ustrpbrk(string, "$\\") != NULL)
   {
@@ -8368,19 +8372,22 @@ if (Ustrpbrk(string, "$\\") != NULL)
   f.search_find_defer = FALSE;
   malformed_header = FALSE;
   store_pool = POOL_MAIN;
-    s = expand_string_internal(string, FALSE, NULL, FALSE, TRUE, NULL);
+    s = expand_string_internal(string, FALSE, NULL, FALSE, TRUE, NULL, textonly_p);
   store_pool = old_pool;
   return s;
   }
+if (textonly_p) *textonly_p = TRUE;
 return string;
 }
 
+const uschar *
+expand_cstring(const uschar * string)
+{ return expand_string_2(string, NULL); }
 
 uschar *
 expand_string(uschar * string)
-{
-return US expand_cstring(CUS string);
-}
+{ return US expand_string_2(CUS string, NULL); }
+
 
 
 
index a3e31a3b52cd8a9de2721789fd46bd90c7df518d..cc4af230e0dbb0b11b1de29c17789d321c659ff3 100644 (file)
@@ -1424,213 +1424,203 @@ Returns:         TRUE if the condition is met
 */
 
 static BOOL
-test_condition(condition_block *c, BOOL toplevel)
+test_condition(condition_block * c, BOOL toplevel)
 {
-BOOL yield = FALSE;
-const uschar *exp[2], * p, * pp;
+BOOL yield = FALSE, textonly_re;
+const uschar * exp[2], * p, * pp;
 int val[2];
-int i;
 
-if (c == NULL) return TRUE;  /* does this ever occur? */
+if (!c) return TRUE;  /* does this ever occur? */
 
 switch (c->type)
   {
   case cond_and:
-  yield = test_condition(c->left.c, FALSE) &&
-          *error_pointer == NULL &&
-          test_condition(c->right.c, FALSE);
-  break;
+    yield = test_condition(c->left.c, FALSE) &&
+           *error_pointer == NULL &&
+           test_condition(c->right.c, FALSE);
+    break;
 
   case cond_or:
-  yield = test_condition(c->left.c, FALSE) ||
-          (*error_pointer == NULL &&
-          test_condition(c->right.c, FALSE));
-  break;
+    yield = test_condition(c->left.c, FALSE) ||
+           (*error_pointer == NULL &&
+           test_condition(c->right.c, FALSE));
+    break;
 
-  /* The personal test is meaningless in a system filter. The tests are now in
-  a separate function (so Sieve can use them). However, an Exim filter does not
-  scan Cc: (hence the FALSE argument). */
+    /* The personal test is meaningless in a system filter. The tests are now in
+    a separate function (so Sieve can use them). However, an Exim filter does not
+    scan Cc: (hence the FALSE argument). */
 
   case cond_personal:
-  yield = f.system_filtering? FALSE : filter_personal(c->left.a, FALSE);
-  break;
+    yield = f.system_filtering? FALSE : filter_personal(c->left.a, FALSE);
+    break;
 
   case cond_delivered:
-  yield = filter_delivered;
-  break;
+    yield = filter_delivered;
+    break;
 
-  /* Only TRUE if a message is actually being processed; FALSE for address
-  testing and verification. */
+    /* Only TRUE if a message is actually being processed; FALSE for address
+    testing and verification. */
 
   case cond_errormsg:
-  yield = message_id[0] != 0 &&
-    (sender_address == NULL || sender_address[0] == 0);
-  break;
+    yield = message_id[0] != 0 &&
+      (sender_address == NULL || sender_address[0] == 0);
+    break;
 
-  /* Only FALSE if a message is actually being processed; TRUE for address
-  and filter testing and verification. */
+    /* Only FALSE if a message is actually being processed; TRUE for address
+    and filter testing and verification. */
 
   case cond_firsttime:
-  yield = filter_test != FTEST_NONE || message_id[0] == 0 || f.deliver_firsttime;
-  break;
+    yield = filter_test != FTEST_NONE || message_id[0] == 0 || f.deliver_firsttime;
+    break;
 
-  /* Only TRUE if a message is actually being processed; FALSE for address
-  testing and verification. */
+    /* Only TRUE if a message is actually being processed; FALSE for address
+    testing and verification. */
 
   case cond_manualthaw:
-  yield = message_id[0] != 0 && f.deliver_manual_thaw;
-  break;
+    yield = message_id[0] != 0 && f.deliver_manual_thaw;
+    break;
 
-  /* The foranyaddress condition loops through a list of addresses */
+    /* The foranyaddress condition loops through a list of addresses */
 
   case cond_foranyaddress:
-  p = c->left.u;
-  if (!(pp = expand_cstring(p)))
-    {
-    *error_pointer = string_sprintf("failed to expand \"%s\" in "
-      "filter file: %s", p, expand_string_message);
-    return FALSE;
-    }
+    p = c->left.u;
+    if (!(pp = expand_cstring(p)))
+      {
+      *error_pointer = string_sprintf("failed to expand \"%s\" in "
+       "filter file: %s", p, expand_string_message);
+      return FALSE;
+      }
 
-  yield = FALSE;
-  f.parse_allow_group = TRUE;     /* Allow group syntax */
+    yield = FALSE;
+    f.parse_allow_group = TRUE;     /* Allow group syntax */
 
-  while (*pp)
-    {
-    uschar *error;
-    int start, end, domain;
-    uschar * s;
+    while (*pp)
+      {
+      uschar *error;
+      int start, end, domain;
+      uschar * s;
 
-    p = parse_find_address_end(pp, FALSE);
-    s = string_copyn(pp, p - pp);
+      p = parse_find_address_end(pp, FALSE);
+      s = string_copyn(pp, p - pp);
 
-    filter_thisaddress =
-      parse_extract_address(s, &error, &start, &end, &domain, FALSE);
+      filter_thisaddress =
+       parse_extract_address(s, &error, &start, &end, &domain, FALSE);
 
-    if (filter_thisaddress)
-      {
-      if ((filter_test != FTEST_NONE && debug_selector != 0) ||
-          (debug_selector & D_filter) != 0)
-        {
-        indent();
-        debug_printf_indent("Extracted address %s\n", filter_thisaddress);
-        }
-      yield = test_condition(c->right.c, FALSE);
-      }
+      if (filter_thisaddress)
+       {
+       if ((filter_test != FTEST_NONE && debug_selector != 0) ||
+           (debug_selector & D_filter) != 0)
+         {
+         indent();
+         debug_printf_indent("Extracted address %s\n", filter_thisaddress);
+         }
+       yield = test_condition(c->right.c, FALSE);
+       }
 
-    if (yield) break;
-    if (!*p) break;
-    pp = p + 1;
-    }
+      if (yield) break;
+      if (!*p) break;
+      pp = p + 1;
+      }
 
-  f.parse_allow_group = FALSE;      /* Reset group syntax flags */
-  f.parse_found_group = FALSE;
-  break;
+    f.parse_allow_group = FALSE;      /* Reset group syntax flags */
+    f.parse_found_group = FALSE;
+    break;
 
-  /* All other conditions have left and right values that need expanding;
-  on error, it doesn't matter what value is returned. */
+    /* All other conditions have left and right values that need expanding;
+    on error, it doesn't matter what value is returned. */
 
-  default:
-  p = c->left.u;
-  for (i = 0; i < 2; i++)
-    {
-    if (!(exp[i] = expand_cstring(p)))
+    default:
+    p = c->left.u;
+    for (int i = 0; i < 2; i++)
       {
-      *error_pointer = string_sprintf("failed to expand \"%s\" in "
-        "filter file: %s", p, expand_string_message);
-      return FALSE;
+      if (!(exp[i] = expand_string_2(p, &textonly_re)))
+       {
+       *error_pointer = string_sprintf("failed to expand \"%s\" in "
+         "filter file: %s", p, expand_string_message);
+       return FALSE;
+       }
+      p = c->right.u;
       }
-    p = c->right.u;
-    }
 
-  /* Inner switch for the different cases */
-
-  switch(c->type)
-    {
-    case cond_is:
-    yield = strcmpic(exp[0], exp[1]) == 0;
-    break;
+    /* Inner switch for the different cases */
 
-    case cond_IS:
-    yield = Ustrcmp(exp[0], exp[1]) == 0;
-    break;
-
-    case cond_contains:
-    yield = strstric_c(exp[0], exp[1], FALSE) != NULL;
-    break;
+    switch(c->type)
+      {
+      case cond_is:
+       yield = strcmpic(exp[0], exp[1]) == 0;
+       break;
 
-    case cond_CONTAINS:
-    yield = Ustrstr(exp[0], exp[1]) != NULL;
-    break;
+      case cond_IS:
+       yield = Ustrcmp(exp[0], exp[1]) == 0;
+       break;
 
-    case cond_begins:
-    yield = strncmpic(exp[0], exp[1], Ustrlen(exp[1])) == 0;
-    break;
+      case cond_contains:
+       yield = strstric_c(exp[0], exp[1], FALSE) != NULL;
+       break;
 
-    case cond_BEGINS:
-    yield = Ustrncmp(exp[0], exp[1], Ustrlen(exp[1])) == 0;
-    break;
+      case cond_CONTAINS:
+       yield = Ustrstr(exp[0], exp[1]) != NULL;
+       break;
 
-    case cond_ends:
-    case cond_ENDS:
-      {
-      int len = Ustrlen(exp[1]);
-      const uschar *s = exp[0] + Ustrlen(exp[0]) - len;
-      yield = s < exp[0]
-       ? FALSE
-       : (c->type == cond_ends ? strcmpic(s, exp[1]) : Ustrcmp(s, exp[1])) == 0;
-      }
-    break;
+      case cond_begins:
+       yield = strncmpic(exp[0], exp[1], Ustrlen(exp[1])) == 0;
+       break;
 
-    case cond_matches:
-    case cond_MATCHES:
-      {
-      const pcre2_code *re;
-      int err;
-      PCRE2_SIZE offset;
+      case cond_BEGINS:
+       yield = Ustrncmp(exp[0], exp[1], Ustrlen(exp[1])) == 0;
+       break;
 
-      if ((filter_test != FTEST_NONE && debug_selector != 0) ||
-         (debug_selector & D_filter) != 0)
+      case cond_ends:
+      case cond_ENDS:
        {
-       debug_printf_indent("Match expanded arguments:\n");
-       debug_printf_indent("  Subject = %s\n", exp[0]);
-       debug_printf_indent("  Pattern = %s\n", exp[1]);
+       int len = Ustrlen(exp[1]);
+       const uschar *s = exp[0] + Ustrlen(exp[0]) - len;
+       yield = s < exp[0]
+         ? FALSE
+         : (c->type == cond_ends ? strcmpic(s, exp[1]) : Ustrcmp(s, exp[1])) == 0;
+       break;
        }
 
-      if (!(re = pcre2_compile((PCRE2_SPTR)exp[1], PCRE2_ZERO_TERMINATED,
-                 PCRE_COPT | (c->type == cond_matches ? PCRE2_CASELESS : 0),
-                 &err, &offset, pcre_gen_cmp_ctx)))
+      case cond_matches:
+      case cond_MATCHES:
        {
-       uschar errbuf[128];
-       pcre2_get_error_message(err, errbuf, sizeof(errbuf));
-       *error_pointer = string_sprintf("error while compiling "
-         "regular expression \"%s\": %s at offset %ld",
-         exp[1], errbuf, (long)offset);
-       return FALSE;
-       }
+       const pcre2_code * re;
+       mcs_flags flags = textonly_re ? MCS_CACHEABLE : MCS_NOFLAGS;
 
-      yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1);
-      break;
-      }
+       if ((filter_test != FTEST_NONE && debug_selector != 0) ||
+           (debug_selector & D_filter) != 0)
+         {
+         debug_printf_indent("Match expanded arguments:\n");
+         debug_printf_indent("  Subject = %s\n", exp[0]);
+         debug_printf_indent("  Pattern = %s\n", exp[1]);
+         }
 
-    /* For above and below, convert the strings to numbers */
+       if (c->type == cond_matches) flags |= MCS_CASELESS;
+       if (!(re = regex_compile(exp[1], flags, error_pointer, pcre_gen_cmp_ctx)))
+         return FALSE;
 
-    case cond_above:
-    case cond_below:
-    for (i = 0; i < 2; i++)
-      {
-      val[i] = get_number(exp[i], &yield);
-      if (!yield)
-        {
-        *error_pointer = string_sprintf("malformed numerical string \"%s\"",
-          exp[i]);
-        return FALSE;
-        }
+       yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1);
+       break;
+       }
+
+      /* For above and below, convert the strings to numbers */
+
+      case cond_above:
+      case cond_below:
+       for (int i = 0; i < 2; i++)
+         {
+         val[i] = get_number(exp[i], &yield);
+         if (!yield)
+           {
+           *error_pointer = string_sprintf("malformed numerical string \"%s\"",
+             exp[i]);
+           return FALSE;
+           }
+         }
+       yield = c->type == cond_above ? (val[0] > val[1]) : (val[0] < val[1]);
+       break;
       }
-    yield = (c->type == cond_above)? (val[0] > val[1]) : (val[0] < val[1]);
     break;
-    }
-  break;
   }
 
 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
@@ -2356,7 +2346,7 @@ while (commands)
   commands = commands->next;
   }
 
-return filter_delivered? FF_DELIVERED : FF_NOTDELIVERED;
+return filter_delivered ? FF_DELIVERED : FF_NOTDELIVERED;
 }
 
 
index 9c5e379d4cd101ead9f36bda2b6e05588965d4b8..4caae346df35885f6cb5909302f003bc55c62a07 100644 (file)
@@ -183,6 +183,7 @@ extern void    release_cutthrough_connection(const uschar *);
 
 extern void    daemon_go(void);
 #ifndef COMPILE_UTILITY
+extern ssize_t daemon_client_sockname(struct sockaddr_un *, uschar **);
 extern ssize_t daemon_notifier_sockname(struct sockaddr_un *);
 #endif
 
@@ -263,6 +264,7 @@ extern int     exp_bool(address_item *addr,
 extern BOOL    expand_check_condition(uschar *, uschar *, uschar *);
 extern uschar *expand_file_big_buffer(const uschar *);
 extern uschar *expand_string(uschar *);        /* public, cannot make const */
+extern const uschar *expand_string_2(const uschar *, BOOL *);
 extern const uschar *expand_cstring(const uschar *); /* ... so use this one */
 extern uschar *expand_getkeyed(const uschar *, const uschar *);
 
@@ -335,7 +337,7 @@ extern BOOL    macro_read_assignment(uschar *);
 extern uschar *macros_expand(int, int *, BOOL *);
 extern void    mainlog_close(void);
 #ifdef WITH_CONTENT_SCAN
-extern int     malware(const uschar *, int);
+extern int     malware(const uschar *, BOOL, int);
 extern int     malware_in_file(uschar *);
 extern void    malware_init(void);
 extern gstring * malware_show_supported(gstring *);
@@ -348,7 +350,7 @@ extern int     match_check_list(const uschar **, int, tree_node **, unsigned int
                  const uschar *, const uschar **);
 extern int     match_isinlist(const uschar *, const uschar **, int, tree_node **,
                  unsigned int *, int, BOOL, const uschar **);
-extern int     match_check_string(const uschar *, const uschar *, int, BOOL, BOOL, BOOL,
+extern int     match_check_string(const uschar *, const uschar *, int, mcs_flags,
                  const uschar **);
 
 extern void    message_start(void);
@@ -363,7 +365,7 @@ extern int     mime_acl_check(uschar *acl, FILE *f,
                  struct mime_boundary_context *, uschar **, uschar **);
 extern int     mime_decode(const uschar **);
 extern ssize_t mime_decode_base64(FILE *, FILE *, uschar *);
-extern int     mime_regex(const uschar **);
+extern int     mime_regex(const uschar **, BOOL);
 extern void    mime_set_anomaly(int);
 #endif
 extern uschar *moan_check_errorcopy(uschar *);
@@ -436,11 +438,14 @@ extern BOOL    receive_msg(BOOL);
 extern int_eximarith_t receive_statvfs(BOOL, int *);
 extern void    receive_swallow_smtp(void);
 #ifdef WITH_CONTENT_SCAN
-extern int     regex(const uschar **);
+extern int     regex(const uschar **, BOOL);
 #endif
+extern void    regex_at_daemon(const uschar *);
 extern BOOL    regex_match(const pcre2_code *, const uschar *, int, uschar **);
 extern BOOL    regex_match_and_setup(const pcre2_code *, const uschar *, int, int);
-extern const pcre2_code *regex_must_compile(const uschar *, BOOL, BOOL);
+extern const pcre2_code *regex_compile(const uschar *, mcs_flags, uschar **,
+               pcre2_compile_context *);
+extern const pcre2_code *regex_must_compile(const uschar *, mcs_flags, BOOL);
 extern void    retry_add_item(address_item *, uschar *, int);
 extern BOOL    retry_check_address(const uschar *, host_item *, uschar *, BOOL,
                  uschar **, uschar **);
@@ -1224,6 +1229,7 @@ pid_t pid;
 DEBUG(D_any) debug_printf("%s forking for %s\n", process_purpose, purpose);
 if ((pid = fork()) == 0)
   {
+  f.daemon_listen = FALSE;
   process_purpose = purpose;
   DEBUG(D_any) debug_printf("postfork: %s\n", purpose);
   }
index 62c9b265989908d1cd5bfe9f262f4aea6bf6a40d..49988a8cc9c0e4eaf04ad50627e6ffe594e9bdc6 100644 (file)
@@ -234,6 +234,7 @@ struct global_flags f =
        .continue_more          = FALSE,
 
        .daemon_listen          = FALSE,
+       .daemon_scion           = FALSE,
        .debug_daemon           = FALSE,
        .deliver_firsttime      = FALSE,
        .deliver_force          = FALSE,
index 0f047110109f61968ad3c579d760079f48bea428..3d558455582b1d39b5b908093e0593e27308a374 100644 (file)
@@ -199,6 +199,7 @@ extern struct global_flags {
  BOOL   continue_more                  :1; /* Flag more addresses waiting */
 
  BOOL   daemon_listen                  :1; /* True if listening required */
+ BOOL   daemon_scion                   :1; /* Ancestor proc is daemon, and not re-exec'd */
  BOOL   debug_daemon                   :1; /* Debug the daemon process only */
  BOOL   deliver_firsttime              :1; /* True for first delivery attempt */
  BOOL   deliver_force                  :1; /* TRUE if delivery was forced */
index 898d8d5c490c68b594bc9cd0fc8e834ff3631a72..7ef59ff536889b88bfd2ee638d4724b719f116c2 100644 (file)
@@ -368,7 +368,7 @@ Returns:         cond if the header exists and contains one of the strings;
 /* First we have a local subroutine to handle a single pattern */
 
 static BOOL
-one_pattern_match(uschar *name, int slen, BOOL has_addresses, uschar *pattern)
+one_pattern_match(uschar * name, int slen, BOOL has_addresses, uschar * pattern)
 {
 BOOL yield = FALSE;
 const pcre2_code *re = NULL;
@@ -376,7 +376,7 @@ const pcre2_code *re = NULL;
 /* If the pattern is a regex, compile it. Bomb out if compiling fails; these
 patterns are all constructed internally and should be valid. */
 
-if (*pattern == '^') re = regex_must_compile(pattern, TRUE, FALSE);
+if (*pattern == '^') re = regex_must_compile(pattern, MCS_CASELESS, FALSE);
 
 /* Scan for the required header(s) and scan each one */
 
@@ -443,7 +443,7 @@ return yield;
 /* The externally visible interface */
 
 BOOL
-header_match(uschar *name, BOOL has_addresses, BOOL cond, string_item *strings,
+header_match(uschar * name, BOOL has_addresses, BOOL cond, string_item * strings,
   int count, ...)
 {
 va_list ap;
index fa89de12d747eb22ae214219fc3d5b7adf91880a..adbe6a267ce3993a18d1a67223290c67a60274ec 100644 (file)
@@ -1111,7 +1111,17 @@ should not be one active. */
 
 
 #define NOTIFIER_SOCKET_NAME   "exim_daemon_notify"
-#define NOTIFY_MSG_QRUN                1       /* Notify message types */
+/* Notify message types */
+#define NOTIFY_MSG_QRUN                1
 #define NOTIFY_QUEUE_SIZE_REQ  2
+#define NOTIFY_REGEX           3
+
+/* Flags for match_check_string() */
+typedef unsigned mcs_flags;
+#define MCS_NOFLAGS            0
+#define MCS_PARTIAL            BIT(0)  /* permit partial- search types */
+#define MCS_CASELESS           BIT(1)  /* caseless matching where possible */
+#define MCS_AT_SPECIAL         BIT(2)  /* recognize @, @[], etc. */
+#define MCS_CACHEABLE          BIT(3)  /* no dynamic expansions used for pattern */
 
 /* End of macros.h */
index 97643851154096ad164281043572cf40a62c6534..8b5ec27c40f795a9e6502c3552fa8c6f765b998a 100644 (file)
@@ -299,21 +299,10 @@ return sock;
 }
 
 static const pcre2_code *
-m_pcre_compile(const uschar * re, uschar ** errstr)
+m_pcre_compile(const uschar * re, BOOL cacheable, uschar ** errstr)
 {
-int err;
-PCRE2_SIZE roffset;
-const pcre2_code * cre;
-
-if (!(cre = pcre2_compile((PCRE2_SPTR)re, PCRE2_ZERO_TERMINATED,
-             PCRE_COPT, &err, &roffset, pcre_gen_cmp_ctx)))
-  {
-  uschar errbuf[128];
-  pcre2_get_error_message(err, errbuf, sizeof(errbuf));
-  *errstr= string_sprintf("regular expression error in '%s': %s at offset %ld",
-      re, errbuf, (long)roffset);
-  }
-return cre;
+return regex_compile(re, cacheable ? MCS_CACHEABLE : MCS_NOFLAGS, errstr,
+                     pcre_gen_cmp_ctx);
 }
 
 uschar *
@@ -332,7 +321,7 @@ return US substr;
 
 static const pcre2_code *
 m_pcre_nextinlist(const uschar ** list, int * sep,
- char * listerr, uschar ** errstr)
BOOL cacheable, char * listerr, uschar ** errstr)
 {
 const uschar * list_ele;
 const pcre2_code * cre = NULL;
@@ -343,7 +332,7 @@ else
   {
   DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "RE: ",
     string_printing(list_ele));
-  cre = m_pcre_compile(CUS list_ele, errstr);
+  cre = m_pcre_compile(CUS list_ele, cacheable, errstr);
   }
 return cre;
 }
@@ -569,6 +558,7 @@ is via malware(), or there's malware_in_file() used for testing/debugging.
 
 Arguments:
   malware_re    match condition for "malware="
+  cacheable    the RE did not use any dynamic elements during expansion
   scan_filename  the file holding the email to be scanned, if we're faking
                this up for the -bmalware test, else NULL
   timeout      if nonzero, non-default timeoutl
@@ -577,11 +567,12 @@ Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
                 where true means malware was found (condition applies)
 */
 static int
-malware_internal(const uschar * malware_re, const uschar * scan_filename,
-  int timeout)
+malware_internal(const uschar * malware_re, BOOL cacheable,
+  const uschar * scan_filename, int timeout)
 {
 int sep = 0;
 const uschar *av_scanner_work = av_scanner;
+BOOL av_scanner_textonly;
 uschar *scanner_name;
 unsigned long mbox_size;
 FILE *mbox_file;
@@ -608,30 +599,30 @@ the name), so we can close it right away.  Get the directory too. */
 eml_dir = string_copyn(eml_filename, Ustrrchr(eml_filename, '/') - eml_filename);
 
 /* parse 1st option */
-if (strcmpic(malware_re, US"false") == 0  ||  Ustrcmp(malware_re,"0") == 0)
+if (strcmpic(malware_re, US"false") == 0  ||  Ustrcmp(malware_re, "0") == 0)
   return FAIL;         /* explicitly no matching */
 
 /* special cases (match anything except empty) */
-if (  strcmpic(malware_re,US"true") == 0
-   || Ustrcmp(malware_re,"*") == 0
-   || Ustrcmp(malware_re,"1") == 0
+if (  strcmpic(malware_re, US"true") == 0
+   || Ustrcmp(malware_re, "*") == 0
+   || Ustrcmp(malware_re, "1") == 0
    )
   {
   if (  !malware_default_re
-     && !(malware_default_re = m_pcre_compile(malware_regex_default, &errstr)))
+     && !(malware_default_re = m_pcre_compile(malware_regex_default, FALSE, &errstr)))
     return malware_panic_defer(errstr);
   malware_re = malware_regex_default;
   re = malware_default_re;
   }
 
 /* compile the regex, see if it works */
-else if (!(re = m_pcre_compile(malware_re, &errstr)))
+else if (!(re = m_pcre_compile(malware_re, cacheable, &errstr)))
   return malware_panic_defer(errstr);
 
 /* if av_scanner starts with a dollar, expand it first */
 if (*av_scanner == '$')
   {
-  if (!(av_scanner_work = expand_string(av_scanner)))
+  if (!(av_scanner_work = expand_string_2(av_scanner, &av_scanner_textonly)))
     return malware_panic_defer(
         string_sprintf("av_scanner starts with $, but expansion failed: %s",
         expand_string_message));
@@ -642,6 +633,8 @@ if (*av_scanner == '$')
   malware_name = NULL;
   malware_ok = FALSE;
   }
+else
+  av_scanner_textonly = TRUE;
 
 /* Do not scan twice (unless av_scanner is dynamic). */
 if (!malware_ok)
@@ -746,13 +739,11 @@ if (!malware_ok)
     case M_FPROT6D: /* "f-prot6d" scanner type ----------------------------------- */
       {
       int bread;
-      uschar * e;
-      uschar * linebuffer;
-      uschar * scanrequest;
+      uschar * e, * linebuffer, * scanrequest;
       uschar av_buffer[1024];
 
-      if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, &errstr)))
-        || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, &errstr))))
+      if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, FALSE, &errstr)))
+        || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, FALSE, &errstr))))
         return malware_panic_defer(errstr);
 
       scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename);
@@ -922,7 +913,7 @@ badseek:  err = errno;
 
        /* set up match regex */
        if (!drweb_re)
-         drweb_re = m_pcre_compile(drweb_re_str, &errstr);
+         drweb_re = m_pcre_compile(drweb_re_str, FALSE, &errstr);
 
        /* read and concatenate virus names into one string */
        for (int i = 0; i < drweb_vnum; i++)
@@ -1101,7 +1092,7 @@ badseek:  err = errno;
       /* set up match */
       /* todo also SUSPICION\t */
       if (!fsec_re)
-       fsec_re = m_pcre_compile(fsec_re_str, &errstr);
+       fsec_re = m_pcre_compile(fsec_re_str, FALSE, &errstr);
 
       /* read report, linewise. Apply a timeout as the Fsecure daemon
       sometimes wants an answer to "PING" but they won't tell us what */
@@ -1225,12 +1216,12 @@ badseek:  err = errno;
            /* set up match regex, depends on retcode */
            if (kav_rc == 3)
              {
-             if (!kav_re_sus) kav_re_sus = m_pcre_compile(kav_re_sus_str, &errstr);
+             if (!kav_re_sus) kav_re_sus = m_pcre_compile(kav_re_sus_str, FALSE, &errstr);
              kav_re = kav_re_sus;
              }
            else
              {
-             if (!kav_re_inf) kav_re_inf = m_pcre_compile(kav_re_inf_str, &errstr);
+             if (!kav_re_inf) kav_re_inf = m_pcre_compile(kav_re_inf_str, FALSE, &errstr);
              kav_re = kav_re_inf;
              }
 
@@ -1279,13 +1270,13 @@ badseek:  err = errno;
        return m_panic_defer(scanent, NULL, errstr);
 
       /* find scanner output trigger */
-      cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep,
+      cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep, av_scanner_textonly,
                                "missing trigger specification", &errstr);
       if (!cmdline_trigger_re)
        return m_panic_defer(scanent, NULL, errstr);
 
       /* find scanner name regex */
-      cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep,
+      cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep, av_scanner_textonly,
                          "missing virus name regex specification", &errstr);
       if (!cmdline_regex_re)
        return m_panic_defer(scanent, NULL, errstr);
@@ -1908,13 +1899,13 @@ badseek:  err = errno;
        string_printing(sockline_scanner));
 
       /* find scanner output trigger */
-      sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
+      sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep, av_scanner_textonly,
                                "missing trigger specification", &errstr);
       if (!sockline_trig_re)
        return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
 
       /* find virus name regex */
-      sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
+      sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep, av_scanner_textonly,
                          "missing virus name regex specification", &errstr);
       if (!sockline_name_re)
        return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock);
@@ -2045,11 +2036,11 @@ badseek:  err = errno;
        */
 
       if (  (  !ava_re_clean
-            && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr)))
+            && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, FALSE, &errstr)))
         || (  !ava_re_virus
-           && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, &errstr)))
+           && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, FALSE, &errstr)))
         || (  !ava_re_error
-           && !(ava_re_error = m_pcre_compile(ava_re_error_str, &errstr)))
+           && !(ava_re_error = m_pcre_compile(ava_re_error_str, FALSE, &errstr)))
         )
        return malware_panic_defer(errstr);
 
@@ -2211,15 +2202,16 @@ filename; it's a wrapper around the malware_file function.
 
 Arguments:
   malware_re  match condition for "malware="
+  cacheable   the RE did not use any dynamic elements during expansion
   timeout     if nonzero, timeout in seconds
 
 Returns:      Exim message processing code (OK, FAIL, DEFER, ...)
               where true means malware was found (condition applies)
 */
 int
-malware(const uschar * malware_re, int timeout)
+malware(const uschar * malware_re, BOOL cacheable, int timeout)
 {
-int ret = malware_internal(malware_re, NULL, timeout);
+int ret = malware_internal(malware_re, cacheable, NULL, timeout);
 
 if (ret == DEFER) av_failed = TRUE;
 return ret;
@@ -2259,7 +2251,7 @@ recipients_list = NULL;
 receive_add_recipient(US"malware-victim@example.net", -1);
 f.enable_dollar_recipients = TRUE;
 
-ret = malware_internal(US"*", eml_filename, 0);
+ret = malware_internal(US"*", TRUE, eml_filename, 0);
 
 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
 spool_mbox_ok = 1;
@@ -2280,35 +2272,35 @@ void
 malware_init(void)
 {
 if (!malware_default_re)
-  malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE);
+  malware_default_re = regex_must_compile(malware_regex_default, MCS_NOFLAGS, TRUE);
 
 #ifndef DISABLE_MAL_DRWEB
 if (!drweb_re)
-  drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE);
+  drweb_re = regex_must_compile(drweb_re_str, MCS_NOFLAGS, TRUE);
 #endif
 #ifndef DISABLE_MAL_FSECURE
 if (!fsec_re)
-  fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE);
+  fsec_re = regex_must_compile(fsec_re_str, MCS_NOFLAGS, TRUE);
 #endif
 #ifndef DISABLE_MAL_KAV
 if (!kav_re_sus)
-  kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE);
+  kav_re_sus = regex_must_compile(kav_re_sus_str, MCS_NOFLAGS, TRUE);
 if (!kav_re_inf)
-  kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE);
+  kav_re_inf = regex_must_compile(kav_re_inf_str, MCS_NOFLAGS, TRUE);
 #endif
 #ifndef DISABLE_MAL_AVAST
 if (!ava_re_clean)
-  ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE);
+  ava_re_clean = regex_must_compile(ava_re_clean_str, MCS_NOFLAGS, TRUE);
 if (!ava_re_virus)
-  ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE);
+  ava_re_virus = regex_must_compile(ava_re_virus_str, MCS_NOFLAGS, TRUE);
 if (!ava_re_error)
-  ava_re_error = regex_must_compile(ava_re_error_str, FALSE, TRUE);
+  ava_re_error = regex_must_compile(ava_re_error_str, MCS_NOFLAGS, TRUE);
 #endif
 #ifndef DISABLE_MAL_FFROT6D
 if (!fprot6d_re_error)
-  fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE);
+  fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, MCS_NOFLAGS, TRUE);
 if (!fprot6d_re_virus)
-  fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE);
+  fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, MCS_NOFLAGS, TRUE);
 #endif
 }
 
index 2e4bff078eacfe6195b26f0c63c02938c1932e95..b4a0352ee735e0c6e2143001b3b3ff233301fdf8 100644 (file)
@@ -19,9 +19,7 @@ typedef struct check_string_block {
   const uschar *origsubject;           /* caseful; keep these two first, in */
   const uschar *subject;               /* step with the block below */
   int    expand_setup;
-  BOOL   use_partial;
-  BOOL   caseless;
-  BOOL   at_is_special;
+  mcs_flags flags;                     /* MCS_* defs in macros.h */
 } check_string_block;
 
 
@@ -32,7 +30,7 @@ typedef struct check_address_block {
   const uschar *origaddress;         /* caseful; keep these two first, in */
   uschar *address;                   /* step with the block above */
   int    expand_setup;
-  BOOL   caseless;
+  mcs_flags flags;                     /* MCS_CASELESS, MCS_TEXTONLY_RE */
 } check_address_block;
 
 
@@ -93,9 +91,10 @@ Returns:       OK    if matched
 */
 
 static int
-check_string(void *arg, const uschar *pattern, const uschar **valueptr, uschar **error)
+check_string(void * arg, const uschar * pattern, const uschar ** valueptr,
+  uschar ** error)
 {
-const check_string_block *cb = arg;
+const check_string_block * cb = arg;
 int search_type, partial, affixlen, starflags;
 int expand_setup = cb->expand_setup;
 const uschar * affix, * opts;
@@ -128,7 +127,8 @@ required. */
 
 if (pattern[0] == '^')
   {
-  const pcre2_code * re = regex_must_compile(pattern, cb->caseless, FALSE);
+  const pcre2_code * re = regex_must_compile(pattern,
+      cb->flags & (MCS_CACHEABLE | MCS_CASELESS), FALSE);
   if (expand_setup < 0
       ? !regex_match(re, s, -1, NULL)
       : !regex_match_and_setup(re, s, 0, expand_setup)
@@ -147,7 +147,7 @@ if (pattern[0] == '*')
 
   patlen = Ustrlen(++pattern);
   if (patlen > slen) return FAIL;
-  if (cb->caseless
+  if (cb->flags & MCS_CASELESS
       ? strncmpic(s + slen - patlen, pattern, patlen) != 0
       : Ustrncmp(s + slen - patlen, pattern, patlen) != 0)
     return FAIL;
@@ -166,7 +166,7 @@ the primary host name - implement this by changing the pattern. For the other
 cases we have to do some more work. If we don't recognize a special pattern,
 just fall through - the match will fail. */
 
-if (cb->at_is_special && pattern[0] == '@')
+if (cb->flags & MCS_AT_SPECIAL && pattern[0] == '@')
   {
   if (pattern[1] == 0)
     {
@@ -260,10 +260,10 @@ NOT_AT_SPECIAL:
 
 if ((semicolon = Ustrchr(pattern, ';')) == NULL)
   {
-  if (cb->caseless ? strcmpic(s, pattern) != 0 : Ustrcmp(s, pattern) != 0)
+  if (cb->flags & MCS_CASELESS ? strcmpic(s, pattern) != 0 : Ustrcmp(s, pattern) != 0)
     return FAIL;
-  if (expand_setup >= 0) expand_nmax = expand_setup;   /* Original code!   $0 gets the matched subject */
-  if (valueptr) *valueptr = pattern;   /* "value" gets the pattern */
+  if (expand_setup >= 0) expand_nmax = expand_setup;   /* $0 gets the matched subject */
+  if (valueptr) *valueptr = pattern;                   /* "value" gets the pattern */
   return OK;
   }
 
@@ -280,7 +280,7 @@ if (search_type < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
 /* Partial matching is not appropriate for certain lookups (e.g. when looking
 up user@domain for sender rejection). There's a flag to disable it. */
 
-if (!cb->use_partial) partial = -1;
+if (!(cb->flags & MCS_PARTIAL)) partial = -1;
 
 /* Set the parameters for the three different kinds of lookup. */
 
@@ -316,9 +316,10 @@ Arguments:
   s            the subject string to be checked
   pattern      the pattern to check it against
   expand_setup expansion setup option (see check_string())
-  use_partial  if FALSE, override any partial- search types
-  caseless     TRUE for caseless matching where possible
-  at_is_special TRUE to recognize @, @[], etc.
+  flags
+   use_partial  if FALSE, override any partial- search types
+   caseless     TRUE for caseless matching where possible
+   at_is_special TRUE to recognize @, @[], etc.
   valueptr     if not NULL, and a file lookup was done, return the result
                  here instead of discarding it; else set it to point to NULL
 
@@ -328,16 +329,14 @@ Returns:       OK    if matched
 */
 
 int
-match_check_string(const uschar *s, const uschar *pattern, int expand_setup,
-  BOOL use_partial, BOOL caseless, BOOL at_is_special, const uschar **valueptr)
+match_check_string(const uschar * s, const uschar * pattern, int expand_setup,
+  mcs_flags flags, const uschar ** valueptr)
 {
 check_string_block cb;
 cb.origsubject = s;
-cb.subject = caseless ? string_copylc(s) : string_copy(s);
+cb.subject = flags & MCS_CASELESS ? string_copylc(s) : string_copy(s);
 cb.expand_setup = expand_setup;
-cb.use_partial = use_partial;
-cb.caseless = caseless;
-cb.at_is_special = at_is_special;
+cb.flags = flags;
 return check_string(&cb, pattern, valueptr, NULL);
 }
 
@@ -364,14 +363,9 @@ switch(type)
   {
   case MCL_STRING:
   case MCL_DOMAIN:
-  case MCL_LOCALPART:
-    return ((check_string_block *)arg)->subject;
-
-  case MCL_HOST:
-    return ((check_host_block *)arg)->host_address;
-
-  case MCL_ADDRESS:
-    return ((check_address_block *)arg)->address;
+  case MCL_LOCALPART:  return ((check_string_block *)arg)->subject;
+  case MCL_HOST:       return ((check_host_block *)arg)->host_address;
+  case MCL_ADDRESS:    return ((check_address_block *)arg)->address;
   }
 return US"";  /* In practice, should never happen */
 }
@@ -438,6 +432,7 @@ BOOL include_unknown = FALSE, ignore_unknown = FALSE,
 const uschar *list;
 uschar *sss;
 uschar *ot = NULL;
+BOOL textonly_re;
 
 /* Save time by not scanning for the option name when we don't need it. */
 
@@ -465,6 +460,7 @@ if (type >= MCL_NOEXPAND)
   {
   list = *listptr;
   type -= MCL_NOEXPAND;       /* Remove the "no expand" flag */
+  textonly_re = TRUE;
   }
 else
   {
@@ -475,11 +471,11 @@ else
     {
     check_string_block *cb = (check_string_block *)arg;
     deliver_domain = string_copy(cb->subject);
-    list = expand_cstring(*listptr);
+    list = expand_string_2(*listptr, &textonly_re);
     deliver_domain = NULL;
     }
   else
-    list = expand_cstring(*listptr);
+    list = expand_string_2(*listptr, &textonly_re);
 
   if (!list)
     {
@@ -495,6 +491,15 @@ else
     }
   }
 
+if (textonly_re) switch (type)
+  {
+  case MCL_STRING:
+  case MCL_DOMAIN:
+  case MCL_LOCALPART: ((check_string_block *)arg)->flags |= MCS_CACHEABLE; break;
+  case MCL_HOST:     ((check_host_block *)arg)->flags |= MCS_CACHEABLE; break;
+  case MCL_ADDRESS: ((check_address_block *)arg)->flags |= MCS_CACHEABLE; break;
+  }
+
 /* For an unnamed list, use the expanded version in comments */
 #define LIST_LIMIT_PR 2048
 
@@ -530,7 +535,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
 
       if (at)
         Ustrncpy(cb->address, cb->origaddress, at - cb->origaddress);
-      cb->caseless = FALSE;
+      cb->flags &= ~MCS_CASELESS;
       continue;
       }
     }
@@ -543,7 +548,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
       {
       check_string_block *cb = (check_string_block *)arg;
       Ustrcpy(US cb->subject, cb->origsubject);
-      cb->caseless = FALSE;
+      cb->flags &= ~MCS_CASELESS;
       continue;
       }
     }
@@ -958,15 +963,13 @@ unsigned int *local_cache_bits = cache_bits;
 check_string_block cb;
 cb.origsubject = s;
 cb.subject = caseless ? string_copylc(s) : string_copy(s);
-cb.at_is_special = FALSE;
+cb.flags = caseless ? MCS_PARTIAL+MCS_CASELESS : MCS_PARTIAL;
 switch (type & ~MCL_NOEXPAND)
   {
-  case MCL_DOMAIN:     cb.at_is_special = TRUE;        /*FALLTHROUGH*/
+  case MCL_DOMAIN:     cb.flags |= MCS_AT_SPECIAL;     /*FALLTHROUGH*/
   case MCL_LOCALPART:  cb.expand_setup = 0;                            break;
   default:             cb.expand_setup = sep > UCHAR_MAX ? 0 : -1;     break;
   }
-cb.use_partial = TRUE;
-cb.caseless = caseless;
 if (valueptr) *valueptr = NULL;
 return  match_check_list(listptr, sep, anchorptr, &local_cache_bits,
   check_string, &cb, type, s, valueptr);
@@ -1003,7 +1006,8 @@ Returns:         OK     for a match
 */
 
 static int
-check_address(void *arg, const uschar *pattern, const uschar **valueptr, uschar **error)
+check_address(void * arg, const uschar * pattern, const uschar ** valueptr,
+  uschar ** error)
 {
 check_address_block * cb = (check_address_block *)arg;
 check_string_block csb;
@@ -1026,7 +1030,7 @@ sdomain = Ustrrchr(subject, '@');
 /* The only case where a subject may not have a domain is if the subject is
 empty. Otherwise, a subject with no domain is a serious configuration error. */
 
-if (sdomain == NULL && *subject != 0)
+if (!sdomain && *subject)
   {
   log_write(0, LOG_MAIN|LOG_PANIC, "no @ found in the subject of an "
     "address list match: subject=\"%s\" pattern=\"%s\"", subject, pattern);
@@ -1037,14 +1041,14 @@ if (sdomain == NULL && *subject != 0)
 This may be the empty address. */
 
 if (*pattern == '^')
-  return match_check_string(subject, pattern, cb->expand_setup, TRUE,
-    cb->caseless, FALSE, NULL);
+  return match_check_string(subject, pattern, cb->expand_setup,
+           cb->flags | MCS_PARTIAL, NULL);
 
 /* Handle a pattern that is just a lookup. Skip over possible lookup names
 (letters, digits, hyphens). Skip over a possible * or *@ at the end. Then we
 must have a semicolon for it to be a lookup. */
 
-for (s = pattern; isalnum(*s) || *s == '-'; s++);
+for (s = pattern; isalnum(*s) || *s == '-'; s++) ;
 if (*s == '*') s++;
 if (*s == '@') s++;
 
@@ -1057,8 +1061,7 @@ if (*s == ';')
   if (Ustrncmp(pattern, "partial-", 8) == 0)
     log_write(0, LOG_MAIN|LOG_PANIC, "partial matching is not applicable to "
       "whole-address lookups: ignored \"partial-\" in \"%s\"", pattern);
-  return match_check_string(subject, pattern, -1, FALSE, cb->caseless, FALSE,
-    valueptr);
+  return match_check_string(subject, pattern, -1, cb->flags, valueptr);
   }
 
 /* For the remaining cases, an empty subject matches only an empty pattern,
@@ -1085,19 +1088,20 @@ if (pattern[0] == '@' && pattern[1] == '@')
     {
     int sep = 0;
 
-    if ((rc = match_check_string(key, pattern + 2, -1, TRUE, FALSE, FALSE,
-      CUSS &list)) != OK) return rc;
+    if ((rc = match_check_string(key, pattern + 2, -1, MCS_PARTIAL, CUSS &list))
+       != OK)
+      return rc;
 
     /* Check for chaining from the last item; set up the next key if one
     is found. */
 
     ss = Ustrrchr(list, ':');
-    if (ss == NULL) ss = list; else ss++;
-    while (isspace(*ss)) ss++;
+    if (!ss) ss = list; else ss++;
+    Uskip_whitespace(&ss);
     if (*ss == '>')
       {
       *ss++ = 0;
-      while (isspace(*ss)) ss++;
+      Uskip_whitespace(&ss);
       key = string_copy(ss);
       }
     else key = NULL;
@@ -1117,8 +1121,7 @@ if (pattern[0] == '@' && pattern[1] == '@')
       else local_yield = OK;
 
       *sdomain = 0;
-      rc = match_check_string(subject, ss, -1, TRUE, cb->caseless, FALSE,
-        valueptr);
+      rc = match_check_string(subject, ss, -1, cb->flags + MCS_PARTIAL, valueptr);
       *sdomain = '@';
 
       switch(rc)
@@ -1148,8 +1151,7 @@ if (pattern[0] == '@' && pattern[1] == '@')
 /* We get here if the pattern is not a lookup or a regular expression. If it
 contains an @ there is both a local part and a domain. */
 
-pdomain = Ustrrchr(pattern, '@');
-if (pdomain != NULL)
+if ((pdomain = Ustrrchr(pattern, '@')))
   {
   int pllen, sllen;
 
@@ -1177,7 +1179,7 @@ if (pdomain != NULL)
     {
     int cllen = pllen - 1;
     if (sllen < cllen) return FAIL;
-    if (cb->caseless
+    if (cb->flags & MCS_CASELESS
         ? strncmpic(subject+sllen-cllen, pattern + 1, cllen) != 0
         : Ustrncmp(subject+sllen-cllen, pattern + 1, cllen) != 0)
         return FAIL;
@@ -1192,7 +1194,7 @@ if (pdomain != NULL)
   else
     {
     if (sllen != pllen) return FAIL;
-    if (cb->caseless
+    if (cb->flags & MCS_CASELESS
         ? strncmpic(subject, pattern, sllen) != 0
        : Ustrncmp(subject, pattern, sllen) != 0) return FAIL;
     }
@@ -1205,18 +1207,17 @@ original code read as follows:
 
   return match_check_string(sdomain + 1,
       pdomain ? pdomain + 1 : pattern,
-      cb->expand_setup + expand_inc, TRUE, cb->caseless, TRUE, NULL);
+      cb->expand_setup + expand_inc, cb->flags, NULL);
 
 This supported only literal domains and *.x.y patterns. In order to allow for
-named domain lists (so that you can right, for example, "senders=+xxxx"), it
+named domain lists (so that you can write, for example, "senders=+xxxx"), it
 was changed to use the list scanning function. */
 
 csb.origsubject = sdomain + 1;
-csb.subject = cb->caseless ? string_copylc(sdomain+1) : string_copy(sdomain+1);
+csb.subject = cb->flags & MCS_CASELESS
+  ? string_copylc(sdomain+1) : string_copy(sdomain+1);
 csb.expand_setup = cb->expand_setup + expand_inc;
-csb.use_partial = TRUE;
-csb.caseless = cb->caseless;
-csb.at_is_special = TRUE;
+csb.flags = MCS_PARTIAL | MCS_AT_SPECIAL | cb->flags & MCS_CASELESS;
 
 listptr = pdomain ? pdomain + 1 : pattern;
 if (valueptr) *valueptr = NULL;
@@ -1321,10 +1322,10 @@ if (expand_setup == 0)
 ab.origaddress = address;
 /* ab.address is above */
 ab.expand_setup = expand_setup;
-ab.caseless = caseless;
+ab.flags = caseless ? MCS_CASELESS : 0;
 
 return match_check_list(listptr, sep, &addresslist_anchor, &local_cache_bits,
-  check_address, &ab, MCL_ADDRESS + (expand? 0:MCL_NOEXPAND), address,
+  check_address, &ab, MCL_ADDRESS + (expand ? 0 : MCL_NOEXPAND), address,
     valueptr);
 }
 
index c0a1cd18222d0b2cc4300511c1b288dd481c66e3..6e47d2c8a9de7eaef73afb0dfeac910a06035cda 100644 (file)
@@ -423,11 +423,11 @@ if (!recurse)
 /* If deliver_selectstring is a regex, compile it. */
 
 if (deliver_selectstring && f.deliver_selectstring_regex)
-  selectstring_regex = regex_must_compile(deliver_selectstring, TRUE, FALSE);
+  selectstring_regex = regex_must_compile(deliver_selectstring, MCS_CASELESS, FALSE);
 
 if (deliver_selectstring_sender && f.deliver_selectstring_sender_regex)
   selectstring_regex_sender =
-    regex_must_compile(deliver_selectstring_sender, TRUE, FALSE);
+    regex_must_compile(deliver_selectstring_sender, MCS_CASELESS, FALSE);
 
 /* If the spool is split into subdirectories, we want to process it one
 directory at a time, so as to spread out the directory scanning and the
index c74b70b555e87d21090e5e67f0519af32ba07463..5068dc60e539b116866c5f6353b6c31c1bdf33dc 100644 (file)
@@ -3498,7 +3498,7 @@ if (!process_log_path || !*process_log_path)
 /* Compile the regex for matching a UUCP-style "From_" line in an incoming
 message. */
 
-regex_From = regex_must_compile(uucp_from_pattern, FALSE, TRUE);
+regex_From = regex_must_compile(uucp_from_pattern, MCS_NOFLAGS, TRUE);
 
 /* Unpick the SMTP rate limiting options, if set */
 
index 9b7b07405a78c1b3ac7f1436906026884f0b44ac..5de1c1704d02bc850d625b1d65b9f3d48209e9dd 100644 (file)
@@ -18,9 +18,9 @@
 
 /* Structure to hold a list of Regular expressions */
 typedef struct pcre_list {
-  pcre2_code *re;
-  uschar *pcre_text;
-  struct pcre_list *next;
+  const pcre2_code *   re;
+  uschar *             pcre_text;
+  struct pcre_list *   next;
 } pcre_list;
 
 uschar regex_match_string_buffer[1024];
@@ -28,31 +28,27 @@ uschar regex_match_string_buffer[1024];
 extern FILE *mime_stream;
 extern uschar *mime_current_boundary;
 
+
 static pcre_list *
-compile(const uschar * list)
+compile(const uschar * list, BOOL cacheable)
 {
 int sep = 0;
-uschar *regex_string;
-pcre_list *re_list_head = NULL;
-pcre_list *ri;
+uschar * regex_string;
+pcre_list * re_list_head = NULL;
+pcre_list * ri;
 
 /* precompile our regexes */
 while ((regex_string = string_nextinlist(&list, &sep, NULL, 0)))
   if (strcmpic(regex_string, US"false") != 0 && Ustrcmp(regex_string, "0") != 0)
     {
-    pcre2_code * re;
-    int err;
-    PCRE2_SIZE pcre_erroffset;
-
     /* compile our regular expression */
-    if (!(re = pcre2_compile( (PCRE2_SPTR) regex_string, PCRE2_ZERO_TERMINATED,
-                 0, &err, &pcre_erroffset, pcre_gen_cmp_ctx)))
+    uschar * errstr;
+    const pcre2_code * re = regex_compile(regex_string,
+      cacheable ? MCS_CACHEABLE : MCS_NOFLAGS, &errstr, pcre_gen_cmp_ctx);
+
+    if (!re)
       {
-      uschar errbuf[128];
-      pcre2_get_error_message(err, errbuf, sizeof(errbuf));
-      log_write(0, LOG_MAIN,
-          "regex acl condition warning - error in regex '%s': %s at offset %ld, skipped.",
-          regex_string, errbuf, (long)pcre_erroffset);
+      log_write(0, LOG_MAIN, "regex acl condition warning - %s, skipped", errstr);
       continue;
       }
 
@@ -96,8 +92,10 @@ for (pcre_list * ri = re_list_head; ri; ri = ri->next)
 return FAIL;
 }
 
+
+
 int
-regex(const uschar **listptr)
+regex(const uschar **listptr, BOOL cacheable)
 {
 unsigned long mbox_size;
 FILE *mbox_file;
@@ -130,7 +128,7 @@ else
   }
 
 /* precompile our regexes */
-if (!(re_list_head = compile(*listptr)))
+if (!(re_list_head = compile(*listptr, cacheable)))
   return FAIL;                 /* no regexes -> nothing to do */
 
 /* match each line against all regexes */
@@ -167,7 +165,7 @@ return ret;
 
 
 int
-mime_regex(const uschar **listptr)
+mime_regex(const uschar **listptr, BOOL cacheable)
 {
 pcre_list *re_list_head = NULL;
 FILE *f;
@@ -179,7 +177,7 @@ int ret;
 regex_match_string = NULL;
 
 /* precompile our regexes */
-if (!(re_list_head = compile(*listptr)))
+if (!(re_list_head = compile(*listptr, cacheable)))
   return FAIL;                 /* no regexes -> nothing to do */
 
 /* check if the file is already decoded */
diff --git a/src/src/regex_cache.c b/src/src/regex_cache.c
new file mode 100644 (file)
index 0000000..6ac134c
--- /dev/null
@@ -0,0 +1,245 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/*
+ * Copyright (c) The Exim Maintainers 2022
+ * License: GPL
+ */
+
+/* Caching layers for compiled REs.  There is a local layer in the process,
+implemented as a tree for inserts and lookup.  This cache is inherited from
+the daemon, for the process tree deriving from there - but not by re-exec'd
+proceses or commandline submission processes.
+
+If the process has to compile, and is not the daemon or a re-exec'd exim,
+it notifies the use of the RE to the daemon via a unix-domain socket.
+This is a fire-and-forget send with no response, hence cheap from the point-of
+view of the sender.  I have not measured the overall comms costs.  The
+daemon also compiles the RE, and caches the result.
+
+A second layer would be possible by asking the daemon via the notifier socket
+(for a result from its cache, or a compile if it must).  The comms overhead
+is significant, not only for the channel but also for de/serialisation of
+the compiled object.  This makes it untenable for the primary use-case, the
+transport process which has been re-exec'd to gain privs - and therefore does not
+have the daemon-maintained cache.  Using shared-memory might reduce that cost
+(the attach time for the memory segment will matter); the implimentation
+would require suitable R/W locks.
+*/
+
+#include "exim.h"
+
+typedef struct re_req {
+  uschar       notifier_reqtype;
+  BOOL         caseless;
+  uschar       re[1];          /* extensible */
+} re_req;
+
+static tree_node * regex_cache = NULL;
+static tree_node * regex_caseless_cache = NULL;
+
+/******************************************************************************/
+
+static void
+regex_to_daemon(const uschar * key, BOOL caseless)
+{
+int klen = Ustrlen(key) + 1;
+int rlen = sizeof(re_req) + klen;
+re_req * req;
+int fd, old_pool = store_pool;
+
+DEBUG(D_expand|D_lists)
+  debug_printf_indent("sending RE '%s' to daemon\n", key);
+
+store_pool = POOL_MAIN;
+  req = store_get(rlen, key);  /* maybe need a size limit */
+store_pool = old_pool;;
+req->notifier_reqtype = NOTIFY_REGEX;
+req->caseless = caseless;
+memcpy(req->re, key, klen);
+
+if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) >= 0)
+  {
+  struct sockaddr_un sa_un = {.sun_family = AF_UNIX};
+  ssize_t len = daemon_notifier_sockname(&sa_un);
+
+  if (sendto(fd, req, rlen, 0, (struct sockaddr *)&sa_un, (socklen_t)len) < 0)
+    DEBUG(D_queue_run)
+      debug_printf("%s: sendto %s\n", __FUNCTION__, strerror(errno));
+  close(fd);
+  }
+else DEBUG(D_queue_run) debug_printf(" socket: %s\n", strerror(errno));
+}
+
+
+static const pcre2_code *
+regex_from_cache(const uschar * key, BOOL caseless)
+{
+tree_node * node  =
+  tree_search(caseless ? regex_caseless_cache : regex_cache, key);
+DEBUG(D_expand|D_lists)
+  debug_printf_indent("compiled %sRE '%s' %sfound in local cache\n",
+                     caseless ? "caseless " : "", key, node ? "" : "not ");
+
+return node ? node->data.ptr : NULL;
+}
+
+
+static void
+regex_to_cache(const uschar * key, BOOL caseless, const pcre2_code * cre)
+{
+PCRE2_SIZE srelen;
+uschar * sre;
+tree_node * node;
+
+node = store_get(sizeof(tree_node) + Ustrlen(key) + 1, key);   /* we are called with STORE_PERM */
+Ustrcpy(node->name, key);
+node->data.ptr = (void *)cre;
+
+if (!tree_insertnode(caseless ? &regex_caseless_cache : &regex_cache, node))
+  { DEBUG(D_expand|D_lists) debug_printf_indent("duplicate key!\n"); }
+else DEBUG(D_expand|D_lists)
+  debug_printf_indent("compiled RE '%s' saved in local cache\n", key);
+
+/* Additionally, if not re-execed and not the daemon, tell the daemon of the RE
+so it can add to the cache */
+
+if (f.daemon_scion && !f.daemon_listen)
+  regex_to_daemon(key, caseless);
+
+return;
+}
+
+/******************************************************************************/
+
+/*************************************************
+*  Compile regular expression and panic on fail  *
+*************************************************/
+
+/* This function is called when failure to compile a regular expression leads
+to a panic exit. In other cases, pcre_compile() is called directly. In many
+cases where this function is used, the results of the compilation are to be
+placed in long-lived store, so we temporarily reset the store management
+functions that PCRE uses if the use_malloc flag is set.
+
+Argument:
+  pattern     the pattern to compile
+  flags
+   caseless    caseless matching is required
+   cacheable   use (writeback) cache
+  use_malloc  TRUE if compile into malloc store
+
+Returns:      pointer to the compiled pattern
+*/
+
+const pcre2_code *
+regex_must_compile(const uschar * pattern, mcs_flags flags, BOOL use_malloc)
+{
+BOOL caseless = !!(flags & MCS_CASELESS);
+size_t offset;
+const pcre2_code * yield;
+int old_pool = store_pool, err;
+
+/* Optionall, check the cache and return if found */
+
+if (  flags & MCS_CACHEABLE
+   && (yield = regex_from_cache(pattern, caseless)))
+  return yield;
+
+store_pool = POOL_PERM;
+
+if (!(yield = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED,
+  caseless ? PCRE_COPT|PCRE2_CASELESS : PCRE_COPT,
+  &err, &offset, use_malloc ? pcre_mlc_cmp_ctx : pcre_gen_cmp_ctx)))
+  {
+  uschar errbuf[128];
+  pcre2_get_error_message(err, errbuf, sizeof(errbuf));
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE, "regular expression error: "
+    "%s at offset %ld while compiling %s", errbuf, (long)offset, pattern);
+  }
+
+if (use_malloc)
+  {
+  /*pcre2_general_context_free(gctx);*/
+  }
+
+if (flags & MCS_CACHEABLE)
+  regex_to_cache(pattern, caseless, yield);
+
+store_pool = old_pool;
+return yield;
+}
+
+
+
+
+/* Wrapper for pcre2_compile() and error-message handling.
+
+Arguments:     pattern         regex to compile
+               flags
+                caseless       flag for match variant
+                cacheable      use (writeback) cache
+               errstr          on error, filled in with error message
+               cctx            compile-context for pcre2
+
+Return:                NULL on error, with errstr set. Otherwise, the compiled RE object
+*/
+
+const pcre2_code *
+regex_compile(const uschar * pattern, mcs_flags flags, uschar ** errstr,
+  pcre2_compile_context * cctx)
+{
+const uschar * key = pattern;
+BOOL caseless = !!(flags & MCS_CASELESS);
+int err;
+PCRE2_SIZE offset;
+const pcre2_code * yield;
+int old_pool = store_pool;
+
+/* Optionally, check the cache and return if found */
+
+if (  flags & MCS_CACHEABLE
+   && (yield = regex_from_cache(key, caseless)))
+  return yield;
+
+DEBUG(D_expand|D_lists) debug_printf_indent("compiling %sRE '%s'\n",
+                               caseless ? "caseless " : "", pattern);
+
+store_pool = POOL_PERM;
+if (!(yield = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED,
+               caseless ? PCRE_COPT|PCRE2_CASELESS : PCRE_COPT,
+               &err, &offset, cctx)))
+  {
+  uschar errbuf[128];
+  pcre2_get_error_message(err, errbuf, sizeof(errbuf));
+  store_pool = old_pool;
+  *errstr = string_sprintf("regular expression error in "
+           "\"%s\": %s at offset %ld", pattern, errbuf, (long)offset);
+  }
+else if (flags & MCS_CACHEABLE)
+  regex_to_cache(key, caseless, yield);
+store_pool = old_pool;
+
+return yield;
+}
+
+
+
+/* Handle a regex notify arriving at the daemon.  We get sent the original RE;
+compile it (again) and write to the cache.  Later forked procs will be able to
+read from the cache, unless they re-execed.  Therefore, those latter never bother
+sending us a notification. */
+
+void
+regex_at_daemon(const uschar * reqbuf)
+{
+const re_req * req = (const re_req *)reqbuf;
+uschar * errstr;
+const pcre2_code * cre = regex_compile(req->re,
+  req->caseless ? MCS_CASELESS | MCS_CACHEABLE : MCS_CACHEABLE,
+  &errstr, pcre_gen_cmp_ctx);
+
+DEBUG(D_any) if (!cre) debug_printf("%s\n", errstr);
+return;
+}
index 005dc51fefa882e5e678526adb59326115cd834e..bfd78b5f11a23242d827501de5a4bddce2e25aa9 100644 (file)
@@ -136,7 +136,8 @@ for (rewrite_rule * rule = rewrite_rules;
 
   if (flag & rewrite_smtp)
     {
-    uschar *key = expand_string(rule->key);
+    BOOL textonly_re;
+    const uschar * key = expand_string_2(rule->key, &textonly_re);
     if (!key)
       {
       if (!f.expand_string_forcedfail)
@@ -144,7 +145,8 @@ for (rewrite_rule * rule = rewrite_rules;
           "checking for SMTP rewriting: %s", rule->key, expand_string_message);
       continue;
       }
-    if (match_check_string(subject, key, 0, TRUE, FALSE, FALSE, NULL) != OK)
+    if (match_check_string(subject, key, 0,
+      textonly_re ? MCS_CACHEABLE | MCS_PARTIAL : MCS_PARTIAL, NULL) != OK)
       continue;
     new = expand_string(rule->replacement);
     }
index 94cde4e046d67e9d38a5ea1734489a009bba4718..8b67f3116a63fcc33928a29e6938c188646c79d8 100644 (file)
@@ -84,10 +84,10 @@ iplookup_router_options_block iplookup_router_option_defaults = {
 consistency checks to be done, or anything else that needs to be set up. */
 
 void
-iplookup_router_init(router_instance *rblock)
+iplookup_router_init(router_instance * rblock)
 {
-iplookup_router_options_block *ob =
-  (iplookup_router_options_block *)(rblock->options_block);
+iplookup_router_options_block * ob =
+  (iplookup_router_options_block *) rblock->options_block;
 
 /* A port and a host list must be given */
 
@@ -95,13 +95,13 @@ if (ob->port < 0)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n  "
     "a port must be specified", rblock->name);
 
-if (ob->hosts == NULL)
+if (!ob->hosts)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n  "
     "a host list must be specified", rblock->name);
 
 /* Translate protocol name into value */
 
-if (ob->protocol_name != NULL)
+if (ob->protocol_name)
   {
   if (Ustrcmp(ob->protocol_name, "udp") == 0) ob->protocol = ip_udp;
   else if (Ustrcmp(ob->protocol_name, "tcp") == 0) ob->protocol = ip_tcp;
@@ -111,9 +111,9 @@ if (ob->protocol_name != NULL)
 
 /* If a response pattern is given, compile it now to get the error early. */
 
-if (ob->response_pattern != NULL)
+if (ob->response_pattern)
   ob->re_response_pattern =
-    regex_must_compile(ob->response_pattern, FALSE, TRUE);
+    regex_must_compile(ob->response_pattern, MCS_NOFLAGS, TRUE);
 }
 
 
index b38aa6a9d4427b70482e4948584e5929d573fcd8..06cd06084cdbf45b4f64aa9ce4a97138c876f134 100644 (file)
@@ -895,7 +895,7 @@ typedef struct check_host_block {
   const uschar *host_name;
   const uschar *host_address;
   const uschar *host_ipv4;
-  BOOL   negative;
+  mcs_flags    flags;
 } check_host_block;
 
 /* Structure for remembering lookup data when caching the result of
index 600fb6125e4b458c46c7df060fdfe00f11135948..7e29dd3bca4629f1da9bb58684be7b43783ed81d 100644 (file)
@@ -2213,23 +2213,14 @@ else
 
   if (ob->quota_value > 0 || THRESHOLD_CHECK || ob->maildir_use_size_file)
     {
-    PCRE2_SIZE offset;
-    int err;
-
     /* Compile the regex if there is one. */
 
     if (ob->quota_size_regex)
       {
-      if (!(re = pcre2_compile((PCRE2_SPTR)ob->quota_size_regex,
-                 PCRE2_ZERO_TERMINATED, PCRE_COPT, &err, &offset, pcre_gen_cmp_ctx)))
-        {
-       uschar errbuf[128];
-       pcre2_get_error_message(err, errbuf, sizeof(errbuf));
-        addr->message = string_sprintf("appendfile: regular expression "
-          "error: %s at offset %ld while compiling %s", errbuf, (long)offset,
-          ob->quota_size_regex);
+      if (!(re = regex_compile(ob->quota_size_regex,
+                 MCS_NOFLAGS, &addr->message, pcre_gen_cmp_ctx)))
         return FALSE;
-        }
+
       DEBUG(D_transport) debug_printf("using regex for file sizes: %s\n",
         ob->quota_size_regex);
       }
@@ -2302,23 +2293,14 @@ else
   if (ob->maildir_use_size_file)
     {
     const pcre2_code * dir_regex = NULL;
-    PCRE2_SIZE offset;
-    int err;
 
     if (ob->maildir_dir_regex)
       {
       int check_path_len = Ustrlen(check_path);
 
-      if (!(dir_regex = pcre2_compile((PCRE2_SPTR)ob->maildir_dir_regex,
-           PCRE2_ZERO_TERMINATED, PCRE_COPT, &err, &offset, pcre_gen_cmp_ctx)))
-        {
-       uschar errbuf[128];
-       pcre2_get_error_message(err, errbuf, sizeof(errbuf));
-        addr->message = string_sprintf("appendfile: regular expression "
-          "error: %s at offset %ld while compiling %s", errbuf, (long)offset,
-          ob->maildir_dir_regex);
+      if (!(dir_regex = regex_compile(ob->maildir_dir_regex,
+           MCS_NOFLAGS, &addr->message, pcre_gen_cmp_ctx)))
         return FALSE;
-        }
 
       DEBUG(D_transport)
         debug_printf("using regex for maildir directory selection: %s\n",
index 4450d948d39c2da3f1e7ea2954138f13a7ade135..6eee04d03124ffa77964065129a82f07cc0f50e6 100644 (file)
@@ -275,7 +275,7 @@ struct list
 
 for (struct list * l = list; l < list + nelem(list); l++)
   if (!*l->re)
-    *l->re = regex_must_compile(l->string, FALSE, TRUE);
+    *l->re = regex_must_compile(l->string, MCS_NOFLAGS, TRUE);
 }
 
 
@@ -1000,7 +1000,7 @@ uschar authnum;
 unsigned short authbits = 0;
 
 if (!sx->esmtp) return 0;
-if (!regex_AUTH) regex_AUTH = regex_must_compile(AUTHS_REGEX, FALSE, TRUE);
+if (!regex_AUTH) regex_AUTH = regex_must_compile(AUTHS_REGEX, MCS_NOFLAGS, TRUE);
 if (!regex_match_and_setup(regex_AUTH, sx->buffer, 0, -1)) return 0;
 expand_nmax = -1;                                              /* reset */
 names = string_copyn(expand_nstring[1], expand_nlength[1]);
@@ -1563,7 +1563,7 @@ f.smtp_authenticated = FALSE;
 client_authenticator = client_authenticated_id = client_authenticated_sender = NULL;
 
 if (!regex_AUTH)
-  regex_AUTH = regex_must_compile(AUTHS_REGEX, FALSE, TRUE);
+  regex_AUTH = regex_must_compile(AUTHS_REGEX, MCS_NOFLAGS, TRUE);
 
 /* Is the server offering AUTH? */
 
index 6bff1eb69769197f152d1e1a65c49174e1f6668a..205ee41cb09e3f45ea4346a707d5a1501617b88c 100644 (file)
@@ -142,22 +142,13 @@ a subfolder, and should ensure that a maildirfolder file exists. */
 
 if (maildirfolder_create_regex)
   {
-  int err;
-  PCRE2_SIZE offset;
   const pcre2_code * re;
 
   DEBUG(D_transport) debug_printf("checking for maildirfolder requirement\n");
 
-  if (!(re = pcre2_compile((PCRE2_SPTR)maildirfolder_create_regex,
-             PCRE2_ZERO_TERMINATED, PCRE_COPT, &err, &offset, pcre_gen_cmp_ctx)))
-    {
-    uschar errbuf[128];
-    pcre2_get_error_message(err, errbuf, sizeof(errbuf));
-    addr->message = string_sprintf("appendfile: regular expression "
-      "error: %s at offset %ld while compiling %s", errbuf, (long)offset,
-      maildirfolder_create_regex);
+  if (!(re = regex_compile(maildirfolder_create_regex,
+             MCS_NOFLAGS, &addr->message, pcre_gen_cmp_ctx)))
     return FALSE;
-    }
 
   if (regex_match(re, path, -1, NULL))
     {
index b4c2b9a8fbecb81f2e2fc8a90b8459d10a2db468..afc18d553f21766e5c162f1856500b8518752d29 100644 (file)
@@ -3074,7 +3074,7 @@ digits, full stops, and hyphens (the constituents of domain names). Allow
 underscores, as they are all too commonly found. Sigh. Also, if
 allow_utf8_domains is set, allow top-bit characters. */
 
-for (t = ss; *t != 0; t++)
+for (t = ss; *t; t++)
   if (!isalnum(*t) && *t != '.' && *t != '-' && *t != '_' &&
       (!allow_utf8_domains || *t < 128)) break;
 
@@ -3082,7 +3082,7 @@ for (t = ss; *t != 0; t++)
 its IP address and match against that. Note that a multi-homed host will add
 items to the chain. */
 
-if (*t == 0)
+if (!*t)
   {
   int rc;
   host_item h;
@@ -3113,8 +3113,8 @@ outgoing hosts, the name is always given explicitly. If it is NULL, it means we
 must use sender_host_name and its aliases, looking them up if necessary. */
 
 if (cb->host_name)   /* Explicit host name given */
-  return match_check_string(cb->host_name, ss, -1, TRUE, TRUE, TRUE,
-    valueptr);
+  return match_check_string(cb->host_name, ss, -1,
+    MCS_PARTIAL | MCS_CASELESS | MCS_AT_SPECIAL | cb->flags, valueptr);
 
 /* Host name not given; in principle we need the sender host name and its
 aliases. However, for query-style lookups, we do not need the name if the
@@ -3143,7 +3143,9 @@ if ((semicolon = Ustrchr(ss, ';')))
 
 if (isquery)
   {
-  switch(match_check_string(US"", ss, -1, TRUE, TRUE, TRUE, valueptr))
+  switch(match_check_string(US"", ss, -1,
+      MCS_PARTIAL| MCS_CASELESS| MCS_AT_SPECIAL | (cb->flags & MCS_CACHEABLE),
+      valueptr))
     {
     case OK:    return OK;
     case DEFER: return DEFER;
@@ -3169,7 +3171,9 @@ if (!sender_host_name)
 
 /* Match on the sender host name, using the general matching function */
 
-switch(match_check_string(sender_host_name, ss, -1, TRUE, TRUE, TRUE, valueptr))
+switch(match_check_string(sender_host_name, ss, -1,
+      MCS_PARTIAL| MCS_CASELESS| MCS_AT_SPECIAL | (cb->flags & MCS_CACHEABLE),
+      valueptr))
   {
   case OK:    return OK;
   case DEFER: return DEFER;
@@ -3179,7 +3183,9 @@ switch(match_check_string(sender_host_name, ss, -1, TRUE, TRUE, TRUE, valueptr))
 
 aliases = sender_host_aliases;
 while (*aliases)
-  switch(match_check_string(*aliases++, ss, -1, TRUE, TRUE, TRUE, valueptr))
+  switch(match_check_string(*aliases++, ss, -1,
+      MCS_PARTIAL| MCS_CASELESS| MCS_AT_SPECIAL | (cb->flags & MCS_CACHEABLE),
+      valueptr))
     {
     case OK:    return OK;
     case DEFER: return DEFER;
@@ -3255,8 +3261,8 @@ rc = match_check_list(
        check_host,                             /* function for testing */
        &cb,                                    /* argument for function */
        MCL_HOST,                               /* type of check */
-       (host_address == sender_host_address)?
-         US"host" : host_address,              /* text for debugging */
+       host_address == sender_host_address
+         ? US"host" : host_address,           /* text for debugging */
        valueptr);                              /* where to pass back data */
 deliver_host_address = save_host_address;
 return rc;
diff --git a/test/confs/0632 b/test/confs/0632
new file mode 100644 (file)
index 0000000..55592bf
--- /dev/null
@@ -0,0 +1,37 @@
+# Exim test configuration 0632
+
+.include DIR/aux-var/std_conf_prefix
+
+primary_hostname = myhost.test.ex
+queue_only
+
+# ----- Main settings -----
+
+acl_smtp_rcpt = chk_rcpt
+
+# ----- ACL -----
+
+begin acl
+
+chk_rcpt:
+  # We're doing these to see what REs the daemon compiles, in stderr
+  warn domains =       ^nomatch_list
+       logwrite =      should not match RE in list
+
+  warn condition =     ${if match {a_random_string} {static_RE}}
+       logwrite =      should not match RE in match cond
+  warn condition =     ${if match {a_random_string} {tricky_static_RE\$}}
+       logwrite =      should not match RE in match cond
+  warn condition =     ${if match {a_random_string} {pid=${pid} uncacheable_RE}}
+       logwrite =      should not match RE in match cond
+  accept
+
+# ----- Routers -----
+
+begin routers
+
+r0:
+    driver =   redirect
+    data =     :blackhole:
+#
+# End
diff --git a/test/log/0632 b/test/log/0632
new file mode 100644 (file)
index 0000000..fec06c9
--- /dev/null
@@ -0,0 +1,5 @@
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=p1234, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex H=(test.ex) [127.0.0.1] P=smtp S=sss
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@test.ex H=(test.ex) [127.0.0.1] P=smtp S=sss
diff --git a/test/scripts/0000-Basic/0632 b/test/scripts/0000-Basic/0632
new file mode 100644 (file)
index 0000000..4bdefca
--- /dev/null
@@ -0,0 +1,39 @@
+# regex caching
+#
+exim -d-all+queue_run+expand+lookup -DSERVER=server -bd -oX PORT_D
+****
+#
+client 127.0.0.1 PORT_D
+??? 220
+HELO test.ex
+??? 250 
+MAIL FROM:<CALLER@test.ex>
+??? 250
+RCPT TO:<dest_1@test.ex>
+??? 250
+DATA
+??? 354
+.
+??? 250
+QUIT
+??? 221
+****
+client 127.0.0.1 PORT_D
+??? 220
+HELO test.ex
+??? 250 
+MAIL FROM:<CALLER@test.ex>
+??? 250
+RCPT TO:<dest_2@test.ex>
+??? 250
+DATA
+??? 354
+.
+??? 250
+QUIT
+??? 221
+****
+#
+killdaemon
+no_msglog_check
+no_stdout_check
index 8f6bf872050411951d960ce3a98ae228a332a45b..2f48f709f35ecb2184efb0ab1fa57b1858f4c8af 100644 (file)
@@ -26,6 +26,9 @@ dropping to exim gid; retaining priv uid
   ├considering: }{$2$1}fail}
   ├──expanding: \N^([ab]+)(\w+)$\N
   ╰─────result: ^([ab]+)(\w+)$
+ compiled RE '^([ab]+)(\w+)$' not found in local cache
+ compiling RE '^([ab]+)(\w+)$'
+ compiled RE '^([ab]+)(\w+)$' saved in local cache
  ├──condition: match{abcd}{\N^([ab]+)(\w+)$\N}
  ├─────result: true
   ╭considering: $2$1}fail}
@@ -49,6 +52,7 @@ dropping to exim gid; retaining priv uid
   ├considering: }{$2$1}fail}
   ├──expanding: \N^([ab]+)(\w+)$\N
   ╰─────result: ^([ab]+)(\w+)$
+ compiled RE '^([ab]+)(\w+)$' found in local cache
  ├──condition: match{wxyz}{\N^([ab]+)(\w+)$\N}
  ├─────result: false
   ╭───scanning: $2$1}fail}
@@ -151,6 +155,9 @@ dropping to exim gid; retaining priv uid
   |considering: }{$2$1}fail}
   |--expanding: \N^([ab]+)(\w+)$\N
   \_____result: ^([ab]+)(\w+)$
+ compiled RE '^([ab]+)(\w+)$' not found in local cache
+ compiling RE '^([ab]+)(\w+)$'
+ compiled RE '^([ab]+)(\w+)$' saved in local cache
  |--condition: match{abcd}{\N^([ab]+)(\w+)$\N}
  |-----result: true
   /considering: $2$1}fail}
@@ -174,6 +181,7 @@ dropping to exim gid; retaining priv uid
   |considering: }{$2$1}fail}
   |--expanding: \N^([ab]+)(\w+)$\N
   \_____result: ^([ab]+)(\w+)$
+ compiled RE '^([ab]+)(\w+)$' found in local cache
  |--condition: match{wxyz}{\N^([ab]+)(\w+)$\N}
  |-----result: false
   /---scanning: $2$1}fail}
index 2e2969aaaa462b0958c7322199ca6d6e17fa076b..63a3125658a6fc6f356bfb4426838acdcee652bd 100644 (file)
@@ -16,6 +16,8 @@ routing userx@test.again.dns
 --------> srv router <--------
 local_part=userx domain=test.again.dns
 checking local_parts
+compiled caseless RE '^srv' not found in local cache
+compiled RE '^srv' saved in local cache
 userx in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -47,6 +49,7 @@ routing abcd@test.again.dns
 --------> srv router <--------
 local_part=abcd domain=test.again.dns
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 abcd in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -74,6 +77,7 @@ routing abcd@ten-1.test.ex
 --------> srv router <--------
 local_part=abcd domain=ten-1.test.ex
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 abcd in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -117,6 +121,7 @@ routing usery@test.again.dns
 --------> srv router <--------
 local_part=usery domain=test.again.dns
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 usery in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -150,6 +155,7 @@ routing userz@test.again.dns
 --------> srv router <--------
 local_part=userz domain=test.again.dns
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 userz in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -178,6 +184,7 @@ routing xyz@ten-1.test.ex
 --------> srv router <--------
 local_part=xyz domain=ten-1.test.ex
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 xyz in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -226,6 +233,8 @@ routing userx@test.fail.dns
 --------> srv router <--------
 local_part=userx domain=test.fail.dns
 checking local_parts
+compiled caseless RE '^srv' not found in local cache
+compiled RE '^srv' saved in local cache
 userx in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -256,6 +265,7 @@ routing abcd@test.fail.dns
 --------> srv router <--------
 local_part=abcd domain=test.fail.dns
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 abcd in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -283,6 +293,7 @@ routing abcd@ten-1.test.ex
 --------> srv router <--------
 local_part=abcd domain=ten-1.test.ex
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 abcd in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -326,6 +337,7 @@ routing usery@test.fail.dns
 --------> srv router <--------
 local_part=usery domain=test.fail.dns
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 usery in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -358,6 +370,7 @@ routing userz@test.fail.dns
 --------> srv router <--------
 local_part=userz domain=test.fail.dns
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 userz in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -386,6 +399,7 @@ routing xyz@ten-1.test.ex
 --------> srv router <--------
 local_part=xyz domain=ten-1.test.ex
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 xyz in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -434,6 +448,8 @@ routing userx@nonexist.test.ex
 --------> srv router <--------
 local_part=userx domain=nonexist.test.ex
 checking local_parts
+compiled caseless RE '^srv' not found in local cache
+compiled RE '^srv' saved in local cache
 userx in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -466,6 +482,7 @@ routing abcd@nonexist.test.ex
 --------> srv router <--------
 local_part=abcd domain=nonexist.test.ex
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 abcd in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -494,6 +511,7 @@ routing abcd@ten-1.test.ex
 --------> srv router <--------
 local_part=abcd domain=ten-1.test.ex
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 abcd in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -537,6 +555,7 @@ routing usery@nonexist.test.ex
 --------> srv router <--------
 local_part=usery domain=nonexist.test.ex
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 usery in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -570,6 +589,7 @@ routing userz@nonexist.test.ex
 --------> srv router <--------
 local_part=userz domain=nonexist.test.ex
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 userz in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -598,6 +618,7 @@ routing xyz@ten-1.test.ex
 --------> srv router <--------
 local_part=xyz domain=ten-1.test.ex
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 xyz in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -646,6 +667,8 @@ routing srv@test.again.dns
 --------> srv router <--------
 local_part=srv domain=test.again.dns
 checking local_parts
+compiled caseless RE '^srv' not found in local cache
+compiled RE '^srv' saved in local cache
 srv in "^srv"? yes (matched "^srv")
 calling srv router
 srv router called for srv@test.again.dns
@@ -668,6 +691,7 @@ routing srv@test.fail.dns
 --------> srv router <--------
 local_part=srv domain=test.fail.dns
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 srv in "^srv"? yes (matched "^srv")
 calling srv router
 srv router called for srv@test.fail.dns
@@ -711,6 +735,8 @@ routing userx@nonexist.example.com
 --------> srv router <--------
 local_part=userx domain=nonexist.example.com
 checking local_parts
+compiled caseless RE '^srv' not found in local cache
+compiled RE '^srv' saved in local cache
 userx in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
@@ -743,6 +769,7 @@ routing userd@nonexist.example.com
 --------> srv router <--------
 local_part=userd domain=nonexist.example.com
 checking local_parts
+compiled caseless RE '^srv' found in local cache
 userd in "^srv"? no (end of list)
 srv router skipped: local_parts mismatch
 --------> useryz router <--------
index e4275b92fff6618b5d149089d8cebdac265ee554..6b675d4ab684a0567dee7e3a2c494f5be8e9ccc3 100644 (file)
@@ -50,6 +50,8 @@ check acl = TESTSUITE/aux-fixed/0386.acl1
  accept: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1"
  processing "deny" (TESTSUITE/test-config 44)
  check local_parts = ^.*[@%!/|]
+ compiled caseless RE '^.*[@%!/|]' not found in local cache
+ compiled RE '^.*[@%!/|]' saved in local cache
  1 in "^.*[@%!/|]"? no (end of list)
  deny: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1"
  processing "require" (TESTSUITE/test-config 44)
@@ -106,6 +108,7 @@ check acl = TESTSUITE/aux-fixed/0386.acl1
  accept: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1"
  processing "deny" (TESTSUITE/test-config 44)
  check local_parts = ^.*[@%!/|]
+ compiled caseless RE '^.*[@%!/|]' found in local cache
  1 in "^.*[@%!/|]"? no (end of list)
  deny: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1"
  processing "require" (TESTSUITE/test-config 44)
index 49b407430b374b951a94daea4034fbc62ba1a6d8..c3c11ca75977c2152d2bc92e2ee7999d69e0761c 100644 (file)
@@ -87,6 +87,9 @@ Connecting to 127.0.0.1 [127.0.0.1]:PORT_S ...  connected
   SMTP>> EHLO myhost.test.ex
 cmd buf flush ddd bytes
   SMTP<< 250 OK
+ compiled RE '.outlook.com$' not found in local cache
+ compiling RE '.outlook.com$'
+ compiled RE '.outlook.com$' saved in local cache
 127.0.0.1 in hosts_require_auth? no (option unset)
   SMTP>> MAIL FROM:<CALLER@myhost.test.ex>
 cmd buf flush ddd bytes
index 6ca63ed965dd8a9789cc9485e2abf7c37af88a56..bdd3940aeadd21ec9e113f64cd3e8b6894f0d156 100644 (file)
@@ -136,6 +136,9 @@ Connecting to 127.0.0.1 [127.0.0.1]:PORT_S ...  connected
   SMTP>> EHLO mail.test.ex
 cmd buf flush ddd bytes
   SMTP<< 250 OK
+ compiled RE '.outlook.com$' not found in local cache
+ compiling RE '.outlook.com$'
+ compiled RE '.outlook.com$' saved in local cache
 not using PIPELINING
 not using DSN
 127.0.0.1 in hosts_require_auth? no (option unset)
index b29e326c6c6088fa23014885a686b103a4a055be..be534ca18a050e5ededfa5c9daf51829b01bc575 100644 (file)
@@ -103,6 +103,9 @@ Connecting to 127.0.0.1 [127.0.0.1]:PORT_S ...  connected
   SMTP>> EHLO myhost.test.ex
 cmd buf flush ddd bytes
   SMTP<< 250 OK
+ compiled RE '.outlook.com$' not found in local cache
+ compiling RE '.outlook.com$'
+ compiled RE '.outlook.com$' saved in local cache
 not using PIPELINING
 not using DSN
 127.0.0.1 in hosts_require_auth? no (option unset)
index 0c170e1bc1012a2830dea2840737bc21ca141225..7e4d62809921405f7add7ce5ba1ad51c64cd9522 100644 (file)
@@ -100,6 +100,8 @@ To: random@test.example,
 
 qualify & rewrite recipients list
  address match test: subject=r1@test.ex pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' not found in local cache
+ compiled RE '^.{40,}@*' saved in local cache
  r1@test.ex in "^.{40,}@*"? no (end of list)
  address match test: subject=r1@test.ex pattern=*@*
  test.ex in "*"? yes (matched "*")
@@ -125,6 +127,7 @@ qualify & rewrite recipients list
   lookup failed
 global rewrite rules
  address match test: subject=CALLER@myhost.test.ex pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  CALLER@myhost.test.ex in "^.{40,}@*"? no (end of list)
  address match test: subject=CALLER@myhost.test.ex pattern=*@*
  myhost.test.ex in "*"? yes (matched "*")
@@ -239,6 +242,7 @@ rewrite headers
     random@test.exam
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -421,6 +425,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -602,6 +607,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -783,6 +789,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -964,6 +971,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -1145,6 +1153,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -1326,6 +1335,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -1507,6 +1517,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -1688,6 +1699,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -1869,6 +1881,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -2049,6 +2062,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -2229,6 +2243,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -2409,6 +2424,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -2589,6 +2605,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -2769,6 +2786,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -2949,6 +2967,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -3129,6 +3148,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -3309,6 +3329,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -3489,6 +3510,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -3669,6 +3691,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -3849,6 +3872,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -4029,6 +4053,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -4209,6 +4234,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -4388,6 +4414,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -4567,6 +4594,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -4746,6 +4774,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -4925,6 +4954,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -5104,6 +5134,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -5283,6 +5314,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -5462,6 +5494,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -5641,6 +5674,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -5820,6 +5854,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -5999,6 +6034,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -6178,6 +6214,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -6357,6 +6394,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -6535,6 +6573,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -6713,6 +6752,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -6891,6 +6931,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -7069,6 +7110,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -7247,6 +7289,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -7425,6 +7468,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -7603,6 +7647,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -7781,6 +7826,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -7959,6 +8005,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -8137,6 +8184,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -8315,6 +8363,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -8493,6 +8542,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -8671,6 +8721,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -8848,6 +8899,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -9025,6 +9077,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -9202,6 +9255,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -9379,6 +9433,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -9556,6 +9611,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -9733,6 +9789,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -9910,6 +9967,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -10087,6 +10145,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -10264,6 +10323,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -10441,6 +10501,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -10618,6 +10679,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -10795,6 +10857,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -10971,6 +11034,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -11147,6 +11211,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -11323,6 +11388,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -11499,6 +11565,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -11675,6 +11742,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -11851,6 +11919,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -12027,6 +12096,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -12203,6 +12273,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -12379,6 +12450,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -12555,6 +12627,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -12731,6 +12804,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -12907,6 +12981,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -13083,6 +13158,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -13258,6 +13334,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -13433,6 +13510,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -13608,6 +13686,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -13783,6 +13862,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -13958,6 +14038,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -14133,6 +14214,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -14308,6 +14390,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -14483,6 +14566,7 @@ remainder: random@test.example,
     random@test.exa
 **** debug string too long - truncated ****
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -14658,6 +14742,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -14832,6 +14917,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -15005,6 +15091,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -15177,6 +15264,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -15348,6 +15436,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -15518,6 +15607,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -15687,6 +15777,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -15855,6 +15946,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -16022,6 +16114,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -16188,6 +16281,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -16353,6 +16447,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -16517,6 +16612,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -16680,6 +16776,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -16842,6 +16939,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -17003,6 +17101,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -17163,6 +17262,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -17322,6 +17422,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -17480,6 +17581,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -17637,6 +17739,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -17793,6 +17896,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -17948,6 +18052,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -18102,6 +18207,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -18255,6 +18361,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -18407,6 +18514,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -18558,6 +18666,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -18708,6 +18817,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -18857,6 +18967,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -19005,6 +19116,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -19152,6 +19264,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -19298,6 +19411,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -19443,6 +19557,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -19587,6 +19702,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -19730,6 +19846,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -19872,6 +19989,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -20013,6 +20131,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -20153,6 +20272,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -20292,6 +20412,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -20430,6 +20551,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -20567,6 +20689,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -20703,6 +20826,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -20838,6 +20962,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -20972,6 +21097,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -21105,6 +21231,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -21237,6 +21364,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -21368,6 +21496,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -21498,6 +21627,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -21627,6 +21757,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -21755,6 +21886,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -21882,6 +22014,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -22008,6 +22141,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -22133,6 +22267,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -22257,6 +22392,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -22380,6 +22516,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -22502,6 +22639,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -22623,6 +22761,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -22743,6 +22882,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -22862,6 +23002,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -22980,6 +23121,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -23097,6 +23239,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -23213,6 +23356,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -23328,6 +23472,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -23442,6 +23587,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -23555,6 +23701,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -23667,6 +23814,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -23778,6 +23926,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -23888,6 +24037,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -23997,6 +24147,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -24105,6 +24256,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -24212,6 +24364,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -24318,6 +24471,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -24423,6 +24577,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -24527,6 +24682,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -24630,6 +24786,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -24732,6 +24889,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -24833,6 +24991,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -24933,6 +25092,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -25032,6 +25192,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -25130,6 +25291,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -25227,6 +25389,7 @@ remainder: random@test.example,
     random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -25323,6 +25486,7 @@ To: random@rwtest.example,
 remainder: random@test.example,
     random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -25418,6 +25582,7 @@ To: random@rwtest.example,
 **** debug string too long - truncated ****
 remainder: random@test.example
  address match test: subject=random@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  random@test.example in "^.{40,}@*"? no (end of list)
  address match test: subject=random@test.example pattern=*@*
  test.example in "*"? yes (matched "*")
@@ -25515,6 +25680,7 @@ remainder:
  rewrite_one_header: type=F:
    From: CALLER_NAME <CALLER@myhost.test.ex>
  address match test: subject=CALLER@myhost.test.ex pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  CALLER@myhost.test.ex in "^.{40,}@*"? no (end of list)
  address match test: subject=CALLER@myhost.test.ex pattern=*@*
  myhost.test.ex in "*"? yes (matched "*")
@@ -25744,6 +25910,8 @@ To: localpart_with_056_chars_56789012345678901234567890123456@test.example
 
 qualify & rewrite recipients list
  address match test: subject=r2@test.ex pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' not found in local cache
+ compiled RE '^.{40,}@*' saved in local cache
  r2@test.ex in "^.{40,}@*"? no (end of list)
  address match test: subject=r2@test.ex pattern=*@*
  test.ex in "*"? yes (matched "*")
@@ -25769,6 +25937,7 @@ qualify & rewrite recipients list
   lookup failed
 global rewrite rules
  address match test: subject=CALLER@myhost.test.ex pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  CALLER@myhost.test.ex in "^.{40,}@*"? no (end of list)
  address match test: subject=CALLER@myhost.test.ex pattern=*@*
  myhost.test.ex in "*"? yes (matched "*")
@@ -25804,6 +25973,7 @@ rewrite headers
  rewrite_one_header: type=T:
    To: localpart_with_056_chars_56789012345678901234567890123456@test.example
  address match test: subject=localpart_with_056_chars_56789012345678901234567890123456@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  localpart_with_056_chars_56789012345678901234567890123456@test.example in "^.{40,}@*"? yes (matched "^.{40,}@*")
 LOG: address_rewrite MAIN
   "localpart_with_056_chars_56789012345678901234567890123456@test.example" from to: rewritten as "deny_me@test.example" by rule 1
@@ -25831,6 +26001,7 @@ remainder:
  rewrite_one_header: type=F:
    From: CALLER_NAME <CALLER@myhost.test.ex>
  address match test: subject=CALLER@myhost.test.ex pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  CALLER@myhost.test.ex in "^.{40,}@*"? no (end of list)
  address match test: subject=CALLER@myhost.test.ex pattern=*@*
  myhost.test.ex in "*"? yes (matched "*")
@@ -25904,6 +26075,8 @@ To: localpart_with_236_chars_567890123456789012345678901234567890123456789012345
 
 qualify & rewrite recipients list
  address match test: subject=r3@test.ex pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' not found in local cache
+ compiled RE '^.{40,}@*' saved in local cache
  r3@test.ex in "^.{40,}@*"? no (end of list)
  address match test: subject=r3@test.ex pattern=*@*
  test.ex in "*"? yes (matched "*")
@@ -25929,6 +26102,7 @@ qualify & rewrite recipients list
   lookup failed
 global rewrite rules
  address match test: subject=CALLER@myhost.test.ex pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  CALLER@myhost.test.ex in "^.{40,}@*"? no (end of list)
  address match test: subject=CALLER@myhost.test.ex pattern=*@*
  myhost.test.ex in "*"? yes (matched "*")
@@ -25964,6 +26138,7 @@ rewrite headers
  rewrite_one_header: type=T:
    To: localpart_with_236_chars_56789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example
  address match test: subject=localpart_with_236_chars_56789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  localpart_with_236_chars_56789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example in "^.{40,}@*"? yes (matched "^.{40,}@*")
 LOG: address_rewrite MAIN
   "localpart_with_236_chars_56789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456@test.example" from to: rewritten as "deny_me@test.example" by rule 1
@@ -25991,6 +26166,7 @@ remainder:
  rewrite_one_header: type=F:
    From: CALLER_NAME <CALLER@myhost.test.ex>
  address match test: subject=CALLER@myhost.test.ex pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  CALLER@myhost.test.ex in "^.{40,}@*"? no (end of list)
  address match test: subject=CALLER@myhost.test.ex pattern=*@*
  myhost.test.ex in "*"? yes (matched "*")
@@ -26064,6 +26240,8 @@ To: localpart_with_256_chars_567890123456789012345678901234567890123456789012345
 
 qualify & rewrite recipients list
  address match test: subject=r4@test.ex pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' not found in local cache
+ compiled RE '^.{40,}@*' saved in local cache
  r4@test.ex in "^.{40,}@*"? no (end of list)
  address match test: subject=r4@test.ex pattern=*@*
  test.ex in "*"? yes (matched "*")
@@ -26089,6 +26267,7 @@ qualify & rewrite recipients list
   lookup failed
 global rewrite rules
  address match test: subject=CALLER@myhost.test.ex pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  CALLER@myhost.test.ex in "^.{40,}@*"? no (end of list)
  address match test: subject=CALLER@myhost.test.ex pattern=*@*
  myhost.test.ex in "*"? yes (matched "*")
@@ -26148,6 +26327,8 @@ To: undisclosed recpients:;
 
 qualify & rewrite recipients list
  address match test: subject=r5@test.ex pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' not found in local cache
+ compiled RE '^.{40,}@*' saved in local cache
  r5@test.ex in "^.{40,}@*"? no (end of list)
  address match test: subject=r5@test.ex pattern=*@*
  test.ex in "*"? yes (matched "*")
@@ -26173,6 +26354,7 @@ qualify & rewrite recipients list
   lookup failed
 global rewrite rules
  address match test: subject=CALLER@myhost.test.ex pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  CALLER@myhost.test.ex in "^.{40,}@*"? no (end of list)
  address match test: subject=CALLER@myhost.test.ex pattern=*@*
  myhost.test.ex in "*"? yes (matched "*")
@@ -26210,6 +26392,7 @@ rewrite headers
  rewrite_one_header: type=F:
    From: CALLER_NAME <CALLER@myhost.test.ex>
  address match test: subject=CALLER@myhost.test.ex pattern=^.{40,}@*
+ compiled RE '^.{40,}@*' found in local cache
  CALLER@myhost.test.ex in "^.{40,}@*"? no (end of list)
  address match test: subject=CALLER@myhost.test.ex pattern=*@*
  myhost.test.ex in "*"? yes (matched "*")
index 6f298e2f179fd9cf75d07cde909f76350aedba73..2d9f129c0806c457ba6309d326d75234a248f79b 100644 (file)
@@ -363,6 +363,9 @@ LOG: MAIN
   ├considering: } }{ match{$h_auto-submitted:}{(?i)auto-generated|auto-replied} }} {no}{yes}}
   ├──expanding: (?i)bulk|list|junk
   ╰─────result: (?i)bulk|list|junk
+ compiled RE '(?i)bulk|list|junk' not found in local cache
+ compiling RE '(?i)bulk|list|junk'
+ compiled RE '(?i)bulk|list|junk' saved in local cache
   ╭considering: $h_auto-submitted:}{(?i)auto-generated|auto-replied} }} {no}{yes}}
   ├considering: }{(?i)auto-generated|auto-replied} }} {no}{yes}}
   ├──expanding: $h_auto-submitted:
@@ -372,6 +375,9 @@ LOG: MAIN
   ├considering: } }} {no}{yes}}
   ├──expanding: (?i)auto-generated|auto-replied
   ╰─────result: (?i)auto-generated|auto-replied
+ compiled RE '(?i)auto-generated|auto-replied' not found in local cache
+ compiling RE '(?i)auto-generated|auto-replied'
+ compiled RE '(?i)auto-generated|auto-replied' saved in local cache
  ├──condition: or {{ !eq{$h_list-id:$h_list-post:$h_list-subscribe:}{} }{ match{$h_precedence:}{(?i)bulk|list|junk} }{ match{$h_auto-submitted:}{(?i)auto-generated|auto-replied} }}
  ├─────result: false
   ╭───scanning: no}{yes}}
diff --git a/test/stderr/0632 b/test/stderr/0632
new file mode 100644 (file)
index 0000000..eca5a27
--- /dev/null
@@ -0,0 +1,908 @@
+
+******** SERVER ********
+Exim version x.yz ....
+adding SSLKEYLOGFILE=TESTSUITE/spool/sslkeys
+configuration file is TESTSUITE/test-config
+admin user
+dropping to exim gid; retaining priv uid
+daemon_smtp_port overridden by -oX:
+  <: 1225
+creating notifier socket
+ ╭considering: $spool_directory/exim_daemon_notify
+ ├considering: /exim_daemon_notify
+ ├───────text: /exim_daemon_notify
+ ├──expanding: $spool_directory/exim_daemon_notify
+ ╰─────result: TESTSUITE/spool/exim_daemon_notify
+ TESTSUITE/spool/exim_daemon_notify
+listening on all interfaces (IPv6) port PORT_D
+listening on all interfaces (IPv4) port PORT_D
+pid written to TESTSUITE/spool/exim-daemon.pid
+LOG: MAIN
+  exim x.yz daemon started: pid=p1234, no queue runs, listening for SMTP on port PORT_D
+daemon running with uid=EXIM_UID gid=EXIM_GID euid=EXIM_UID egid=EXIM_GID
+Listening...
+Connection request from 127.0.0.1 port sssss
+search_tidyup called
+p1235 Process p1235 is handling incoming connection from [127.0.0.1]
+p1235  ╭considering: $smtp_active_hostname ESMTP Exim $version_number $tod_full
+p1235  ├considering:  ESMTP Exim $version_number $tod_full
+p1235  ├───────text:  ESMTP Exim 
+p1235  ├considering: $version_number $tod_full
+p1235  ├considering:  $tod_full
+p1235  ├───────text:  
+p1235  ├considering: $tod_full
+p1235  ├──expanding: $smtp_active_hostname ESMTP Exim $version_number $tod_full
+p1235  ╰─────result: myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+p1235 Process p1235 is ready for new message
+p1235 compiled caseless RE '^nomatch_list' not found in local cache
+p1235 compiled RE '^nomatch_list' saved in local cache
+p1235 sending RE '^nomatch_list' to daemon
+p1235  ╭considering: $spool_directory/exim_daemon_notify
+p1235  ├considering: /exim_daemon_notify
+p1235  ├───────text: /exim_daemon_notify
+p1235  ├──expanding: $spool_directory/exim_daemon_notify
+p1235  ╰─────result: TESTSUITE/spool/exim_daemon_notify
+p1235  ╭considering: ${if match {a_random_string} {static_RE}}
+p1235   ╭considering: a_random_string} {static_RE}}
+p1235   ├───────text: a_random_string
+p1235   ├considering: } {static_RE}}
+p1235   ├──expanding: a_random_string
+p1235   ╰─────result: a_random_string
+p1235   ╭considering: static_RE}}
+p1235   ├───────text: static_RE
+p1235   ├considering: }}
+p1235   ├──expanding: static_RE
+p1235   ╰─────result: static_RE
+p1235  compiled RE 'static_RE' not found in local cache
+p1235  compiling RE 'static_RE'
+p1235  compiled RE 'static_RE' saved in local cache
+p1235  sending RE 'static_RE' to daemon
+p1235   ╭considering: $spool_directory/exim_daemon_notify
+p1235   ├considering: /exim_daemon_notify
+p1235   ├───────text: /exim_daemon_notify
+p1235   ├──expanding: $spool_directory/exim_daemon_notify
+p1235   ╰─────result: TESTSUITE/spool/exim_daemon_notify
+p1235  ├──condition: match {a_random_string} {static_RE}
+p1235  ├─────result: false
+p1235  ├──expanding: ${if match {a_random_string} {static_RE}}
+p1235  ╰─────result: 
+p1235  ╭considering: ${if match {a_random_string} {tricky_static_RE\$}}
+p1235   ╭considering: a_random_string} {tricky_static_RE\$}}
+p1235   ├───────text: a_random_string
+p1235   ├considering: } {tricky_static_RE\$}}
+p1235   ├──expanding: a_random_string
+p1235   ╰─────result: a_random_string
+p1235   ╭considering: tricky_static_RE\$}}
+p1235   ├───────text: tricky_static_RE
+p1235   ├considering: \$}}
+p1235   ├backslashed: '\$'
+p1235   ├considering: }}
+p1235   ├──expanding: tricky_static_RE\$
+p1235   ╰─────result: tricky_static_RE$
+p1235  compiled RE 'tricky_static_RE$' not found in local cache
+p1235  compiling RE 'tricky_static_RE$'
+p1235  compiled RE 'tricky_static_RE$' saved in local cache
+p1235  sending RE 'tricky_static_RE$' to daemon
+p1235   ╭considering: $spool_directory/exim_daemon_notify
+p1235   ├considering: /exim_daemon_notify
+p1235   ├───────text: /exim_daemon_notify
+p1235   ├──expanding: $spool_directory/exim_daemon_notify
+p1235   ╰─────result: TESTSUITE/spool/exim_daemon_notify
+p1235  ├──condition: match {a_random_string} {tricky_static_RE\$}
+p1235  ├─────result: false
+p1235  ├──expanding: ${if match {a_random_string} {tricky_static_RE\$}}
+p1235  ╰─────result: 
+p1235  ╭considering: ${if match {a_random_string} {pid=${pid} uncacheable_RE}}
+p1235   ╭considering: a_random_string} {pid=${pid} uncacheable_RE}}
+p1235   ├───────text: a_random_string
+p1235   ├considering: } {pid=${pid} uncacheable_RE}}
+p1235   ├──expanding: a_random_string
+p1235   ╰─────result: a_random_string
+p1235   ╭considering: pid=${pid} uncacheable_RE}}
+p1235   ├───────text: pid=
+p1235   ├considering: ${pid} uncacheable_RE}}
+p1235   ├considering:  uncacheable_RE}}
+p1235   ├───────text:  uncacheable_RE
+p1235   ├considering: }}
+p1235   ├──expanding: pid=${pid} uncacheable_RE
+p1235   ╰─────result: pid=p1235 uncacheable_RE
+p1235  compiling RE 'pid=p1235 uncacheable_RE'
+p1235  ├──condition: match {a_random_string} {pid=${pid} uncacheable_RE}
+p1235  ├─────result: false
+p1235  ├──expanding: ${if match {a_random_string} {pid=${pid} uncacheable_RE}}
+p1235  ╰─────result: 
+p1235 search_tidyup called
+p1235 search_tidyup called
+p1235  ╭considering: ${tod_full}
+p1235  ├──expanding: ${tod_full}
+p1235  ╰─────result: Tue, 2 Mar 1999 09:44:33 +0000
+p1235  ╭considering: Received: ${if def:sender_rcvhost {from $sender_rcvhost
+p1235          }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1235          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235  ├───────text: Received: 
+p1235  ├considering: ${if def:sender_rcvhost {from $sender_rcvhost
+p1235          }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1235          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235  ├──condition: def:sender_rcvhost
+p1235  ├─────result: true
+p1235   ╭considering: from $sender_rcvhost
+p1235          }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1235          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├───────text: from 
+p1235   ├considering: $sender_rcvhost
+p1235          }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1235          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├considering: 
+p1235          }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1235          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├───────text: 
+p1235          
+p1235   ├considering: }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1235          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├──expanding: from $sender_rcvhost
+p1235          
+p1235   ╰─────result: from [127.0.0.1] (helo=test.ex)
+p1235          
+p1235              ╰──(tainted)
+p1235   ╭───scanning: ${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1235          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├──condition: def:sender_ident
+p1235   ├─────result: false
+p1235    ╭───scanning: from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1235          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235    ├───────text: from 
+p1235    ├───scanning: ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1235          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235    ╎╭───scanning: $sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1235    ╎   }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235    ╎   }}(Exim $version_number)
+p1235    ╎   ${if def:sender_address {(envelope-from <$sender_address>)
+p1235    ╎   }}id $message_exim_id${if def:received_for {
+p1235    ╎   for $received_for}}
+p1235    ╎├───scanning: } }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1235    ╎   }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235    ╎   }}(Exim $version_number)
+p1235    ╎   ${if def:sender_address {(envelope-from <$sender_address>)
+p1235    ╎   }}id $message_exim_id${if def:received_for {
+p1235    ╎   for $received_for}}
+p1235    ╎├──expanding: $sender_ident
+p1235    ╎├─────result: 
+p1235    ╎╰───skipping: result is not used
+p1235    ├───scanning:  }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1235          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235    ├───────text:  
+p1235    ├───scanning: }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1235          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235    ├──expanding: from ${quote_local_part:$sender_ident} 
+p1235    ├─────result: from  
+p1235    ╰───skipping: result is not used
+p1235   ├───item-res: 
+p1235   ├───scanning: ${if def:sender_helo_name {(helo=$sender_helo_name)
+p1235          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├──condition: def:sender_helo_name
+p1235   ├─────result: false
+p1235    ╭───scanning: (helo=$sender_helo_name)
+p1235          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235    ├───────text: (helo=
+p1235    ├───scanning: $sender_helo_name)
+p1235          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235    ├───scanning: )
+p1235          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235    ├───────text: )
+p1235          
+p1235    ├───scanning: }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235    ├──expanding: (helo=$sender_helo_name)
+p1235          
+p1235    ├─────result: (helo=)
+p1235          
+p1235    ╰───skipping: result is not used
+p1235   ├───item-res: 
+p1235   ├───scanning: }}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├──expanding: ${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1235          }}
+p1235   ├─────result: 
+p1235   ╰───skipping: result is not used
+p1235  ├───item-res: from [127.0.0.1] (helo=test.ex)
+p1235          
+p1235             ╰──(tainted)
+p1235  ├considering: by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235  ├───────text: by 
+p1235  ├considering: $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235  ├considering:  ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235  ├───────text:  
+p1235  ├considering: ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235  ├──condition: def:received_protocol
+p1235  ├─────result: true
+p1235   ╭considering: with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├───────text: with 
+p1235   ├considering: $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├considering:  }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├───────text:  
+p1235   ├considering: }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├──expanding: with $received_protocol 
+p1235   ╰─────result: with smtp 
+p1235  ├───item-res: with smtp 
+p1235             ╰──(tainted)
+p1235  ├considering: ${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├──expanding:  ($tls_in_ver)
+p1235   ├─────result:  ()
+p1235   ╰───skipping: result is not used
+p1235  ├───item-res: 
+p1235             ╰──(tainted)
+p1235  ├considering: ${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235  ├──condition: def:tls_in_cipher_std
+p1235  ├─────result: false
+p1235   ╭───scanning:  tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├───────text:  tls 
+p1235   ├───scanning: $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├───scanning: 
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├───────text: 
+p1235          
+p1235   ├───scanning: }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├──expanding:  tls $tls_in_cipher_std
+p1235          
+p1235   ├─────result:  tls 
+p1235          
+p1235   ╰───skipping: result is not used
+p1235  ├───item-res: 
+p1235             ╰──(tainted)
+p1235  ├considering: (Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235  ├───────text: (Exim 
+p1235  ├considering: $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235  ├considering: )
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235  ├───────text: )
+p1235          
+p1235  ├considering: ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235  ├──condition: def:sender_address
+p1235  ├─────result: true
+p1235   ╭considering: (envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├───────text: (envelope-from <
+p1235   ├considering: $sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├considering: >)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├───────text: >)
+p1235          
+p1235   ├considering: }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235   ├──expanding: (envelope-from <$sender_address>)
+p1235          
+p1235   ╰─────result: (envelope-from <CALLER@test.ex>)
+p1235          
+p1235              ╰──(tainted)
+p1235  ├───item-res: (envelope-from <CALLER@test.ex>)
+p1235          
+p1235             ╰──(tainted)
+p1235  ├considering: id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235  ├───────text: id 
+p1235  ├considering: $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235  ├considering: ${if def:received_for {
+p1235          for $received_for}}
+p1235  ├──condition: def:received_for
+p1235  ├─────result: true
+p1235   ╭considering: 
+p1235          for $received_for}}
+p1235   ├───────text: 
+p1235          for 
+p1235   ├considering: $received_for}}
+p1235   ├considering: }}
+p1235   ├──expanding: 
+p1235          for $received_for
+p1235   ╰─────result: 
+p1235          for dest_1@test.ex
+p1235              ╰──(tainted)
+p1235  ├───item-res: 
+p1235          for dest_1@test.ex
+p1235             ╰──(tainted)
+p1235  ├──expanding: Received: ${if def:sender_rcvhost {from $sender_rcvhost
+p1235          }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1235          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1235          }}(Exim $version_number)
+p1235          ${if def:sender_address {(envelope-from <$sender_address>)
+p1235          }}id $message_exim_id${if def:received_for {
+p1235          for $received_for}}
+p1235  ╰─────result: Received: from [127.0.0.1] (helo=test.ex)
+p1235          by myhost.test.ex with smtp (Exim x.yz)
+p1235          (envelope-from <CALLER@test.ex>)
+p1235          id 10HmaX-0005vi-00
+p1235          for dest_1@test.ex
+p1235             ╰──(tainted)
+p1235  ╭considering: ${tod_full}
+p1235  ├──expanding: ${tod_full}
+p1235  ╰─────result: Tue, 2 Mar 1999 09:44:33 +0000
+LOG: MAIN
+  <= CALLER@test.ex H=(test.ex) [127.0.0.1] P=smtp S=sss
+search_tidyup called
+Process p1235 is ready for new message
+LOG: smtp_connection MAIN
+  SMTP connection from (test.ex) [127.0.0.1] closed by QUIT
+p1234 1 SMTP accept process running
+p1234 Listening...
+p1234 daemon_notification from addr ''
+p1234 compiled caseless RE '^nomatch_list' not found in local cache
+p1234 compiling caseless RE '^nomatch_list'
+p1234 compiled RE '^nomatch_list' saved in local cache
+p1234 Listening...
+p1234 daemon_notification from addr ''
+p1234 compiled RE 'static_RE' not found in local cache
+p1234 compiling RE 'static_RE'
+p1234 compiled RE 'static_RE' saved in local cache
+p1234 Listening...
+p1234 daemon_notification from addr ''
+p1234 compiled RE 'tricky_static_RE$' not found in local cache
+p1234 compiling RE 'tricky_static_RE$'
+p1234 compiled RE 'tricky_static_RE$' saved in local cache
+p1234 Listening...
+search_tidyup called
+>>>>>>>>>>>>>>>> Exim pid=p1235 (daemon-accept) terminating with rc=0 >>>>>>>>>>>>>>>>
+p1234 child p1235 ended: status=0x0
+p1234   normal exit, 0
+p1234 0 SMTP accept processes now running
+p1234 Listening...
+p1234 Connection request from 127.0.0.1 port sssss
+p1234 search_tidyup called
+p1236 Process p1236 is handling incoming connection from [127.0.0.1]
+p1236  ╭considering: $smtp_active_hostname ESMTP Exim $version_number $tod_full
+p1236  ├considering:  ESMTP Exim $version_number $tod_full
+p1236  ├───────text:  ESMTP Exim 
+p1236  ├considering: $version_number $tod_full
+p1236  ├considering:  $tod_full
+p1236  ├───────text:  
+p1236  ├considering: $tod_full
+p1236  ├──expanding: $smtp_active_hostname ESMTP Exim $version_number $tod_full
+p1236  ╰─────result: myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+p1236 Process p1236 is ready for new message
+p1236 compiled caseless RE '^nomatch_list' found in local cache
+p1236  ╭considering: ${if match {a_random_string} {static_RE}}
+p1236   ╭considering: a_random_string} {static_RE}}
+p1236   ├───────text: a_random_string
+p1236   ├considering: } {static_RE}}
+p1236   ├──expanding: a_random_string
+p1236   ╰─────result: a_random_string
+p1236   ╭considering: static_RE}}
+p1236   ├───────text: static_RE
+p1236   ├considering: }}
+p1236   ├──expanding: static_RE
+p1236   ╰─────result: static_RE
+p1236  compiled RE 'static_RE' found in local cache
+p1236  ├──condition: match {a_random_string} {static_RE}
+p1236  ├─────result: false
+p1236  ├──expanding: ${if match {a_random_string} {static_RE}}
+p1236  ╰─────result: 
+p1236  ╭considering: ${if match {a_random_string} {tricky_static_RE\$}}
+p1236   ╭considering: a_random_string} {tricky_static_RE\$}}
+p1236   ├───────text: a_random_string
+p1236   ├considering: } {tricky_static_RE\$}}
+p1236   ├──expanding: a_random_string
+p1236   ╰─────result: a_random_string
+p1236   ╭considering: tricky_static_RE\$}}
+p1236   ├───────text: tricky_static_RE
+p1236   ├considering: \$}}
+p1236   ├backslashed: '\$'
+p1236   ├considering: }}
+p1236   ├──expanding: tricky_static_RE\$
+p1236   ╰─────result: tricky_static_RE$
+p1236  compiled RE 'tricky_static_RE$' found in local cache
+p1236  ├──condition: match {a_random_string} {tricky_static_RE\$}
+p1236  ├─────result: false
+p1236  ├──expanding: ${if match {a_random_string} {tricky_static_RE\$}}
+p1236  ╰─────result: 
+p1236  ╭considering: ${if match {a_random_string} {pid=${pid} uncacheable_RE}}
+p1236   ╭considering: a_random_string} {pid=${pid} uncacheable_RE}}
+p1236   ├───────text: a_random_string
+p1236   ├considering: } {pid=${pid} uncacheable_RE}}
+p1236   ├──expanding: a_random_string
+p1236   ╰─────result: a_random_string
+p1236   ╭considering: pid=${pid} uncacheable_RE}}
+p1236   ├───────text: pid=
+p1236   ├considering: ${pid} uncacheable_RE}}
+p1236   ├considering:  uncacheable_RE}}
+p1236   ├───────text:  uncacheable_RE
+p1236   ├considering: }}
+p1236   ├──expanding: pid=${pid} uncacheable_RE
+p1236   ╰─────result: pid=p1236 uncacheable_RE
+p1236  compiling RE 'pid=p1236 uncacheable_RE'
+p1236  ├──condition: match {a_random_string} {pid=${pid} uncacheable_RE}
+p1236  ├─────result: false
+p1236  ├──expanding: ${if match {a_random_string} {pid=${pid} uncacheable_RE}}
+p1236  ╰─────result: 
+p1236 search_tidyup called
+p1236 search_tidyup called
+p1236  ╭considering: ${tod_full}
+p1236  ├──expanding: ${tod_full}
+p1236  ╰─────result: Tue, 2 Mar 1999 09:44:33 +0000
+p1236  ╭considering: Received: ${if def:sender_rcvhost {from $sender_rcvhost
+p1236          }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1236          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236  ├───────text: Received: 
+p1236  ├considering: ${if def:sender_rcvhost {from $sender_rcvhost
+p1236          }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1236          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236  ├──condition: def:sender_rcvhost
+p1236  ├─────result: true
+p1236   ╭considering: from $sender_rcvhost
+p1236          }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1236          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├───────text: from 
+p1236   ├considering: $sender_rcvhost
+p1236          }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1236          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├considering: 
+p1236          }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1236          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├───────text: 
+p1236          
+p1236   ├considering: }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1236          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├──expanding: from $sender_rcvhost
+p1236          
+p1236   ╰─────result: from [127.0.0.1] (helo=test.ex)
+p1236          
+p1236              ╰──(tainted)
+p1236   ╭───scanning: ${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1236          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├──condition: def:sender_ident
+p1236   ├─────result: false
+p1236    ╭───scanning: from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1236          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236    ├───────text: from 
+p1236    ├───scanning: ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1236          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236    ╎╭───scanning: $sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1236    ╎   }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236    ╎   }}(Exim $version_number)
+p1236    ╎   ${if def:sender_address {(envelope-from <$sender_address>)
+p1236    ╎   }}id $message_exim_id${if def:received_for {
+p1236    ╎   for $received_for}}
+p1236    ╎├───scanning: } }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1236    ╎   }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236    ╎   }}(Exim $version_number)
+p1236    ╎   ${if def:sender_address {(envelope-from <$sender_address>)
+p1236    ╎   }}id $message_exim_id${if def:received_for {
+p1236    ╎   for $received_for}}
+p1236    ╎├──expanding: $sender_ident
+p1236    ╎├─────result: 
+p1236    ╎╰───skipping: result is not used
+p1236    ├───scanning:  }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1236          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236    ├───────text:  
+p1236    ├───scanning: }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1236          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236    ├──expanding: from ${quote_local_part:$sender_ident} 
+p1236    ├─────result: from  
+p1236    ╰───skipping: result is not used
+p1236   ├───item-res: 
+p1236   ├───scanning: ${if def:sender_helo_name {(helo=$sender_helo_name)
+p1236          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├──condition: def:sender_helo_name
+p1236   ├─────result: false
+p1236    ╭───scanning: (helo=$sender_helo_name)
+p1236          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236    ├───────text: (helo=
+p1236    ├───scanning: $sender_helo_name)
+p1236          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236    ├───scanning: )
+p1236          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236    ├───────text: )
+p1236          
+p1236    ├───scanning: }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236    ├──expanding: (helo=$sender_helo_name)
+p1236          
+p1236    ├─────result: (helo=)
+p1236          
+p1236    ╰───skipping: result is not used
+p1236   ├───item-res: 
+p1236   ├───scanning: }}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├──expanding: ${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1236          }}
+p1236   ├─────result: 
+p1236   ╰───skipping: result is not used
+p1236  ├───item-res: from [127.0.0.1] (helo=test.ex)
+p1236          
+p1236             ╰──(tainted)
+p1236  ├considering: by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236  ├───────text: by 
+p1236  ├considering: $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236  ├considering:  ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236  ├───────text:  
+p1236  ├considering: ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236  ├──condition: def:received_protocol
+p1236  ├─────result: true
+p1236   ╭considering: with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├───────text: with 
+p1236   ├considering: $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├considering:  }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├───────text:  
+p1236   ├considering: }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├──expanding: with $received_protocol 
+p1236   ╰─────result: with smtp 
+p1236  ├───item-res: with smtp 
+p1236             ╰──(tainted)
+p1236  ├considering: ${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├──expanding:  ($tls_in_ver)
+p1236   ├─────result:  ()
+p1236   ╰───skipping: result is not used
+p1236  ├───item-res: 
+p1236             ╰──(tainted)
+p1236  ├considering: ${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236  ├──condition: def:tls_in_cipher_std
+p1236  ├─────result: false
+p1236   ╭───scanning:  tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├───────text:  tls 
+p1236   ├───scanning: $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├───scanning: 
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├───────text: 
+p1236          
+p1236   ├───scanning: }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├──expanding:  tls $tls_in_cipher_std
+p1236          
+p1236   ├─────result:  tls 
+p1236          
+p1236   ╰───skipping: result is not used
+p1236  ├───item-res: 
+p1236             ╰──(tainted)
+p1236  ├considering: (Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236  ├───────text: (Exim 
+p1236  ├considering: $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236  ├considering: )
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236  ├───────text: )
+p1236          
+p1236  ├considering: ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236  ├──condition: def:sender_address
+p1236  ├─────result: true
+p1236   ╭considering: (envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├───────text: (envelope-from <
+p1236   ├considering: $sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├considering: >)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├───────text: >)
+p1236          
+p1236   ├considering: }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236   ├──expanding: (envelope-from <$sender_address>)
+p1236          
+p1236   ╰─────result: (envelope-from <CALLER@test.ex>)
+p1236          
+p1236              ╰──(tainted)
+p1236  ├───item-res: (envelope-from <CALLER@test.ex>)
+p1236          
+p1236             ╰──(tainted)
+p1236  ├considering: id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236  ├───────text: id 
+p1236  ├considering: $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236  ├considering: ${if def:received_for {
+p1236          for $received_for}}
+p1236  ├──condition: def:received_for
+p1236  ├─────result: true
+p1236   ╭considering: 
+p1236          for $received_for}}
+p1236   ├───────text: 
+p1236          for 
+p1236   ├considering: $received_for}}
+p1236   ├considering: }}
+p1236   ├──expanding: 
+p1236          for $received_for
+p1236   ╰─────result: 
+p1236          for dest_2@test.ex
+p1236              ╰──(tainted)
+p1236  ├───item-res: 
+p1236          for dest_2@test.ex
+p1236             ╰──(tainted)
+p1236  ├──expanding: Received: ${if def:sender_rcvhost {from $sender_rcvhost
+p1236          }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name)
+p1236          }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_ver        { ($tls_in_ver)}}${if def:tls_in_cipher_std { tls $tls_in_cipher_std
+p1236          }}(Exim $version_number)
+p1236          ${if def:sender_address {(envelope-from <$sender_address>)
+p1236          }}id $message_exim_id${if def:received_for {
+p1236          for $received_for}}
+p1236  ╰─────result: Received: from [127.0.0.1] (helo=test.ex)
+p1236          by myhost.test.ex with smtp (Exim x.yz)
+p1236          (envelope-from <CALLER@test.ex>)
+p1236          id 10HmaY-0005vi-00
+p1236          for dest_2@test.ex
+p1236             ╰──(tainted)
+p1236  ╭considering: ${tod_full}
+p1236  ├──expanding: ${tod_full}
+p1236  ╰─────result: Tue, 2 Mar 1999 09:44:33 +0000
+LOG: MAIN
+  <= CALLER@test.ex H=(test.ex) [127.0.0.1] P=smtp S=sss
+search_tidyup called
+Process p1236 is ready for new message
+LOG: smtp_connection MAIN
+  SMTP connection from (test.ex) [127.0.0.1] closed by QUIT
+p1234 1 SMTP accept process running
+p1234 Listening...
+search_tidyup called
+>>>>>>>>>>>>>>>> Exim pid=p1236 (daemon-accept) terminating with rc=0 >>>>>>>>>>>>>>>>
+p1234 child p1236 ended: status=0x0
+p1234   normal exit, 0
+p1234 0 SMTP accept processes now running
+p1234 Listening...
+p1234 SIGTERM/SIGINT seen
+p1234 search_tidyup called
+p1234 >>>>>>>>>>>>>>>> Exim pid=p1234 (daemon) terminating with rc=0 >>>>>>>>>>>>>>>>
index 836dab2026ff5bdf3a6fb087da610b436c8eb949..49e31375a58b4a0991de8dd7b38e199f68da3903 100644 (file)
@@ -462,6 +462,9 @@ mylogin authenticator server_condition:
   $auth1 = userx secret
   $1 = userx secret
 +++MYLOGIN $1="userx secret" $2="" $3=""
+ compiled RE '^(\S+)\s+(\S+)$' not found in local cache
+ compiling RE '^(\S+)\s+(\S+)$'
+ compiled RE '^(\S+)\s+(\S+)$' saved in local cache
 expanded string: yes
 SMTP>> 235 Authentication succeeded
 SMTP<< quit
index c7cc3453962a0155ef658c0a0c8556e93606d1b2..d96971064bef060167dcbfccda4f521bc800f86c 100644 (file)
@@ -130,6 +130,7 @@ created directory TESTSUITE/test-mail/nofile
 created directory TESTSUITE/test-mail/nofile/tmp
 created directory TESTSUITE/test-mail/nofile/new
 created directory TESTSUITE/test-mail/nofile/cur
+compiling RE '^(?:cur|new|\..*)$'
 using regex for maildir directory selection: ^(?:cur|new|\..*)$
 looking for maildirsize in TESTSUITE/test-mail/nofile
 TESTSUITE/test-mail/nofile/maildirsize does not exist: recalculating
@@ -318,6 +319,7 @@ ensuring maildir directories exist in TESTSUITE/test-mail/userx
 created directory TESTSUITE/test-mail/userx/tmp
 created directory TESTSUITE/test-mail/userx/new
 created directory TESTSUITE/test-mail/userx/cur
+compiling RE '^(?:cur|new|\..*)$'
 using regex for maildir directory selection: ^(?:cur|new|\..*)$
 looking for maildirsize in TESTSUITE/test-mail/userx
 reading quota parameters from maildirsize data
@@ -506,6 +508,7 @@ appendfile: mode=600 notify_comsat=0 quota=500 warning=0
   maildir_use_size_file=yes
 de-tainting path 'TESTSUITE/test-mail/userx'
 ensuring maildir directories exist in TESTSUITE/test-mail/userx
+compiling RE '^(?:cur|new|\..*)$'
 using regex for maildir directory selection: ^(?:cur|new|\..*)$
 looking for maildirsize in TESTSUITE/test-mail/userx
 reading quota parameters from maildirsize data
@@ -711,6 +714,7 @@ appendfile: mode=600 notify_comsat=0 quota=500 warning=0
   maildir_use_size_file=yes
 de-tainting path 'TESTSUITE/test-mail/userx'
 ensuring maildir directories exist in TESTSUITE/test-mail/userx
+compiling RE '^(?:cur|new|\..*)$'
 using regex for maildir directory selection: ^(?:cur|new|\..*)$
 looking for maildirsize in TESTSUITE/test-mail/userx
 reading quota parameters from maildirsize data
index 4c7c490b0b04c5db9527935a04aa18512ee884f1..4194360f111beafeeccc74ce461707114396b119 100644 (file)
@@ -130,6 +130,7 @@ created directory TESTSUITE/test-mail/userx
 created directory TESTSUITE/test-mail/userx/tmp
 created directory TESTSUITE/test-mail/userx/new
 created directory TESTSUITE/test-mail/userx/cur
+compiling RE '^(?:cur|new|\..*)$'
 using regex for maildir directory selection: ^(?:cur|new|\..*)$
 looking for maildirsize in TESTSUITE/test-mail/userx
 TESTSUITE/test-mail/userx/maildirsize does not exist: recalculating
index 17168982c7a5c072525864baa07f930880e42ba0..f4fd5654d8f992c255c80e7ce2e629163f588bba 100644 (file)
@@ -92,6 +92,9 @@ cmd buf flush ddd bytes
   ├considering: }} {match{$item}{\N^250-([\w.]+)\s\N}}} {$1}}
   ├──expanding: .outlook.com\$
   ╰─────result: .outlook.com$
+ compiled RE '.outlook.com$' not found in local cache
+ compiling RE '.outlook.com$'
+ compiled RE '.outlook.com$' saved in local cache
   ╭───scanning: $item}{\N^250-([\w.]+)\s\N}}} {$1}}
   ├───scanning: }{\N^250-([\w.]+)\s\N}}} {$1}}
   ├──expanding: $item
@@ -648,6 +651,9 @@ cmd buf flush ddd bytes
   ├considering: }} {match{$item}{\N^250-([\w.]+)\s\N}}} {$1}}
   ├──expanding: .outlook.com\$
   ╰─────result: .outlook.com$
+ compiled RE '.outlook.com$' not found in local cache
+ compiling RE '.outlook.com$'
+ compiled RE '.outlook.com$' saved in local cache
   ╭───scanning: $item}{\N^250-([\w.]+)\s\N}}} {$1}}
   ├───scanning: }{\N^250-([\w.]+)\s\N}}} {$1}}
   ├──expanding: $item
@@ -1165,6 +1171,9 @@ cmd buf flush ddd bytes
   ├considering: }} {match{$item}{\N^250-([\w.]+)\s\N}}} {$1}}
   ├──expanding: .outlook.com\$
   ╰─────result: .outlook.com$
+ compiled RE '.outlook.com$' not found in local cache
+ compiling RE '.outlook.com$'
+ compiled RE '.outlook.com$' saved in local cache
   ╭───scanning: $item}{\N^250-([\w.]+)\s\N}}} {$1}}
   ├───scanning: }{\N^250-([\w.]+)\s\N}}} {$1}}
   ├──expanding: $item
index 241cedf689c275726bdadbdad040585fb28114bd..2436aa14b408523a63f22020ec1b6919a3c1550f 100644 (file)
@@ -92,6 +92,9 @@ cmd buf flush ddd bytes
   ├considering: }} {match{$item}{\N^250-([\w.]+)\s\N}}} {$1}}
   ├──expanding: .outlook.com\$
   ╰─────result: .outlook.com$
+ compiled RE '.outlook.com$' not found in local cache
+ compiling RE '.outlook.com$'
+ compiled RE '.outlook.com$' saved in local cache
   ╭───scanning: $item}{\N^250-([\w.]+)\s\N}}} {$1}}
   ├───scanning: }{\N^250-([\w.]+)\s\N}}} {$1}}
   ├──expanding: $item
@@ -649,6 +652,9 @@ cmd buf flush ddd bytes
   ├considering: }} {match{$item}{\N^250-([\w.]+)\s\N}}} {$1}}
   ├──expanding: .outlook.com\$
   ╰─────result: .outlook.com$
+ compiled RE '.outlook.com$' not found in local cache
+ compiling RE '.outlook.com$'
+ compiled RE '.outlook.com$' saved in local cache
   ╭───scanning: $item}{\N^250-([\w.]+)\s\N}}} {$1}}
   ├───scanning: }{\N^250-([\w.]+)\s\N}}} {$1}}
   ├──expanding: $item
@@ -1166,6 +1172,9 @@ cmd buf flush ddd bytes
   ├considering: }} {match{$item}{\N^250-([\w.]+)\s\N}}} {$1}}
   ├──expanding: .outlook.com\$
   ╰─────result: .outlook.com$
+ compiled RE '.outlook.com$' not found in local cache
+ compiling RE '.outlook.com$'
+ compiled RE '.outlook.com$' saved in local cache
   ╭───scanning: $item}{\N^250-([\w.]+)\s\N}}} {$1}}
   ├───scanning: }{\N^250-([\w.]+)\s\N}}} {$1}}
   ├──expanding: $item