dsearch: filter-matches option
authorJeremy Harris <jgh146exb@wizmail.org>
Fri, 3 Apr 2020 13:38:31 +0000 (14:38 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Fri, 3 Apr 2020 14:44:22 +0000 (15:44 +0100)
doc/doc-docbook/spec.xfpt
doc/doc-txt/NewStuff
src/src/lookups/dsearch.c
test/aux-fixed/2500.dir/regfile [new file with mode: 0644]
test/scripts/2500-dsearch/2500
test/stderr/2200
test/stderr/2201
test/stderr/2600
test/stdout/2500

index 295835d..9a7f911 100644 (file)
@@ -6780,22 +6780,31 @@ The key may not
 contain any forward slash characters.
 If &[lstat()]& succeeds then so does the lookup.
 .new
+.cindex "tainted data" "dsearch result"
+The result is regarded as untainted.
+
 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
+separated by a comma.  Options, if present, are a comma-separated list having
 each element starting with a tag name and an equals.
 
-The only option currently supported requests an alternate output value of
+Two options are supported, for the return value and for filtering match
+candidates.
+The "ret" option requests an alternate result 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"
-The result is regarded as untainted.
+The "filter" option requests that only directory entries of a given type
+are matched. The match value is one of "file", "dir" or "subdir" (the latter
+not matching "." or ".."). Example:
+.code
+${lookup {passwd} dsearch,filter=file {/etc}}
+.endd
+The default matching is for any entry type, including directories
+and symlinks.
 .wen
+
 An example of how this
 lookup can be used to support virtual domains is given in section
 &<<SECTvirtualdomains>>&.
@@ -8100,8 +8109,8 @@ daemon as in the other SQL databases.
 .oindex &%sqlite_dbfile%&
 The preferred way of specifying the file is by using the 
 &%sqlite_dbfile%& option, set to
-.wen
 an absolute path.
+.wen
 A deprecated method is available, prefixing the query with the filename
 separated by white space.
 This means that the path name cannot contain white space.
@@ -8110,6 +8119,7 @@ It also means that the query cannot use any tainted values, as that taints
 the entire query including the filename - resulting in a refusal to open
 the file.
 
+.new
 Here is a lookup expansion example:
 .code
 sqlite_dbfile = /some/thing/sqlitedb
@@ -8121,6 +8131,7 @@ In a list, the syntax is similar. For example:
 domainlist relay_to_domains = sqlite;\
    select * from relays where ip='$sender_host_address';
 .endd
+.wen
 The only character affected by the &%quote_sqlite%& operator is a single
 quote, which it doubles.
 
index 9a06fea..1573f34 100644 (file)
@@ -48,6 +48,8 @@ Version 4.94
     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.
+12. Options on the dsearch lookup, to return the full path and to filter
+    filetypes for matching.
 
 
 
index 0509a76..9bb76cc 100644 (file)
@@ -65,6 +65,11 @@ return FALSE;
 *************************************************/
 
 #define RET_FULL       BIT(0)
+#define FILTER_TYPE    BIT(1)
+#define FILTER_ALL     BIT(1)
+#define FILTER_FILE    BIT(2)
+#define FILTER_DIR     BIT(3)
+#define FILTER_SUBDIR  BIT(4)
 
 /* 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
@@ -99,10 +104,29 @@ if (opts)
   while ((ele = string_nextinlist(&opts, &sep, NULL, 0)))
     if (Ustrcmp(ele, "ret=full") == 0)
       flags |= RET_FULL;
+    else if (Ustrncmp(ele, "filter=", 7) == 0)
+      {
+      ele += 7;
+      if (Ustrcmp(ele, "file") == 0)
+       flags |= FILTER_TYPE | FILTER_FILE;
+      else if (Ustrcmp(ele, "dir") == 0)
+       flags |= FILTER_TYPE | FILTER_DIR;
+      else if (Ustrcmp(ele, "subdir") == 0)
+       flags |= FILTER_TYPE | FILTER_SUBDIR;   /* like dir but not "." or ".." */
+      }
   }
 
 filename = string_sprintf("%s/%s", dirname, keystring);
