tidying
[exim.git] / src / src / filter.c
index ad017e5673383b7bd68ef066cccf1267280664e2..18567f17c32d43f9fa0df4e8c0b07636881a4b22 100644 (file)
@@ -2,9 +2,10 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) The Exim Maintainers 2020 - 2022 */
+/* Copyright (c) The Exim Maintainers 2020 - 2023 */
 /* 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 */
 
 
 /* Code for mail filtering functions. */
@@ -193,8 +194,7 @@ static int cond_types[] = { cond_BEGINS, cond_BEGINS, cond_CONTAINS,
   cond_above, cond_begins, cond_begins, cond_below, cond_contains,
   cond_contains, cond_ends, cond_ends, cond_is, cond_matches, cond_matches };
 
-/* Command identities: must be kept in step with the list of command words
-and the list of expanded argument counts which follow. */
+/* Command identities */
 
 enum { add_command, defer_command, deliver_command, elif_command, else_command,
        endif_command, finish_command, fail_command, freeze_command,
@@ -202,10 +202,28 @@ enum { add_command, defer_command, deliver_command, elif_command, else_command,
        mail_command, noerror_command, pipe_command, save_command, seen_command,
        testprint_command, unseen_command, vacation_command };
 
-static const char *command_list[] = {
-  "add",     "defer",   "deliver", "elif", "else",      "endif",    "finish",
-  "fail",    "freeze",  "headers", "if",   "logfile",   "logwrite", "mail",
-  "noerror", "pipe",    "save",    "seen", "testprint", "unseen",   "vacation"
+static const char * command_list[] = {
+  [add_command] =      "add",
+  [defer_command] =    "defer",
+  [deliver_command] =  "deliver",
+  [elif_command] =     "elif",
+  [else_command] =     "else",
+  [endif_command] =    "endif",
+  [finish_command] =   "finish",
+  [fail_command] =     "fail",
+  [freeze_command] =   "freeze",
+  [headers_command] =  "headers",
+  [if_command] =       "if",
+  [logfile_command] =  "logfile",
+  [logwrite_command] = "logwrite",
+  [mail_command] =     "mail",
+  [noerror_command] =  "noerror",
+  [pipe_command] =     "pipe",
+  [save_command] =     "save",
+  [seen_command] =     "seen",
+  [testprint_command] =        "testprint",
+  [unseen_command] =   "unseen",
+  [vacation_command] = "vacation"
 };
 
 static int command_list_count = nelem(command_list);
@@ -214,27 +232,27 @@ static int command_list_count = nelem(command_list);
 If the top bit is set, it means that the default for the command is "seen". */
 
 static uschar command_exparg_count[] = {
-      2, /* add */
-      1, /* defer */
-  128+2, /* deliver */
-      0, /* elif */
-      0, /* else */
-      0, /* endif */
-      0, /* finish */
-      1, /* fail */
-      1, /* freeze */
-      1, /* headers */
-      0, /* if */
-      1, /* logfile */
-      1, /* logwrite */
-      MAILARGS_STRING_COUNT, /* mail */
-      0, /* noerror */
-  128+0, /* pipe */
-  128+1, /* save */
-      0, /* seen */
-      1, /* testprint */
-      0, /* unseen */
-      MAILARGS_STRING_COUNT /* vacation */
+  [add_command] =      2,
+  [defer_command] =    1,
+  [deliver_command] =  128+2,
+  [elif_command] =     0,
+  [else_command] =     0,
+  [endif_command] =    0,
+  [finish_command] =   0,
+  [fail_command] =     1,
+  [freeze_command] =   1,
+  [headers_command] =  1,
+  [if_command] =       0,
+  [logfile_command] =  1,
+  [logwrite_command] = 1,
+  [mail_command] =     MAILARGS_STRING_COUNT,
+  [noerror_command] =  0,
+  [pipe_command] =     128+0,
+  [save_command] =     128+1,
+  [seen_command] =     0,
+  [testprint_command] =        1,
+  [unseen_command] =   0,
+  [vacation_command] = MAILARGS_STRING_COUNT
 };
 
 
@@ -258,16 +276,11 @@ nextsigchar(const uschar *ptr, BOOL comment_allowed)
 for (;;)
   {
   while (isspace(*ptr))
-    {
-    if (*ptr == '\n') line_number++;
-    ptr++;
-    }
+    if (*ptr++ == '\n') line_number++;
   if (comment_allowed && *ptr == '#')
-    {
-    while (*(++ptr) != '\n' && *ptr != 0);
-    continue;
-    }
-  else break;
+    while (*++ptr != '\n' && *ptr) ;
+  else
+    break;
   }
 return ptr;
 }
@@ -293,18 +306,19 @@ Returns:    pointer to the next significant character after the word
 static const uschar *
 nextword(const uschar *ptr, uschar *buffer, int size, BOOL bracket)
 {
-uschar *bp = buffer;
-while (*ptr != 0 && !isspace(*ptr) &&
+uschar * bp = buffer;
+while (*ptr && !isspace(*ptr) &&
        (!bracket || (*ptr != '(' && *ptr != ')')))
-  {
-  if (bp - buffer < size - 1) *bp++ = *ptr++; else
+  if (bp - buffer < size - 1)
+    *bp++ = *ptr++;
+  else
     {
     *error_pointer = string_sprintf("word is too long in line %d of "
       "filter file (max = %d chars)", line_number, size);
     break;
     }
-  }
-*bp = 0;
+
+*bp = '\0';
 return nextsigchar(ptr, TRUE);
 }
 
@@ -392,8 +406,8 @@ int value, count;
 if (sscanf(CS s, "%i%n", &value, &count) != 1) return 0;
 if (tolower(s[count]) == 'k') { value *= 1024; count++; }
 if (tolower(s[count]) == 'm') { value *= 1024*1024; count++; }
-while (isspace((s[count]))) count++;
-if (s[count] != 0) return 0;
+while (isspace(s[count])) count++;
+if (s[count]) return 0;
 *ok = TRUE;
 return value;
 }
@@ -670,8 +684,8 @@ for (;;)
       {
 //      if (toplevel) *saveptr = 0;
 //      else
-   if (!toplevel)
-      *error_pointer = string_sprintf("missing \")\" at end of "
+      if (!toplevel)
+        *error_pointer = string_sprintf("missing \")\" at end of "
           "condition near line %d of filter file", line_number);
       break;
       }
@@ -1424,213 +1438,202 @@ Returns:         TRUE if the condition is met
 */
 
 static BOOL
-test_condition(condition_block *c, BOOL toplevel)
+test_condition(condition_block * c, BOOL toplevel)
 {
-BOOL yield = FALSE;
-const uschar *exp[2], * p, * pp;
+BOOL yield = FALSE, textonly_re;
+const uschar * exp[2], * p, * pp;
 int val[2];
-int i;
 
-if (c == NULL) return TRUE;  /* does this ever occur? */
+if (!c) return TRUE;  /* does this ever occur? */
 
 switch (c->type)
   {
   case cond_and:
-  yield = test_condition(c->left.c, FALSE) &&
-          *error_pointer == NULL &&
-          test_condition(c->right.c, FALSE);
-  break;
+    yield = test_condition(c->left.c, FALSE) &&
+           *error_pointer == NULL &&
+           test_condition(c->right.c, FALSE);
+    break;
 
   case cond_or:
-  yield = test_condition(c->left.c, FALSE) ||
-          (*error_pointer == NULL &&
-          test_condition(c->right.c, FALSE));
-  break;
+    yield = test_condition(c->left.c, FALSE) ||
+           (*error_pointer == NULL &&
+           test_condition(c->right.c, FALSE));
+    break;
 
-  /* The personal test is meaningless in a system filter. The tests are now in
-  a separate function (so Sieve can use them). However, an Exim filter does not
-  scan Cc: (hence the FALSE argument). */
+    /* The personal test is meaningless in a system filter. The tests are now in
+    a separate function (so Sieve can use them). However, an Exim filter does not
+    scan Cc: (hence the FALSE argument). */
 
   case cond_personal:
-  yield = f.system_filtering? FALSE : filter_personal(c->left.a, FALSE);
-  break;
+    yield = f.system_filtering? FALSE : filter_personal(c->left.a, FALSE);
+    break;
 
   case cond_delivered:
-  yield = filter_delivered;
-  break;
+    yield = filter_delivered;
+    break;
 
-  /* Only TRUE if a message is actually being processed; FALSE for address
-  testing and verification. */
+    /* Only TRUE if a message is actually being processed; FALSE for address
+    testing and verification. */
 
   case cond_errormsg:
-  yield = message_id[0] != 0 &&
-    (sender_address == NULL || sender_address[0] == 0);
-  break;
+    yield = message_id[0] && (!sender_address || !*sender_address);
+    break;
 
-  /* Only FALSE if a message is actually being processed; TRUE for address
-  and filter testing and verification. */
+    /* Only FALSE if a message is actually being processed; TRUE for address
+    and filter testing and verification. */
 
   case cond_firsttime:
-  yield = filter_test != FTEST_NONE || message_id[0] == 0 || f.deliver_firsttime;
-  break;
+    yield = filter_test != FTEST_NONE || !message_id[0] || f.deliver_firsttime;
+    break;
 
-  /* Only TRUE if a message is actually being processed; FALSE for address
-  testing and verification. */
+    /* Only TRUE if a message is actually being processed; FALSE for address
+    testing and verification. */
 
   case cond_manualthaw:
-  yield = message_id[0] != 0 && f.deliver_manual_thaw;
-  break;
+    yield = message_id[0] && f.deliver_manual_thaw;
+    break;
 
-  /* The foranyaddress condition loops through a list of addresses */
+    /* The foranyaddress condition loops through a list of addresses */
 
   case cond_foranyaddress:
-  p = c->left.u;
-  if (!(pp = expand_cstring(p)))
-    {
-    *error_pointer = string_sprintf("failed to expand \"%s\" in "
-      "filter file: %s", p, expand_string_message);
-    return FALSE;
-    }
+    p = c->left.u;
+    if (!(pp = expand_cstring(p)))
+      {
+      *error_pointer = string_sprintf("failed to expand \"%s\" in "
+       "filter file: %s", p, expand_string_message);
+      return FALSE;
+      }
 
-  yield = FALSE;
-  f.parse_allow_group = TRUE;     /* Allow group syntax */
+    yield = FALSE;
+    f.parse_allow_group = TRUE;     /* Allow group syntax */
 
-  while (*pp)
-    {
-    uschar *error;
-    int start, end, domain;
-    uschar * s;
+    while (*pp)
+      {
+      uschar *error;
+      int start, end, domain;
+      uschar * s;
 
-    p = parse_find_address_end(pp, FALSE);
-    s = string_copyn(pp, p - pp);
+      p = parse_find_address_end(pp, FALSE);
+      s = string_copyn(pp, p - pp);
 
-    filter_thisaddress =
-      parse_extract_address(s, &error, &start, &end, &domain, FALSE);
+      filter_thisaddress =
+       parse_extract_address(s, &error, &start, &end, &domain, FALSE);
 
-    if (filter_thisaddress)
-      {
-      if ((filter_test != FTEST_NONE && debug_selector != 0) ||
-          (debug_selector & D_filter) != 0)
-        {
-        indent();
-        debug_printf_indent("Extracted address %s\n", filter_thisaddress);
-        }
-      yield = test_condition(c->right.c, FALSE);
-      }
+      if (filter_thisaddress)
+       {
+       if ((filter_test != FTEST_NONE && debug_selector != 0) ||
+           (debug_selector & D_filter) != 0)
+         {
+         indent();
+         debug_printf_indent("Extracted address %s\n", filter_thisaddress);
+         }
+       yield = test_condition(c->right.c, FALSE);
+       }
 
-    if (yield) break;
-    if (!*p) break;
-    pp = p + 1;
-    }
+      if (yield) break;
+      if (!*p) break;
+      pp = p + 1;
+      }
 
-  f.parse_allow_group = FALSE;      /* Reset group syntax flags */
-  f.parse_found_group = FALSE;
-  break;
+    f.parse_allow_group = FALSE;      /* Reset group syntax flags */
+    f.parse_found_group = FALSE;
+    break;
 
-  /* All other conditions have left and right values that need expanding;
-  on error, it doesn't matter what value is returned. */
+    /* All other conditions have left and right values that need expanding;
+    on error, it doesn't matter what value is returned. */
 
-  default:
-  p = c->left.u;
-  for (i = 0; i < 2; i++)
-    {
-    if (!(exp[i] = expand_cstring(p)))
+    default:
+    p = c->left.u;
+    for (int i = 0; i < 2; i++)
       {
-      *error_pointer = string_sprintf("failed to expand \"%s\" in "
-        "filter file: %s", p, expand_string_message);
-      return FALSE;
+      if (!(exp[i] = expand_string_2(p, &textonly_re)))
+       {
+       *error_pointer = string_sprintf("failed to expand \"%s\" in "
+         "filter file: %s", p, expand_string_message);
+       return FALSE;
+       }
+      p = c->right.u;
       }
-    p = c->right.u;
-    }
-
-  /* Inner switch for the different cases */
-
-  switch(c->type)
-    {
-    case cond_is:
-    yield = strcmpic(exp[0], exp[1]) == 0;
-    break;
 
-    case cond_IS:
-    yield = Ustrcmp(exp[0], exp[1]) == 0;
-    break;
+    /* Inner switch for the different cases */
 
-    case cond_contains:
-    yield = strstric_c(exp[0], exp[1], FALSE) != NULL;
-    break;
+    switch(c->type)
+      {
+      case cond_is:
+       yield = strcmpic(exp[0], exp[1]) == 0;
+       break;
 
-    case cond_CONTAINS:
-    yield = Ustrstr(exp[0], exp[1]) != NULL;
-    break;
+      case cond_IS:
+       yield = Ustrcmp(exp[0], exp[1]) == 0;
+       break;
 
-    case cond_begins:
-    yield = strncmpic(exp[0], exp[1], Ustrlen(exp[1])) == 0;
-    break;
+      case cond_contains:
+       yield = strstric_c(exp[0], exp[1], FALSE) != NULL;
+       break;
 
-    case cond_BEGINS:
-    yield = Ustrncmp(exp[0], exp[1], Ustrlen(exp[1])) == 0;
-    break;
+      case cond_CONTAINS:
+       yield = Ustrstr(exp[0], exp[1]) != NULL;
+       break;
 
-    case cond_ends:
-    case cond_ENDS:
-      {
-      int len = Ustrlen(exp[1]);
-      const uschar *s = exp[0] + Ustrlen(exp[0]) - len;
-      yield = s < exp[0]
-       ? FALSE
-       : (c->type == cond_ends ? strcmpic(s, exp[1]) : Ustrcmp(s, exp[1])) == 0;
-      }
-    break;
+      case cond_begins:
+       yield = strncmpic(exp[0], exp[1], Ustrlen(exp[1])) == 0;
+       break;
 
-    case cond_matches:
-    case cond_MATCHES:
-      {
-      const pcre2_code *re;
-      int err;
-      PCRE2_SIZE offset;
+      case cond_BEGINS:
+       yield = Ustrncmp(exp[0], exp[1], Ustrlen(exp[1])) == 0;
+       break;
 
-      if ((filter_test != FTEST_NONE && debug_selector != 0) ||
-         (debug_selector & D_filter) != 0)
+      case cond_ends:
+      case cond_ENDS:
        {
-       debug_printf_indent("Match expanded arguments:\n");
-       debug_printf_indent("  Subject = %s\n", exp[0]);
-       debug_printf_indent("  Pattern = %s\n", exp[1]);
+       int len = Ustrlen(exp[1]);
+       const uschar *s = exp[0] + Ustrlen(exp[0]) - len;
+       yield = s < exp[0]
+         ? FALSE
+         : (c->type == cond_ends ? strcmpic(s, exp[1]) : Ustrcmp(s, exp[1])) == 0;
+       break;
        }
 
-      if (!(re = pcre2_compile((PCRE2_SPTR)exp[1], PCRE2_ZERO_TERMINATED,
-                 PCRE_COPT | (c->type == cond_matches ? PCRE2_CASELESS : 0),
-                 &err, &offset, pcre_cmp_ctx)))
+      case cond_matches:
+      case cond_MATCHES:
        {
-       uschar errbuf[128];
-       pcre2_get_error_message(err, errbuf, sizeof(errbuf));
-       *error_pointer = string_sprintf("error while compiling "
-         "regular expression \"%s\": %s at offset %ld",
-         exp[1], errbuf, (long)offset);
-       return FALSE;
-       }
+       const pcre2_code * re;
+       mcs_flags flags = textonly_re ? MCS_CACHEABLE : MCS_NOFLAGS;
 
-      yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1);
-      break;
-      }
+       if ((filter_test != FTEST_NONE && debug_selector != 0) ||
+           (debug_selector & D_filter) != 0)
+         {
+         debug_printf_indent("Match expanded arguments:\n");
+         debug_printf_indent("  Subject = %s\n", exp[0]);
+         debug_printf_indent("  Pattern = %s\n", exp[1]);
+         }
 
-    /* For above and below, convert the strings to numbers */
+       if (c->type == cond_matches) flags |= MCS_CASELESS;
+       if (!(re = regex_compile(exp[1], flags, error_pointer, pcre_gen_cmp_ctx)))
+         return FALSE;
 
-    case cond_above:
-    case cond_below:
-    for (i = 0; i < 2; i++)
-      {
-      val[i] = get_number(exp[i], &yield);
-      if (!yield)
-        {
-        *error_pointer = string_sprintf("malformed numerical string \"%s\"",
-          exp[i]);
-        return FALSE;
-        }
+       yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1);
+       break;
+       }
+
+      /* For above and below, convert the strings to numbers */
+
+      case cond_above:
+      case cond_below:
+       for (int i = 0; i < 2; i++)
+         {
+         val[i] = get_number(exp[i], &yield);
+         if (!yield)
+           {
+           *error_pointer = string_sprintf("malformed numerical string \"%s\"",
+             exp[i]);
+           return FALSE;
+           }
+         }
+       yield = c->type == cond_above ? (val[0] > val[1]) : (val[0] < val[1]);
+       break;
       }
-    yield = (c->type == cond_above)? (val[0] > val[1]) : (val[0] < val[1]);
     break;
-    }
-  break;
   }
 
 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
@@ -1999,8 +2002,7 @@ while (commands)
 
        if (subtype == TRUE)
          {
-         while (isspace(*s)) s++;
-         if (*s)
+         if (Uskip_whitespace(&s))
            {
            header_add(htype_other, "%s%s", s,
              s[Ustrlen(s)-1] == '\n' ? "" : "\n");
@@ -2219,7 +2221,7 @@ while (commands)
          gstring * log_addr = NULL;
 
          if (!to) to = expand_string(US"$reply_address");
-         while (isspace(*to)) to++;
+         Uskip_whitespace(&to);
 
          for (tt = to; *tt; tt++)     /* Get rid of newlines */
            if (*tt == '\n')
@@ -2291,7 +2293,7 @@ while (commands)
            /* Move on past this address */
 
            tt = ss + (*ss ? 1 : 0);
-           while (isspace(*tt)) tt++;
+           Uskip_whitespace(&tt);
            }
 
          if (log_addr)
@@ -2356,7 +2358,7 @@ while (commands)
   commands = commands->next;
   }
 
-return filter_delivered? FF_DELIVERED : FF_NOTDELIVERED;
+return filter_delivered ? FF_DELIVERED : FF_NOTDELIVERED;
 }
 
 
@@ -2605,3 +2607,5 @@ return yield;
 
 
 /* End of filter.c */
+/* vi: aw ai sw=2
+*/