testcases for value return
[exim.git] / src / src / match.c
index 43f5912fd4cc8a50ba3c26bb55dfe04b367cee45..f9b539d10e87ca931a5d033ef1d0ecfe8341800d 100644 (file)
@@ -3,6 +3,7 @@
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for matching strings */
@@ -97,7 +98,7 @@ check_string(void *arg, const uschar *pattern, const uschar **valueptr, uschar *
 const check_string_block *cb = arg;
 int search_type, partial, affixlen, starflags;
 int expand_setup = cb->expand_setup;
-const uschar *affix;
+const uschar * affix, * opts;
 uschar *s;
 uschar *filename = NULL;
 uschar *keyquery, *result, *semicolon;
@@ -119,7 +120,7 @@ expand_nmax until the match is assured. */
 expand_nmax = -1;
 if (expand_setup == 0)
   {
-  expand_nstring[0] = s;
+  expand_nstring[0] = s;       /* $0 (might be) the matched subject in full */
   expand_nlength[0] = Ustrlen(s);
   }
 else if (expand_setup > 0) expand_setup--;
@@ -129,35 +130,38 @@ required. */
 
 if (pattern[0] == '^')
   {
-  const pcre *re = regex_must_compile(pattern, cb->caseless, FALSE);
-  return ((expand_setup < 0)?
-           pcre_exec(re, NULL, CCS s, Ustrlen(s), 0, PCRE_EOPT, NULL, 0) >= 0
-           :
-           regex_match_and_setup(re, s, 0, expand_setup)
-         )?
-         OK : FAIL;
+  const pcre * re = regex_must_compile(pattern, cb->caseless, FALSE);
+  if (expand_setup < 0
+      ? pcre_exec(re, NULL, CCS s, Ustrlen(s), 0, PCRE_EOPT, NULL, 0) < 0
+      : !regex_match_and_setup(re, s, 0, expand_setup)
+     )
+    return FAIL;
+  /* assume the above wrote $0, $n... TODO: CHECK THAT !! */
+  if (valueptr) *valueptr = pattern;   /* "value" gets the RE */
+  return OK;
   }
 
 /* Tail match */
 
 if (pattern[0] == '*')
   {
-  BOOL yield;
   int slen = Ustrlen(s);
   int patlen;    /* Sun compiler doesn't like non-constant initializer */
 
   patlen = Ustrlen(++pattern);
   if (patlen > slen) return FAIL;
-  yield = cb->caseless?
-    (strncmpic(s + slen - patlen, pattern, patlen) == 0) :
-    (Ustrncmp(s + slen - patlen, pattern, patlen) == 0);
-  if (yield && expand_setup >= 0)
+  if (cb->caseless
+      ? strncmpic(s + slen - patlen, pattern, patlen) != 0
+      : Ustrncmp(s + slen - patlen, pattern, patlen) != 0)
+    return FAIL;
+  if (expand_setup >= 0)
     {
-    expand_nstring[++expand_setup] = s;
+    expand_nstring[++expand_setup] = s;                /* write a $n, the matched subject variable-part */
     expand_nlength[expand_setup] = slen - patlen;
     expand_nmax = expand_setup;
     }
-  return yield? OK : FAIL;
+  if (valueptr) *valueptr = pattern - 1;       /* "value" gets the (original) pattern */
+  return OK;
   }
 
 /* Match a special item starting with @ if so enabled. On its own, "@" matches
@@ -176,11 +180,15 @@ if (cb->at_is_special && pattern[0] == '@')
   if (Ustrcmp(pattern, "@[]") == 0)
     {
     int slen = Ustrlen(s);
-    if (s[0] != '[' && s[slen-1] != ']') return FAIL;
+    if (s[0] != '[' && s[slen-1] != ']') return FAIL;          /*XXX should this be || ? */
     for (ip_address_item * ip = host_find_interfaces(); ip; ip = ip->next)
       if (Ustrncmp(ip->address, s+1, slen - 2) == 0
             && ip->address[slen - 2] == 0)
+       {
+/* I see no reason not to return $0, the matchd IP.  if (expand_setup >= 0) expand_nmax = expand_setup; */
+       if (valueptr) *valueptr = pattern;      /* "value" gets the pattern */
         return OK;
+       }
     return FAIL;
     }
 
@@ -208,7 +216,7 @@ if (cb->at_is_special && pattern[0] == '@')
     else goto NOT_AT_SPECIAL;
 
     if (strncmpic(ss, US"/ignore=", 8) == 0) ignore_target_hosts = ss + 8;
-      else if (*ss != 0) goto NOT_AT_SPECIAL;
+    else if (*ss) goto NOT_AT_SPECIAL;
 
     h.next = NULL;
     h.name = s;
@@ -230,9 +238,12 @@ if (cb->at_is_special && pattern[0] == '@')
       return DEFER;
       }
 
