Support Rspamd. Patch from Andrew Lewis, lightly editorialised
[users/jgh/exim.git] / test / scripts / 0000-Basic / 0002
index 760ffb3585743f4b6e783705a212230cdfebc1f5..328f5a3caa264928b6aeed6eba665accd88e91fe 100644 (file)
@@ -4,6 +4,7 @@
 # be present in the basic Exim binary which we require in order to run these
 # tests at all. Specialized expansion tests also exist for optional features
 # in other test scripts.
 # be present in the basic Exim binary which we require in order to run these
 # tests at all. Specialized expansion tests also exist for optional features
 # in other test scripts.
+munge dnssec
 
 exim -be
 
 
 exim -be
 
@@ -14,6 +15,9 @@ exim -be
 # Some fixed variables
 
 exim_path: $exim_path
 # Some fixed variables
 
 exim_path: $exim_path
+exim_version: $exim_version
+config_dir: $config_dir
+config_file: $config_file
 primary_hostname: $primary_hostname
 primary_hostname: ${primary_hostname}
 qualify_domain: $qualify_domain
 primary_hostname: $primary_hostname
 primary_hostname: ${primary_hostname}
 qualify_domain: $qualify_domain
@@ -40,12 +44,93 @@ x\
 +$11111111111111111111111111111111111
 +${11111111111111111111111111111111111}
 
 +$11111111111111111111111111111111111
 +${11111111111111111111111111111111111}
 
