SPDX: license tags (mostly by guesswork)
[exim.git] / src / src / lookups / lsearch.c
index b78e858c34e0849c9b620a522ac84c631d1d9c2a..da73ae8bf57064f356ae1b1c4ee4cf607c9d6241 100644 (file)
@@ -2,9 +2,10 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-only */
 
 #include "../exim.h"
 #include "lf_functions.h"
@@ -29,14 +30,9 @@ enum {
 static void *
 lsearch_open(const uschar * filename, uschar ** errmsg)
 {
-FILE *f = Ufopen(filename, "rb");
-if (f == NULL)
-  {
-  int save_errno = errno;
-  *errmsg = string_open_failed(errno, "%s for linear search", filename);
-  errno = save_errno;
-  return NULL;
-  }
+FILE * f = Ufopen(filename, "rb");
+if (!f)
+  *errmsg = string_open_failed("%s for linear search", filename);
 return f;
 }
 
@@ -74,29 +70,38 @@ but people do occasionally do weird things. */
 static int
 internal_lsearch_find(void * handle, const uschar * filename,
   const uschar * keystring, int length, uschar ** result, uschar ** errmsg,
int type)
 int type, const uschar * opts)
 {
-FILE *f = (FILE *)handle;
-BOOL last_was_eol = TRUE;
-BOOL this_is_eol = TRUE;
+FILE *f = handle;
+BOOL ret_full = FALSE;
 int old_pool = store_pool;
 rmark reset_point = NULL;
 uschar buffer[4096];
 
+if (opts)
+  {
+  int sep = ',';
+  uschar * ele;
+
+  while ((ele = string_nextinlist(&opts, &sep, NULL, 0)))
+    if (Ustrcmp(ele, "ret=full") == 0)
+      { ret_full = TRUE; break; }
+  }
+
 /* Wildcard searches may use up some store, because of expansions. We don't
 want them to fill up our search store. What we do is set the pool to the main
 pool and get a point to reset to later. Wildcard searches could also issue
 lookups, but internal_search_find will take care of that, and the cache will be
 safely stored in the search pool again. */
 
-if(type == LSEARCH_WILD || type == LSEARCH_NWILD)
+if (type == LSEARCH_WILD || type == LSEARCH_NWILD)
   {
   store_pool = POOL_MAIN;
   reset_point = store_mark();
   }
 
 rewind(f);
-for (last_was_eol = TRUE;
+for (BOOL this_is_eol, last_was_eol = TRUE;
      Ufgets(buffer, sizeof(buffer), f) != NULL;
      last_was_eol = this_is_eol)
   {
@@ -142,21 +147,22 @@ for (last_was_eol = TRUE;
   if (*s == '\"')
     {
     uschar *t = s++;
-    while (*s != 0 && *s != '\"')
+    while (*s && *s != '\"')
       {
-      if (*s == '\\') *t++ = string_interpret_escape(CUSS &s);
-        else *t++ = *s;
+      *t++ = *s == '\\' ? string_interpret_escape(CUSS &s) : *s;
       s++;
       }
-    if (*s != 0) s++;               /* Past terminating " */
     linekeylength = t - buffer;
+    if (*s) s++;                       /* Past terminating " */
+    if (ret_full)
+      memmove(t, s, Ustrlen(s)+1);     /* copy the rest of line also */
     }
 
   /* Otherwise it is terminated by a colon or white space */
 
   else
     {
-    while (*s != 0 && *s != ':' && !isspace(*s)) s++;
+    while (*s && *s != ':' && !isspace(*s)) s++;
     linekeylength = s - buffer;
     }
 
@@ -167,9 +173,9 @@ for (last_was_eol = TRUE;
     /* A plain lsearch treats each key as a literal */
 
     case LSEARCH_PLAIN:
-    if (linekeylength != length || strncmpic(buffer, keystring, length) != 0)
-      continue;
-    break;      /* Key matched */
+      if (linekeylength != length || strncmpic(buffer, keystring, length) != 0)
+       continue;
+      break;      /* Key matched */
 
     /* A wild lsearch treats each key as a possible wildcarded string; no
     expansion is done for nwildlsearch. */
@@ -186,7 +192,7 @@ for (last_was_eol = TRUE;
         UCHAR_MAX+1,              /* Single-item list */
         NULL,                     /* No anchor */
         NULL,                     /* No caching */
-        MCL_STRING + ((type == LSEARCH_WILD)? 0:MCL_NOEXPAND),
+        MCL_STRING + (type == LSEARCH_WILD ? 0 : MCL_NOEXPAND),
         TRUE,                     /* Caseless */
         NULL);
       buffer[linekeylength] = save;
@@ -194,47 +200,47 @@ for (last_was_eol = TRUE;
       if (rc == DEFER) return DEFER;
       }
 
-    /* The key has matched. If the search involved a regular expression, it
-    might have caused numerical variables to be set. However, their values will
-    be in the wrong storage pool for external use. Copying them to the standard
-    pool is not feasible because of the caching of lookup results - a repeated
-    lookup will not match the regular expression again. Therefore, we flatten
-    all numeric variables at this point. */
+      /* The key has matched. If the search involved a regular expression, it
+      might have caused numerical variables to be set. However, their values will
+      be in the wrong storage pool for external use. Copying them to the standard
+      pool is not feasible because of the caching of lookup results - a repeated
+      lookup will not match the regular expression again. Therefore, we drop
+      all numeric variables at this point. */
 
-    expand_nmax = -1;
-    break;
+      expand_nmax = -1;
+      break;
 
     /* Compare an ip address against a list of network/ip addresses. We have to
     allow for the "*" case specially. */
 
     case LSEARCH_IP:
-    if (linekeylength == 1 && buffer[0] == '*')
-      {
-      if (length != 1 || keystring[0] != '*') continue;
-      }
-    else if (length == 1 && keystring[0] == '*') continue;
-    else
-      {
-      int maskoffset;
-      int save = buffer[linekeylength];
-      buffer[linekeylength] = 0;
-      if (string_is_ip_address(buffer, &maskoffset) == 0 ||
-          !host_is_in_net(keystring, buffer, maskoffset)) continue;
-      buffer[linekeylength] = save;
-      }
-    break;      /* Key matched */
+      if (linekeylength == 1 && buffer[0] == '*')
+       {
+       if (length != 1 || keystring[0] != '*') continue;
+       }
+      else if (length == 1 && keystring[0] == '*') continue;
+      else
+       {
+       int maskoffset;
+       int save = buffer[linekeylength];
+       buffer[linekeylength] = 0;
+       if (string_is_ip_address(buffer, &maskoffset) == 0 ||
+           !host_is_in_net(keystring, buffer, maskoffset)) continue;
+       buffer[linekeylength] = save;
+       }
+      break;      /* Key matched */
     }
 
   /* The key has matched. Skip spaces after the key, and allow an optional
   colon after the spaces. This is an odd specification, but it's for
   compatibility. */
 
-  while (isspace((uschar)*s)) s++;
-  if (*s == ':')
-    {
-    s++;
-    while (isspace((uschar)*s)) s++;
-    }
+  if (!ret_full)
+    if (Uskip_whitespace(&s) == ':')
+      {
+      s++;
+      Uskip_whitespace(&s);
+      }
 
   /* Reset dynamic store, if we need to, and revert to the search pool */
 
@@ -253,7 +259,9 @@ for (last_was_eol = TRUE;
 
   this_is_comment = FALSE;
   yield = string_get(100);
-  if (*s != 0)
+  if (ret_full)
+    yield = string_cat(yield, buffer);
+  else if (*s)
     yield = string_cat(yield, s);
 
   /* Now handle continuations */
@@ -322,7 +330,7 @@ lsearch_find(void * handle, const uschar * filename, const uschar * keystring,
   const uschar * opts)
 {
 return internal_lsearch_find(handle, filename, keystring, length, result,
-  errmsg, LSEARCH_PLAIN);
+  errmsg, LSEARCH_PLAIN, opts);
 }
 
 
@@ -339,7 +347,7 @@ wildlsearch_find(void * handle, const uschar * filename, const uschar * keystrin
   const uschar * opts)
 {
 return internal_lsearch_find(handle, filename, keystring, length, result,
-  errmsg, LSEARCH_WILD);
+  errmsg, LSEARCH_WILD, opts);
 }
 
 
@@ -356,7 +364,7 @@ nwildlsearch_find(void * handle, const uschar * filename, const uschar * keystri
   const uschar * opts)
 {
 return internal_lsearch_find(handle, filename, keystring, length, result,
-  errmsg, LSEARCH_NWILD);
+  errmsg, LSEARCH_NWILD, opts);
 }
 
 
@@ -376,7 +384,7 @@ iplsearch_find(void * handle, uschar const * filename, const uschar * keystring,
 if ((length == 1 && keystring[0] == '*') ||
     string_is_ip_address(keystring, NULL) != 0)
   return internal_lsearch_find(handle, filename, keystring, length, result,
-    errmsg, LSEARCH_IP);
+    errmsg, LSEARCH_IP, opts);
 
 *errmsg = string_sprintf("\"%s\" is not a valid iplsearch key (an IP "
 "address, with optional CIDR mask, is wanted): "
@@ -409,12 +417,13 @@ lsearch_close(void *handle)
 
 #include "../version.h"
 
-void
-lsearch_version_report(FILE *f)
+gstring *
+lsearch_version_report(gstring * g)
 {
 #ifdef DYNLOOKUP
-fprintf(f, "Library version: lsearch: Exim version %s\n", EXIM_VERSION_STR);
+g = string_fmt_append(g, "Library version: lsearch: Exim version %s\n", EXIM_VERSION_STR));
 #endif
+return g;
 }