Add predefined macros for expansions
[exim.git] / src / src / expand.c
index 5147c51e7827c5d6ac77886bf3b70bb088452b73..6def3c102a40be4ca82f0385bc0823051b264e5c 100644 (file)
@@ -5,6 +5,7 @@
 /* Copyright (c) The Exim Maintainers 2020 - 2022 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 
 /* Functions for handling string expansion. */
 
 #include "exim.h"
 
+#ifdef MACRO_PREDEF
+# include "macro_predef.h"
+#endif
+
 typedef unsigned esi_flags;
 #define ESI_NOFLAGS            0
 #define ESI_BRACE_ENDS         BIT(0)  /* expansion should stop at } */
@@ -692,6 +697,7 @@ static var_entry var_table[] = {
   { "recipient_verify_failure",vtype_stringptr,&recipient_verify_failure },
   { "recipients",          vtype_string_func, (void *) &fn_recipients },
   { "recipients_count",    vtype_int,         &recipients_count },
+  { "regex_cachesize",     vtype_int,         &regex_cachesize },/* undocumented; devel observability */
 #ifdef WITH_CONTENT_SCAN
   { "regex_match_string",  vtype_stringptr,   &regex_match_string },
 #endif
@@ -829,6 +835,76 @@ static var_entry var_table[] = {
 };
 
 static int var_table_size = nelem(var_table);
+
+#ifdef MACRO_PREDEF
+
+/* dummies */
+uschar * fn_arc_domains(void) {return NULL;}
+uschar * fn_hdrs_added(void) {return NULL;}
+uschar * fn_queue_size(void) {return NULL;}
+uschar * fn_recipients(void) {return NULL;}
+uschar * sender_helo_verified_boolstr(void) {return NULL;}
+uschar * smtp_cmd_hist(void) {return NULL;}
+
+
+
+static void
+expansion_items(void)
+{
+uschar buf[64];
+for (int i = 0; i < nelem(item_table); i++)
+  {
+  spf(buf, sizeof(buf), CUS"_EXP_ITEM_%T", item_table[i]);
+  builtin_macro_create(buf);
+  }
+}
+static void
+expansion_operators(void)
+{
+uschar buf[64];
+for (int i = 0; i < nelem(op_table_underscore); i++)
+  {
+  spf(buf, sizeof(buf), CUS"_EXP_OP_%T", op_table_underscore[i]);
+  builtin_macro_create(buf);
+  }
+for (int i = 0; i < nelem(op_table_main); i++)
+  {
+  spf(buf, sizeof(buf), CUS"_EXP_OP_%T", op_table_main[i]);
+  builtin_macro_create(buf);
+  }
+}
+static void
+expansion_conditions(void)
+{
+uschar buf[64];
+for (int i = 0; i < nelem(cond_table); i++)
+  {
+  spf(buf, sizeof(buf), CUS"_EXP_COND_%T", cond_table[i]);
+  builtin_macro_create(buf);
+  }
+}
+static void
+expansion_variables(void)
+{
+uschar buf[64];
+for (int i = 0; i < nelem(var_table); i++)
+  {
+  spf(buf, sizeof(buf), CUS"_EXP_VAR_%T", var_table[i].name);
+  builtin_macro_create(buf);
+  }
+}
+
+void
+expansions(void)
+{
+expansion_items();
+expansion_operators();
+expansion_conditions();
+expansion_variables();
+}
+
+#else  /*!MACRO_PREDEF*/
+
 static uschar var_buffer[256];
 static BOOL malformed_header;
 
@@ -1859,7 +1935,7 @@ else if (Ustrncmp(name, "r_", 2) == 0)
   return node ? node->data.ptr : strict_acl_vars ? NULL : US"";
   }
 
-/* Handle $auth<n> variables. */
+/* Handle $auth<n>, $regex<n> variables. */
 
 if (Ustrncmp(name, "auth", 4) == 0)
   {
@@ -1868,6 +1944,7 @@ if (Ustrncmp(name, "auth", 4) == 0)
   if (!*endptr && n != 0 && n <= AUTH_VARS)
     return auth_vars[n-1] ? auth_vars[n-1] : US"";
   }
