INT_MIN {/,%} -1 = INT_MAX for our purposes.
[exim.git] / src / src / expand.c
index 6e47125bb1f177a3fa6af8a9807c9cd600e743da..d1b81678bb88bdbea67b6646fdf6cb970d5d5872 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/expand.c,v 1.105 2009/11/16 19:50:36 nm4 Exp $ */
+/* $Cambridge: exim/src/src/expand.c,v 1.108 2010/06/07 08:42:15 pdp Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -156,6 +156,7 @@ static uschar *op_table_underscore[] = {
   US"from_utf8",
   US"local_part",
   US"quote_local_part",
+  US"reverse_ip",
   US"time_eval",
   US"time_interval"};
 
@@ -163,6 +164,7 @@ enum {
   EOP_FROM_UTF8,
   EOP_LOCAL_PART,
   EOP_QUOTE_LOCAL_PART,
+  EOP_REVERSE_IP,
   EOP_TIME_EVAL,
   EOP_TIME_INTERVAL };
 
@@ -245,6 +247,7 @@ static uschar *cond_table[] = {
   US">=",
   US"and",
   US"bool",
+  US"bool_lax",
   US"crypteq",
   US"def",
   US"eq",
@@ -287,6 +290,7 @@ enum {
   ECOND_NUM_GE,
   ECOND_AND,
   ECOND_BOOL,
+  ECOND_BOOL_LAX,
   ECOND_CRYPTEQ,
   ECOND_DEF,
   ECOND_STR_EQ,
@@ -324,9 +328,9 @@ enum {
 /* Type for main variable table */
 
 typedef struct {
-  char *name;
-  int   type;
-  void *value;
+  const char *name;
+  int         type;
+  void       *value;
 } var_entry;
 
 /* Type for entries pointing to address/length pairs. Not currently
@@ -628,9 +632,9 @@ static BOOL malformed_header;
 
 /* For textual hashes */
 
-static char *hashcodes = "abcdefghijklmnopqrtsuvwxyz"
-                         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-                         "0123456789";
+static const char *hashcodes = "abcdefghijklmnopqrtsuvwxyz"
+                               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                               "0123456789";
 
 enum { HMAC_MD5, HMAC_SHA1 };
 
@@ -732,6 +736,8 @@ or "false" value. Failure of the expansion yields FALSE; logged unless it was a
 forced fail or lookup defer. All store used by the function can be released on
 exit.
 
+The actual false-value tests should be replicated for ECOND_BOOL_LAX.
+
 Arguments:
   condition     the condition string
   m1            text to be incorporated in panic error
@@ -1581,7 +1587,7 @@ while (last > first)
     return tod_stamp(tod_zulu);
 
     case vtype_todlf:                          /* Log file datestamp tod */
