Fix crash in expansions
[exim.git] / src / src / expand.c
index 57ad76f777c46d90a36406c493a172a6ba0f1097..a7e6e4fb3ea7dbeb1893af97b1080d0c62177e9d 100644 (file)
@@ -23,16 +23,18 @@ typedef unsigned esi_flags;
 #define ESI_HONOR_DOLLAR       BIT(1)  /* $ is meaningfull */
 #define ESI_SKIPPING           BIT(2)  /* value will not be needed */
 
+#ifdef STAND_ALONE
+# ifndef SUPPORT_CRYPTEQ
+#  define SUPPORT_CRYPTEQ
+# endif
+#else
+
 /* Recursively called function */
 
 static uschar *expand_string_internal(const uschar *, esi_flags, const uschar **, BOOL *, BOOL *);
 static int_eximarith_t expanded_string_integer(const uschar *, BOOL);
 
-#ifdef STAND_ALONE
-# ifndef SUPPORT_CRYPTEQ
-#  define SUPPORT_CRYPTEQ
-# endif
-#endif
+#endif /*!STAND_ALONE*/
 
 #ifdef LOOKUP_LDAP
 # include "lookups/ldap.h"
@@ -835,8 +837,6 @@ static var_entry var_table[] = {
   { "warnmsg_recipients",  vtype_stringptr,   &warnmsg_recipients }
 };
 
-static int var_table_size = nelem(var_table);
-
 #ifdef MACRO_PREDEF
 
 /* dummies */
@@ -1278,7 +1278,7 @@ static var_entry *
 find_var_ent(uschar * name)
 {
 int first = 0;
-int last = var_table_size;
+int last = nelem(var_table);
 
 while (last > first)
   {
@@ -4733,6 +4733,7 @@ while (*s)
     reset in the middle of the buffer will make it inaccessible. */
 
     len = Ustrlen(value);
+    DEBUG(D_expand) debug_expansion_interim(US"value", value, len, !!(flags & ESI_SKIPPING));
     if (!yield && newsize != 0)
       {
       yield = g;
@@ -4746,12 +4747,15 @@ while (*s)
     continue;
     }
 
-  if (isdigit(*s))
+  if (isdigit(*s))             /* A $<n> variable */
     {
     int n;
     s = read_cnumber(&n, s);
     if (n >= 0 && n <= expand_nmax)
+      {
+      DEBUG(D_expand) debug_expansion_interim(US"value", expand_nstring[n], expand_nlength[n], !!(flags & ESI_SKIPPING));
       yield = string_catn(yield, expand_nstring[n], expand_nlength[n]);
+      }
     continue;
     }
 
@@ -4776,7 +4780,10 @@ while (*s)
       goto EXPAND_FAILED;
       }
     if (n >= 0 && n <= expand_nmax)
+      {
+      DEBUG(D_expand) debug_expansion_interim(US"value", expand_nstring[n], expand_nlength[n], !!(flags & ESI_SKIPPING));
       yield = string_catn(yield, expand_nstring[n], expand_nlength[n]);
+      }
     continue;
     }
 
@@ -4853,11 +4860,11 @@ while (*s)
 
       switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, flags, TRUE, name, &resetok, NULL))
         {
+       case -1: continue;      /* If skipping, we don't actually do anything */
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:
         case 3: goto EXPAND_FAILED;
         }
-      /*XXX no skipping-optimisation? */
 
       yield = string_append(yield, 3,
                        US"Authentication-Results: ", sub_arg[0], US"; none");
@@ -4944,7 +4951,6 @@ while (*s)
         case 2:
         case 3: goto EXPAND_FAILED;
         }
-      /*XXX no skipping-optimisation? */
 
       if (!sub_arg[1])                 /* One argument */
        {
@@ -5439,15 +5445,12 @@ while (*s)
 
       switch(read_subs(sub_arg, 2, 1, &s, flags, TRUE, name, &resetok, NULL))
         {
+       case -1: continue;      /* If skipping, we don't actually do anything */
         case 1: goto EXPAND_FAILED_CURLY;
         case 2:
         case 3: goto EXPAND_FAILED;
         }
 
-      /* If skipping, we don't actually do anything */
-
-      if (flags & ESI_SKIPPING) continue;
-
       /* Open the file and read it */
 
       if (!(f = Ufopen(sub_arg[0], "rb")))
@@ -5614,7 +5617,7 @@ while (*s)
       const uschar * arg, ** argv;
       BOOL late_expand = TRUE;
 
-      if ((expand_forbid & RDO_RUN) != 0)
+      if (expand_forbid & RDO_RUN)
         {
         expand_string_message = US"running a command is not permitted";
         goto EXPAND_FAILED;
@@ -5645,13 +5648,20 @@ while (*s)
       s++;
 
       if (late_expand)         /* this is the default case */
-       {                                               /*{*/
-       int n = Ustrcspn(s, "}");
+       {
+       int n;
+       const uschar * t;
+       /* Locate the end of the args */
+       (void) expand_string_internal(s,
+         ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | ESI_SKIPPING, &t, NULL, NULL);
+       n = t - s;
        arg = flags & ESI_SKIPPING ? NULL : string_copyn(s, n);
        s += n;
        }
       else
        {
+       DEBUG(D_expand)
+         debug_printf_indent("args string for ${run} expand before split\n");
        if (!(arg = expand_string_internal(s,
                ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, &s, &resetok, NULL)))
          goto EXPAND_FAILED;
@@ -6320,6 +6330,7 @@ while (*s)
         save_expand_strings(save_expand_nstring, save_expand_nlength);
 
       /* Read the field & list arguments */
+      /*XXX Could we use read_subs here (and get better efficiency for skipping)? */
 
       for (int i = 0; i < 2; i++)
         {
@@ -7154,6 +7165,7 @@ NOT_ITEM: ;
 
     /* Deal specially with operators that might take a certificate variable
     as we do not want to do the usual expansion. For most, expand the string.*/
+
     switch(c)
       {
 #ifndef DISABLE_TLS
@@ -7202,7 +7214,7 @@ NOT_ITEM: ;
     to the main loop top. */
 
      {
-     int start = yield->ptr;
+     unsigned expansion_start = gstring_length(yield);
      switch(c)
       {
       case EOP_BASE32:
@@ -8264,8 +8276,8 @@ NOT_ITEM: ;
 
        DEBUG(D_expand)
        {
-       const uschar * s = yield->s + start;
-       int i = yield->ptr - start;
+       const uschar * s = yield->s + expansion_start;
+       int i = gstring_length(yield) - expansion_start;
        BOOL tainted = is_tainted(s);
 
        DEBUG(D_noutf8)
@@ -8802,7 +8814,7 @@ for (int i = 0; i < REGEX_VARS; i++) if (regex_vars[i])
 #endif
 
 /* check known-name variables */
-for (var_entry * v = var_table; v < var_table + var_table_size; v++)
+for (var_entry * v = var_table; v < var_table + nelem(var_table); v++)
   if (v->type == vtype_stringptr)
     assert_variable_notin(US v->name, *(USS v->value), &e);