* Exim - an Internet mail transport agent *
*************************************************/
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Code for mail filtering functions. */
struct condition_block *c;
struct filter_cmd *f;
int i;
- uschar *u;
+ const uschar *u;
};
/* Local structures used in this module */
/* Miscellaneous other declarations */
static uschar **error_pointer;
-static uschar *log_filename;
+static const uschar *log_filename;
static int filter_options;
static int line_number;
static int expect_endif;
enum { had_neither, had_else, had_elif, had_endif };
-static BOOL read_command_list(uschar **, filter_cmd ***, BOOL);
+static BOOL read_command_list(const uschar **, filter_cmd ***, BOOL);
/* The string arguments for the mail command. The header line ones (that are
Returns: pointer to next non-whitespace character
*/
-static uschar *
-nextsigchar(uschar *ptr, BOOL comment_allowed)
+static const uschar *
+nextsigchar(const uschar *ptr, BOOL comment_allowed)
{
for (;;)
{
Returns: pointer to the next significant character after the word
*/
-static uschar *
-nextword(uschar *ptr, uschar *buffer, int size, BOOL bracket)
+static const uschar *
+nextword(const uschar *ptr, uschar *buffer, int size, BOOL bracket)
{
uschar *bp = buffer;
while (*ptr != 0 && !isspace(*ptr) &&
Returns: the next significant character after the item
*/
-static uschar *
-nextitem(uschar *ptr, uschar *buffer, int size, BOOL bracket)
+static const uschar *
+nextitem(const uschar *ptr, uschar *buffer, int size, BOOL bracket)
{
uschar *bp = buffer;
if (*ptr != '\"') return nextword(ptr, buffer, size, bracket);
-while (*(++ptr) != 0 && *ptr != '\"' && *ptr != '\n')
+while (*++ptr && *ptr != '\"' && *ptr != '\n')
{
if (bp - buffer >= size - 1)
{
{
if (isspace(ptr[1])) /* \<whitespace>NL<whitespace> ignored */
{
- uschar *p = ptr + 1;
+ const uschar *p = ptr + 1;
while (*p != '\n' && isspace(*p)) p++;
if (*p == '\n')
{
*/
static int
-get_number(uschar *s, BOOL *ok)
+get_number(const uschar *s, BOOL *ok)
{
int value, count;
*ok = FALSE;
Returns: points to next character after "then"
*/
-static uschar *
-read_condition(uschar *ptr, condition_block **cond, BOOL toplevel)
+static const uschar *
+read_condition(const uschar *ptr, condition_block **cond, BOOL toplevel)
{
uschar buffer[1024];
BOOL testfor = TRUE;
/* reaching the end of the input is an error. */
- if (*ptr == 0)
+ if (!*ptr)
{
*error_pointer = US"\"then\" missing at end of filter file";
break;
else
{
ptr = nextitem(ptr, buffer, sizeof(buffer), TRUE);
- if (*error_pointer != NULL) break;
+ if (*error_pointer) break;
/* "Then" at the start of a condition is an error */
/* 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;
for (;;)
{
string_item *aa;
- uschar *saveptr = ptr;
+ const uschar * saveptr = ptr;
ptr = nextword(ptr, buffer, sizeof(buffer), TRUE);
if (*error_pointer) break;
if (Ustrcmp(buffer, "alias") != 0)
}
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
{
int i;
- uschar *isptr = NULL;
+ const uschar *isptr = NULL;
c->left.u = string_copy(buffer);
ptr = nextword(ptr, buffer, sizeof(buffer), TRUE);
else
{
- uschar *saveptr = ptr;
+// const uschar *saveptr = ptr;
ptr = nextword(ptr, buffer, sizeof(buffer), FALSE);
if (*error_pointer) break;
/* "Then" terminates a toplevel condition; otherwise a closing bracket
has been omitted. Put a string terminator at the start of "then" so
that reflecting the condition can be done when testing. */
+ /*XXX This stops us doing a constification job in this file, unfortunately.
+ Comment it out and see if anything breaks.
+ With one addition down at DEFERFREEZEFAIL it passes the testsuite. */
if (Ustrcmp(buffer, "then") == 0)
{
- if (toplevel) *saveptr = 0;
- else *error_pointer = string_sprintf("missing \")\" at end of "
+// if (toplevel) *saveptr = 0;
+// else
+ 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)
{
*/
static BOOL
-read_command(uschar **pptr, filter_cmd ***lastcmdptr)
+read_command(const uschar **pptr, filter_cmd ***lastcmdptr)
{
int command, i, cmd_bit;
filter_cmd *new, **newlastcmdptr;
BOOL was_seen_or_unseen = FALSE;
BOOL was_noerror = FALSE;
uschar buffer[1024];
-uschar *ptr = *pptr;
-uschar *saveptr;
+const uschar *ptr = *pptr;
+const uschar *saveptr;
uschar *fmsg = NULL;
/* Read the next word and find which command it is. Command words are normally
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");
else
{
ptr = nextword(ptr, buffer, sizeof(buffer), FALSE);
- if (*error_pointer != NULL) return FALSE;
+ if (*error_pointer) return FALSE;
}
for (command = 0; command < command_list_count; command++)
case testprint_command:
ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE);
- if (*buffer == 0)
+ if (!*buffer)
*error_pointer = string_sprintf("\"%s\" requires an argument "
"near line %d of filter file", command_list[command], line_number);
- if (*error_pointer != NULL) yield = FALSE; else
+ if (*error_pointer) yield = FALSE; else
{
union argtypes argument, second_argument;
{
argument.u = string_copy(buffer);
ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE);
- if (*buffer == 0 || Ustrcmp(buffer, "to") != 0)
+ if (!*buffer || Ustrcmp(buffer, "to") != 0)
*error_pointer = string_sprintf("\"to\" expected in \"add\" command "
"near line %d of filter file", line_number);
else
{
ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE);
- if (*buffer == 0)
+ if (!*buffer)
*error_pointer = string_sprintf("value missing after \"to\" "
"near line %d of filter file", line_number);
else second_argument.u = string_copy(buffer);
if (yield)
{
ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE);
- if (*buffer == 0)
+ if (!*buffer)
*error_pointer = string_sprintf("value missing after \"add\", "
"\"remove\", or \"charset\" near line %d of filter file",
line_number);
else if (command == deliver_command)
{
- uschar *save_ptr = ptr;
+ const uschar *save_ptr = ptr;
ptr = nextword(ptr, buffer, sizeof(buffer), FALSE);
if (Ustrcmp(buffer, "errors_to") == 0)
{
FALSE for logging commands, and it doesn't matter for testprint, as
that doesn't change the "delivered" status. */
- if (*error_pointer != NULL) 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);
saveptr = ptr;
ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE);
- if (*saveptr != '\"' && (*buffer == 0 || Ustrcmp(buffer, "text") != 0))
+ if (*saveptr != '\"' && (!*buffer || Ustrcmp(buffer, "text") != 0))
{
ptr = saveptr;
fmsg = US"";
/* 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);
+ *lastcmdptr = &new->next;
new->command = command;
new->seen = FALSE;
new->args[0].u = NULL;
/* Read the condition */
- ptr = read_condition(ptr, &(new->args[0].c), TRUE);
- if (*error_pointer != NULL) { yield = FALSE; break; }
+ ptr = read_condition(ptr, &new->args[0].c, TRUE);
+ if (*error_pointer) { yield = FALSE; break; }
/* Read the commands to be obeyed if the condition is true */
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;
new->args[1].u = new->args[2].u = NULL;
new->args[3].u = ptr;
- ptr = read_condition(ptr, &(new->args[0].c), TRUE);
- if (*error_pointer != NULL) { yield = FALSE; break; }
+ ptr = read_condition(ptr, &new->args[0].c, TRUE);
+ if (*error_pointer) { yield = FALSE; break; }
newlastcmdptr = &(new->args[1].f);
if (!read_command_list(&ptr, &newlastcmdptr, TRUE))
yield = FALSE;
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;
for (;;)
{
- uschar *saveptr = ptr;
+ const uschar *saveptr = ptr;
ptr = nextword(ptr, buffer, sizeof(buffer), FALSE);
- if (*error_pointer != NULL)
- {
- yield = FALSE;
- break;
- }
+ if (*error_pointer)
+ { yield = FALSE; break; }
/* Ensure "return" is followed by "message"; that's a complete option */
/* Found keyword, read the data item */
ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE);
- if (*error_pointer != NULL)
- {
- yield = FALSE;
- break;
- }
+ if (*error_pointer)
+ { yield = FALSE; break; }
else new->args[i].u = string_copy(buffer);
}
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");
}
case seen_command:
case unseen_command:
- if (*ptr == 0)
+ if (!*ptr)
{
*error_pointer = string_sprintf("\"seen\" or \"unseen\" "
"near line %d is not followed by a command", line_number);
/* So does noerror */
case noerror_command:
- if (*ptr == 0)
+ if (!*ptr)
{
*error_pointer = string_sprintf("\"noerror\" "
"near line %d is not followed by a command", line_number);
*/
static BOOL
-read_command_list(uschar **pptr, filter_cmd ***lastcmdptr, BOOL conditional)
+read_command_list(const uschar **pptr, filter_cmd ***lastcmdptr, BOOL conditional)
{
if (conditional) expect_endif++;
had_else_endif = had_neither;
-while (**pptr != 0 && had_else_endif == had_neither)
+while (**pptr && had_else_endif == had_neither)
{
if (!read_command(pptr, lastcmdptr)) return FALSE;
*pptr = nextsigchar(*pptr, TRUE);
*/
static BOOL
-test_condition(condition_block *c, BOOL toplevel)
+test_condition(condition_block * c, BOOL toplevel)
{
-BOOL yield = FALSE;
-const pcre *re;
-uschar *exp[2], *p, *pp;
-const uschar *regcomp_error = NULL;
-int regcomp_error_offset;
+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;
- pp = expand_string(p);
- if (pp == NULL)
- {
- *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 != 0)
- {
- uschar *error;
- int start, end, domain;
- int saveend;
+ while (*pp)
+ {
+ uschar *error;
+ int start, end, domain;
+ uschar * s;
- p = parse_find_address_end(pp, FALSE);
- saveend = *p;
+ p = parse_find_address_end(pp, FALSE);
+ s = string_copyn(pp, p - pp);
- *p = 0;
- filter_thisaddress =
- parse_extract_address(pp, &error, &start, &end, &domain, FALSE);
- *p = saveend;
+ filter_thisaddress =
+ parse_extract_address(s, &error, &start, &end, &domain, FALSE);
- if (filter_thisaddress != NULL)
- {
- 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 (saveend == 0) 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++)
- {
- exp[i] = expand_string(p);
- if (exp[i] == NULL)
+ 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 */
+ /* Inner switch for the different cases */
- switch(c->type)
- {
- case cond_is:
- yield = strcmpic(exp[0], exp[1]) == 0;
- break;
+ 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;
+ case cond_IS:
+ yield = Ustrcmp(exp[0], exp[1]) == 0;
+ break;
- case cond_contains:
- yield = strstric(exp[0], exp[1], FALSE) != NULL;
- break;
+ case cond_contains:
+ yield = strstric_c(exp[0], exp[1], FALSE) != NULL;
+ break;
- case cond_CONTAINS:
- yield = Ustrstr(exp[0], exp[1]) != NULL;
- break;
+ case cond_CONTAINS:
+ yield = Ustrstr(exp[0], exp[1]) != NULL;
+ break;
- case cond_begins:
- yield = strncmpic(exp[0], exp[1], Ustrlen(exp[1])) == 0;
- break;
+ case cond_begins:
+ yield = strncmpic(exp[0], exp[1], Ustrlen(exp[1])) == 0;
+ break;
- case cond_BEGINS:
- yield = Ustrncmp(exp[0], exp[1], Ustrlen(exp[1])) == 0;
- break;
+ case cond_BEGINS:
+ yield = Ustrncmp(exp[0], exp[1], Ustrlen(exp[1])) == 0;
+ break;
- case cond_ends:
- case cond_ENDS:
- {
- int len = Ustrlen(exp[1]);
- 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_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_matches:
- case cond_MATCHES:
- 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]);
- }
+ case cond_matches:
+ case cond_MATCHES:
+ {
+ const pcre2_code * re;
+ mcs_flags flags = textonly_re ? MCS_CACHEABLE : MCS_NOFLAGS;
- re = pcre_compile(CS exp[1],
- PCRE_COPT | ((c->type == cond_matches)? PCRE_CASELESS : 0),
- (const char **)®comp_error, ®comp_error_offset, NULL);
+ 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]);
+ }
- if (re == NULL)
- {
- *error_pointer = string_sprintf("error while compiling "
- "regular expression \"%s\": %s at offset %d",
- exp[1], regcomp_error, regcomp_error_offset);
- return FALSE;
- }
+ if (c->type == cond_matches) flags |= MCS_CASELESS;
+ if (!(re = regex_compile(exp[1], flags, error_pointer, pcre_gen_cmp_ctx)))
+ return FALSE;
- yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1);
- break;
+ yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1);
+ break;
+ }
- /* For above and below, convert the strings to numbers */
+ /* For above and below, convert the strings to numbers */
- 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;
- }
+ 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) ||
static int
interpret_commands(filter_cmd *commands, address_item **generated)
{
-uschar *s;
+const uschar *s;
int mode;
address_item *addr;
BOOL condition_value;
{
int ff_ret;
uschar *fmsg, *ff_name;
- uschar *expargs[MAILARGS_STRING_COUNT];
+ const uschar *expargs[MAILARGS_STRING_COUNT];
int i, n[2];
for (i = 0; i < (command_exparg_count[commands->command] & 15); i++)
{
- uschar *ss = commands->args[i].u;
+ const uschar *ss = commands->args[i].u;
if (!ss)
expargs[i] = NULL;
- else
- if (!(expargs[i] = expand_string(ss)))
- {
- *error_pointer = string_sprintf("failed to expand \"%s\" in "
- "%s command: %s", ss, command_list[commands->command],
- expand_string_message);
- return FF_ERROR;
- }
+ else if (!(expargs[i] = expand_cstring(ss)))
+ {
+ *error_pointer = string_sprintf("failed to expand \"%s\" in "
+ "%s command: %s", ss, command_list[commands->command],
+ expand_string_message);
+ return FF_ERROR;
+ }
}
/* Now switch for each command, setting the "delivered" flag if any of them
case add_command:
for (i = 0; i < 2; i++)
{
- uschar *ss = expargs[i];
+ const uschar *ss = expargs[i];
uschar *end;
if (i == 1 && (*ss++ != 'n' || ss[1] != 0))
uschar *error;
uschar *ss = parse_extract_address(s, &error, &start, &end, &domain,
FALSE);
- if (ss != NULL)
- expargs[i] = ((filter_options & RDO_REWRITE) != 0)?
- rewrite_address(ss, TRUE, FALSE, global_rewrite_rules,
- rewrite_existflags) :
- rewrite_address_qualify(ss, TRUE);
+ if (ss)
+ expargs[i] = filter_options & RDO_REWRITE
+ ? rewrite_address(ss, TRUE, FALSE, global_rewrite_rules,
+ rewrite_existflags)
+ : rewrite_address_qualify(ss, TRUE);
else
{
*error_pointer = string_sprintf("malformed address \"%s\" in "
af_ignore_error flag if necessary, and the errors address, which can be
set in a system filter and to the local address in user filters. */
- addr = deliver_make_addr(expargs[0], TRUE); /* TRUE => copy s */
- addr->prop.errors_address = (s == NULL)?
- s : string_copy(s); /* Default is NULL */
+ addr = deliver_make_addr(US expargs[0], TRUE); /* TRUE => copy s, so deconst ok */
+ addr->prop.errors_address = !s ? NULL : string_copy(s); /* Default is NULL */
if (commands->noerror) addr->prop.ignore_error = TRUE;
addr->next = *generated;
*generated = addr;
af_pfr and af_file flags, the af_ignore_error flag if necessary, and the
mode value. */
- addr = deliver_make_addr(s, TRUE); /* TRUE => copy s */
+ addr = deliver_make_addr(US s, TRUE); /* TRUE => copy s, so deconst ok */
setflag(addr, af_pfr);
setflag(addr, af_file);
if (commands->noerror) addr->prop.ignore_error = TRUE;
each command argument is expanded in the transport after the command
has been split up into separate arguments. */
- addr = deliver_make_addr(s, TRUE); /* TRUE => copy s */
+ addr = deliver_make_addr(US s, TRUE); /* TRUE => copy s, so deconst ok */
setflag(addr, af_pfr);
setflag(addr, af_expand_pipe);
if (commands->noerror) addr->prop.ignore_error = TRUE;
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"";
(long int)geteuid());
if (log_fd < 0)
{
- if (log_filename == NULL)
+ if (!log_filename)
{
*error_pointer = US"attempt to obey \"logwrite\" command "
"without a previous \"logfile\"";
log_fd = Uopen(log_filename, O_CREAT|O_APPEND|O_WRONLY, log_mode);
if (log_fd < 0)
{
- *error_pointer = string_open_failed(errno, "filter log file \"%s\"",
+ *error_pointer = string_open_failed("filter log file \"%s\"",
log_filename);
return FF_ERROR;
}
}
}
else
- {
- DEBUG(D_filter) debug_printf_indent("skipping logwrite (verifying or testing)\n");
- }
+ DEBUG(D_filter)
+ debug_printf_indent("skipping logwrite (verifying or testing)\n");
break;
/* Header addition and removal is available only in the system filter. The
s = expargs[0];
if (filter_test != FTEST_NONE)
- printf("Headers %s \"%s\"\n", (subtype == TRUE)? "add" :
- (subtype == FALSE)? "remove" : "charset", string_printing(s));
+ printf("Headers %s \"%s\"\n",
+ subtype == TRUE ? "add"
+ : subtype == FALSE ? "remove"
+ : "charset",
+ string_printing(s));
if (subtype == TRUE)
{
while (isspace(*s)) s++;
- if (s[0] != 0)
+ if (*s)
{
- header_add(htype_other, "%s%s", s, (s[Ustrlen(s)-1] == '\n')?
- "" : "\n");
+ header_add(htype_other, "%s%s", s,
+ s[Ustrlen(s)-1] == '\n' ? "" : "\n");
header_last->type = header_checkname(header_last, FALSE);
if (header_last->type >= 'a') header_last->type = htype_other;
}
else if (subtype == FALSE)
{
int sep = 0;
- uschar *ss;
- const uschar *list = s;
- uschar buffer[128];
- while ((ss = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))
- != NULL)
+ const uschar * list = s;
+
+ for (uschar * ss; ss = string_nextinlist(&list, &sep, NULL, 0); )
header_remove(0, ss);
}
ff_name = US"freeze";
ff_ret = FF_FREEZE;
- DEFERFREEZEFAIL:
- fmsg = expargs[0];
- if (Ustrlen(fmsg) > 1024) Ustrcpy(fmsg + 1000, US" ... (truncated)");
- fmsg = US string_printing(fmsg);
- *error_pointer = fmsg;
+ DEFERFREEZEFAIL:
+ *error_pointer = fmsg = US string_printing(Ustrlen(expargs[0]) > 1024
+ ? string_sprintf("%.1000s ... (truncated)", expargs[0])
+ : string_copy(expargs[0]));
+ for(uschar * s = fmsg; *s; s++)
+ if (!s[1] && *s == '\n') { *s = '\0'; break; } /* drop trailing newline */
if (filter_test != FTEST_NONE)
{
indent();
printf("%c%s text \"%s\"\n", toupper(ff_name[0]), ff_name+1, fmsg);
}
- else DEBUG(D_filter) debug_printf_indent("Filter: %s \"%s\"\n", ff_name, fmsg);
+ else
+ DEBUG(D_filter) debug_printf_indent("Filter: %s \"%s\"\n", ff_name, fmsg);
return ff_ret;
case finish_command:
printf("%sinish\n", (commands->seen)? "Seen f" : "F");
}
else
- {
DEBUG(D_filter) debug_printf_indent("Filter: %sfinish\n",
- (commands->seen)? " Seen " : "");
- }
+ commands->seen ? " Seen " : "");
finish_obeyed = TRUE;
- return filter_delivered? FF_DELIVERED : FF_NOTDELIVERED;
+ return filter_delivered ? FF_DELIVERED : FF_NOTDELIVERED;
case if_command:
{
uschar *save_address = filter_thisaddress;
int ok = FF_DELIVERED;
condition_value = test_condition(commands->args[0].c, TRUE);
- if (*error_pointer != NULL) ok = FF_ERROR; else
+ if (*error_pointer)
+ ok = FF_ERROR;
+ else
{
output_indent += 2;
ok = interpret_commands(commands->args[condition_value? 1:2].f,
output_indent -= 2;
}
filter_thisaddress = save_address;
- if (finish_obeyed || (ok != FF_DELIVERED && ok != FF_NOTDELIVERED))
+ if (finish_obeyed || ok != FF_DELIVERED && ok != FF_NOTDELIVERED)
return ok;
}
break;
case mail_command:
case vacation_command:
- if (return_path == NULL || return_path[0] == 0)
+ if (!return_path || !*return_path)
{
if (filter_test != FTEST_NONE)
printf("%s command ignored because return_path is empty\n",
for (i = 0; i < MAILARGS_STRING_COUNT; i++)
{
- uschar *p;
- uschar *s = expargs[i];
+ const uschar *s = expargs[i];
- if (s == NULL) continue;
+ if (!s) continue;
- if (i != mailarg_index_text) for (p = s; *p != 0; p++)
+ if (i != mailarg_index_text) for (const uschar * p = s; *p; p++)
{
int c = *p;
if (i > mailarg_index_text)
else
{
- uschar *pp;
+ const uschar *pp;
for (pp = p + 1;; pp++)
{
c = *pp;
if (c == ':' && pp != p + 1) break;
- if (c == 0 || c == ':' || isspace(*pp))
+ if (!c || c == ':' || isspace(c))
{
*error_pointer = string_sprintf("\\n not followed by space or "
"valid header name in \"%.1024s\" in %s command",
if (filter_test != FTEST_NONE)
{
- uschar *to = commands->args[mailarg_index_to].u;
+ const uschar *to = commands->args[mailarg_index_to].u;
indent();
printf("%sail to: %s%s%s\n", (commands->seen)? "Seen m" : "M",
to ? to : US"<default>",
commands->noerror ? " (noerror)" : "");
for (i = 1; i < MAILARGS_STRING_COUNT; i++)
{
- uschar *arg = commands->args[i].u;
+ const uschar *arg = commands->args[i].u;
if (arg)
{
int len = Ustrlen(mailargs[i]);
}
else
{
- uschar *tt;
- uschar *to = commands->args[mailarg_index_to].u;
+ const uschar *tt;
+ const uschar *to = commands->args[mailarg_index_to].u;
gstring * log_addr = NULL;
if (!to) to = expand_string(US"$reply_address");
while (isspace(*to)) to++;
- for (tt = to; *tt != 0; tt++) /* Get rid of newlines */
- if (*tt == '\n') *tt = ' ';
+ for (tt = to; *tt; tt++) /* Get rid of newlines */
+ if (*tt == '\n')
+ {
+ uschar * s = string_copy(to);
+ for (uschar * ss = s; *ss; ss++)
+ if (*ss == '\n') *ss = ' ';
+ to = s;
+ break;
+ }
DEBUG(D_filter)
{
commands->noerror ? " (noerror)" : "");
for (i = 1; i < MAILARGS_STRING_COUNT; i++)
{
- uschar *arg = commands->args[i].u;
- if (arg != NULL)
+ const uschar *arg = commands->args[i].u;
+ if (arg)
{
int len = Ustrlen(mailargs[i]);
while (len++ < 15) debug_printf_indent(" ");
string gets too long. */
tt = to;
- while (*tt != 0)
+ while (*tt)
{
uschar *ss = parse_find_address_end(tt, FALSE);
uschar *recipient, *errmess;
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 =
for (i = 1; i < mailargs_string_passed; i++)
{
- uschar *ss = commands->args[i].u;
+ const uschar *ss = commands->args[i].u;
*(USS((US addr->reply) + reply_offsets[i])) =
ss ? string_copy(ss) : NULL;
}
commands = commands->next;
}
-return filter_delivered? FF_DELIVERED : FF_NOTDELIVERED;
+return filter_delivered ? FF_DELIVERED : FF_NOTDELIVERED;
}
BOOL
filter_personal(string_item *aliases, BOOL scan_cc)
{
-uschar *self, *self_from, *self_to;
-uschar *psself = NULL, *psself_from = NULL, *psself_to = NULL;
+const uschar *self, *self_from, *self_to;
+uschar *psself = NULL;
+const uschar *psself_from = NULL, *psself_to = NULL;
rmark reset_point = store_mark();
BOOL yield;
header_line *h;
for (h = header_list; h; h = h->next)
{
- uschar *s;
if (h->type == htype_old) continue;
if (strncmpic(h->text, US"List-", 5) == 0)
{
- s = h->text + 5;
+ uschar * s = h->text + 5;
if (strncmpic(s, US"Id:", 3) == 0 ||
strncmpic(s, US"Help:", 5) == 0 ||
strncmpic(s, US"Subscribe:", 10) == 0 ||
else if (strncmpic(h->text, US"Auto-submitted:", 15) == 0)
{
- s = h->text + 15;
- while (isspace(*s)) s++;
+ uschar * s = h->text + 15;
+ Uskip_whitespace(&s);
if (strncmpic(s, US"no", 2) != 0) return FALSE;
s += 2;
- while (isspace(*s)) s++;
- if (*s != 0) return FALSE;
+ Uskip_whitespace(&s);
+ if (*s) return FALSE;
}
}
global_rewrite_rules);
-if (self_from == NULL) self_from = self;
-if (self_to == NULL) self_to = self;
+if (!self_from) self_from = self;
+if (self_to) self_to = self;
/* If there's a prefix or suffix set, we must include the prefixed/
suffixed version of the local part in the tests. */
-if (deliver_localpart_prefix != NULL || deliver_localpart_suffix != NULL)
+if (deliver_localpart_prefix || deliver_localpart_suffix)
{
psself = string_sprintf("%s%s%s@%s",
- (deliver_localpart_prefix == NULL)? US"" : deliver_localpart_prefix,
+ deliver_localpart_prefix ? deliver_localpart_prefix : US"",
deliver_localpart,
- (deliver_localpart_suffix == NULL)? US"" : deliver_localpart_suffix,
+ deliver_localpart_suffix ? deliver_localpart_suffix : US"",
deliver_domain);
psself_from = rewrite_one(psself, rewrite_from, NULL, FALSE, US"",
global_rewrite_rules);
*/
int
-filter_interpret(uschar *filter, int options, address_item **generated,
+filter_interpret(const uschar *filter, int options, address_item **generated,
uschar **error)
{
int i;
int yield = FF_ERROR;
-uschar *ptr = filter;
-uschar *save_headers_charset = headers_charset;
+const uschar *ptr = filter;
+const uschar *save_headers_charset = headers_charset;
filter_cmd *commands = NULL;
filter_cmd **lastcmdptr = &commands;