Use pool storage for regex operations
[exim.git] / src / src / expand.c
index b7719f642a6027044ffe04a657aedc4c6783036a..03ae3206e6550e74fca03e1f790763d5ba020a2d 100644 (file)
@@ -2,8 +2,8 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 - 2021 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 
@@ -2949,7 +2949,7 @@ switch(cond_type = identify_operator(&s, &opname))
       int err;
 
       if (!(re = pcre2_compile((PCRE2_SPTR)sub[1], PCRE2_ZERO_TERMINATED,
-                               PCRE_COPT, &err, &offset, pcre_cmp_ctx)))
+                               PCRE_COPT, &err, &offset, pcre_gen_cmp_ctx)))
        {
        uschar errbuf[128];
        pcre2_get_error_message(err, errbuf, sizeof(errbuf));
@@ -2964,12 +2964,12 @@ switch(cond_type = identify_operator(&s, &opname))
 
     case ECOND_MATCH_ADDRESS:  /* Match in an address list */
       rc = match_address_list(sub[0], TRUE, FALSE, &(sub[1]), NULL, -1, 0,
-                             NULL);
+                             CUSS &lookup_value);
       goto MATCHED_SOMETHING;
 
     case ECOND_MATCH_DOMAIN:   /* Match in a domain list */
       rc = match_isinlist(sub[0], &(sub[1]), 0, &domainlist_anchor, NULL,
-       MCL_DOMAIN + MCL_NOEXPAND, TRUE, NULL);
+       MCL_DOMAIN + MCL_NOEXPAND, TRUE, CUSS &lookup_value);
       goto MATCHED_SOMETHING;
 
     case ECOND_MATCH_IP:       /* Match IP address in a host list */
@@ -3003,13 +3003,13 @@ switch(cond_type = identify_operator(&s, &opname))
               &cb,                       /* argument for function */
               MCL_HOST,                  /* type of check */
               sub[0],                    /* text for debugging */
-              NULL);                     /* where to pass back data */
+              CUSS &lookup_value);       /* where to pass back data */
        }
       goto MATCHED_SOMETHING;
 
     case ECOND_MATCH_LOCAL_PART:
       rc = match_isinlist(sub[0], &(sub[1]), 0, &localpartlist_anchor, NULL,
-       MCL_LOCALPART + MCL_NOEXPAND, TRUE, NULL);
+       MCL_LOCALPART + MCL_NOEXPAND, TRUE, CUSS &lookup_value);
       /* Fall through */
       /* VVVVVVVVVVVV */
       MATCHED_SOMETHING:
@@ -3187,6 +3187,7 @@ switch(cond_type = identify_operator(&s, &opname))
         if (compare(sub[0], iterate_item) == 0)
           {
           tempcond = TRUE;
+         lookup_value = iterate_item;
           break;
           }
        }
@@ -3443,7 +3444,7 @@ switch(cond_type = identify_operator(&s, &opname))
                            TRUE, 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_mtc_ctx) < 0)
+                   md, pcre_gen_mtc_ctx) < 0)
       {
       DEBUG(D_expand) debug_printf("no match for SRS'd local-part pattern\n");
       goto srs_result;
@@ -3520,6 +3521,7 @@ switch(cond_type = identify_operator(&s, &opname))
     boolvalue = TRUE;
 
 srs_result:
+    /* pcre2_match_data_free(md);      gen ctx needs no free */
     if (yield) *yield = (boolvalue == testfor);
     return s;
     }
@@ -3918,7 +3920,7 @@ Returns:       new pointer for expandable string, terminated if non-null
 */
 
 gstring *
-cat_file(FILE *f, gstring *yield, uschar *eol)
+cat_file(FILE * f, gstring * yield, uschar * eol)
 {
 uschar buffer[1024];
 
@@ -3930,8 +3932,6 @@ while (Ufgets(buffer, sizeof(buffer), f))
   if (eol && buffer[len])
     yield = string_cat(yield, eol);
   }
-
-(void) string_from_gstring(yield);
 return yield;
 }
 
@@ -3953,7 +3953,6 @@ while ((rc = tls_read(tls_ctx, buffer, sizeof(buffer))) > 0)
 /* We assume that all errors, and any returns of zero bytes,
 are actually EOF. */
 
-(void) string_from_gstring(yield);
 return yield;
 }
 #endif
@@ -4801,6 +4800,7 @@ while (*s)
       const uschar *next_s;
       int save_expand_nmax =
         save_expand_strings(save_expand_nstring, save_expand_nlength);
+      uschar * save_lookup_value = lookup_value;
 
       Uskip_whitespace(&s);
       if (!(next_s = eval_condition(s, &resetok, skipping ? NULL : &cond)))
@@ -4834,6 +4834,7 @@ while (*s)
       /* Restore external setting of expansion variables for continuation
       at this level. */
 
+      lookup_value = save_lookup_value;
       restore_expand_strings(save_expand_nmax, save_expand_nstring,
         save_expand_nlength);
       break;