-if (Ulstat(filename, &statbuf) >= 0)
+if (  Ulstat(filename, &statbuf) >= 0
+   && (  !(flags & FILTER_TYPE)
+      || (flags & FILTER_FILE && S_ISREG(statbuf.st_mode))
+      || (  flags & (FILTER_DIR | FILTER_SUBDIR)
+                && S_ISDIR(statbuf.st_mode)
+        && (  flags & FILTER_DIR
+           || keystring[0] != '.'
+           || keystring[1] != '.'
+           || keystring[1] && keystring[2]
+   )  )  )  )
   {
   /* Since the filename exists in the filesystem, we can return a
   non-tainted result. */
diff --git a/test/aux-fixed/2500.dir/regfile b/test/aux-fixed/2500.dir/regfile
new file mode 100644 (file)
index 0000000..e69de29
index 14cf31b..5886903 100644 (file)
@@ -7,7 +7,14 @@ 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}}
+ok,full:    ${lookup{TESTNUM.tst} dsearch,ret=full      {DIR/aux-fixed}{$value}{FAIL}}
+ok,file:    ${lookup{TESTNUM.tst} dsearch,filter=file   {DIR/aux-fixed}{$value}{FAIL}}
+fail,file:  ${lookup{TESTNUM.dir} dsearch,filter=file   {DIR/aux-fixed}{$value}{FAIL}}
+ok,dir:     ${lookup{TESTNUM.dir} dsearch,filter=dir    {DIR/aux-fixed}{$value}{FAIL}}
+fail,dir:   ${lookup{TESTNUM.tst} dsearch,filter=dir    {DIR/aux-fixed}{$value}{FAIL}}
+ok,subdir:  ${lookup{TESTNUM.dir} dsearch,filter=subdir {DIR/aux-fixed}{$value}{FAIL}}
+fail,subdir:${lookup{..}          dsearch,filter=subdir {DIR/aux-fixed}{$value}{FAIL}}
+fail,subdir:${lookup{TESTNUM.tst} dsearch,filter=subdir {DIR/aux-fixed}{$value}{FAIL}}
 ****
 #
 1
index 8efc38f..c832032 100644 (file)
@@ -43,7 +43,7 @@ search_tidyup called
   LRU list:
   internal_search_find: file="NULL"
     type=dnsdb key="a=shorthost.test.ex" opts=NULL
-  cached data found but past valid time;   database lookup required for a=shorthost.test.ex
+  cached data found but either wrong opts or dated;   database lookup required for a=shorthost.test.ex
   dnsdb key: shorthost.test.ex
   lookup yielded: 127.0.0.1
 LOG: MAIN
index 9764069..fb618fc 100644 (file)
@@ -176,7 +176,7 @@ search_find: file="NULL"
 LRU list:
 internal_search_find: file="NULL"
   type=dnsdb key="a=shorthost.test.ex" opts=NULL
-cached data found but past valid time; database lookup required for a=shorthost.test.ex
+cached data found but either wrong opts or dated; database lookup required for a=shorthost.test.ex
 dnsdb key: shorthost.test.ex
 lookup yielded: 127.0.0.1
 LOG: MAIN
index 3c4a592..627e477 100644 (file)
@@ -495,10 +495,10 @@ admin user
 dropping to exim gid; retaining priv uid
  search_open: sqlite "NULL"
  search_find: file="NULL"
-   key="select name from them where id='userx';" partial=-1 affix=NULL starflags=0
+   key="select name from them where id='userx';" partial=-1 affix=NULL starflags=0 opts=NULL
  LRU list:
  internal_search_find: file="NULL"
-   type=sqlite key="select name from them where id='userx';"
+   type=sqlite key="select name from them where id='userx';" opts=NULL
  database lookup required for select name from them where id='userx';
  lookup yielded: Ayen Other
 search_tidyup called
index d10a5f8..ef5b2a1 100644 (file)
@@ -5,4 +5,11 @@
 > 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
+> ok,file:    2500.tst
+> fail,file:  FAIL
+> ok,dir:     2500.dir
+> fail,dir:   FAIL
+> ok,subdir:  2500.dir
+> fail,subdir:FAIL
+> fail,subdir:FAIL
 >