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
 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
 .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"
 .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
 .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.
     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                  *
 *************************************************/
 
 *              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. */
 /* 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;
 struct stat statbuf;
 int save_errno;
 uschar * filename;
+unsigned flags = 0;
 
 handle = handle;  /* Keep picky compilers happy */
 length = length;
 
 handle = handle;  /* Keep picky compilers happy */
 length = length;
@@ -88,12 +91,22 @@ if (Ustrchr(keystring, '/') != 0)
   return DEFER;
   }
 
   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. */
 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;
   }
 
   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 "," */
 
 /* 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, '*')))
   {
 len = Ustrlen(ss);
 if ((t = Ustrchr(ss, '*')))
   {
@@ -195,7 +181,14 @@ if ((t = Ustrchr(ss, '*')))
 else
   t = 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
 
 /* 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))
 
 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;
    )
   { /* 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)
     {
 
   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,
     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;
     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->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);
       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
 /* 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 {
 
 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
     {
   union
     {
-    void  *ptr;                   /* pointer to data */
-    int val;                      /* or integer data */
+    void  *    ptr;            /* pointer to data */
+    int                val;            /* or integer data */
     } data;
 } expiring_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}}
 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
 ****
 #
 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
 > 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
 > 
 >