+# List operations
+
+filter: "${filter{a:b:c}{eq{1}{1}}}"
+filter: ${filter{a:b:c}{!eq{$item}{b}}}
+filter: ${filter{<' a'b'c}{!eq{$item}{b}}}
+filter: ${filter{<' ''a'b' ''c}{!eq{$item}{b}}}
+filter: "${filter{}{!eq{$item}{b}}}"
+
+map: "${map{}{$item}}"
+map: ${map{a:b:c}{$item}}
+map: ${map{a:b:c}{:$item:}}
+map: ${if eq{1}{0}{${map{a:b:c}{:$item:}}}{fail string}}
+map: ${map{:b:c}{[$item]}}
+
+reduce: "${reduce{}{+}{$value$item}}"
+reduce: ${reduce{a:b:c}{+}{$value$item}}
+reduce: ${reduce {<, 1,2,3}{0}{${eval:$value+$item}}}
+reduce: ${reduce {3:0:9:4:6}{0}{${if >{$item}{$value}{$item}{$value}}}}
+
+listnamed: ${listnamed:dlist}
+listnamed: ${listnamed:+dlist}
+listnamed: ${listnamed:hlist}
+listnamed: ${listnamed:elist}
+listnamed: ${listnamed:flist}
+listnamed: ${listnamed:nolist}
+listnamed: ${listnamed_d:dlist}
+listnamed: ${listnamed_d:hlist}
+listnamed: ${listnamed_z:dlist}
+
+listcount: ${listcount:a:b:c}
+listcount: ${listcount:}
+listcount: ${listcount:<;a;b;c}
+listcount: ${listcount:${listnamed:dlist}}
+
+listextract: ${listextract{ 2}{a:b:c:d}}
+listextract: ${listextract{-2}{<,a,b,c,d}{X${value}X}}
+listextract: ${listextract{ 5}{a:b:c:d}}
+listextract: ${listextract{-5}{a:b:c:d}}
+listextract: ${listextract{ 5}{a:b:c:d}{}{fail}}
+listextract: ${listextract{ 5}{a:b:c:d}{}fail}
+
+sort: ${sort{3:2:1:4}{<}{$item}}
+sort: ${sort {<, 3,2,1,4}{>}{$item}}
+sort: ${sort{c:B:a:aa}{lti}{$item}}
+sort: ${sort{666 r99.ex.com:10 smtp.ex.com:100 r2.ex.com}{<}{${sg {$item}{([0-9]*).*\$}{\$1}}}}
+sort: ${sort{666,r99.ex.com:10,smtp.ex.com:100,r2.ex.com}{<}{${listextract{1}{<,$item}}}}
+sort: "${sort{}{<}{$item}}"
+
+# Tests with iscntrl() and illegal separators
+
+map: ${map{<\n a\n\nb\nc}{'$item'}}
+
+reduce: ${reduce {<n 1\n2\n3}{0}{${eval:$value+$item}}}
+reduce: ${reduce {<\n 1\n2\n3}{0}{${eval:$value+$item}}}
+reduce: ${reduce { <\n 1\n 2 \n 3 }{0}{${eval:$value+$item}}}
+reduce: ${reduce {<\x7f 1\x7f2\177 3}{0}{${eval:$value+$item}}}
+
 # Operators
 
 # Operators
 
+acl: ${acl
+acl: ${acl}
+acl: ${acl {a_nosuch}}
+acl: ${acl {a_ret}}
+acl: ${acl {a_ret}{person@dom.ain}}
+acl: ${acl {a_ret}{firstarg}{secondarg}}
+acl: ${acl {a_ret}{arg with spaces}}
+acl: ${acl {a_none}}
+acl: ${acl {a_none}{person@dom.ain}}
+acl: ${acl {a_deny}}
+acl: ${acl {a_deny}{person@dom.ain}}
+acl: ${acl {a_defer}}
+acl: ${acl {a_sub}{top_arg_1}{top_arg_2}{top_arg_3}}
+acl: ${reduce {1:2:3:4} {} {$value ${acl {a_ret}{$item}}}}
+
 addrss: ${address:local-part@dom.ain}
 addrss: ${address:Exim Person <local-part@dom.ain> (that's me)}
 domain: ${domain:local-part@dom.ain}
 domain: ${domain:Exim Person <local-part@dom.ain> (that's me)}
 addrss: ${address:local-part@dom.ain}
 addrss: ${address:Exim Person <local-part@dom.ain> (that's me)}
 domain: ${domain:local-part@dom.ain}
 domain: ${domain:Exim Person <local-part@dom.ain> (that's me)}
+
+addresses: ${addresses:>' 'abc@xyz, 'pqr@xyz}
+addresses: ${addresses:Exim Person <local-part@dom.ain> (that's me)}
+addresses: ${addresses:>+ Exim Person <local-part@dom.ain> (that's me),\
+           xyz@abc}
+addresses: ${addresses:Exim Person <local-part@dom.ain> (that's me), \
+           xyz@abc, nullgroupname:;, group: p@q, r@s; }
+addresses: ${addresses:local-part@dom.ain <local-part@dom.ain>}
+
 escape: ${escape:B7·F2ò}
 eval:   ${eval:1+1}
 eval:   ${eval:1+2*3}
 escape: ${escape:B7·F2ò}
 eval:   ${eval:1+1}
 eval:   ${eval:1+2*3}
@@ -69,17 +154,17 @@ eval10: ${eval10:077}
 eval10: ${eval10:08}
 eval10: ${eval10:0x1234}
 eval:   ${eval:2+42%5}
 eval10: ${eval10:08}
 eval10: ${eval10:0x1234}
 eval:   ${eval:2+42%5}
-eval:   ${eval:0xc&5}          
-eval:   ${eval:0xc & 5 }          
-eval:   ${eval:0x0c|5}          
-eval:   ${eval:0xc^5}          
-eval:   ${eval:0xc>>1}         
-eval:   ${eval:0xc >> 2}         
-eval:   ${eval:0xc >> 4 }         
-eval:   ${eval:0xc<<1}         
-eval:   ${eval:~255&0x1234}    
-eval:   ${eval:~ 255&0x1234}    
-eval:   ${eval: -(~255&0x1234)} 
+eval:   ${eval:0xc&5}
+eval:   ${eval:0xc & 5 }
+eval:   ${eval:0x0c|5}
+eval:   ${eval:0xc^5}
+eval:   ${eval:0xc>>1}
+eval:   ${eval:0xc >> 2}
+eval:   ${eval:0xc >> 4 }
+eval:   ${eval:0xc<<1}
+eval:   ${eval:~255&0x1234}
+eval:   ${eval:~ 255&0x1234}
+eval:   ${eval: -(~255&0x1234)}
 
 expand: \$primary_hostname ${expand:\$primary_hostname}
 hash:   ${hash_3:monty} ${hash_5:monty} ${hash_4_62:monty python}
 
 expand: \$primary_hostname ${expand:\$primary_hostname}
 hash:   ${hash_3:monty} ${hash_5:monty} ${hash_4_62:monty python}
@@ -100,15 +185,15 @@ while doing a reasonable check.
 base62:  ${if or {\
                  {eq {${base62:12345}}{0003D7}}\
                  {eq {${base62:12345}}{0009IX}}\
 base62:  ${if or {\
                  {eq {${base62:12345}}{0003D7}}\
                  {eq {${base62:12345}}{0009IX}}\
-                 }{OK}{NOT OK}} 
+                 }{OK}{NOT OK}}
 base62d: ${if or {\
                  {eq {${base62d:0003D7}}{12345}}\
                  {eq {${base62d:0009IX}}{12345}}\
 base62d: ${if or {\
                  {eq {${base62d:0003D7}}{12345}}\
                  {eq {${base62d:0009IX}}{12345}}\
-                 }{OK}{NOT OK}} 
+                 }{OK}{NOT OK}}
 base62d: ${if or {\
                  {eq {${base62d:3D7}}{12345}}\
                  {eq {${base62d:9IX}}{12345}}\
 base62d: ${if or {\
                  {eq {${base62d:3D7}}{12345}}\
                  {eq {${base62d:9IX}}{12345}}\
-                 }{OK}{NOT OK}} 
+                 }{OK}{NOT OK}}
 base62 error: ${base62:12345x}
 base62d error:${base62d:0003D7.}
 
 base62 error: ${base62:12345x}
 base62d error:${base62d:0003D7.}
 
@@ -147,6 +232,15 @@ quote_local_part: ${quote_local_part:x:y}
 quote_local_part: ${quote_local_part:ab\cd"ef}
 quote_local_part: ${quote_local_part:}
 rxquote:${rxquote:aZ09_,-Q} ${rxquote:ab*cd} ${rxquote:ab\cd"ef}
 quote_local_part: ${quote_local_part:ab\cd"ef}
 quote_local_part: ${quote_local_part:}
 rxquote:${rxquote:aZ09_,-Q} ${rxquote:ab*cd} ${rxquote:ab\cd"ef}
+hexquote: ${hexquote:\
+    \001\002\003\004\005\006\007 \010\011\012\013\014\015\016\017 \
+\020\021\022\023\024\025\026\027 \030\031\032\033\034\035\036\037 \
+\040\041\042\043\044\045\046\047 \050\051\052\053\054\055\056\057 \
+\060\061\062\063\064\065\066\067 \070\071\072\073\074\075\076\077 \
+\100\101\102\103\104\105\106\107 \110\111\112\113\114\115\116\117 \
+\120\121\122\123\124\125\126\127 \130\131\132\133\134\135\136\137 \
+\140\141\142\143\144\145\146\147 \150\151\152\153\154\155\156\157 \
+\160\161\162\163\164\165\166\167 \170\171\172\173\174\175\176\177}
 substr: ${substr_3_2:rhubarb} ${s_-5_2:1234567} ${s_-5_2:12} ${s_-3_2:12}
 substr: ${s_3:rhubarb} ${s_-2:rhubarb}
 substr: ${s_1:}
 substr: ${substr_3_2:rhubarb} ${s_-5_2:1234567} ${s_-5_2:12} ${s_-3_2:12}
 substr: ${s_3:rhubarb} ${s_-2:rhubarb}
 substr: ${s_1:}
@@ -201,10 +295,21 @@ hash:   ${if eq {1}{2}{${hash_3:invalid}}{NO}}
 md5:    ${if eq {1}{2}{${md5:invalid}}{NO}}
 mask:   ${if eq {1}{2}{${mask:invalid}}{NO}}
 
 md5:    ${if eq {1}{2}{${md5:invalid}}{NO}}
 mask:   ${if eq {1}{2}{${mask:invalid}}{NO}}
 
+# Number suffixes in conditions
+1k: ${if >{1}{1k}{n}{y}}
+1K: ${if >{1}{1K}{n}{y}}
+1M: ${if >{1}{1M}{n}{y}}
+1G: ${if >{1}{1G}{n}{y}}
+
 # Numeric overflow
 # Numeric overflow
+# >32b should work, >64b not
 
 
-4096M      ${if >{1}{4096M}{y}{n}}
-4096000000 ${if >{1}{4096000000}{y}{n}}
+1 > 2047M                ${if >{1}{2047M}{y}{n}}
+1 > 2048M                ${if >{1}{2048M}{y}{n}}
+1 > 4096000000           ${if >{1}{4096000000}{y}{n}}
+1 > 4096M                ${if >{1}{4096M}{y}{n}}
+1 > 4611686018427387904  ${if >{1}{4611686018427387904} {y}{n}}
+1 > 46116860184273879040 ${if >{1}{46116860184273879040}{y}{n}}
 
 # Conditions
 
 
 # Conditions
 
@@ -236,6 +341,13 @@ mask:   ${if eq {1}{2}{${mask:invalid}}{NO}}
 5>3m:   ${if >{5 } {3m }{y}{n}}
 5>3z:   ${if >{5 } {3z }{y}{n}}
 5>a:    ${if >{ 5 } {a}{y}{n}}
 5>3m:   ${if >{5 } {3m }{y}{n}}
 5>3z:   ${if >{5 } {3z }{y}{n}}
 5>a:    ${if >{ 5 } {a}{y}{n}}
+5>bad:  ${if >{5 } {${lookup{trick}lsearch{DIR/aux-fixed/0002.lsearch}}} {y}{n}}
+
+>0:     ${if > {}{0}{y}{n}}
+=:      ${if = {}{}{y}{n}}
+-2<:    ${if < {-2}{}{y}{n}}
+08>07:  ${if > {08}{07}{y}{n}}
+011=11: ${if = {011}{11}{y}{n}}
 
 def:y   ${if def:tod_log{y}{n}}
 def:n   ${if def:host{y}{n}}
 
 def:y   ${if def:tod_log{y}{n}}
 def:n   ${if def:host{y}{n}}
@@ -298,19 +410,19 @@ match_domain:    ${if match_domain{xyz}{+dlist}{yes}{no}}
 
 ${if match{x@zz.aa.bb}{^(.*)} \
   { \
 
 ${if match{x@zz.aa.bb}{^(.*)} \
   { \
-  >$1< \ 
+  >$1< \
   ${if match_domain{${domain:$1}}{+dlist}{[$1]}} \
   >$1< \
   } \
   ${if match_domain{${domain:$1}}{+dlist}{[$1]}} \
   >$1< \
   } \
-  { CAN'T HAPPEN}} 
+  { CAN'T HAPPEN}}
 
 ${if match{x@xxxabc}{^(.*)} \
   { \
 
 ${if match{x@xxxabc}{^(.*)} \
   { \
-  >$1< \ 
+  >$1< \
   ${if match_domain{${domain:$1}}{^\Nxxx(.*)\N}{[$1]}} \
   >$1< \
   } \
   ${if match_domain{${domain:$1}}{^\Nxxx(.*)\N}{[$1]}} \
   >$1< \
   } \
-  { CAN'T HAPPEN}} 
+  { CAN'T HAPPEN}}
 
 match_address:   ${if match_address{x@y.z}{p@q:*@y.z}{yes}{no}}
 match_address:   ${if match_address{x@y.z}{p@q:x@*.z}{yes}{no}}
 
 match_address:   ${if match_address{x@y.z}{p@q:*@y.z}{yes}{no}}
 match_address:   ${if match_address{x@y.z}{p@q:x@*.z}{yes}{no}}
@@ -332,6 +444,7 @@ match_ip:        11 ${if match_ip{somename}{+hlist}}
 match_ip:        12 ${if match_ip{1.2.3.4}{lsearch;DIR/aux-fixed/0002.matchip}}
 match_ip:        13 ${if match_ip{1.2.3.4}{net-lsearch;DIR/aux-fixed/0002.matchip}}
 match_ip:        14 ${if match_ip{5.6.7.8}{net24-lsearch;DIR/aux-fixed/0002.matchip}}
 match_ip:        12 ${if match_ip{1.2.3.4}{lsearch;DIR/aux-fixed/0002.matchip}}
 match_ip:        13 ${if match_ip{1.2.3.4}{net-lsearch;DIR/aux-fixed/0002.matchip}}
 match_ip:        14 ${if match_ip{5.6.7.8}{net24-lsearch;DIR/aux-fixed/0002.matchip}}
+match_ip:        15 ${if match_ip{abcd::dcba}{net-iplsearch;DIR/aux-fixed/0002.matchip}}
 
 queue_running:  ${if queue_running{y}{n}}
 first_delivery: ${if first_delivery{y}{n}}
 
 queue_running:  ${if queue_running{y}{n}}
 first_delivery: ${if first_delivery{y}{n}}
@@ -339,6 +452,12 @@ first_delivery: ${if first_delivery{y}{n}}
 queue_running after or: ${if or{{eq {0}{0}}{queue_running}}{y}{n}}
 first_delivery after or: ${if or{{eq {0}{0}}{first_delivery}}{y}{n}}
 
 queue_running after or: ${if or{{eq {0}{0}}{queue_running}}{y}{n}}
 first_delivery after or: ${if or{{eq {0}{0}}{first_delivery}}{y}{n}}
 
+# acl expansion condition
+acl if: ${if acl {{a_ret}}               {Y:$value}{N:$value}}
+acl if: ${if acl {{a_ret}{argY}}         {Y:$value}{N:$value}}
+acl if: ${if acl {{a_deny}{argN}{arg2}}  {Y:$value}{N:$value}}
+acl if: ${if acl {{a_defer}{argN}{arg2}} {Y:$value}{N:$value}}
+
 # Default values for both if strings
 
 \${if eq{1}{1}}  >${if eq{1}{1}}<
 # Default values for both if strings
 
 \${if eq{1}{1}}  >${if eq{1}{1}}<
@@ -488,6 +607,50 @@ abcdea a   z   ${tr{abcdea}{a}{z}}
 abcdea a       ${tr{abcdea}{a}{}}
 abcdea abc z   ${tr{abcdea}{abc}{z}}
 
 abcdea a       ${tr{abcdea}{a}{}}
 abcdea abc z   ${tr{abcdea}{abc}{z}}
 
+# Boolean
+"TrUe"                ${if bool{TrUe}{true}{false}}      EXPECT: true
+"FALSE"               ${if bool{FALSE}{true}{false}}     EXPECT: false
+" yes"                ${if bool{ yes}{true}{false}}      EXPECT: true
+" no"                 ${if bool{ no}{true}{false}}     EXPECT: false
+"yes "                ${if bool{yes }{true}{false}}      EXPECT: true
+"-1"                  ${if bool{-1}{true}{false}}     EXPECT: true
+"0"                   ${if bool{0}{true}{false}}     EXPECT: false
+"1"                   ${if bool{1}{true}{false}}      EXPECT: true
+" 0 "                 ${if bool{ 0 }{true}{false}}     EXPECT: false
+" 1 "                 ${if bool{ 1 }{true}{false}}      EXPECT: true
+"1111111111111111111" ${if bool{1111111111111111111}{true}{false}}      EXPECT: true
+"9"                   ${if bool{9}{true}{false}}      EXPECT: true
+" "                   ${if bool{ }{true}{false}}     EXPECT: false
+"text"                ${if bool{text}{true}{false}}     EXPECT: error
+" text"               ${if bool{ text}{true}{false}}     EXPECT: error
+"-text"               ${if bool{-text}{true}{false}}     EXPECT: error
+"text "               ${if bool{text }{true}{false}}     EXPECT: error
+" text "              ${if bool{ text }{true}{false}}     EXPECT: error
+"00"                  ${if bool{00}{true}{false}}     EXPECT: false
+"!true"               ${if !bool{true}{true}{false}}     EXPECT: false
+"!false"              ${if !bool{false}{true}{false}}      EXPECT: true
+
+"TrUe"                ${if bool_lax{TrUe}{true}{false}}      EXPECT: true
+"FALSE"               ${if bool_lax{FALSE}{true}{false}}     EXPECT: false
+" yes"                ${if bool_lax{ yes}{true}{false}}      EXPECT: true
+" no"                 ${if bool_lax{ no}{true}{false}}     EXPECT: false
+"yes "                ${if bool_lax{yes }{true}{false}}      EXPECT: true
+"-1"                  ${if bool_lax{-1}{true}{false}}      EXPECT: true
+"0"                   ${if bool_lax{0}{true}{false}}     EXPECT: false
+"1"                   ${if bool_lax{1}{true}{false}}      EXPECT: true
+" 0 "                 ${if bool_lax{ 0 }{true}{false}}     EXPECT: false
+" 1 "                 ${if bool_lax{ 1 }{true}{false}}      EXPECT: true
+"1111111111111111111" ${if bool_lax{1111111111111111111}{true}{false}}      EXPECT: true
+"9"                   ${if bool_lax{9}{true}{false}}      EXPECT: true
+" "                   ${if bool_lax{ }{true}{false}}     EXPECT: false
+"text"                ${if bool_lax{text}{true}{false}}      EXPECT: true
+" text"               ${if bool_lax{ text}{true}{false}}      EXPECT: true
+"text "               ${if bool_lax{text }{true}{false}}      EXPECT: true
+" text "              ${if bool_lax{ text }{true}{false}}      EXPECT: true
+"00"                  ${if bool_lax{00}{true}{false}}      EXPECT: true
+"!true"               ${if !bool_lax{true}{true}{false}}     EXPECT: false
+"!false"              ${if !bool_lax{false}{true}{false}}      EXPECT: true
+
 # RFC 2047
 
 abcd      ${rfc2047:abcd}
 # RFC 2047
 
 abcd      ${rfc2047:abcd}
@@ -496,6 +659,13 @@ abcd      ${rfc2047:abcd}
 Long:     ${rfc2047: here we go: a string that is going to be encoded: it will go over the 75-char limit}
 Long:     ${rfc2047: here we go: a string that is going to be encoded: it will go over the 75-char limit by a long way; in fact this one will go over the 150 character limit}
 
 Long:     ${rfc2047: here we go: a string that is going to be encoded: it will go over the 75-char limit}
 Long:     ${rfc2047: here we go: a string that is going to be encoded: it will go over the 75-char limit by a long way; in fact this one will go over the 150 character limit}
 
+# RFC 2047 decode
+
+${rfc2047d:abcd      abcd}
+${rfc2047d:<:abcd:>  =?iso-8859-8?Q?=3C=3Aabcd=3A=3E?=}
+${rfc2047d:<:ab cd:> =?iso-8859-8?Q?=3C=3Aab_cd=3A=3E?=}
+${rfc2047d:Long:     =?iso-8859-8?Q?_here_we_go=3A_a_string_that_is_going_to_be_encoded=3A_i?= =?iso-8859-8?Q?t_will_go_over_the_75-char_limit?=}
+${rfc2047d:Long:     =?iso-8859-8?Q?_here_we_go=3A_a_string_that_is_going_to_be_encoded=3A_i?= =?iso-8859-8?Q?t_will_go_over_the_75-char_limit_by_a_long_way=3B_in_fac?= =?iso-8859-8?Q?t_this_one_will_go_over_the_150_character_limit?=}
 
 # UTF-8
 
 
 # UTF-8
 
@@ -556,7 +726,7 @@ ${prvs{userx@test.ex}{secret}{rhubarb}}
 ${prvs{userx@test.ex}{secret}{}}
 
 # Correct checks; can't put explicit addresses in the tests, because they
 ${prvs{userx@test.ex}{secret}{}}
 
 # Correct checks; can't put explicit addresses in the tests, because they
-# will change over time. 
+# will change over time.
 
 ${prvscheck{${prvs{userx@test.ex}{secret}}}{secret}}
 result=$prvscheck_result
 
 ${prvscheck{${prvs{userx@test.ex}{secret}}}{secret}}
 result=$prvscheck_result
@@ -600,6 +770,59 @@ ${if or {{eq {}{}}{yes}{no}}
 ${substr_1_:12345}
 ${substr__3:12345}
 
 ${substr_1_:12345}
 ${substr__3:12345}
 
+# Iterations: forany and forall
+
+${if forany{a:b:c}{eq{$item}{a}}{yes}{no}}
+${if forany{a:b:c}{eq{$item}{b}}{yes}{no}}
+${if forany{a:b:c}{eq{$item}{c}}{yes}{no}}
+${if forany {a:b:c} {eq {$item} {z}} {yes} {no}}
+${if !forany{a:b:c}{eq{$item}{z}}{yes}{no}}
+${if !forany{a:b:c}{eq{$item}{a}}{yes}{no}}
+${if forany{}{eq{$item}{a}}{yes}{no}}
+${if !forany{}{eq{$item}{a}}{yes}{no}}
+${if forany{<, $primary_hostname,foo,bar}{eq{$item}{$primary_hostname}}{yes}{no}}
+
+${if forany{}{yes}{no}}
+${if forany{a:b:c}{gt{$item}{a}{yes}{no}}
+
+${if forall{a:b:c}{match{$item}{^[a-c]\$}}{yes}{no}}
+${if forall{q:b:c}{match{$item}{^[a-c]\$}}{yes}{no}}
+${if forall{a:b:z}{match{$item}{^[a-c]\$}}{yes}{no}}
+${if forall{}{match{$item}{^[a-c]\$}}{yes}{no}}
+
+${if !forall{a:b:c}{match{$item}{^[a-c]\$}}{yes}{no}}
+${if !forall{q:b:c}{match{$item}{^[a-c]\$}}{yes}{no}}
+${if !forall{a:b:z}{match{$item}{^[a-c]\$}}{yes}{no}}
+${if !forall{}{match{$item}{^[a-c]\$}}{yes}{no}}
+
+# Expect yes
+${if forany{a:b:c}\
+  {\
+  eq\
+    {$item: ${if forall{x:y:z}{match{$item}{^[x-z]\$}}{true}{false}}}\
+    {$item: true}\
+  }\
+{outer=yes}{outer=no}} item='$item' (unset)
+
+# Expect no
+${if forany{a:b:c}\
+  {\
+  eq\
+    {$item: ${if !forall{x:y:z}{match{$item}{^[x-z]\$}}{true}{false}}}\
+    {$item: true}\
+  }\
+{outer=yes}{outer=no}}
+
+# Error inside nest - check message is helpful
+${if forany{a:b:c}\
+  {\
+  eq\
+    {$item: ${if forall{x:y:z}{match{$item}{^[x-z]\$}{true}{false}}}\
+    {$item: true}\
+  }\
+{outer=yes}{outer=no}}
+
+
 # Miscellaneous (for bug fixes, etc)
 
 ${if ={1}{1} {true}{${if ={1}{1} {true}{${if ={1}{1}{true}fail}}}}}
 # Miscellaneous (for bug fixes, etc)
 
 ${if ={1}{1} {true}{${if ={1}{1} {true}{${if ={1}{1}{true}fail}}}}}
@@ -733,3 +956,8 @@ exim -d -be
 match_ip:        15 ${if match_ip{1.2.3.4}{1.2.3}}
 match_ip:        16 ${if match_ip{1.2.3.4}{1.2.3.4/abc}}
 ****
 match_ip:        15 ${if match_ip{1.2.3.4}{1.2.3}}
 match_ip:        16 ${if match_ip{1.2.3.4}{1.2.3.4/abc}}
 ****
+# Operation of inlist and negated inlist
+exim -be
+${if inlist{aa}{aa} {in list}{not in list}}
+${if !inlist{aa}{aa} {not in list}{in list}}
+****