* Exim - an Internet mail transport agent *
*************************************************/
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 - 2021 */
/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Code for mail filtering functions. */
/* Build a condition block from the specific word. */
- c = store_get(sizeof(condition_block), FALSE);
+ c = store_get(sizeof(condition_block), GET_UNTAINTED);
c->left.u = c->right.u = NULL;
c->testfor = testfor;
testfor = TRUE;
}
ptr = nextitem(ptr, buffer, sizeof(buffer), TRUE);
if (*error_pointer) break;
- aa = store_get(sizeof(string_item), FALSE);
+ aa = store_get(sizeof(string_item), GET_UNTAINTED);
aa->text = string_copy(buffer);
aa->next = c->left.a;
c->left.a = aa;
else
{
- const uschar *saveptr = ptr;
+// const uschar *saveptr = ptr;
ptr = nextword(ptr, buffer, sizeof(buffer), FALSE);
if (*error_pointer) break;
{
// if (toplevel) *saveptr = 0;
// else
- if (!toplevel)
- *error_pointer = string_sprintf("missing \")\" at end of "
+ if (!toplevel)
+ *error_pointer = string_sprintf("missing \")\" at end of "
"condition near line %d of filter file", line_number);
break;
}
else if (Ustrcmp(buffer, "and") == 0)
{
- condition_block *andc = store_get(sizeof(condition_block), FALSE);
+ condition_block * andc = store_get(sizeof(condition_block), GET_UNTAINTED);
andc->parent = current_parent;
andc->type = cond_and;
andc->testfor = TRUE;
else if (Ustrcmp(buffer, "or") == 0)
{
- condition_block *orc = store_get(sizeof(condition_block), FALSE);
- condition_block *or_parent = NULL;
+ condition_block * orc = store_get(sizeof(condition_block), GET_UNTAINTED);
+ condition_block * or_parent = NULL;
if (current_parent)
{
as brackets are allowed in conditions and users will expect not to require
white space here. */
+*buffer = '\0'; /* compiler quietening */
+
if (Ustrncmp(ptr, "if(", 3) == 0)
{
Ustrcpy(buffer, US"if");
FALSE for logging commands, and it doesn't matter for testprint, as
that doesn't change the "delivered" status. */
- if (*error_pointer) yield = FALSE; else
+ if (*error_pointer) yield = FALSE;
+ else
{
- new = store_get(sizeof(filter_cmd) + sizeof(union argtypes), FALSE);
+ new = store_get(sizeof(filter_cmd) + sizeof(union argtypes), GET_UNTAINTED);
new->next = NULL;
**lastcmdptr = new;
*lastcmdptr = &(new->next);
/* Finish has no arguments; fmsg defaults to NULL */
case finish_command:
- new = store_get(sizeof(filter_cmd), FALSE);
+ new = store_get(sizeof(filter_cmd), GET_UNTAINTED);
new->next = NULL;
**lastcmdptr = new;
*lastcmdptr = &(new->next);
new->command = command;
- new->seen = seen_force? seen_value : FALSE;
+ new->seen = seen_force ? seen_value : FALSE;
new->args[0].u = fmsg;
break;
/* Set up the command block for if */
- new = store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), FALSE);
+ new = store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), GET_UNTAINTED);
new->next = NULL;
**lastcmdptr = new;
*lastcmdptr = &new->next;
while (had_else_endif == had_elif)
{
filter_cmd *newnew =
- store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), FALSE);
+ store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), GET_UNTAINTED);
new->args[2].f = newnew;
new = newnew;
new->next = NULL;
case mail_command:
case vacation_command:
- new = store_get(sizeof(filter_cmd) + mailargs_total * sizeof(union argtypes), FALSE);
+ new = store_get(sizeof(filter_cmd) + mailargs_total * sizeof(union argtypes), GET_UNTAINTED);
new->next = NULL;
new->command = command;
- new->seen = seen_force? seen_value : FALSE;
+ new->seen = seen_force ? seen_value : FALSE;
new->noerror = noerror_force;
for (i = 0; i < mailargs_total; i++) new->args[i].u = NULL;
if (command == vacation_command)
{
- if (new->args[mailarg_index_file].u == NULL)
+ if (!new->args[mailarg_index_file].u)
{
new->args[mailarg_index_file].u = string_copy(US".vacation.msg");
new->args[mailarg_index_expand].u = US""; /* not NULL => TRUE */
}
- if (new->args[mailarg_index_log].u == NULL)
+ if (!new->args[mailarg_index_log].u)
new->args[mailarg_index_log].u = string_copy(US".vacation.log");
- if (new->args[mailarg_index_once].u == NULL)
+ if (!new->args[mailarg_index_once].u)
new->args[mailarg_index_once].u = string_copy(US".vacation");
- if (new->args[mailarg_index_once_repeat].u == NULL)
+ if (!new->args[mailarg_index_once_repeat].u)
new->args[mailarg_index_once_repeat].u = string_copy(US"7d");
- if (new->args[mailarg_index_subject].u == NULL)
+ if (!new->args[mailarg_index_subject].u)
new->args[mailarg_index_subject].u = string_copy(US"On vacation");
}
*/
static BOOL
-test_condition(condition_block *c, BOOL toplevel)
+test_condition(condition_block * c, BOOL toplevel)
{
-BOOL yield = FALSE;
-const uschar *exp[2], * p, * pp;
+BOOL yield = FALSE, textonly_re;
+const uschar * exp[2], * p, * pp;
int val[2];
-int i;
-if (c == NULL) return TRUE; /* does this ever occur? */
+if (!c) return TRUE; /* does this ever occur? */
switch (c->type)
{
case cond_and:
- yield = test_condition(c->left.c, FALSE) &&
- *error_pointer == NULL &&
- test_condition(c->right.c, FALSE);
- break;
+ yield = test_condition(c->left.c, FALSE) &&
+ *error_pointer == NULL &&
+ test_condition(c->right.c, FALSE);
+ break;
case cond_or:
- yield = test_condition(c->left.c, FALSE) ||
- (*error_pointer == NULL &&
- test_condition(c->right.c, FALSE));
- break;
+ yield = test_condition(c->left.c, FALSE) ||
+ (*error_pointer == NULL &&
+ test_condition(c->right.c, FALSE));
+ break;
- /* The personal test is meaningless in a system filter. The tests are now in
- a separate function (so Sieve can use them). However, an Exim filter does not
- scan Cc: (hence the FALSE argument). */
+ /* The personal test is meaningless in a system filter. The tests are now in
+ a separate function (so Sieve can use them). However, an Exim filter does not
+ scan Cc: (hence the FALSE argument). */
case cond_personal:
- yield = f.system_filtering? FALSE : filter_personal(c->left.a, FALSE);
- break;
+ yield = f.system_filtering? FALSE : filter_personal(c->left.a, FALSE);
+ break;
case cond_delivered:
- yield = filter_delivered;
- break;
+ yield = filter_delivered;
+ break;
- /* Only TRUE if a message is actually being processed; FALSE for address
- testing and verification. */
+ /* Only TRUE if a message is actually being processed; FALSE for address
+ testing and verification. */
case cond_errormsg:
- yield = message_id[0] != 0 &&
- (sender_address == NULL || sender_address[0] == 0);
- break;
+ yield = message_id[0] != 0 &&
+ (sender_address == NULL || sender_address[0] == 0);
+ break;
- /* Only FALSE if a message is actually being processed; TRUE for address
- and filter testing and verification. */
+ /* Only FALSE if a message is actually being processed; TRUE for address
+ and filter testing and verification. */
case cond_firsttime:
- yield = filter_test != FTEST_NONE || message_id[0] == 0 || f.deliver_firsttime;
- break;
+ yield = filter_test != FTEST_NONE || message_id[0] == 0 || f.deliver_firsttime;
+ break;
- /* Only TRUE if a message is actually being processed; FALSE for address
- testing and verification. */
+ /* Only TRUE if a message is actually being processed; FALSE for address
+ testing and verification. */
case cond_manualthaw:
- yield = message_id[0] != 0 && f.deliver_manual_thaw;
- break;
+ yield = message_id[0] != 0 && f.deliver_manual_thaw;
+ break;
- /* The foranyaddress condition loops through a list of addresses */
+ /* The foranyaddress condition loops through a list of addresses */
case cond_foranyaddress:
- p = c->left.u;
- if (!(pp = expand_cstring(p)))
- {
- *error_pointer = string_sprintf("failed to expand \"%s\" in "
- "filter file: %s", p, expand_string_message);
- return FALSE;
- }
+ p = c->left.u;
+ if (!(pp = expand_cstring(p)))
+ {
+ *error_pointer = string_sprintf("failed to expand \"%s\" in "
+ "filter file: %s", p, expand_string_message);
+ return FALSE;
+ }
- yield = FALSE;
- f.parse_allow_group = TRUE; /* Allow group syntax */
+ yield = FALSE;
+ f.parse_allow_group = TRUE; /* Allow group syntax */
- while (*pp)
- {
- uschar *error;
- int start, end, domain;
- uschar * s;
+ while (*pp)
+ {
+ uschar *error;
+ int start, end, domain;
+ uschar * s;
- p = parse_find_address_end(pp, FALSE);
- s = string_copyn(pp, p - pp);
+ p = parse_find_address_end(pp, FALSE);
+ s = string_copyn(pp, p - pp);
- filter_thisaddress =
- parse_extract_address(s, &error, &start, &end, &domain, FALSE);
+ filter_thisaddress =
+ parse_extract_address(s, &error, &start, &end, &domain, FALSE);
- if (filter_thisaddress)
- {
- if ((filter_test != FTEST_NONE && debug_selector != 0) ||
- (debug_selector & D_filter) != 0)
- {
- indent();
- debug_printf_indent("Extracted address %s\n", filter_thisaddress);
- }
- yield = test_condition(c->right.c, FALSE);
- }
+ if (filter_thisaddress)
+ {
+ if ((filter_test != FTEST_NONE && debug_selector != 0) ||
+ (debug_selector & D_filter) != 0)
+ {
+ indent();
+ debug_printf_indent("Extracted address %s\n", filter_thisaddress);
+ }
+ yield = test_condition(c->right.c, FALSE);
+ }
- if (yield) break;
- if (!*p) break;
- pp = p + 1;
- }
+ if (yield) break;
+ if (!*p) break;
+ pp = p + 1;
+ }
- f.parse_allow_group = FALSE; /* Reset group syntax flags */
- f.parse_found_group = FALSE;
- break;
+ f.parse_allow_group = FALSE; /* Reset group syntax flags */
+ f.parse_found_group = FALSE;
+ break;
- /* All other conditions have left and right values that need expanding;
- on error, it doesn't matter what value is returned. */
+ /* All other conditions have left and right values that need expanding;
+ on error, it doesn't matter what value is returned. */
- default:
- p = c->left.u;
- for (i = 0; i < 2; i++)
- {
- if (!(exp[i] = expand_cstring(p)))
+ default:
+ p = c->left.u;
+ for (int i = 0; i < 2; i++)
{
- *error_pointer = string_sprintf("failed to expand \"%s\" in "
- "filter file: %s", p, expand_string_message);
- return FALSE;
+ if (!(exp[i] = expand_string_2(p, &textonly_re)))
+ {
+ *error_pointer = string_sprintf("failed to expand \"%s\" in "
+ "filter file: %s", p, expand_string_message);
+ return FALSE;
+ }
+ p = c->right.u;
}
- p = c->right.u;
- }
-
- /* Inner switch for the different cases */
-
- switch(c->type)
- {
- case cond_is:
- yield = strcmpic(exp[0], exp[1]) == 0;
- break;
- case cond_IS:
- yield = Ustrcmp(exp[0], exp[1]) == 0;
- break;
+ /* Inner switch for the different cases */
- case cond_contains:
- yield = strstric(exp[0], exp[1], FALSE) != NULL;
- break;
+ switch(c->type)
+ {
+ case cond_is:
+ yield = strcmpic(exp[0], exp[1]) == 0;
+ break;
- case cond_CONTAINS:
- yield = Ustrstr(exp[0], exp[1]) != NULL;
- break;
+ case cond_IS:
+ yield = Ustrcmp(exp[0], exp[1]) == 0;
+ break;
- case cond_begins:
- yield = strncmpic(exp[0], exp[1], Ustrlen(exp[1])) == 0;
- break;
+ case cond_contains:
+ yield = strstric_c(exp[0], exp[1], FALSE) != NULL;
+ break;
- case cond_BEGINS:
- yield = Ustrncmp(exp[0], exp[1], Ustrlen(exp[1])) == 0;
- break;
+ case cond_CONTAINS:
+ yield = Ustrstr(exp[0], exp[1]) != NULL;
+ break;
- case cond_ends:
- case cond_ENDS:
- {
- int len = Ustrlen(exp[1]);
- const uschar *s = exp[0] + Ustrlen(exp[0]) - len;
- yield = s < exp[0]
- ? FALSE
- : (c->type == cond_ends ? strcmpic(s, exp[1]) : Ustrcmp(s, exp[1])) == 0;
- }
- break;
+ case cond_begins:
+ yield = strncmpic(exp[0], exp[1], Ustrlen(exp[1])) == 0;
+ break;
- case cond_matches:
- case cond_MATCHES:
- {
- const pcre2_code *re;
- int err;
- PCRE2_SIZE offset;
+ case cond_BEGINS:
+ yield = Ustrncmp(exp[0], exp[1], Ustrlen(exp[1])) == 0;
+ break;
- if ((filter_test != FTEST_NONE && debug_selector != 0) ||
- (debug_selector & D_filter) != 0)
+ case cond_ends:
+ case cond_ENDS:
{
- debug_printf_indent("Match expanded arguments:\n");
- debug_printf_indent(" Subject = %s\n", exp[0]);
- debug_printf_indent(" Pattern = %s\n", exp[1]);
+ int len = Ustrlen(exp[1]);
+ const uschar *s = exp[0] + Ustrlen(exp[0]) - len;
+ yield = s < exp[0]
+ ? FALSE
+ : (c->type == cond_ends ? strcmpic(s, exp[1]) : Ustrcmp(s, exp[1])) == 0;
+ break;
}
- if (!(re = pcre2_compile((PCRE2_SPTR)exp[1], PCRE2_ZERO_TERMINATED,
- PCRE_COPT | (c->type == cond_matches ? PCRE2_CASELESS : 0),
- &err, &offset, pcre_cmp_ctx)))
+ case cond_matches:
+ case cond_MATCHES:
{
- uschar errbuf[128];
- pcre2_get_error_message(err, errbuf, sizeof(errbuf));
- *error_pointer = string_sprintf("error while compiling "
- "regular expression \"%s\": %s at offset %ld",
- exp[1], errbuf, (long)offset);
- return FALSE;
- }
+ const pcre2_code * re;
+ mcs_flags flags = textonly_re ? MCS_CACHEABLE : MCS_NOFLAGS;
- yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1);
- break;
- }
+ if ((filter_test != FTEST_NONE && debug_selector != 0) ||
+ (debug_selector & D_filter) != 0)
+ {
+ debug_printf_indent("Match expanded arguments:\n");
+ debug_printf_indent(" Subject = %s\n", exp[0]);
+ debug_printf_indent(" Pattern = %s\n", exp[1]);
+ }
- /* For above and below, convert the strings to numbers */
+ if (c->type == cond_matches) flags |= MCS_CASELESS;
+ if (!(re = regex_compile(exp[1], flags, error_pointer, pcre_gen_cmp_ctx)))
+ return FALSE;
- case cond_above:
- case cond_below:
- for (i = 0; i < 2; i++)
- {
- val[i] = get_number(exp[i], &yield);
- if (!yield)
- {
- *error_pointer = string_sprintf("malformed numerical string \"%s\"",
- exp[i]);
- return FALSE;
- }
+ yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1);
+ break;
+ }
+
+ /* For above and below, convert the strings to numbers */
+
+ case cond_above:
+ case cond_below:
+ for (int i = 0; i < 2; i++)
+ {
+ val[i] = get_number(exp[i], &yield);
+ if (!yield)
+ {
+ *error_pointer = string_sprintf("malformed numerical string \"%s\"",
+ exp[i]);
+ return FALSE;
+ }
+ }
+ yield = c->type == cond_above ? (val[0] > val[1]) : (val[0] < val[1]);
+ break;
}
- yield = (c->type == cond_above)? (val[0] > val[1]) : (val[0] < val[1]);
break;
- }
- break;
}
if ((filter_test != FTEST_NONE && debug_selector != 0) ||
if (expand_nmax >= 0 || filter_thisaddress != NULL)
{
int ecount = expand_nmax >= 0 ? expand_nmax : -1;
- uschar **ss = store_get(sizeof(uschar *) * (ecount + 3), FALSE);
+ uschar ** ss = store_get(sizeof(uschar *) * (ecount + 3), GET_UNTAINTED);
addr->pipe_expandn = ss;
if (!filter_thisaddress) filter_thisaddress = US"";
addr->next = *generated;
*generated = addr;
- addr->reply = store_get(sizeof(reply_item), FALSE);
+ addr->reply = store_get(sizeof(reply_item), GET_UNTAINTED);
addr->reply->from = NULL;
addr->reply->to = string_copy(to);
addr->reply->file_expand =
commands = commands->next;
}
-return filter_delivered? FF_DELIVERED : FF_NOTDELIVERED;
+return filter_delivered ? FF_DELIVERED : FF_NOTDELIVERED;
}