-    return tod_stamp(tod_log_datestamp);
+    return tod_stamp(tod_log_datestamp_daily);
 
     case vtype_reply:                          /* Get reply address */
     s = find_header(US"reply-to:", exists_only, newsize, TRUE,
@@ -2489,19 +2495,25 @@ switch(cond_type)
   interpretation, where general data can be used and only a few values
   map to FALSE.
   Note that readconf.c boolean matching, for boolean configuration options,
-  only matches true/yes/false/no. */
+  only matches true/yes/false/no.
+  The bool_lax{} condition matches the Router logic, which is much more
+  liberal. */
   case ECOND_BOOL:
+  case ECOND_BOOL_LAX:
     {
     uschar *sub_arg[1];
-    uschar *t;
+    uschar *t, *t2;
+    uschar *ourname;
     size_t len;
     BOOL boolvalue = FALSE;
     while (isspace(*s)) s++;
     if (*s != '{') goto COND_FAILED_CURLY_START;
-    switch(read_subs(sub_arg, 1, 1, &s, yield == NULL, FALSE, US"bool"))
+    ourname = cond_type == ECOND_BOOL_LAX ? US"bool_lax" : US"bool";
+    switch(read_subs(sub_arg, 1, 1, &s, yield == NULL, FALSE, ourname))
       {
-      case 1: expand_string_message = US"too few arguments or bracketing "
-        "error for bool";
+      case 1: expand_string_message = string_sprintf(
+                  "too few arguments or bracketing error for %s",
+                  ourname);
       /*FALLTHROUGH*/
       case 2:
       case 3: return NULL;
@@ -2509,23 +2521,44 @@ switch(cond_type)
     t = sub_arg[0];
     while (isspace(*t)) t++;
     len = Ustrlen(t);
+    if (len)
+      {
+      /* trailing whitespace: seems like a good idea to ignore it too */
+      t2 = t + len - 1;
+      while (isspace(*t2)) t2--;
+      if (t2 != (t + len))
+        {
+        *++t2 = '\0';
+        len = t2 - t;
+        }
+      }
     DEBUG(D_expand)
-      debug_printf("considering bool: %s\n", len ? t : US"<empty>");
+      debug_printf("considering %s: %s\n", ourname, len ? t : US"<empty>");
+    /* logic for the lax case from expand_check_condition(), which also does
+    expands, and the logic is both short and stable enough that there should
+    be no maintenance burden from replicating it. */
     if (len == 0)
       boolvalue = FALSE;
     else if (Ustrspn(t, "0123456789") == len)
+      {
       boolvalue = (Uatoi(t) == 0) ? FALSE : TRUE;
+      /* expand_check_condition only does a literal string "0" check */
+      if ((cond_type == ECOND_BOOL_LAX) && (len > 1))
+        boolvalue = TRUE;
+      }
     else if (strcmpic(t, US"true") == 0 || strcmpic(t, US"yes") == 0)
       boolvalue = TRUE;
     else if (strcmpic(t, US"false") == 0 || strcmpic(t, US"no") == 0)
       boolvalue = FALSE;
+    else if (cond_type == ECOND_BOOL_LAX)
+      boolvalue = TRUE;
     else
       {
       expand_string_message = string_sprintf("unrecognised boolean "
        "value \"%s\"", t);
       return NULL;
       }
-    if (yield != NULL) *yield = (boolvalue != 0);
+    if (yield != NULL) *yield = (boolvalue == testfor);
     return s;
     }
 
@@ -3073,9 +3106,47 @@ if (*error == NULL)
     int op = *s++;
     int y = eval_op_unary(&s, decimal, error);
     if (*error != NULL) break;
-    if (op == '*') x *= y;
-      else if (op == '/') x /= y;
-      else x %= y;
+    /* SIGFPE both on div/mod by zero and on INT_MIN / -1, which would give
+     * a value of INT_MAX+1. Note that INT_MIN * -1 gives INT_MIN for me, which
+     * is a bug somewhere in [gcc 4.2.1, FreeBSD, amd64].  In fact, -N*-M where
+     * -N*M is INT_MIN will yielf INT_MIN.
+     * Since we don't support floating point, this is somewhat simpler.
+     * Ideally, we'd return an error, but since we overflow for all other
+     * arithmetic, consistency suggests otherwise, but what's the correct value
+     * to use?  There is none.
+     * The C standard guarantees overflow for unsigned arithmetic but signed
+     * overflow invokes undefined behaviour; in practice, this is overflow
+     * except for converting INT_MIN to INT_MAX+1.  We also can't guarantee
+     * that long/longlong larger than int are available, or we could just work
+     * with larger types.  We should consider whether to guarantee 32bit eval
+     * and 64-bit working variables, with errors returned.  For now ...
+     * So, the only SIGFPEs occur with a non-shrinking div/mod, thus -1; we
+     * can just let the other invalid results occur otherwise, as they have
+     * until now.  For this one case, we can coerce.
+     */
+    if (y == -1 && x == INT_MIN && op != '*')
+      {
+      DEBUG(D_expand)
+        debug_printf("Integer exception dodging: %d%c-1 coerced to %d\n",
+            INT_MIN, op, INT_MAX);
+      x = INT_MAX;
+      continue;
+      }
+    if (op == '*')
+      x *= y;
+    else
+      {
+      if (y == 0)
+        {
+        *error = (op == '/') ? US"divide by zero" : US"modulo by zero";
+        x = 0;
+        break;
+        }
+      if (op == '/')
+        x /= y;
+      else
+        x %= y;
+      }
     }
   }
 *sptr = s;
@@ -3642,8 +3713,8 @@ while (*s != 0)
         if (search_find_defer)
           {
           expand_string_message =
-            string_sprintf("lookup of \"%s\" gave DEFER: %s", key,
-              search_error_message);
+            string_sprintf("lookup of \"%s\" gave DEFER: %s",
+              string_printing2(key, FALSE), search_error_message);
           goto EXPAND_FAILED;
           }
         if (expand_setup > 0) expand_nmax = expand_setup;
@@ -5473,8 +5544,8 @@ while (*s != 0)
           goto EXPAND_FAILED;
           }
 
-        if (lookup_list[n].quote != NULL)
-          sub = (lookup_list[n].quote)(sub, opt);
+        if (lookup_list[n]->quote != NULL)
+          sub = (lookup_list[n]->quote)(sub, opt);
         else if (opt != NULL) sub = NULL;
 
         if (sub == NULL)
@@ -5790,6 +5861,25 @@ while (*s != 0)
         continue;
         }
 
+      /* Reverse IP, including IPv6 to dotted-nibble */
+
+      case EOP_REVERSE_IP:
+        {
+        int family, maskptr;
+        uschar reversed[128];
+
+        family = string_is_ip_address(sub, &maskptr);
+        if (family == 0)
+          {
+          expand_string_message = string_sprintf(
+              "reverse_ip() not given an IP address [%s]", sub);
+          goto EXPAND_FAILED;
+          }
+        invert_address(reversed, sub);
+        yield = string_cat(yield, &size, &ptr, reversed, Ustrlen(reversed));
+        continue;
+        }
+
       /* Unknown operator */
 
       default: