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
 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",
 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.
 
 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 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
 .wen
+
 An example of how this
 lookup can be used to support virtual domains is given in section
 &<<SECTvirtualdomains>>&.
 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
 .oindex &%sqlite_dbfile%&
 The preferred way of specifying the file is by using the 
 &%sqlite_dbfile%& option, set to
-.wen
 an absolute path.
 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.
 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.
 
 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
 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
 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.
 
 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.
     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 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
 
 /* 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;
   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);
   }
 
 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. */
   {
   /* 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}}
 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
 ****
 #
 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
   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
   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
 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
 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"
 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"
  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
  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
 > 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
 > 
 >