-    if (rc == HOST_FOUND_LOCAL && !secy) return OK;
-    if (prim) return FAIL;
-    return removed? OK : FAIL;
+    if (rc != HOST_FOUND_LOCAL || secy)
+      if (prim || !removed) return FAIL;
+
+/* again, $0 getting the subject, the matched IP.  if (expand_setup >= 0) expand_nmax = expand_setup; */
+    if (valueptr) *valueptr = pattern; /* "vaulue" gets the patterm */
+    return OK;
 
     /*** The above line used to be the following line, but this is incorrect,
     because host_find_bydns() may return HOST_NOT_FOUND if it removed some MX
@@ -252,10 +263,15 @@ NOT_AT_SPECIAL:
 
 if ((semicolon = Ustrchr(pattern, ';')) == NULL)
   {
-  BOOL yield = cb->caseless?
-    (strcmpic(s, pattern) == 0) : (Ustrcmp(s, pattern) == 0);
-  if (yield && expand_setup >= 0) expand_nmax = expand_setup;
-  return yield? OK : FAIL;
+  if (cb->caseless ? strcmpic(s, pattern) != 0 : Ustrcmp(s, pattern) != 0)
+    return FAIL;
+  if (expand_setup >= 0) expand_nmax = expand_setup;   /* Original code!   $0 gets the matched subject */
+  if (valueptr) *valueptr = pattern;   /* "value" gets the pattern */
+  return OK;
+
+/*
+XXX  looks like $0 may be usable
+*/
   }
 
 /* Otherwise we have a lookup item. The lookup type, including partial, etc. is
@@ -263,7 +279,7 @@ the part of the string preceding the semicolon. */
 
 *semicolon = 0;
 search_type = search_findtype_partial(pattern, &partial, &affix, &affixlen,
-  &starflags);
+  &starflags, &opts);
 *semicolon = ';';
 if (search_type < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
   search_error_message);
@@ -276,14 +292,14 @@ if (!cb->use_partial) partial = -1;
 /* Set the parameters for the three different kinds of lookup. */
 
 keyquery = semicolon + 1;
-while (isspace(*keyquery)) keyquery++;
+Uskip_whitespace(&keyquery);
 
 if (mac_islookup(search_type, lookup_absfilequery))
   {
   filename = keyquery;
-  while (*keyquery != 0 && !isspace(*keyquery)) keyquery++;
+  while (*keyquery && !isspace(*keyquery)) keyquery++;
   filename = string_copyn(filename, keyquery - filename);
-  while (isspace(*keyquery)) keyquery++;
+  Uskip_whitespace(&keyquery);
   }
 
 else if (!mac_islookup(search_type, lookup_querystyle))
@@ -299,9 +315,9 @@ no search_close() because of the caching arrangements. */
 if (!(handle = search_open(filename, search_type, 0, NULL, NULL)))
   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", search_error_message);
 result = search_find(handle, filename, keyquery, partial, affix, affixlen,
-  starflags, &expand_setup);
+  starflags, &expand_setup, opts);
 
-if (!result) return f.search_find_defer? DEFER : FAIL;
+if (!result) return f.search_find_defer ? DEFER : FAIL;
 if (valueptr) *valueptr = result;
 
 expand_nmax = expand_setup;
@@ -371,13 +387,13 @@ switch(type)
   case MCL_STRING:
   case MCL_DOMAIN:
   case MCL_LOCALPART:
-  return ((check_string_block *)arg)->subject;
+    return ((check_string_block *)arg)->subject;
 
   case MCL_HOST:
-  return ((check_host_block *)arg)->host_address;
+    return ((check_host_block *)arg)->host_address;
 
   case MCL_ADDRESS:
-  return ((check_address_block *)arg)->address;
+    return ((check_address_block *)arg)->address;
   }
 return US"";  /* In practice, should never happen */
 }
@@ -446,7 +462,6 @@ BOOL ignore_defer = FALSE;
 const uschar *list;
 uschar *sss;
 uschar *ot = NULL;
-uschar buffer[1024];
 
 /* Save time by not scanning for the option name when we don't need it. */
 
@@ -506,12 +521,12 @@ else
 
 /* For an unnamed list, use the expanded version in comments */
 
-HDEBUG(D_any) if (ot == NULL) ot = string_sprintf("%s in \"%s\"?", name, list);
+HDEBUG(D_any) if (!ot) ot = string_sprintf("%s in \"%s\"?", name, list);
 
 /* Now scan the list and process each item in turn, until one of them matches,
 or we hit an error. */
 
