Cond !bool{}/!bool_lax{} did not negate. Fixed.
[exim.git] / src / src / expand.c
index 6e47125bb1f177a3fa6af8a9807c9cd600e743da..2e59c4084e8d16990a31ff851983c5916fff10ec 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    *
 
 /*************************************************
 *     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"from_utf8",
   US"local_part",
   US"quote_local_part",
+  US"reverse_ip",
   US"time_eval",
   US"time_interval"};
 
   US"time_eval",
   US"time_interval"};
 
@@ -163,6 +164,7 @@ enum {
   EOP_FROM_UTF8,
   EOP_LOCAL_PART,
   EOP_QUOTE_LOCAL_PART,
   EOP_FROM_UTF8,
   EOP_LOCAL_PART,
   EOP_QUOTE_LOCAL_PART,
+  EOP_REVERSE_IP,
   EOP_TIME_EVAL,
   EOP_TIME_INTERVAL };
 
   EOP_TIME_EVAL,
   EOP_TIME_INTERVAL };
 
@@ -245,6 +247,7 @@ static uschar *cond_table[] = {
   US">=",
   US"and",
   US"bool",
   US">=",
   US"and",
   US"bool",
+  US"bool_lax",
   US"crypteq",
   US"def",
   US"eq",
   US"crypteq",
   US"def",
   US"eq",
@@ -287,6 +290,7 @@ enum {
   ECOND_NUM_GE,
   ECOND_AND,
   ECOND_BOOL,
   ECOND_NUM_GE,
   ECOND_AND,
   ECOND_BOOL,
+  ECOND_BOOL_LAX,
   ECOND_CRYPTEQ,
   ECOND_DEF,
   ECOND_STR_EQ,
   ECOND_CRYPTEQ,
   ECOND_DEF,
   ECOND_STR_EQ,
@@ -324,9 +328,9 @@ enum {
 /* Type for main variable table */
 
 typedef struct {
 /* 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
 } var_entry;
 
 /* Type for entries pointing to address/length pairs. Not currently
@@ -628,9 +632,9 @@ static BOOL malformed_header;
 
 /* For textual hashes */
 
 
 /* For textual hashes */
 
-static char *hashcodes = "abcdefghijklmnopqrtsuvwxyz"
-                         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-                         "0123456789";
+static const char *hashcodes = "abcdefghijklmnopqrtsuvwxyz"
+                               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                               "0123456789";
 
 enum { HMAC_MD5, HMAC_SHA1 };
 
 
 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.
 
 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
 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_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,
 
     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,
   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:
+  case ECOND_BOOL_LAX:
     {
     uschar *sub_arg[1];
     {
     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;
     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;
       /*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);
     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(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)
     if (len == 0)
       boolvalue = FALSE;
     else if (Ustrspn(t, "0123456789") == len)
+      {
       boolvalue = (Uatoi(t) == 0) ? FALSE : TRUE;
       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 (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;
       }
     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;
     }
 
     return s;
     }
 
@@ -3073,9 +3106,21 @@ if (*error == NULL)
     int op = *s++;
     int y = eval_op_unary(&s, decimal, error);
     if (*error != NULL) break;
     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;
+    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;
     }
   }
 *sptr = s;
@@ -3642,8 +3687,8 @@ while (*s != 0)
         if (search_find_defer)
           {
           expand_string_message =
         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;
           goto EXPAND_FAILED;
           }
         if (expand_setup > 0) expand_nmax = expand_setup;
@@ -5473,8 +5518,8 @@ while (*s != 0)
           goto EXPAND_FAILED;
           }
 
           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)
         else if (opt != NULL) sub = NULL;
 
         if (sub == NULL)
@@ -5790,6 +5835,25 @@ while (*s != 0)
         continue;
         }
 
         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:
       /* Unknown operator */
 
       default: