From 723c72e64c04d3b7bdf03b26c65761c1ae569312 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Mon, 11 Jun 2012 22:00:11 +0100 Subject: Add ${acl {name}{arg}} expansion item. --- doc/doc-docbook/spec.xfpt | 14 ++++++++++++++ doc/doc-txt/ChangeLog | 2 ++ doc/doc-txt/NewStuff | 4 ++++ src/src/expand.c | 42 +++++++++++++++++++++++++++++++++++++++++- src/src/globals.c | 7 ++++--- src/src/macros.h | 4 +++- test/confs/0002 | 8 ++++++++ test/scripts/0000-Basic/0002 | 11 +++++++++++ test/stdout/0002 | 11 +++++++++++ 9 files changed, 98 insertions(+), 5 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index dcf6b6cfb..cde80a17c 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -8758,6 +8758,20 @@ string easier to understand. This item inserts &"basic"& header lines. It is described with the &%header%& expansion item below. + +.vitem "&*${acl{*&<&'name'&>&*}{*&<&'string'&>&*}}*&" +.cindex "expansion" "calling an acl" +.cindex "&%acl%&" "call from expansion" +The name and <&'string'&> are first expanded separately. The expanded +<&'string'&> is assigned to the &$address_data$& variable. If {<&'string'&>} +is omitted, &$address_data$& is made empty. The named ACL (see chapter +&<>&) is called and may use &$address_data$&. If the ACL sets +a value using a "message =" modifier and returns accept, the value becomes +the result of the expansion. +If no message was set but the ACL returned accept, or if the ACL returned defer, +the value is an empty string. Otherwise the expansion fails. + + .vitem "&*${dlfunc{*&<&'file'&>&*}{*&<&'function'&>&*}{*&<&'arg'&>&*}&&& {*&<&'arg'&>&*}...}*&" .cindex &%dlfunc%& diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 34521098e..4a2215956 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -44,6 +44,8 @@ NM/01 Bugzilla 1197 - Spec typo JH/03 Add expansion operators ${listnamed:name} and ${listcount:string} +JH/04 Add expansion item ${acl {name}{argument}} + Exim version 4.80 ----------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 6d64faa00..b13a5a0f2 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -87,6 +87,10 @@ Version 4.81 8. New expansion operators ${listnamed:name} to get the content of a named list and ${listcount:string} to count the items in a list. + 9. New expansion item ${acl {name}{argument}} to call an ACL. The argument can + be accessed by the ACL in $address_data. The expansion result is set by + a "message =" modifier and an "accept" return from the ACL. + Version 4.80 ------------ diff --git a/src/src/expand.c b/src/src/expand.c index 965842611..16d5d74ab 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -102,6 +102,7 @@ bcrypt ({CRYPT}$2a$). alphabetical order. */ static uschar *item_table[] = { + US"acl", US"dlfunc", US"extract", US"filter", @@ -124,6 +125,7 @@ static uschar *item_table[] = { US"tr" }; enum { + EITEM_ACL, EITEM_DLFUNC, EITEM_EXTRACT, EITEM_FILTER, @@ -3641,6 +3643,45 @@ while (*s != 0) switch(item_type) { + /* Call an ACL from an expansion. We feed data in via $address_data. + If the ACL returns acceptance we return content set by "message =" + There is currently no limit on recursion; this would have us call + acl_check_internal() directly and get a current level from somewhere. + */ + + case EITEM_ACL: + { + int rc; + uschar *sub[2]; + uschar *new_yield; + uschar *user_msg; + uschar *log_msg; + switch(read_subs(sub, 2, 1, &s, skipping, TRUE, US"acl")) + { + case 1: goto EXPAND_FAILED_CURLY; + case 2: + case 3: goto EXPAND_FAILED; + } + if (skipping) continue; + + DEBUG(D_expand) + debug_printf("expanding: acl: %s arg: %s\n", sub[0], sub[1]?sub[1]:US""); + + deliver_address_data = sub[1]; + switch(rc = acl_check(ACL_WHERE_EXPANSION, NULL, sub[0], &user_msg, &log_msg)) + { + case OK: + if (user_msg) + yield = string_cat(yield, &size, &ptr, user_msg, Ustrlen(user_msg)); + continue; + case DEFER: + continue; + default: + expand_string_message = string_sprintf("acl \"%s\" did not accept", sub[0]); + goto EXPAND_FAILED; + } + } + /* Handle conditionals - preserve the values of the numerical expansion variables in case they get changed by a regular expression match in the condition. If not, they retain their external settings. At the end @@ -5533,7 +5574,6 @@ while (*s != 0) goto EXPAND_FAILED; } - if (skipping) continue; list = ((namedlist_block *)(t->data.ptr))->string; while ((item = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) diff --git a/src/src/globals.c b/src/src/globals.c index 97c7166ab..3ad38d39d 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -192,7 +192,6 @@ uschar *acl_not_smtp = NULL; uschar *acl_not_smtp_mime = NULL; #endif uschar *acl_not_smtp_start = NULL; - uschar *acl_smtp_auth = NULL; uschar *acl_smtp_connect = NULL; uschar *acl_smtp_data = NULL; @@ -240,7 +239,8 @@ uschar *acl_wherenames[] = { US"RCPT", US"NOTQUIT", US"QUIT", US"STARTTLS", - US"VRFY" + US"VRFY", + US"expansion" }; uschar *acl_wherecodes[] = { US"550", /* RCPT */ @@ -260,7 +260,8 @@ uschar *acl_wherecodes[] = { US"550", /* RCPT */ US"0", /* NOTQUIT; not relevant */ US"0", /* QUIT; not relevant */ US"550", /* STARTTLS */ - US"252" /* VRFY */ + US"252", /* VRFY */ + US"0" /* unknown; not relevant */ }; BOOL active_local_from_check = FALSE; diff --git a/src/src/macros.h b/src/src/macros.h index b17a80e10..d25071aae 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -820,7 +820,9 @@ enum { ACL_WHERE_RCPT, /* Some controls are for RCPT only */ ACL_WHERE_NOTQUIT, ACL_WHERE_QUIT, ACL_WHERE_STARTTLS, - ACL_WHERE_VRFY + ACL_WHERE_VRFY, + + ACL_WHERE_EXPANSION /* Currently used by a ${acl:name} expansion */ }; /* Situations for spool_write_header() */ diff --git a/test/confs/0002 b/test/confs/0002 index 6983fd87f..317c4a27d 100644 --- a/test/confs/0002 +++ b/test/confs/0002 @@ -44,4 +44,12 @@ check_data: warn logwrite = Subject is: "$h_subject:" deny message = reply_address=<$reply_address> +a_ret: + accept message = [$address_data] + +a_none: + accept + +a_deny: + # End diff --git a/test/scripts/0000-Basic/0002 b/test/scripts/0000-Basic/0002 index 652891615..d567f8486 100644 --- a/test/scripts/0000-Basic/0002 +++ b/test/scripts/0000-Basic/0002 @@ -86,6 +86,17 @@ reduce: ${reduce {<\x7f 1\x7f2\177 3}{0}{${eval:$value+$item}}} # Operators +acl: ${acl +acl: ${acl} +acl: ${acl {a_bad}} +acl: ${acl {a_ret}} +acl: ${acl {a_ret}{person@dom.ain}} +acl: ${acl {a_none}} +acl: ${acl {a_none}{person@dom.ain}} +acl: ${acl {a_deny}} +acl: ${acl {a_deny}{person@dom.ain}} +acl: ${reduce {1:2:3:4} {} {$value ${acl {a_ret}{$item}}}} + addrss: ${address:local-part@dom.ain} addrss: ${address:Exim Person (that's me)} domain: ${domain:local-part@dom.ain} diff --git a/test/stdout/0002 b/test/stdout/0002 index de67f99fc..377fe02cd 100644 --- a/test/stdout/0002 +++ b/test/stdout/0002 @@ -78,6 +78,17 @@ > > # Operators > +> Failed: missing or misplaced { or } +> Failed: missing or misplaced { or } +> Failed: acl "a_bad" did not accept +> acl: [] +> acl: [person@dom.ain] +> acl: +> acl: +> Failed: acl "a_deny" did not accept +> Failed: acl "a_deny" did not accept +> acl: [1] [2] [3] [4] +> > addrss: local-part@dom.ain > addrss: local-part@dom.ain > domain: dom.ain -- cgit v1.2.3 From 525239c16e35d7bf893e0e2232f4c4c4a7c75447 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Tue, 12 Jun 2012 22:50:52 +0100 Subject: Use custom variables for ACL args, up to nine. Add an arg-count variable. --- doc/doc-docbook/spec.xfpt | 12 +++++++----- doc/doc-txt/ChangeLog | 2 +- doc/doc-txt/NewStuff | 7 ++++--- src/src/expand.c | 28 +++++++++++++++++++++++----- src/src/globals.c | 3 +++ src/src/globals.h | 2 ++ test/confs/0002 | 2 +- test/scripts/0000-Basic/0002 | 2 ++ test/stdout/0002 | 8 +++++--- 9 files changed, 48 insertions(+), 18 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index cde80a17c..29aacf61c 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -8759,13 +8759,15 @@ This item inserts &"basic"& header lines. It is described with the &%header%& expansion item below. -.vitem "&*${acl{*&<&'name'&>&*}{*&<&'string'&>&*}}*&" +.vitem "&*${acl{*&<&'name'&>&*}{*&<&'arg'&>&*}...}*&" .cindex "expansion" "calling an acl" .cindex "&%acl%&" "call from expansion" -The name and <&'string'&> are first expanded separately. The expanded -<&'string'&> is assigned to the &$address_data$& variable. If {<&'string'&>} -is omitted, &$address_data$& is made empty. The named ACL (see chapter -&<>&) is called and may use &$address_data$&. If the ACL sets +The name and zero to nine argument strings are first expanded separately. The expanded +arguments are assigned to the variables &$acl_arg1$& to &$acl_arg9$& in order. +Any used are made empty. The variable &$acl_narg$& is set to the number of +arguments. The named ACL (see chapter &<>&) is called +and may use the variables; if another acl expansion is used the values +are overwritten. If the ACL sets a value using a "message =" modifier and returns accept, the value becomes the result of the expansion. If no message was set but the ACL returned accept, or if the ACL returned defer, diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 4a2215956..504c3f551 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -44,7 +44,7 @@ NM/01 Bugzilla 1197 - Spec typo JH/03 Add expansion operators ${listnamed:name} and ${listcount:string} -JH/04 Add expansion item ${acl {name}{argument}} +JH/04 Add expansion item ${acl {name}{arg}...} Exim version 4.80 ----------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index b13a5a0f2..3c5c4913b 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -87,9 +87,10 @@ Version 4.81 8. New expansion operators ${listnamed:name} to get the content of a named list and ${listcount:string} to count the items in a list. - 9. New expansion item ${acl {name}{argument}} to call an ACL. The argument can - be accessed by the ACL in $address_data. The expansion result is set by - a "message =" modifier and an "accept" return from the ACL. + 9. New expansion item ${acl {name}{arg}...} to call an ACL. The argument can + be accessed by the ACL in $acl_arg1 to $acl_arg9. $acl_narg will be the + number of arguments. The expansion result is set by a "message =" modifier + and an "accept" return from the ACL. Version 4.80 ------------ diff --git a/src/src/expand.c b/src/src/expand.c index 16d5d74ab..913a808b8 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -392,6 +392,16 @@ enum { static var_entry var_table[] = { /* WARNING: Do not invent variables whose names start acl_c or acl_m because they will be confused with user-creatable ACL variables. */ + { "acl_arg1", vtype_stringptr, &acl_arg[0] }, + { "acl_arg2", vtype_stringptr, &acl_arg[1] }, + { "acl_arg3", vtype_stringptr, &acl_arg[2] }, + { "acl_arg4", vtype_stringptr, &acl_arg[3] }, + { "acl_arg5", vtype_stringptr, &acl_arg[4] }, + { "acl_arg6", vtype_stringptr, &acl_arg[5] }, + { "acl_arg7", vtype_stringptr, &acl_arg[6] }, + { "acl_arg8", vtype_stringptr, &acl_arg[7] }, + { "acl_arg9", vtype_stringptr, &acl_arg[8] }, + { "acl_narg", vtype_int, &acl_narg }, { "acl_verify_message", vtype_stringptr, &acl_verify_message }, { "address_data", vtype_stringptr, &deliver_address_data }, { "address_file", vtype_stringptr, &address_file }, @@ -3643,7 +3653,7 @@ while (*s != 0) switch(item_type) { - /* Call an ACL from an expansion. We feed data in via $address_data. + /* Call an ACL from an expansion. We feed data in via $acl_arg1 - $acl_arg9. If the ACL returns acceptance we return content set by "message =" There is currently no limit on recursion; this would have us call acl_check_internal() directly and get a current level from somewhere. @@ -3652,11 +3662,11 @@ while (*s != 0) case EITEM_ACL: { int rc; - uschar *sub[2]; + uschar *sub[10]; /* name + arg1-arg9, must match number of acl_arg[] */ uschar *new_yield; uschar *user_msg; uschar *log_msg; - switch(read_subs(sub, 2, 1, &s, skipping, TRUE, US"acl")) + switch(read_subs(sub, 10, 1, &s, skipping, TRUE, US"acl")) { case 1: goto EXPAND_FAILED_CURLY; case 2: @@ -3664,10 +3674,18 @@ while (*s != 0) } if (skipping) continue; + for (rc = 1; rc < sizeof(sub)/sizeof(*sub) && sub[rc]; rc++) + acl_arg[rc-1] = sub[rc]; + acl_narg = rc-1; + while (rc < sizeof(sub)/sizeof(*sub)) + acl_arg[rc++ - 1] = NULL; + DEBUG(D_expand) - debug_printf("expanding: acl: %s arg: %s\n", sub[0], sub[1]?sub[1]:US""); + debug_printf("expanding: acl: %s arg: %s%s\n", + sub[0], + acl_narg>0 ? sub[1] : US"", + acl_narg>1 ? " +more" : ""); - deliver_address_data = sub[1]; switch(rc = acl_check(ACL_WHERE_EXPANSION, NULL, sub[0], &user_msg, &log_msg)) { case OK: diff --git a/src/src/globals.c b/src/src/globals.c index 3ad38d39d..b6db9251d 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -186,6 +186,9 @@ int address_expansions_count = sizeof(address_expansions)/sizeof(uschar **); header_line *acl_added_headers = NULL; tree_node *acl_anchor = NULL; +uschar *acl_arg[9] = {NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL}; +int acl_narg = 0; uschar *acl_not_smtp = NULL; #ifdef WITH_CONTENT_SCAN diff --git a/src/src/globals.h b/src/src/globals.h index e910dbe1b..639d88f31 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -134,6 +134,8 @@ extern uschar **address_expansions[ADDRESS_EXPANSIONS_COUNT]; extern BOOL accept_8bitmime; /* Allow *BITMIME incoming */ extern header_line *acl_added_headers; /* Headers added by an ACL */ extern tree_node *acl_anchor; /* Tree of named ACLs */ +extern uschar *acl_arg[9]; /* Argument to ACL call */ +extern int acl_narg; /* Number of arguments to ACL call */ extern uschar *acl_not_smtp; /* ACL run for non-SMTP messages */ #ifdef WITH_CONTENT_SCAN extern uschar *acl_not_smtp_mime; /* For MIME parts of ditto */ diff --git a/test/confs/0002 b/test/confs/0002 index 317c4a27d..df65e2c30 100644 --- a/test/confs/0002 +++ b/test/confs/0002 @@ -45,7 +45,7 @@ check_data: deny message = reply_address=<$reply_address> a_ret: - accept message = [$address_data] + accept message = ($acl_narg) [$acl_arg1] [$acl_arg2] a_none: accept diff --git a/test/scripts/0000-Basic/0002 b/test/scripts/0000-Basic/0002 index d567f8486..62a0a1f56 100644 --- a/test/scripts/0000-Basic/0002 +++ b/test/scripts/0000-Basic/0002 @@ -91,6 +91,8 @@ acl: ${acl} acl: ${acl {a_bad}} 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}} diff --git a/test/stdout/0002 b/test/stdout/0002 index 377fe02cd..edf18d18c 100644 --- a/test/stdout/0002 +++ b/test/stdout/0002 @@ -81,13 +81,15 @@ > Failed: missing or misplaced { or } > Failed: missing or misplaced { or } > Failed: acl "a_bad" did not accept -> acl: [] -> acl: [person@dom.ain] +> acl: (0) [] [] +> acl: (1) [person@dom.ain] [] +> acl: (2) [firstarg] [secondarg] +> acl: (1) [arg with spaces] [] > acl: > acl: > Failed: acl "a_deny" did not accept > Failed: acl "a_deny" did not accept -> acl: [1] [2] [3] [4] +> acl: (1) [1] [] (1) [2] [] (1) [3] [] (1) [4] [] > > addrss: local-part@dom.ain > addrss: local-part@dom.ain -- cgit v1.2.3 From 7421ecabc69420415b86bd14f2abeee6f6fa4c81 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Thu, 14 Jun 2012 20:44:58 +0100 Subject: Add args to trad. modifier acl call method --- src/src/acl.c | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/src/acl.c b/src/src/acl.c index a721665d4..45be2fd36 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -236,7 +236,7 @@ at the outer level. In the other cases, expansion already occurs in the checking functions. */ static uschar cond_expand_at_top[] = { - TRUE, /* acl */ + FALSE, /* acl */ TRUE, /* add_header */ FALSE, /* authenticated */ #ifdef EXPERIMENTAL_BRIGHTMAIL @@ -2785,13 +2785,40 @@ for (; cb != NULL; cb = cb->next) "discard" verb. */ case ACLC_ACL: - rc = acl_check_internal(where, addr, arg, level+1, user_msgptr, log_msgptr); - if (rc == DISCARD && verb != ACL_ACCEPT && verb != ACL_DISCARD) { - *log_msgptr = string_sprintf("nested ACL returned \"discard\" for " - "\"%s\" command (only allowed with \"accept\" or \"discard\")", - verbs[verb]); - return ERROR; + uschar * cp = arg; + uschar * tmp; + uschar * name; + + if (!(tmp = string_dequote(&cp)) || !(name = expand_string(tmp))) + { + if (expand_string_forcedfail) continue; + *log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", + tmp, expand_string_message); + return search_find_defer? DEFER : ERROR; + } + + for (acl_narg = 0; acl_narg < sizeof(acl_arg)/sizeof(*acl_arg); acl_narg++) + { + while (*cp && isspace(*cp)) cp++; + if (!*cp) break; + if (!(tmp = string_dequote(&cp)) || !(acl_arg[acl_narg] = expand_string(tmp))) + { + if (expand_string_forcedfail) continue; + *log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", + arg, expand_string_message); + return search_find_defer? DEFER : ERROR; + } + } + + rc = acl_check_internal(where, addr, name, level+1, user_msgptr, log_msgptr); + if (rc == DISCARD && verb != ACL_ACCEPT && verb != ACL_DISCARD) + { + *log_msgptr = string_sprintf("nested ACL returned \"discard\" for " + "\"%s\" command (only allowed with \"accept\" or \"discard\")", + verbs[verb]); + return ERROR; + } } break; -- cgit v1.2.3 From 333eea9c862f2368e61fee5ce7231011c19f04ec Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Thu, 14 Jun 2012 23:24:16 +0100 Subject: Add acl call as an expansion condition --- src/src/acl.c | 37 ++++++++++++++++++++++++++++ src/src/expand.c | 69 +++++++++++++++++++++++++++++++++++++++++++---------- src/src/functions.h | 2 ++ 3 files changed, 95 insertions(+), 13 deletions(-) diff --git a/src/src/acl.c b/src/src/acl.c index a721665d4..84b0609cf 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -3863,6 +3863,43 @@ return FAIL; } + + +/* Same args as acl_check_internal() above, but the string s is +the name of an ACL followed optionally by up to 9 space-separated arguments. +The name and args are separately expanded. Args go into $acl_arg globals. */ +int +acl_check_args(int where, address_item *addr, uschar *s, int level, + uschar **user_msgptr, uschar **log_msgptr) +{ +uschar * tmp; +uschar * name; + +if (!(tmp = string_dequote(&s)) || !(name = expand_string(tmp))) + goto bad; + +for (acl_narg = 0; acl_narg < sizeof(acl_arg)/sizeof(*acl_arg); acl_narg++) + { + while (*s && isspace(*s)) s++; + if (!*s) break; + if (!(tmp = string_dequote(&s)) || !(acl_arg[acl_narg] = expand_string(tmp))) + { + tmp = name; + goto bad; + } + } + +return acl_check_internal(where, addr, name, level+1, user_msgptr, log_msgptr); + +bad: +if (expand_string_forcedfail) return ERROR; +*log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", + tmp, expand_string_message); +return search_find_defer?DEFER:ERROR; +} + + + /************************************************* * Check access using an ACL * *************************************************/ diff --git a/src/src/expand.c b/src/src/expand.c index 913a808b8..7529ad1ca 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -249,6 +249,7 @@ static uschar *cond_table[] = { US"==", /* Backward compatibility */ US">", US">=", + US"acl", US"and", US"bool", US"bool_lax", @@ -294,6 +295,7 @@ enum { ECOND_NUM_EE, ECOND_NUM_G, ECOND_NUM_GE, + ECOND_ACL, ECOND_AND, ECOND_BOOL, ECOND_BOOL_LAX, @@ -2066,12 +2068,50 @@ switch(cond_type) return s; + /* call ACL (in a conditional context). Accept true, deny false. + Defer is a forced-fail. Anything set by message= goes to $value. + See also the expansion-item version EITEM_ACL. */ + + case ECOND_ACL: + /* ${if acl {name arg1 arg2...} {yes}{no}} + { + uschar *nameargs; + uschar *user_msg; + uschar *log_msg; + BOOL cond = FALSE; + int size = 0; + + while (isspace(*s)) s++; + if (*s++ != '{') goto COND_FAILED_CURLY_START; + if (!(nameargs = expand_string_internal(s, TRUE, &s, FALSE, FALSE)) return NULL; + if (*s++ != '}') goto COND_FAILED_CURLY_END; + + switch(acl_check_args(ACL_WHERE_EXPANSION, NULL, nameargs, &user_msg, &log_msg)) + { + case OK: + cond = TRUE; + case FAIL: + if (user_msg) + lookup_value = string_cat(NULL, &size, &ptr, user_msg, Ustrlen(user_msg)); + if (yield != NULL) *yield = cond; + return s; + + case DEFER: + expand_string_forcedfail = TRUE; + default: + expand_string_message = string_sprintf("error from acl \"%s\"", nameargs); + return NULL; + } + } + return s; + + /* saslauthd: does Cyrus saslauthd authentication. Four parameters are used: ${if saslauthd {{username}{password}{service}{realm}} {yes}[no}} However, the last two are optional. That is why the whole set is enclosed - in their own set or braces. */ + in their own set of braces. */ case ECOND_SASLAUTHD: #ifndef CYRUS_SASLAUTHD_SOCKET @@ -3654,18 +3694,19 @@ while (*s != 0) switch(item_type) { /* Call an ACL from an expansion. We feed data in via $acl_arg1 - $acl_arg9. - If the ACL returns acceptance we return content set by "message =" + If the ACL returns accept or reject we return content set by "message =" There is currently no limit on recursion; this would have us call acl_check_internal() directly and get a current level from somewhere. */ case EITEM_ACL: + /* ${acl {name} {arg1}{arg2}...} */ { - int rc; - uschar *sub[10]; /* name + arg1-arg9, must match number of acl_arg[] */ - uschar *new_yield; + int i; + uschar *sub[10]; /* name + arg1-arg9 (which must match number of acl_arg[]) */ uschar *user_msg; uschar *log_msg; + switch(read_subs(sub, 10, 1, &s, skipping, TRUE, US"acl")) { case 1: goto EXPAND_FAILED_CURLY; @@ -3674,11 +3715,11 @@ while (*s != 0) } if (skipping) continue; - for (rc = 1; rc < sizeof(sub)/sizeof(*sub) && sub[rc]; rc++) - acl_arg[rc-1] = sub[rc]; - acl_narg = rc-1; - while (rc < sizeof(sub)/sizeof(*sub)) - acl_arg[rc++ - 1] = NULL; + for (i = 1; i < sizeof(sub)/sizeof(*sub) && sub[i]; i++) + acl_arg[i-1] = sub[i]; + acl_narg = i-1; + while (i < sizeof(sub)/sizeof(*sub)) + acl_arg[i++ - 1] = NULL; DEBUG(D_expand) debug_printf("expanding: acl: %s arg: %s%s\n", @@ -3686,16 +3727,18 @@ while (*s != 0) acl_narg>0 ? sub[1] : US"", acl_narg>1 ? " +more" : ""); - switch(rc = acl_check(ACL_WHERE_EXPANSION, NULL, sub[0], &user_msg, &log_msg)) + switch(acl_check(ACL_WHERE_EXPANSION, NULL, sub[0], &user_msg, &log_msg)) { case OK: + case FAIL: if (user_msg) yield = string_cat(yield, &size, &ptr, user_msg, Ustrlen(user_msg)); continue; + case DEFER: - continue; + expand_string_forcedfail = TRUE; default: - expand_string_message = string_sprintf("acl \"%s\" did not accept", sub[0]); + expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]); goto EXPAND_FAILED; } } diff --git a/src/src/functions.h b/src/src/functions.h index 09f7ab95c..2257a3d7c 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -49,6 +49,8 @@ extern BOOL tls_openssl_options_parse(uschar *, long *); extern acl_block *acl_read(uschar *(*)(void), uschar **); extern int acl_check(int, uschar *, uschar *, uschar **, uschar **); +extern int acl_check_args(int, address_item *, uschar *, int, uschar **, uschar **); + extern tree_node *acl_var_create(uschar *); extern void acl_var_write(uschar *, uschar *, void *); extern uschar *auth_b64encode(uschar *, int); -- cgit v1.2.3 From f60d98e8a1d3f9ca2805fdeee7c8062b44c5362d Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sun, 24 Jun 2012 17:14:48 +0100 Subject: Change acl expansion-condition syntax to "acl {{name} {arg1}{arg2}...}" to match saslauthd condition. --- src/src/acl.c | 37 +++--------------- src/src/expand.c | 108 ++++++++++++++++++++++++++++++++++------------------ src/src/functions.h | 1 - 3 files changed, 76 insertions(+), 70 deletions(-) diff --git a/src/src/acl.c b/src/src/acl.c index 5101267c2..d0db7171e 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -696,8 +696,8 @@ static uschar *ratelimit_option_string[] = { /* Enable recursion between acl_check_internal() and acl_check_condition() */ -static int acl_check_internal(int, address_item *, uschar *, int, uschar **, - uschar **); +static int acl_check_wargs(int, address_item *, uschar *, int, uschar **, + uschar **); /************************************************* @@ -2785,33 +2785,7 @@ for (; cb != NULL; cb = cb->next) "discard" verb. */ case ACLC_ACL: - { - uschar * cp = arg; - uschar * tmp; - uschar * name; - - if (!(tmp = string_dequote(&cp)) || !(name = expand_string(tmp))) - { - if (expand_string_forcedfail) continue; - *log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", - tmp, expand_string_message); - return search_find_defer? DEFER : ERROR; - } - - for (acl_narg = 0; acl_narg < sizeof(acl_arg)/sizeof(*acl_arg); acl_narg++) - { - while (*cp && isspace(*cp)) cp++; - if (!*cp) break; - if (!(tmp = string_dequote(&cp)) || !(acl_arg[acl_narg] = expand_string(tmp))) - { - if (expand_string_forcedfail) continue; - *log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", - arg, expand_string_message); - return search_find_defer? DEFER : ERROR; - } - } - - rc = acl_check_internal(where, addr, name, level+1, user_msgptr, log_msgptr); + rc = acl_check_wargs(where, addr, arg, level+1, user_msgptr, log_msgptr); if (rc == DISCARD && verb != ACL_ACCEPT && verb != ACL_DISCARD) { *log_msgptr = string_sprintf("nested ACL returned \"discard\" for " @@ -2819,7 +2793,6 @@ for (; cb != NULL; cb = cb->next) verbs[verb]); return ERROR; } - } break; case ACLC_AUTHENTICATED: @@ -3895,8 +3868,8 @@ return FAIL; /* Same args as acl_check_internal() above, but the string s is the name of an ACL followed optionally by up to 9 space-separated arguments. The name and args are separately expanded. Args go into $acl_arg globals. */ -int -acl_check_args(int where, address_item *addr, uschar *s, int level, +static int +acl_check_wargs(int where, address_item *addr, uschar *s, int level, uschar **user_msgptr, uschar **log_msgptr) { uschar * tmp; diff --git a/src/src/expand.c b/src/src/expand.c index 7529ad1ca..60552a85c 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1836,6 +1836,40 @@ if (Ustrncmp(name, "acl_", 4) == 0) +/* +Load args from sub array to globals, and call acl_check(). + +Returns: OK access is granted by an ACCEPT verb + DISCARD access is granted by a DISCARD verb + FAIL access is denied + FAIL_DROP access is denied; drop the connection + DEFER can't tell at the moment + ERROR disaster +*/ +static int +eval_acl(uschar ** sub, int nsub, uschar ** user_msgp) +{ +int i; +uschar *dummy_log_msg; + +for (i = 1; i < nsub && sub[i]; i++) + acl_arg[i-1] = sub[i]; +acl_narg = i-1; +while (i < sizeof(sub)/sizeof(*sub)) + acl_arg[i++ - 1] = NULL; + +DEBUG(D_expand) + debug_printf("expanding: acl: %s arg: %s%s\n", + sub[0], + acl_narg>0 ? sub[1] : US"", + acl_narg>1 ? " +more" : ""); + +return acl_check(ACL_WHERE_EXPANSION, NULL, sub[0], user_msgp, &dummy_log_msg); +} + + + + /************************************************* * Read and evaluate a condition * *************************************************/ @@ -1863,7 +1897,7 @@ int i, rc, cond_type, roffset; int_eximarith_t num[2]; struct stat statbuf; uschar name[256]; -uschar *sub[4]; +uschar *sub[10]; const pcre *re; const uschar *rerror; @@ -2070,40 +2104,52 @@ switch(cond_type) /* call ACL (in a conditional context). Accept true, deny false. Defer is a forced-fail. Anything set by message= goes to $value. - See also the expansion-item version EITEM_ACL. */ + Up to ten parameters are used; we use the braces round the name+args + like the saslauthd condition does, to permit a variable number of args. + See also the expansion-item version EITEM_ACL and the traditional + acl modifier ACLC_ACL. + */ case ECOND_ACL: - /* ${if acl {name arg1 arg2...} {yes}{no}} + /* ${if acl {{name}{arg1}{arg2}...} {yes}{no}} { uschar *nameargs; uschar *user_msg; - uschar *log_msg; BOOL cond = FALSE; - int size = 0; while (isspace(*s)) s++; if (*s++ != '{') goto COND_FAILED_CURLY_START; - if (!(nameargs = expand_string_internal(s, TRUE, &s, FALSE, FALSE)) return NULL; - if (*s++ != '}') goto COND_FAILED_CURLY_END; - switch(acl_check_args(ACL_WHERE_EXPANSION, NULL, nameargs, &user_msg, &log_msg)) + switch(read_subs(sub, sizeof(sub)/sizeof(*sub), 1, + &s, yield == NULL, TRUE, US"acl")) { - case OK: - cond = TRUE; - case FAIL: - if (user_msg) - lookup_value = string_cat(NULL, &size, &ptr, user_msg, Ustrlen(user_msg)); - if (yield != NULL) *yield = cond; - return s; - - case DEFER: - expand_string_forcedfail = TRUE; - default: - expand_string_message = string_sprintf("error from acl \"%s\"", nameargs); - return NULL; + case 1: expand_string_message = US"too few arguments or bracketing " + "error for acl"; + case 2: + case 3: return NULL; } + + if (yield != NULL) + switch(eval_acl(sub, sizeof(sub)/sizeof(*sub), &user_msg)) + { + case OK: + cond = TRUE; + case FAIL: + if (user_msg) + lookup_value = string_cat(NULL, &size, &ptr, user_msg, Ustrlen(user_msg)); + else + lookup_value = NULL; + *yield = cond; + break; + + case DEFER: + expand_string_forcedfail = TRUE; + default: + expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]); + return NULL; + } + return s; } - return s; /* saslauthd: does Cyrus saslauthd authentication. Four parameters are used: @@ -3697,15 +3743,15 @@ while (*s != 0) If the ACL returns accept or reject we return content set by "message =" There is currently no limit on recursion; this would have us call acl_check_internal() directly and get a current level from somewhere. + See also the acl expansion condition ECOND_ACL and the traditional + acl modifier ACLC_ACL. */ case EITEM_ACL: /* ${acl {name} {arg1}{arg2}...} */ { - int i; uschar *sub[10]; /* name + arg1-arg9 (which must match number of acl_arg[]) */ uschar *user_msg; - uschar *log_msg; switch(read_subs(sub, 10, 1, &s, skipping, TRUE, US"acl")) { @@ -3715,19 +3761,7 @@ while (*s != 0) } if (skipping) continue; - for (i = 1; i < sizeof(sub)/sizeof(*sub) && sub[i]; i++) - acl_arg[i-1] = sub[i]; - acl_narg = i-1; - while (i < sizeof(sub)/sizeof(*sub)) - acl_arg[i++ - 1] = NULL; - - DEBUG(D_expand) - debug_printf("expanding: acl: %s arg: %s%s\n", - sub[0], - acl_narg>0 ? sub[1] : US"", - acl_narg>1 ? " +more" : ""); - - switch(acl_check(ACL_WHERE_EXPANSION, NULL, sub[0], &user_msg, &log_msg)) + switch(eval_acl(sub, sizeof(sub)/sizeof(*sub), &user_msg)) { case OK: case FAIL: diff --git a/src/src/functions.h b/src/src/functions.h index 2257a3d7c..bc791fcdb 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -49,7 +49,6 @@ extern BOOL tls_openssl_options_parse(uschar *, long *); extern acl_block *acl_read(uschar *(*)(void), uschar **); extern int acl_check(int, uschar *, uschar *, uschar **, uschar **); -extern int acl_check_args(int, address_item *, uschar *, int, uschar **, uschar **); extern tree_node *acl_var_create(uschar *); extern void acl_var_write(uschar *, uschar *, void *); -- cgit v1.2.3 From bef3ea7f5de507f4eda7f32ac767ec6ac0441d57 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Wed, 27 Jun 2012 20:55:23 +0100 Subject: Acl expansions: tests and documentation --- doc/doc-docbook/spec.xfpt | 29 +++++++++++++++++++++++++---- doc/doc-txt/ChangeLog | 4 +++- doc/doc-txt/NewStuff | 13 +++++++++---- src/src/acl.c | 11 ++++++++--- src/src/expand.c | 15 +++++++++------ test/confs/0002 | 7 +++++++ test/scripts/0000-Basic/0002 | 10 +++++++++- test/stdout/0002 | 14 +++++++++++--- 8 files changed, 81 insertions(+), 22 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 29aacf61c..eb5bd4cba 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -8764,14 +8764,15 @@ expansion item below. .cindex "&%acl%&" "call from expansion" The name and zero to nine argument strings are first expanded separately. The expanded arguments are assigned to the variables &$acl_arg1$& to &$acl_arg9$& in order. -Any used are made empty. The variable &$acl_narg$& is set to the number of +Any unused are made empty. The variable &$acl_narg$& is set to the number of arguments. The named ACL (see chapter &<>&) is called and may use the variables; if another acl expansion is used the values are overwritten. If the ACL sets -a value using a "message =" modifier and returns accept, the value becomes +a value using a "message =" modifier and returns accept or deny, the value becomes the result of the expansion. -If no message was set but the ACL returned accept, or if the ACL returned defer, -the value is an empty string. Otherwise the expansion fails. +If no message was set and the ACL returned accept or deny +the value is an empty string. +If the ACL returned defer the result is a forced-fail. Otherwise the expansion fails. .vitem "&*${dlfunc{*&<&'file'&>&*}{*&<&'function'&>&*}{*&<&'arg'&>&*}&&& @@ -10059,6 +10060,21 @@ In all cases, a relative comparator OP is testing if <&'string1'&> OP 10M, not if 10M is larger than &$message_size$&. +.vitem &*acl&~{{*&<&'name'&>&*}{*&<&'arg1'&>&*}&&& + {*&<&'arg2'&>&*}...}*& +.cindex "expansion" "calling an acl" +.cindex "&%acl%&" "expansion condition" +The name and zero to nine argument strings are first expanded separately. The expanded +arguments are assigned to the variables &$acl_arg1$& to &$acl_arg9$& in order. +Any unused are made empty. The variable &$acl_narg$& is set to the number of +arguments. The named ACL (see chapter &<>&) is called +and may use the variables; if another acl expansion is used the values +are overwritten. If the ACL sets +a value using a "message =" modifier the variable $value becomes +the result of the expansion, otherwise it is empty. +If the ACL returns accept the condition is true; if deny, false. +If the ACL returns defer the result is a forced-fail. + .vitem &*bool&~{*&<&'string'&>&*}*& .cindex "expansion" "boolean parsing" .cindex "&%bool%& expansion condition" @@ -27301,6 +27317,7 @@ The conditions are as follows: .vitem &*acl&~=&~*&<&'name&~of&~acl&~or&~ACL&~string&~or&~file&~name&~'&> .cindex "&ACL;" "nested" .cindex "&ACL;" "indirect" +.cindex "&ACL;" "arguments" .cindex "&%acl%& ACL condition" The possible values of the argument are the same as for the &%acl_smtp_%&&'xxx'& options. The named or inline ACL is run. If it returns @@ -27310,6 +27327,10 @@ condition is on a &%warn%& verb. In that case, a &"defer"& return makes the condition false. This means that further processing of the &%warn%& verb ceases, but processing of the ACL continues. +If the argument is a named ACL, up to nine space-separated optional values +can be appended; they appear in $acl_arg1 to $acl_arg9, and $acl_narg is set +to the count of values. The name and values are expanded separately. + If the nested &%acl%& returns &"drop"& and the outer condition denies access, the connection is dropped. If it returns &"discard"&, the verb must be &%accept%& or &%discard%&, and the action is taken immediately &-- no further diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 504c3f551..9dbc65c09 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -44,7 +44,9 @@ NM/01 Bugzilla 1197 - Spec typo JH/03 Add expansion operators ${listnamed:name} and ${listcount:string} -JH/04 Add expansion item ${acl {name}{arg}...} +JH/04 Add expansion item ${acl {name}{arg}...}, expansion condition + "acl {{name}{arg}...}", and optional args on acl condition + "acl = name arg..." Exim version 4.80 ----------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 3c5c4913b..df2ede807 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -87,10 +87,15 @@ Version 4.81 8. New expansion operators ${listnamed:name} to get the content of a named list and ${listcount:string} to count the items in a list. - 9. New expansion item ${acl {name}{arg}...} to call an ACL. The argument can - be accessed by the ACL in $acl_arg1 to $acl_arg9. $acl_narg will be the - number of arguments. The expansion result is set by a "message =" modifier - and an "accept" return from the ACL. + 9. The "acl = name" condition on an ACL now supports optional arguments. + New expansion item "${acl {name}{arg}...}" and expansion condition + "acl {{name}{arg}...}" are added. In all cases up to nine arguments + can be used, appearing in $acl_arg1 to $acl_arg9 for the called ACL. + Variable $acl_narg contains the number of arguments. If the ACL sets + a "message =" value this becomes the result of the expansion item, + or the value of $value for the expansion condition. If the ACL returns + accept the expansion condition is true; if reject, false. A defer + return results in a forced fail. Version 4.80 ------------ diff --git a/src/src/acl.c b/src/src/acl.c index d0db7171e..5cd0c3507 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -3873,23 +3873,28 @@ acl_check_wargs(int where, address_item *addr, uschar *s, int level, uschar **user_msgptr, uschar **log_msgptr) { uschar * tmp; +uschar * tmp_arg[9]; /* must match acl_arg[] */ uschar * name; +int i; if (!(tmp = string_dequote(&s)) || !(name = expand_string(tmp))) goto bad; -for (acl_narg = 0; acl_narg < sizeof(acl_arg)/sizeof(*acl_arg); acl_narg++) +for (i = 0; i < 9; i++) { while (*s && isspace(*s)) s++; if (!*s) break; - if (!(tmp = string_dequote(&s)) || !(acl_arg[acl_narg] = expand_string(tmp))) + if (!(tmp = string_dequote(&s)) || !(tmp_arg[i] = expand_string(tmp))) { tmp = name; goto bad; } } +acl_narg = i; +for (i = 0; i < acl_narg; i++) acl_arg[i] = tmp_arg[i]; +while (i < 9) acl_arg[i++] = NULL; -return acl_check_internal(where, addr, name, level+1, user_msgptr, log_msgptr); +return acl_check_internal(where, addr, name, level, user_msgptr, log_msgptr); bad: if (expand_string_forcedfail) return ERROR; diff --git a/src/src/expand.c b/src/src/expand.c index 60552a85c..767e4771a 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1855,7 +1855,7 @@ uschar *dummy_log_msg; for (i = 1; i < nsub && sub[i]; i++) acl_arg[i-1] = sub[i]; acl_narg = i-1; -while (i < sizeof(sub)/sizeof(*sub)) +while (i < nsub) acl_arg[i++ - 1] = NULL; DEBUG(D_expand) @@ -2111,11 +2111,13 @@ switch(cond_type) */ case ECOND_ACL: - /* ${if acl {{name}{arg1}{arg2}...} {yes}{no}} + /* ${if acl {{name}{arg1}{arg2}...} {yes}{no}} */ { uschar *nameargs; uschar *user_msg; BOOL cond = FALSE; + int size = 0; + int ptr = 0; while (isspace(*s)) s++; if (*s++ != '{') goto COND_FAILED_CURLY_START; @@ -2129,16 +2131,17 @@ switch(cond_type) case 3: return NULL; } - if (yield != NULL) - switch(eval_acl(sub, sizeof(sub)/sizeof(*sub), &user_msg)) + if (yield != NULL) switch(eval_acl(sub, sizeof(sub)/sizeof(*sub), &user_msg)) { case OK: cond = TRUE; case FAIL: + lookup_value = NULL; if (user_msg) + { lookup_value = string_cat(NULL, &size, &ptr, user_msg, Ustrlen(user_msg)); - else - lookup_value = NULL; + lookup_value[ptr] = '\0'; + } *yield = cond; break; diff --git a/test/confs/0002 b/test/confs/0002 index df65e2c30..eb473e6b9 100644 --- a/test/confs/0002 +++ b/test/confs/0002 @@ -51,5 +51,12 @@ a_none: accept a_deny: + deny message = ($acl_narg) [$acl_arg1] [$acl_arg2] + +a_defer: + defer + +a_sub: + require acl = a_deny "new arg1" $acl_arg1 # End diff --git a/test/scripts/0000-Basic/0002 b/test/scripts/0000-Basic/0002 index 62a0a1f56..3e2421b1e 100644 --- a/test/scripts/0000-Basic/0002 +++ b/test/scripts/0000-Basic/0002 @@ -88,7 +88,7 @@ reduce: ${reduce {<\x7f 1\x7f2\177 3}{0}{${eval:$value+$item}}} acl: ${acl acl: ${acl} -acl: ${acl {a_bad}} +acl: ${acl {a_nosuch}} acl: ${acl {a_ret}} acl: ${acl {a_ret}{person@dom.ain}} acl: ${acl {a_ret}{firstarg}{secondarg}} @@ -97,6 +97,8 @@ 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} @@ -421,6 +423,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}} +# 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}}< diff --git a/test/stdout/0002 b/test/stdout/0002 index edf18d18c..b4b96dfb5 100644 --- a/test/stdout/0002 +++ b/test/stdout/0002 @@ -80,15 +80,17 @@ > > Failed: missing or misplaced { or } > Failed: missing or misplaced { or } -> Failed: acl "a_bad" did not accept +> Failed: error from acl "a_nosuch" > acl: (0) [] [] > acl: (1) [person@dom.ain] [] > acl: (2) [firstarg] [secondarg] > acl: (1) [arg with spaces] [] > acl: > acl: -> Failed: acl "a_deny" did not accept -> Failed: acl "a_deny" did not accept +> acl: (0) [] [] +> acl: (1) [person@dom.ain] [] +> Failed: error from acl "a_defer" +> acl: (2) [new arg1] [top_arg_1] > acl: (1) [1] [] (1) [2] [] (1) [3] [] (1) [4] [] > > addrss: local-part@dom.ain @@ -390,6 +392,12 @@ > queue_running after or: y > first_delivery after or: y > +> # acl expansion condition +> acl if: Y:(0) [] [] +> acl if: Y:(1) [argY] [] +> acl if: N:(2) [argN] [arg2] +> Failed: error from acl "a_defer" +> > # Default values for both if strings > > ${if eq{1}{1}} >true< -- cgit v1.2.3