Use pool storage for regex operations
[exim.git] / src / src / expand.c
index 85619acfef4c5994ac6cc5655ba664b41690e000..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));
@@ -3444,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;
@@ -3521,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;
     }
@@ -3919,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];
 
@@ -3931,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;
 }
 
@@ -3954,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
@@ -5529,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)
         {
@@ -5540,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;
        }
@@ -5562,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. */
@@ -5579,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. */
 
@@ -5867,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));
@@ -5895,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
@@ -5957,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;
       }
 
@@ -6420,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;
@@ -7247,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++);
@@ -7275,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++);