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 295835dbb0c00e3032632ab6898c9cb9cf66ee02..9a7f9113eb8b09ee377b09dd7af8eced6fb19fd7 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 9a06feab703e39a6742ad56b5cf42cc35e509059..1573f3485d9108b1dbc9680af6488d60dc35514d 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 0509a761b587e7cd2ad47b3da289ced45b3e79e5..9bb76cc25c6a87e8ec2f1445a114542da6eb0925 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 14cf31b263398e9ed0a27250ec823c53c787c230..58869037343439385efde49be1411c626abb506a 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 8efc38f64f874c31ea0e4254325ab5ad0b2424ae..c8320321a27f90554abc41839cbe6bc1f1bf3ebe 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 9764069d89a36e89d24f6ae4603c2ea72e233171..fb618fc64866f741607ea8bbd408db02f8382dc5 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 3c4a59271d55652c5c33386438648c68d2a666a2..627e47755e97195ecf5e962ac8a2a6ef0f1679dd 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 d10a5f8a759ee961cf0cf2694633660a46eec254..ef5b2a18c2555cbf9acdec4ef6a8d62f6df4e573 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
 > 
 >