X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/31f5b3492bde6a055c0c349a3d46718bd5a7e4f0..dbab2d6f08acd5ccf71d5e8a6cdc1224ab857d7a:/src/src/filter.c diff --git a/src/src/filter.c b/src/src/filter.c index 3f9f750b6..887bc8bc1 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -3,7 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 1995 - 2018 */ -/* Copyright (c) The Exim Maintainers 2020 */ +/* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* 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 */ @@ -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) @@ -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; } @@ -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 @@ -868,7 +873,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 +912,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 +926,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 +968,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 +1004,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,7 +1019,7 @@ 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->next = NULL; @@ -1081,7 +1086,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""; @@ -1127,7 +1132,7 @@ switch (command) new = store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), FALSE); new->next = NULL; **lastcmdptr = new; - *lastcmdptr = &(new->next); + *lastcmdptr = &new->next; new->command = command; new->seen = FALSE; new->args[0].u = NULL; @@ -1136,8 +1141,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 */ @@ -1162,8 +1167,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; @@ -1219,13 +1224,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 +1277,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); } @@ -1314,7 +1313,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 +1334,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 +1383,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); @@ -1425,10 +1424,7 @@ static BOOL 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; +const uschar *exp[2], * p, * pp; int val[2]; int i; @@ -1486,8 +1482,7 @@ switch (c->type) case cond_foranyaddress: p = c->left.u; - pp = expand_string(p); - if (pp == NULL) + if (!(pp = expand_cstring(p))) { *error_pointer = string_sprintf("failed to expand \"%s\" in " "filter file: %s", p, expand_string_message); @@ -1497,19 +1492,17 @@ switch (c->type) yield = FALSE; f.parse_allow_group = TRUE; /* Allow group syntax */ - while (*pp != 0) + while (*pp) { uschar *error; int start, end, domain; - int saveend; + uschar * s; p = parse_find_address_end(pp, FALSE); - saveend = *p; + s = string_copyn(pp, p - pp); - *p = 0; filter_thisaddress = - parse_extract_address(pp, &error, &start, &end, &domain, FALSE); - *p = saveend; + parse_extract_address(s, &error, &start, &end, &domain, FALSE); if (filter_thisaddress) { @@ -1523,7 +1516,7 @@ switch (c->type) } if (yield) break; - if (saveend == 0) break; + if (!*p) break; pp = p + 1; } @@ -1538,8 +1531,7 @@ switch (c->type) p = c->left.u; for (i = 0; i < 2; i++) { - exp[i] = expand_string(p); - if (exp[i] == NULL) + if (!(exp[i] = expand_cstring(p))) { *error_pointer = string_sprintf("failed to expand \"%s\" in " "filter file: %s", p, expand_string_message); @@ -1561,7 +1553,7 @@ switch (c->type) break; case cond_contains: - yield = strstric(exp[0], exp[1], FALSE) != NULL; + yield = strstric_c(exp[0], exp[1], FALSE) != NULL; break; case cond_CONTAINS: @@ -1580,34 +1572,43 @@ switch (c->type) 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; + 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]); - } + const pcre2_code *re; + int err; + PCRE2_SIZE offset; - if (!(re = pcre_compile(CS exp[1], - PCRE_COPT | ((c->type == cond_matches)? PCRE_CASELESS : 0), - CCSS ®comp_error, ®comp_error_offset, 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 ((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]); + } - yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1); - 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))) + { + 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; + } + + yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1); + break; + } /* For above and below, convert the strings to numbers */ @@ -1686,17 +1687,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 @@ -1988,16 +1988,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; } @@ -2015,7 +2018,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; @@ -2038,18 +2041,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: @@ -2059,19 +2064,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, @@ -2079,7 +2084,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; @@ -2091,7 +2096,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", @@ -2121,12 +2126,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) @@ -2156,12 +2160,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", @@ -2176,14 +2180,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"", @@ -2191,7 +2195,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]); @@ -2207,15 +2211,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) { @@ -2226,8 +2237,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(" "); @@ -2245,7 +2256,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; @@ -2321,7 +2332,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; } @@ -2494,13 +2505,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;