alphabetical order. */
static uschar *item_table[] = {
+ US"acl",
US"dlfunc",
US"extract",
US"filter",
US"tr" };
enum {
+ EITEM_ACL,
EITEM_DLFUNC,
EITEM_EXTRACT,
EITEM_FILTER,
US"l",
US"lc",
US"length",
- US"list",
+ US"listcount",
+ US"listnamed",
US"mask",
US"md5",
US"nh",
US"nhash",
- US"nlist",
US"quote",
US"randint",
US"rfc2047",
EOP_L,
EOP_LC,
EOP_LENGTH,
- EOP_LIST,
+ EOP_LISTCOUNT,
+ EOP_LISTNAMED,
EOP_MASK,
EOP_MD5,
EOP_NH,
EOP_NHASH,
- EOP_NLIST,
EOP_QUOTE,
EOP_RANDINT,
EOP_RFC2047,
US"==", /* Backward compatibility */
US">",
US">=",
+ US"acl",
US"and",
US"bool",
US"bool_lax",
ECOND_NUM_EE,
ECOND_NUM_G,
ECOND_NUM_GE,
+ ECOND_ACL,
ECOND_AND,
ECOND_BOOL,
ECOND_BOOL_LAX,
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 },
+/*
+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 < nsub)
+ 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"<none>",
+ acl_narg>1 ? " +more" : "");
+
+return acl_check(ACL_WHERE_EXPANSION, NULL, sub[0], user_msgp, &dummy_log_msg);
+}
+
+
+
+
/*************************************************
* Read and evaluate a condition *
*************************************************/
int_eximarith_t num[2];
struct stat statbuf;
uschar name[256];
-uschar *sub[4];
+uschar *sub[10];
const pcre *re;
const uschar *rerror;
Ustrncmp(name, "bheader_", 8) == 0)
{
s = read_header_name(name, 256, s);
+ /* {-for-text-editors */
if (Ustrchr(name, '}') != NULL) malformed_header = TRUE;
if (yield != NULL) *yield =
(find_header(name, TRUE, NULL, FALSE, NULL) != NULL) == testfor;
case ECOND_PWCHECK:
while (isspace(*s)) s++;
- if (*s != '{') goto COND_FAILED_CURLY_START;
+ if (*s != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */
sub[0] = expand_string_internal(s+1, TRUE, &s, yield == NULL, TRUE);
if (sub[0] == NULL) return NULL;
+ /* {-for-text-editors */
if (*s++ != '}') goto COND_FAILED_CURLY_END;
if (yield == NULL) return s; /* No need to run the test if skipping */
return s;
+ /* call ACL (in a conditional context). Accept true, deny false.
+ Defer is a forced-fail. Anything set by message= goes to $value.
+ 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}} */
+ {
+ 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;
+
+ switch(read_subs(sub, sizeof(sub)/sizeof(*sub), 1,
+ &s, yield == NULL, TRUE, US"acl"))
+ {
+ 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:
+ lookup_value = NULL;
+ if (user_msg)
+ {
+ lookup_value = string_cat(NULL, &size, &ptr, user_msg, Ustrlen(user_msg));
+ lookup_value[ptr] = '\0';
+ }
+ *yield = cond == testfor;
+ break;
+
+ case DEFER:
+ expand_string_forcedfail = TRUE;
+ default:
+ expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]);
+ 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
goto COND_FAILED_NOT_COMPILED;
#else
while (isspace(*s)) s++;
- if (*s++ != '{') goto COND_FAILED_CURLY_START;
+ if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */
switch(read_subs(sub, 4, 2, &s, yield == NULL, TRUE, US"saslauthd"))
{
case 1: expand_string_message = US"too few arguments or bracketing "
{
case ECOND_NUM_E:
case ECOND_NUM_EE:
- *yield = (num[0] == num[1]) == testfor;
+ tempcond = (num[0] == num[1]);
break;
case ECOND_NUM_G:
- *yield = (num[0] > num[1]) == testfor;
+ tempcond = (num[0] > num[1]);
break;
case ECOND_NUM_GE:
- *yield = (num[0] >= num[1]) == testfor;
+ tempcond = (num[0] >= num[1]);
break;
case ECOND_NUM_L:
- *yield = (num[0] < num[1]) == testfor;
+ tempcond = (num[0] < num[1]);
break;
case ECOND_NUM_LE:
- *yield = (num[0] <= num[1]) == testfor;
+ tempcond = (num[0] <= num[1]);
break;
case ECOND_STR_LT:
- *yield = (Ustrcmp(sub[0], sub[1]) < 0) == testfor;
+ tempcond = (Ustrcmp(sub[0], sub[1]) < 0);
break;
case ECOND_STR_LTI:
- *yield = (strcmpic(sub[0], sub[1]) < 0) == testfor;
+ tempcond = (strcmpic(sub[0], sub[1]) < 0);
break;
case ECOND_STR_LE:
- *yield = (Ustrcmp(sub[0], sub[1]) <= 0) == testfor;
+ tempcond = (Ustrcmp(sub[0], sub[1]) <= 0);
break;
case ECOND_STR_LEI:
- *yield = (strcmpic(sub[0], sub[1]) <= 0) == testfor;
+ tempcond = (strcmpic(sub[0], sub[1]) <= 0);
break;
case ECOND_STR_EQ:
- *yield = (Ustrcmp(sub[0], sub[1]) == 0) == testfor;
+ tempcond = (Ustrcmp(sub[0], sub[1]) == 0);
break;
case ECOND_STR_EQI:
- *yield = (strcmpic(sub[0], sub[1]) == 0) == testfor;
+ tempcond = (strcmpic(sub[0], sub[1]) == 0);
break;
case ECOND_STR_GT:
- *yield = (Ustrcmp(sub[0], sub[1]) > 0) == testfor;
+ tempcond = (Ustrcmp(sub[0], sub[1]) > 0);
break;
case ECOND_STR_GTI:
- *yield = (strcmpic(sub[0], sub[1]) > 0) == testfor;
+ tempcond = (strcmpic(sub[0], sub[1]) > 0);
break;
case ECOND_STR_GE:
- *yield = (Ustrcmp(sub[0], sub[1]) >= 0) == testfor;
+ tempcond = (Ustrcmp(sub[0], sub[1]) >= 0);
break;
case ECOND_STR_GEI:
- *yield = (strcmpic(sub[0], sub[1]) >= 0) == testfor;
+ tempcond = (strcmpic(sub[0], sub[1]) >= 0);
break;
case ECOND_MATCH: /* Regular expression match */
"\"%s\": %s at offset %d", sub[1], rerror, roffset);
return NULL;
}
- *yield = regex_match_and_setup(re, sub[0], 0, -1) == testfor;
+ tempcond = regex_match_and_setup(re, sub[0], 0, -1);
break;
case ECOND_MATCH_ADDRESS: /* Match in an address list */
switch(rc)
{
case OK:
- *yield = testfor;
+ tempcond = TRUE;
break;
case FAIL:
- *yield = !testfor;
+ tempcond = FALSE;
break;
case DEFER:
/* Various "encrypted" comparisons. If the second string starts with
"{" then an encryption type is given. Default to crypt() or crypt16()
(build-time choice). */
+ /* }-for-text-editors */
case ECOND_CRYPTEQ:
#ifndef SUPPORT_CRYPTEQ
uschar *coded = auth_b64encode((uschar *)digest, 16);
DEBUG(D_auth) debug_printf("crypteq: using MD5+B64 hashing\n"
" subject=%s\n crypted=%s\n", coded, sub[1]+5);
- *yield = (Ustrcmp(coded, sub[1]+5) == 0) == testfor;
+ tempcond = (Ustrcmp(coded, sub[1]+5) == 0);
}
else if (sublen == 32)
{
coded[32] = 0;
DEBUG(D_auth) debug_printf("crypteq: using MD5+hex hashing\n"
" subject=%s\n crypted=%s\n", coded, sub[1]+5);
- *yield = (strcmpic(coded, sub[1]+5) == 0) == testfor;
+ tempcond = (strcmpic(coded, sub[1]+5) == 0);
}
else
{
DEBUG(D_auth) debug_printf("crypteq: length for MD5 not 24 or 32: "
"fail\n crypted=%s\n", sub[1]+5);
- *yield = !testfor;
+ tempcond = FALSE;
}
}
uschar *coded = auth_b64encode((uschar *)digest, 20);
DEBUG(D_auth) debug_printf("crypteq: using SHA1+B64 hashing\n"
" subject=%s\n crypted=%s\n", coded, sub[1]+6);
- *yield = (Ustrcmp(coded, sub[1]+6) == 0) == testfor;
+ tempcond = (Ustrcmp(coded, sub[1]+6) == 0);
}
else if (sublen == 40)
{
coded[40] = 0;
DEBUG(D_auth) debug_printf("crypteq: using SHA1+hex hashing\n"
" subject=%s\n crypted=%s\n", coded, sub[1]+6);
- *yield = (strcmpic(coded, sub[1]+6) == 0) == testfor;
+ tempcond = (strcmpic(coded, sub[1]+6) == 0);
}
else
{
DEBUG(D_auth) debug_printf("crypteq: length for SHA-1 not 28 or 40: "
"fail\n crypted=%s\n", sub[1]+6);
- *yield = !testfor;
+ tempcond = FALSE;
}
}
sub[1] += 9;
which = 2;
}
- else if (sub[1][0] == '{')
+ else if (sub[1][0] == '{') /* }-for-text-editors */
{
expand_string_message = string_sprintf("unknown encryption mechanism "
"in \"%s\"", sub[1]);
salt), force failure. Otherwise we get false positives: with an empty
string the yield of crypt() is an empty string! */
- *yield = (Ustrlen(sub[1]) < 2)? !testfor :
- (Ustrcmp(coded, sub[1]) == 0) == testfor;
+ tempcond = (Ustrlen(sub[1]) < 2)? FALSE :
+ (Ustrcmp(coded, sub[1]) == 0);
}
break;
#endif /* SUPPORT_CRYPTEQ */
case ECOND_INLISTI:
{
int sep = 0;
- BOOL found = FALSE;
uschar *save_iterate_item = iterate_item;
int (*compare)(const uschar *, const uschar *);
+ tempcond = FALSE;
if (cond_type == ECOND_INLISTI)
compare = strcmpic;
else
while ((iterate_item = string_nextinlist(&sub[1], &sep, NULL, 0)) != NULL)
if (compare(sub[0], iterate_item) == 0)
{
- found = TRUE;
+ tempcond = TRUE;
break;
}
iterate_item = save_iterate_item;
- *yield = found;
}
} /* Switch for comparison conditions */
+ *yield = tempcond == testfor;
return s; /* End of comparison conditions */
combined_cond = (cond_type == ECOND_AND);
while (isspace(*s)) s++;
- if (*s++ != '{') goto COND_FAILED_CURLY_START;
+ if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */
for (;;)
{
while (isspace(*s)) s++;
+ /* {-for-text-editors */
if (*s == '}') break;
- if (*s != '{')
+ if (*s != '{') /* }-for-text-editors */
{
expand_string_message = string_sprintf("each subcondition "
"inside an \"%s{...}\" condition must be in its own {}", name);
}
while (isspace(*s)) s++;
+ /* {-for-text-editors */
if (*s++ != '}')
{
+ /* {-for-text-editors */
expand_string_message = string_sprintf("missing } at end of condition "
"inside \"%s\" group", name);
return NULL;
uschar *save_iterate_item = iterate_item;
while (isspace(*s)) s++;
- if (*s++ != '{') goto COND_FAILED_CURLY_START;
+ if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */
sub[0] = expand_string_internal(s, TRUE, &s, (yield == NULL), TRUE);
if (sub[0] == NULL) return NULL;
+ /* {-for-text-editors */
if (*s++ != '}') goto COND_FAILED_CURLY_END;
while (isspace(*s)) s++;
- if (*s++ != '{') goto COND_FAILED_CURLY_START;
+ if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */
sub[1] = s;
}
while (isspace(*s)) s++;
+ /* {-for-text-editors */
if (*s++ != '}')
{
+ /* {-for-text-editors */
expand_string_message = string_sprintf("missing } at end of condition "
"inside \"%s\"", name);
return NULL;
size_t len;
BOOL boolvalue = FALSE;
while (isspace(*s)) s++;
- if (*s != '{') goto COND_FAILED_CURLY_START;
+ if (*s != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */
ourname = cond_type == ECOND_BOOL_LAX ? US"bool_lax" : US"bool";
switch(read_subs(sub_arg, 1, 1, &s, yield == NULL, FALSE, ourname))
{
switch(item_type)
{
+ /* Call an ACL from an expansion. We feed data in via $acl_arg1 - $acl_arg9.
+ 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}...} */
+ {
+ uschar *sub[10]; /* name + arg1-arg9 (which must match number of acl_arg[]) */
+ uschar *user_msg;
+
+ switch(read_subs(sub, 10, 1, &s, skipping, TRUE, US"acl"))
+ {
+ case 1: goto EXPAND_FAILED_CURLY;
+ case 2:
+ case 3: goto EXPAND_FAILED;
+ }
+ if (skipping) continue;
+
+ switch(eval_acl(sub, sizeof(sub)/sizeof(*sub), &user_msg))
+ {
+ case OK:
+ case FAIL:
+ if (user_msg)
+ yield = string_cat(yield, &size, &ptr, user_msg, Ustrlen(user_msg));
+ continue;
+
+ case DEFER:
+ expand_string_forcedfail = TRUE;
+ default:
+ expand_string_message = string_sprintf("error from acl \"%s\"", 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
continue;
}
+ /* count the number of list elements */
+
+ case EOP_LISTCOUNT:
+ {
+ int cnt = 0;
+ int sep = 0;
+ uschar * cp;
+ uschar buffer[256];
+
+ while (string_nextinlist(&sub, &sep, buffer, sizeof(buffer)) != NULL) cnt++;
+ cp = string_sprintf("%d", cnt);
+ yield = string_cat(yield, &size, &ptr, cp, Ustrlen(cp));
+ continue;
+ }
+
/* expand a named list given the name */
- /* handles nested named lists but will confuse the separators in the result */
+ /* handles nested named lists; requotes as colon-sep list */
- case EOP_LIST:
+ case EOP_LISTNAMED:
{
tree_node *t = NULL;
uschar * list;
goto EXPAND_FAILED;
}
- if (skipping) continue;
list = ((namedlist_block *)(t->data.ptr))->string;
while ((item = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
if (*item == '+') /* list item is itself a named list */
{
- uschar * sub = string_sprintf("${list%s:%s}", suffix, item);
+ uschar * sub = string_sprintf("${listnamed%s:%s}", suffix, item);
item = expand_string_internal(sub, FALSE, NULL, FALSE, TRUE);
}
else if (sep != ':') /* item from non-colon-sep list, re-quote for colon list-separator */
continue;
}
- /* count the number of list elements */
-
- case EOP_NLIST:
- {
- int cnt = 0;
- int sep = 0;
- uschar * cp;
- uschar buffer[256];
-
- while (string_nextinlist(&sub, &sep, buffer, sizeof(buffer)) != NULL) cnt++;
- cp = string_sprintf("%d", cnt);
- yield = string_cat(yield, &size, &ptr, cp, Ustrlen(cp));
- continue;
- }
-
/* mask applies a mask to an IP address; for example the result of
${mask:131.111.10.206/28} is 131.111.10.192/28. */