X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/4a852e8c97fa4de42c443107121c7717e1f0c9b2..699f306744e5f0e1ad860a460454efe85fe63c74:/src/src/filter.c diff --git a/src/src/filter.c b/src/src/filter.c index dfcc80271..cc4af230e 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -2,8 +2,8 @@ * 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 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -27,7 +27,7 @@ union argtypes { struct condition_block *c; struct filter_cmd *f; int i; - uschar *u; + const uschar *u; }; /* Local structures used in this module */ @@ -67,7 +67,7 @@ static BOOL noerror_force; 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 @@ -252,8 +252,8 @@ Arguments: 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 (;;) { @@ -290,8 +290,8 @@ Arguments 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) && @@ -326,13 +326,13 @@ Arguments: 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) { @@ -345,7 +345,7 @@ while (*(++ptr) != 0 && *ptr != '\"' && *ptr != '\n') { if (isspace(ptr[1])) /* \NL ignored */ { - uschar *p = ptr + 1; + const uschar *p = ptr + 1; while (*p != '\n' && isspace(*p)) p++; if (*p == '\n') { @@ -385,7 +385,7 @@ Returns: the number, or 0 on error (with *OK FALSE) */ static int -get_number(uschar *s, BOOL *ok) +get_number(const uschar *s, BOOL *ok) { int value, count; *ok = FALSE; @@ -416,8 +416,8 @@ Arguments: 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; @@ -434,7 +434,7 @@ for (;;) /* 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; @@ -477,7 +477,7 @@ for (;;) 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 */ @@ -498,7 +498,7 @@ for (;;) /* 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; @@ -518,7 +518,7 @@ for (;;) 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) @@ -528,7 +528,7 @@ for (;;) } 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; @@ -569,7 +569,7 @@ for (;;) else { int i; - uschar *isptr = NULL; + const uschar *isptr = NULL; c->left.u = string_copy(buffer); ptr = nextword(ptr, buffer, sizeof(buffer), TRUE); @@ -655,18 +655,23 @@ for (;;) 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; } @@ -679,7 +684,7 @@ for (;;) 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; @@ -697,8 +702,8 @@ for (;;) 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) { @@ -837,7 +842,7 @@ Returns: TRUE if command successfully read, else FALSE */ 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; @@ -845,8 +850,8 @@ BOOL yield = TRUE; 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 @@ -855,6 +860,8 @@ terminated by white space, but there are two exceptions, which are the "if" and 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"); @@ -868,7 +875,7 @@ else if (Ustrncmp(ptr, "elif(", 5) == 0) 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++) @@ -907,11 +914,11 @@ switch (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; @@ -921,13 +928,13 @@ switch (command) { 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); @@ -963,7 +970,7 @@ switch (command) 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); @@ -999,7 +1006,7 @@ switch (command) 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) { @@ -1014,9 +1021,10 @@ switch (command) 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); @@ -1081,7 +1089,7 @@ switch (command) 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""; @@ -1100,12 +1108,12 @@ switch (command) /* 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; @@ -1124,10 +1132,10 @@ switch (command) /* 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; @@ -1136,8 +1144,8 @@ switch (command) /* 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 */ @@ -1152,7 +1160,7 @@ switch (command) 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; @@ -1162,8 +1170,8 @@ switch (command) 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; @@ -1205,10 +1213,10 @@ switch (command) 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; @@ -1219,13 +1227,10 @@ switch (command) 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 */ @@ -1275,11 +1280,8 @@ switch (command) /* 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); } @@ -1288,18 +1290,18 @@ switch (command) 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"); } @@ -1314,7 +1316,7 @@ switch (command) 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); @@ -1335,7 +1337,7 @@ switch (command) /* 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); @@ -1384,11 +1386,11 @@ Returns: TRUE on success */ 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); @@ -1422,216 +1424,203 @@ Returns: TRUE if the condition is met */ static BOOL -test_condition(condition_block *c, BOOL toplevel) +test_condition(condition_block * c, BOOL toplevel) { -BOOL yield = FALSE; -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; - 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) - { - 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 */ - - 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]); - 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) || @@ -1691,17 +1680,16 @@ while (commands) 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 @@ -1897,7 +1885,7 @@ while (commands) 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""; @@ -1993,16 +1981,19 @@ while (commands) 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; } @@ -2020,7 +2011,7 @@ while (commands) /* This setting lasts only while the filter is running; on exit, the variable is reset to the previous value. */ - else headers_charset = s; /*XXX loses track of const */ + else headers_charset = s; } break; @@ -2043,18 +2034,20 @@ while (commands) ff_name = US"freeze"; ff_ret = FF_FREEZE; - DEFERFREEZEFAIL: - fmsg = expargs[0]; /*XXX loses track of const */ - 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: @@ -2064,19 +2057,19 @@ while (commands) 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, @@ -2084,7 +2077,7 @@ while (commands) 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; @@ -2096,7 +2089,7 @@ while (commands) 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", @@ -2126,12 +2119,11 @@ while (commands) for (i = 0; i < MAILARGS_STRING_COUNT; i++) { - uschar *p; 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) @@ -2161,12 +2153,12 @@ while (commands) 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", @@ -2181,14 +2173,14 @@ while (commands) /* The string is OK */ - commands->args[i].u = s; /*XXX loses track of const */ + commands->args[i].u = s; } /* Proceed with mail or vacation 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"", @@ -2196,7 +2188,7 @@ while (commands) 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]); @@ -2212,15 +2204,22 @@ while (commands) } 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) { @@ -2231,8 +2230,8 @@ while (commands) 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(" "); @@ -2250,7 +2249,7 @@ while (commands) string gets too long. */ tt = to; - while (*tt != 0) + while (*tt) { uschar *ss = parse_find_address_end(tt, FALSE); uschar *recipient, *errmess; @@ -2298,7 +2297,7 @@ while (commands) 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 = @@ -2326,7 +2325,7 @@ while (commands) 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; } @@ -2347,7 +2346,7 @@ while (commands) commands = commands->next; } -return filter_delivered? FF_DELIVERED : FF_NOTDELIVERED; +return filter_delivered ? FF_DELIVERED : FF_NOTDELIVERED; } @@ -2499,13 +2498,13 @@ Returns: FF_DELIVERED success, a significant action was taken */ 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;