dsearch: full-path return option
authorJeremy Harris <jgh146exb@wizmail.org>
Fri, 3 Apr 2020 13:36:17 +0000 (14:36 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Fri, 3 Apr 2020 13:36:17 +0000 (14:36 +0100)
doc/doc-docbook/spec.xfpt
doc/doc-txt/NewStuff
src/src/lookups/dsearch.c
src/src/search.c
src/src/structs.h
test/scripts/2500-dsearch/2500
test/stdout/2500

index bfe59fcccbcb746dcbcea25149294ba2f0d400f6..295835dbb0c00e3032632ab6898c9cb9cf66ee02 100644 (file)
@@ -6777,12 +6777,24 @@ absolute
 directory path; this is searched for an entry
 whose name is the key by calling the &[lstat()]& function.
 The key may not
-contain any forward slash characters. If &[lstat()]& succeeds, the result of
-the lookup is the name of the entry, which may be a file, directory,
-symbolic link, or any other kind of directory entry.
+contain any forward slash characters.
+If &[lstat()]& succeeds then so does the lookup.
 .new
+Options for the lookup can be given by appending them after the word "dsearch",
+separated by a comma.  Options, if present, are a comma-separated list with
+each element starting with a tag name and an equals.
+
+The only option currently supported requests an alternate output value of
+the entire path for the entry. Example:
+.code
+${lookup {passwd} dsearch,ret=full {/etc}}
+.endd
+The default result is just the requested entry.
+
+The matching entry may be a file, directory,
+symbolic link, or any other kind of directory entry.
 .cindex "tainted data" "dsearch result"
-It is regarded as untainted.
+The result is regarded as untainted.
 .wen
 An example of how this
 lookup can be used to support virtual domains is given in section
index 62763e2acf1f829faf3dfb44394ddfbe0dde4d44..9a06feab703e39a6742ad56b5cf42cc35e509059 100644 (file)
@@ -47,6 +47,7 @@ Version 4.94
     lookup string.  The older method fails when tainted variables are used
     in the lookup, as the filename becomes tainted.  The new method keeps the
     filename separate.
+12. An option on the dsearch lookup, to return the full path.
 
 
 
index d1cbdf73a6262530c7f94ddb69aa957de7525fdb..0509a761b587e7cd2ad47b3da289ced45b3e79e5 100644 (file)
@@ -64,6 +64,8 @@ return FALSE;
 *              Find entry point                  *
 *************************************************/
 
+#define RET_FULL       BIT(0)
+
 /* See local README for interface description. We use lstat() instead of
 scanning the directory, as it is hopefully faster to let the OS do the scanning
 for us. */
@@ -76,6 +78,7 @@ dsearch_find(void * handle, const uschar * dirname, const uschar * keystring,
 struct stat statbuf;
 int save_errno;
 uschar * filename;
+unsigned flags = 0;
 
 handle = handle;  /* Keep picky compilers happy */
 length = length;
@@ -88,12 +91,22 @@ if (Ustrchr(keystring, '/') != 0)
   return DEFER;
   }
 
+if (opts)
+  {
+  int sep = ',';
+  uschar * ele;
+
+  while ((ele = string_nextinlist(&opts, &sep, NULL, 0)))
+    if (Ustrcmp(ele, "ret=full") == 0)
+      flags |= RET_FULL;
+  }
+
 filename = string_sprintf("%s/%s", dirname, keystring);
 if (Ulstat(filename, &statbuf) >= 0)
   {
   /* Since the filename exists in the filesystem, we can return a
   non-tainted result. */
-  *result = string_copy_taint(keystring, FALSE);
+  *result = string_copy_taint(flags & RET_FULL ? filename : keystring, FALSE);
   return OK;
   }
 
index 51bbc6aed12eabe33a016caa4826318d3a39c911..2a60fc78a046c6702179df3840ed038ccdcdd3e4 100644 (file)
@@ -172,20 +172,6 @@ if (Ustrncmp(name, "partial", 7) == 0)
 /* Now we are left with a lookup name, possibly followed by * or *@,
 and then by options starting with a "," */
 
-#ifdef old
-len = Ustrlen(ss);
-if (len >= 2 && Ustrncmp(ss + len - 2, "*@", 2) == 0)
-  {
-  *starflags |= SEARCH_STARAT;
-  len -= 2;
-  }
-else if (len >= 1 && ss[len-1]  == '*')
-  {
-  *starflags |= SEARCH_STAR;
-  len--;
-  }
-#endif
-
 len = Ustrlen(ss);
 if ((t = Ustrchr(ss, '*')))
   {
@@ -195,7 +181,14 @@ if ((t = Ustrchr(ss, '*')))
 else
   t = ss;
 
-* USS opts = (t = Ustrchr(t, ',')) ? string_copy(t+1) : NULL;
+if ((t = Ustrchr(t, ',')))
+  {
+  int l = t - ss;
+  if (l < len) len = l;
+  *opts = string_copy(t+1);
+  }
+else
+  * opts = NULL;
 
 /* Check for the individual search type. Only those that are actually in the
 binary are valid. For query-style types, "partial" and default types are
@@ -513,6 +506,7 @@ file. No need to check c->item_cache for NULL, tree_search will do so. */
 
 if (  (t = tree_search(c->item_cache, keystring))
    && (!(e = t->data.ptr)->expiry || e->expiry > time(NULL))
+   && (!opts && !e->opts  ||  opts && e->opts && Ustrcmp(opts, e->opts) == 0)
    )
   { /* Data was in the cache already; set the pointer from the tree node */
   data = e->data.ptr;
@@ -527,7 +521,8 @@ else
 
   DEBUG(D_lookup)
     {
-    if (t) debug_printf_indent("cached data found but past valid time; ");
+    if (t)
+      debug_printf_indent("cached data found but either wrong opts or dated; ");
     debug_printf_indent("%s lookup required for %s%s%s\n",
       filename ? US"file" : US"database",
       keystring,
@@ -555,12 +550,14 @@ else
     if (t)     /* Previous, out-of-date cache entry.  Update with the */
       {        /* new result and forget the old one */
       e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache;
+      e->opts = opts;
       e->data.ptr = data;
       }
     else
       {
       e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len, is_tainted(keystring));
       e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache;
+      e->opts = opts;
       e->data.ptr = data;
       t = (tree_node *)(e+1);
       memcpy(t->name, keystring, len);
index 7d700fb72ad0333849c18a4bc020ea5851a38f85..ce6e8e857a9eb5c5f6db6013255278cfd4b6e407 100644 (file)
@@ -727,14 +727,16 @@ typedef struct tree_node {
 /* Structure for holding time-limited data such as DNS returns.
 We use this rather than extending tree_node to avoid wasting
 space for most tree use (variables...) at the cost of complexity
-for the lookups cache */
+for the lookups cache.
+We also store any options used for the lookup. */
 
 typedef struct expiring_data {
-  time_t expiry;                 /* if nonzero, data invalid after this time */
+  time_t       expiry;         /* if nonzero, data invalid after this time */
+  const uschar * opts;         /* options, or NULL */
   union
     {
-    void  *ptr;                   /* pointer to data */
-    int val;                      /* or integer data */
+    void  *    ptr;            /* pointer to data */
+    int                val;            /* or integer data */
     } data;
 } expiring_data;
 
index 040ce599eb57c944ed0ec111732f6b908a1e0f2e..14cf31b263398e9ed0a27250ec823c53c787c230 100644 (file)
@@ -7,6 +7,7 @@ fail:       ${lookup{TESTNUM.tst}               dsearch{DIR/dir_not_here}{$value}{FAIL}}
 fail(case): ${lookup{TESTNUM.TST}              dsearch{DIR/aux-fixed}{$value}{FAIL}}
 fail(case): ${lookup{TESTNUM.TST}              dsearch{DIR/AUX-fixed}{$value}{FAIL}}
 fail(path): ${lookup{TESTNUM.tst}              dsearch{.}{$value}{OTHER}}
+ok,full:    ${lookup{TESTNUM.tst} dsearch,ret=full {DIR/aux-fixed}{$value}{FAIL}}
 ****
 #
 1
index 3259e726c10b22c100a04047dc70d250bea9d914..d10a5f8a759ee961cf0cf2694633660a46eec254 100644 (file)
@@ -4,4 +4,5 @@
 > fail(case): FAIL
 > Failed: failed to open TESTSUITE/AUX-fixed for directory search: No such file or directory
 > Failed: dirname '.' for dsearch is not absolute
+> ok,full:    TESTSUITE/aux-fixed/2500.tst
 >