-while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
+while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
   {
   uschar * ss = sss;
 
@@ -541,7 +556,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
     if (Ustrcmp(ss, "+caseful") == 0)
       {
       check_string_block *cb = (check_string_block *)arg;
-      Ustrcpy(cb->subject, cb->origsubject);
+      Ustrcpy(US cb->subject, cb->origsubject);
       cb->caseless = FALSE;
       continue;
       }
@@ -666,7 +681,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
             so we use the permanent store pool */
 
             store_pool = POOL_PERM;
-            p = store_get(sizeof(namedlist_cacheblock));
+            p = store_get(sizeof(namedlist_cacheblock), FALSE);
             p->key = string_copy(get_check_key(arg, type));
 
 
@@ -677,7 +692,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
             nb->cache_data = p;
             if (*valueptr)
               DEBUG(D_lists) debug_printf("data from lookup saved for "
-                "cache for %s: %s\n", ss, *valueptr);
+                "cache for %s: key '%s' value '%s'\n", ss, p->key, *valueptr);
             }
           }
         }
@@ -689,7 +704,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
       else
         {
         DEBUG(D_lists) debug_printf("cached %s match for %s\n",
-          ((bits & (-bits)) == bits)? "yes" : "no", ss);
+          (bits & (-bits)) == bits ? "yes" : "no", ss);
 
         cached = US" - cached";
         if (valueptr)
@@ -1168,16 +1183,10 @@ if (pdomain != NULL)
     {
     int cllen = pllen - 1;
     if (sllen < cllen) return FAIL;
-    if (cb->caseless)
-      {
-      if (strncmpic(subject+sllen-cllen, pattern + 1, cllen) != 0)
+    if (cb->caseless
+        ? strncmpic(subject+sllen-cllen, pattern + 1, cllen) != 0
+        : Ustrncmp(subject+sllen-cllen, pattern + 1, cllen) != 0)
         return FAIL;
-      }
-    else
-      {
-      if (Ustrncmp(subject+sllen-cllen, pattern + 1, cllen) != 0)
-        return FAIL;
-      }
     if (cb->expand_setup > 0)
       {
       expand_nstring[cb->expand_setup] = subject;
@@ -1188,14 +1197,9 @@ if (pdomain != NULL)
   else
     {
     if (sllen != pllen) return FAIL;
-    if (cb->caseless)
-      {
-      if (strncmpic(subject, pattern, sllen) != 0) return FAIL;
-      }
-    else
-      {
-      if (Ustrncmp(subject, pattern, sllen) != 0) return FAIL;
-      }
+    if (cb->caseless
+        ? strncmpic(subject, pattern, sllen) != 0
+       : Ustrncmp(subject, pattern, sllen) != 0) return FAIL;
     }
   }
 
@@ -1204,7 +1208,7 @@ the generalized function, which supports file lookups (which may defer). The
 original code read as follows:
 
   return match_check_string(sdomain + 1,
-      (pdomain == NULL)? pattern : pdomain + 1,
+      pdomain ? pdomain + 1 : pattern,
       cb->expand_setup + expand_inc, TRUE, cb->caseless, TRUE, NULL);
 
 This supported only literal domains and *.x.y patterns. In order to allow for
@@ -1212,14 +1216,14 @@ named domain lists (so that you can right, for example, "senders=+xxxx"), it
 was changed to use the list scanning function. */
 
 csb.origsubject = sdomain + 1;
-csb.subject = (cb->caseless)? string_copylc(sdomain+1) : string_copy(sdomain+1);
+csb.subject = cb->caseless ? string_copylc(sdomain+1) : string_copy(sdomain+1);
 csb.expand_setup = cb->expand_setup + expand_inc;
 csb.use_partial = TRUE;
 csb.caseless = cb->caseless;
 csb.at_is_special = TRUE;
 
-listptr = (pdomain == NULL)? pattern : pdomain + 1;
-if (valueptr != NULL) *valueptr = NULL;
+listptr = pdomain ? pdomain + 1 : pattern;
+if (valueptr) *valueptr = NULL;
 
 return match_check_list(
   &listptr,                  /* list of one item */
@@ -1277,6 +1281,7 @@ match_address_list(const uschar *address, BOOL caseless, BOOL expand,
 {
 check_address_block ab;
 unsigned int *local_cache_bits = cache_bits;
+int len;
 
 /* RFC 2505 recommends that for spam checking, local parts should be caselessly
 compared. Therefore, Exim now forces the entire address into lower case here,
@@ -1285,8 +1290,10 @@ patterns.) Otherwise just the domain is lower cases. A magic item "+caseful" in
 the list can be used to restore a caseful copy of the local part from the
 original address. */
 
-sprintf(CS big_buffer, "%.*s", big_buffer_size - 1, address);
-for (uschar * p = big_buffer + Ustrlen(big_buffer) - 1; p >= big_buffer; p--)
+if ((len = Ustrlen(address)) > 255) len = 255;
+ab.address = string_copyn(address, len);
+
+for (uschar * p = ab.address + len - 1; p >= ab.address; p--)
   {
   if (!caseless && *p == '@') break;
   *p = tolower(*p);
@@ -1307,7 +1314,7 @@ if (expand_setup == 0)
 /* Set up the data to be passed ultimately to check_address. */
 
 ab.origaddress = address;
-ab.address = big_buffer;
+/* ab.address is above */
 ab.expand_setup = expand_setup;
 ab.caseless = caseless;