+#ifdef WITH_CONTENT_SCAN
 else if (Ustrncmp(name, "regex", 5) == 0)
   {
   uschar *endptr;
@@ -1875,6 +1952,7 @@ else if (Ustrncmp(name, "regex", 5) == 0)
   if (!*endptr && n != 0 && n <= REGEX_VARS)
     return regex_vars[n-1] ? regex_vars[n-1] : US"";
   }
+#endif
 
 /* For all other variables, search the table */
 
@@ -6541,6 +6619,9 @@ while (*s)
         if (item_type == EITEM_FILTER)
           {
           BOOL condresult;
+         /* the condition could modify $value, as a side-effect */
+         uschar * save_value = lookup_value;
+
           if (!eval_condition(expr, &resetok, &condresult))
             {
             iterate_item = save_iterate_item;
@@ -6549,6 +6630,7 @@ while (*s)
               expand_string_message, name);
             goto EXPAND_FAILED;
             }
+         lookup_value = save_value;
           DEBUG(D_expand) debug_printf_indent("%s: condition is %s\n", name,
             condresult? "true":"false");
           if (condresult)
@@ -6557,14 +6639,12 @@ while (*s)
             continue;               /* FALSE => skip this item */
           }
 
-        /* EITEM_MAP and EITEM_REDUCE */
-
-        else
+        else                   /* EITEM_MAP and EITEM_REDUCE */
           {
+         /* the expansion could modify $value, as a side-effect */
          uschar * t = expand_string_internal(expr,
            ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, NULL, &resetok, NULL);
-          temp = t;
-          if (!temp)
+          if (!(temp = t))
             {
             iterate_item = save_iterate_item;
             expand_string_message = string_sprintf("%s inside \"%s\" item",
@@ -6957,68 +7037,73 @@ while (*s)
         case 3: goto EXPAND_FAILED;
         }
 
-      g = string_catn(g, US"SRS0=", 5);
-
-      /* ${l_4:${hmac{md5}{SRS_SECRET}{${lc:$return_path}}}}= */
-      hmac_md5(sub[0], string_copylc(sub[1]), cksum, sizeof(cksum));
-      g = string_catn(g, cksum, sizeof(cksum));
-      g = string_catn(g, US"=", 1);
-
-      /* ${base32:${eval:$tod_epoch/86400&0x3ff}}= */
+      if (sub[1] && *(sub[1]))
        {
-       struct timeval now;
-       unsigned long i;
-       gstring * h = NULL;
-
-       gettimeofday(&now, NULL);
-       for (unsigned long i = (now.tv_sec / 86400) & 0x3ff; i; i >>= 5)
-         h = string_catn(h, &base32_chars[i & 0x1f], 1);
-       if (h) while (h->ptr > 0)
-         g = string_catn(g, &h->s[--h->ptr], 1);
-       }
-      g = string_catn(g, US"=", 1);
+       g = string_catn(g, US"SRS0=", 5);
 
-      /* ${domain:$return_path}=${local_part:$return_path} */
-       {
-        int start, end, domain;
-        uschar * t = parse_extract_address(sub[1], &expand_string_message,
-                                         &start, &end, &domain, FALSE);
-       uschar * s;
+       /* ${l_4:${hmac{md5}{SRS_SECRET}{${lc:$return_path}}}}= */
+       hmac_md5(sub[0], string_copylc(sub[1]), cksum, sizeof(cksum));
+       g = string_catn(g, cksum, sizeof(cksum));
+       g = string_catn(g, US"=", 1);
 
-        if (!t)
-         goto EXPAND_FAILED;
+       /* ${base32:${eval:$tod_epoch/86400&0x3ff}}= */
+         {
+         struct timeval now;
+         unsigned long i;
+         gstring * h = NULL;
 
-       if (domain > 0) g = string_cat(g, t + domain);
+         gettimeofday(&now, NULL);
+         for (unsigned long i = (now.tv_sec / 86400) & 0x3ff; i; i >>= 5)
+           h = string_catn(h, &base32_chars[i & 0x1f], 1);
+         if (h) while (h->ptr > 0)
+           g = string_catn(g, &h->s[--h->ptr], 1);
+         }
        g = string_catn(g, US"=", 1);
 
-       s = domain > 0 ? string_copyn(t, domain - 1) : t;
-       if ((quoted = Ustrchr(s, '"') != NULL))
+       /* ${domain:$return_path}=${local_part:$return_path} */
          {
-         gstring * h = NULL;
-         DEBUG(D_expand) debug_printf_indent("auto-quoting local part\n");
-         while (*s)            /* de-quote */
+         int start, end, domain;
+         uschar * t = parse_extract_address(sub[1], &expand_string_message,
+                                           &start, &end, &domain, FALSE);
+         uschar * s;
+
+         if (!t)
+           goto EXPAND_FAILED;
+
+         if (domain > 0) g = string_cat(g, t + domain);
+         g = string_catn(g, US"=", 1);
+
+         s = domain > 0 ? string_copyn(t, domain - 1) : t;
+         if ((quoted = Ustrchr(s, '"') != NULL))
            {
-           while (*s && *s != '"') h = string_catn(h, s++, 1);
-           if (*s) s++;
-           while (*s && *s != '"') h = string_catn(h, s++, 1);
-           if (*s) s++;
+           gstring * h = NULL;
+           DEBUG(D_expand) debug_printf_indent("auto-quoting local part\n");
+           while (*s)          /* de-quote */
+             {
+             while (*s && *s != '"') h = string_catn(h, s++, 1);
+             if (*s) s++;
+             while (*s && *s != '"') h = string_catn(h, s++, 1);
+             if (*s) s++;
+             }
+           gstring_release_unused(h);
+           s = string_from_gstring(h);
            }
-         gstring_release_unused(h);
-         s = string_from_gstring(h);
+         g = string_cat(g, s);
          }
-       g = string_cat(g, s);
-        }
 
-      /* Assume that if the original local_part had quotes
-      it was for good reason */
+       /* Assume that if the original local_part had quotes
+       it was for good reason */
 
-      if (quoted) yield = string_catn(yield, US"\"", 1);
-      yield = string_catn(yield, g->s, g->ptr);
-      if (quoted) yield = string_catn(yield, US"\"", 1);
+       if (quoted) yield = string_catn(yield, US"\"", 1);
+       yield = string_catn(yield, g->s, g->ptr);
+       if (quoted) yield = string_catn(yield, US"\"", 1);
 
-      /* @$original_domain */
-      yield = string_catn(yield, US"@", 1);
-      yield = string_cat(yield, sub[2]);
+       /* @$original_domain */
+       yield = string_catn(yield, US"@", 1);
+       yield = string_cat(yield, sub[2]);
+       }
+      else
+       DEBUG(D_expand) debug_printf_indent("null return_path for srs-encode\n");
 
       break;
       }
@@ -8709,9 +8794,11 @@ assert_variable_notin() treats as const, so deconst is safe. */
 for (int i = 0; i < AUTH_VARS; i++) if (auth_vars[i])
   assert_variable_notin(US"auth<n>", US auth_vars[i], &e);
 
+#ifdef WITH_CONTENT_SCAN
 /* check regex<n> variables. assert_variable_notin() treats as const. */
 for (int i = 0; i < REGEX_VARS; i++) if (regex_vars[i])
   assert_variable_notin(US"regex<n>", US regex_vars[i], &e);
+#endif
 
 /* check known-name variables */
 for (var_entry * v = var_table; v < var_table + var_table_size; v++)
@@ -8848,8 +8935,9 @@ search_tidyup();
 return 0;
 }
 
-#endif
+#endif /*STAND_ALONE*/
 
+#endif /*!MACRO_PREDEF*/
 /* vi: aw ai sw=2
 */
 /* End of expand.c */