@@ -5526,10 +5527,8 @@ while (*s)
     case EITEM_RUN:
       {
       FILE * f;
-      uschar * arg;
-      const uschar ** argv;
-      pid_t pid;
-      int fd_in, fd_out;
+      const uschar * arg, ** argv;
+      BOOL late_expand = TRUE;
 
       if ((expand_forbid & RDO_RUN) != 0)
         {
@@ -5537,17 +5536,45 @@ while (*s)
         goto EXPAND_FAILED;
         }
 
+      /* Handle options to the "run" */
+
+      while (*s == ',')
+       {
+       if (Ustrncmp(++s, "preexpand", 9) == 0)
+         { late_expand = FALSE; s += 9; }
+       else
+         {
+         const uschar * t = s;
+         while (isalpha(*++t)) ;
+         expand_string_message = string_sprintf("bad option '%.*s' for run",
+                                                 (int)(t-s), s);
+         goto EXPAND_FAILED;
+         }
+       }
       Uskip_whitespace(&s);
-      if (*s != '{')
+
+      if (*s != '{')                                   /*}*/
         {
        expand_string_message = US"missing '{' for command arg of run";
-       goto EXPAND_FAILED_CURLY;
+       goto EXPAND_FAILED_CURLY;                       /*"}*/
        }
-      if (!(arg = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok)))
-       goto EXPAND_FAILED;
-      Uskip_whitespace(&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)))
+         goto EXPAND_FAILED;
+       Uskip_whitespace(&s);
+       }
+                                                       /*{*/
       if (*s++ != '}')
-        {
+        {                                              /*{*/
        expand_string_message = US"missing '}' closing command arg of run";
        goto EXPAND_FAILED_CURLY;
        }
@@ -5559,13 +5586,17 @@ while (*s)
        }
       else
         {
+       int fd_in, fd_out;
+       pid_t pid;
+
         if (!transport_set_up_command(&argv,    /* anchor for arg list */
             arg,                                /* raw command */
-            FALSE,                              /* don't expand the arguments */
-            0,                                  /* not relevant when... */
-            NULL,                               /* no transporting address */
-            US"${run} expansion",               /* for error messages */
-            &expand_string_message))            /* where to put error message */
+           late_expand,                /* expand args if not already done */
+            0,                          /* not relevant when... */
+            NULL,                       /* no transporting address */
+           late_expand,                /* allow tainted args, when expand-after-split */
+            US"${run} expansion",       /* for error messages */
+            &expand_string_message))    /* where to put error message */
           goto EXPAND_FAILED;
 
         /* Create the child process, making it a group leader. */
@@ -5576,7 +5607,7 @@ while (*s)
           expand_string_message =
             string_sprintf("couldn't create child process: %s", strerror(errno));
           goto EXPAND_FAILED;
-          }
+         }
 
         /* Nothing is written to the standard input. */
 
@@ -5864,12 +5895,12 @@ while (*s)
         case 2:
         case 3: goto EXPAND_FAILED;
         }
+      if (skipping) continue;
 
-      /*XXX no handling of skipping? */
       /* Compile the regular expression */
 
       if (!(re = pcre2_compile((PCRE2_SPTR)sub[1], PCRE2_ZERO_TERMINATED,
-                 PCRE_COPT, &err, &roffset, pcre_cmp_ctx)))
+                 PCRE_COPT, &err, &roffset, pcre_gen_cmp_ctx)))
         {
         uschar errbuf[128];
        pcre2_get_error_message(err, errbuf, sizeof(errbuf));
@@ -5892,7 +5923,7 @@ while (*s)
         {
        PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
        int n = pcre2_match(re, (PCRE2_SPTR)subject, slen, moffset + moffsetextra,
-         PCRE_EOPT | emptyopt, md, pcre_mtc_ctx);
+         PCRE_EOPT | emptyopt, md, pcre_gen_mtc_ctx);
         uschar * insert;
 
         /* No match - if we previously set PCRE_NOTEMPTY after a null match, this
@@ -5954,9 +5985,9 @@ while (*s)
 
       /* All done - restore numerical variables. */
 
+      /* pcre2_match_data_free(md);    gen ctx needs no free */
       restore_expand_strings(save_expand_nmax, save_expand_nstring,
         save_expand_nlength);
-      if (skipping) continue;
       break;
       }
 
@@ -6417,7 +6448,6 @@ while (*s)
        goto EXPAND_FAILED;                                             /*{{*/
       if (*s++ != '}')
         {
-       /*{*/
        expand_string_message =
          string_sprintf("missing '}' closing first arg of %s", name);
        goto EXPAND_FAILED_CURLY;
@@ -6544,6 +6574,9 @@ while (*s)
         item of the output list, add in a space if the new item begins with the
         separator character, or is an empty string. */
 
+/*XXX is there not a standard support function for this, appending to a list? */
+/* yes, string_append_listele(), but it depends on lack of text before the list */
+
         if (  yield && yield->ptr != save_ptr
           && (temp[0] == *outsep || temp[0] == 0))
           yield = string_catn(yield, US" ", 1);
@@ -7241,7 +7274,7 @@ NOT_ITEM: ;
            goto EXPAND_FAILED;
            }
 
-         exim_sha_update(&h, sub, Ustrlen(sub));
+         exim_sha_update_string(&h, sub);
          exim_sha_finish(&h, &b);
          while (b.len-- > 0)
            yield = string_fmt_append(yield, "%02X", *b.data++);
@@ -7269,7 +7302,7 @@ NOT_ITEM: ;
          goto EXPAND_FAILED;
          }
 
-       exim_sha_update(&h, sub, Ustrlen(sub));
+       exim_sha_update_string(&h, sub);
        exim_sha_finish(&h, &b);
        while (b.len-- > 0)
          yield = string_fmt_append(yield, "%02X", *b.data++);