X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/059ec3d9952740285fb1ebf47961b8aca2eb1b4a..93a680e4c9c899d86ff3fde0933fb5367b34af50:/src/src/filter.c diff --git a/src/src/filter.c b/src/src/filter.c index 0e8e790a4..bba1a520e 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/filter.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2004 */ +/* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -76,8 +74,8 @@ permitted to include \n followed by white space) first, and then the body text one (it can have \n anywhere). Then the file names and once_repeat, which may not contain \n. */ -static char *mailargs[] = { /* "to" must be first, and */ - "to", /* "cc" and "bcc" must follow */ +static const char *mailargs[] = { /* "to" must be first, and */ + "to", /* "cc" and "bcc" must follow */ "cc", "bcc", "from", @@ -146,14 +144,14 @@ enum { cond_and, cond_or, cond_personal, cond_begins, cond_BEGINS, cond_above, cond_below, cond_errormsg, cond_firsttime, cond_manualthaw, cond_foranyaddress }; -static char *cond_names[] = { +static const char *cond_names[] = { "and", "or", "personal", "begins", "BEGINS", "ends", "ENDS", "is", "IS", "matches", "MATCHES", "contains", "CONTAINS", "delivered", "above", "below", "error_message", "first_delivery", "manually_thawed", "foranyaddress" }; -static char *cond_not_names[] = { +static const char *cond_not_names[] = { "", "", "not personal", "does not begin", "does not BEGIN", "does not end", "does not END", @@ -165,7 +163,7 @@ static char *cond_not_names[] = { /* Tables of binary condition words and their corresponding types. Not easy to amalgamate with the above because of the different variants. */ -static char *cond_words[] = { +static const char *cond_words[] = { "BEGIN", "BEGINS", "CONTAIN", @@ -203,7 +201,7 @@ enum { add_command, defer_command, deliver_command, elif_command, else_command, mail_command, noerror_command, pipe_command, save_command, seen_command, testprint_command, unseen_command, vacation_command }; -static char *command_list[] = { +static const char *command_list[] = { "add", "defer", "deliver", "elif", "else", "endif", "finish", "fail", "freeze", "headers", "if", "logfile", "logwrite", "mail", "noerror", "pipe", "save", "seen", "testprint", "unseen", "vacation" @@ -357,7 +355,7 @@ while (*(++ptr) != 0 && *ptr != '\"' && *ptr != '\n') } } - *bp++ = string_interpret_escape(&ptr); + *bp++ = string_interpret_escape(CUSS &ptr); } } @@ -777,7 +775,7 @@ Returns: nothing static void print_condition(condition_block *c, BOOL toplevel) { -char *name = (c->testfor)? cond_names[c->type] : cond_not_names[c->type]; +const char *name = (c->testfor)? cond_names[c->type] : cond_not_names[c->type]; switch(c->type) { case cond_personal: @@ -800,7 +798,7 @@ switch(c->type) case cond_ENDS: case cond_above: case cond_below: - debug_printf("%s %s %s", c->left.u, (char *)name, c->right.u); + debug_printf("%s %s %s", c->left.u, name, c->right.u); break; case cond_and: @@ -1044,6 +1042,13 @@ switch (command) case elif_command: case else_command: case endif_command: + if (seen_force || noerror_force) + { + *error_pointer = string_sprintf("\"seen\", \"unseen\", or \"noerror\" " + "near line %d is not followed by a command", line_number); + yield = FALSE; + } + if (expect_endif > 0) had_else_endif = (command == elif_command)? had_elif : (command == else_command)? had_else : had_endif; @@ -1316,6 +1321,12 @@ switch (command) case seen_command: case unseen_command: + if (*ptr == 0) + { + *error_pointer = string_sprintf("\"seen\" or \"unseen\" " + "near line %d is not followed by a command", line_number); + yield = FALSE; + } if (seen_force) { *error_pointer = string_sprintf("\"seen\" or \"unseen\" repeated " @@ -1331,6 +1342,12 @@ switch (command) /* So does noerror */ case noerror_command: + if (*ptr == 0) + { + *error_pointer = string_sprintf("\"noerror\" " + "near line %d is not followed by a command", line_number); + yield = FALSE; + } noerror_force = TRUE; was_noerror = TRUE; break; @@ -1414,7 +1431,7 @@ Returns: TRUE if the condition is met static BOOL test_condition(condition_block *c, BOOL toplevel) { -BOOL yield; +BOOL yield = FALSE; const pcre *re; uschar *exp[2], *p, *pp; const uschar *regcomp_error = NULL; @@ -1462,7 +1479,7 @@ switch (c->type) and filter testing and verification. */ case cond_firsttime: - yield = filter_test != NULL || message_id[0] == 0 || deliver_firsttime; + yield = filter_test != FTEST_NONE || message_id[0] == 0 || deliver_firsttime; break; /* Only TRUE if a message is actually being processed; FALSE for address @@ -1503,7 +1520,7 @@ switch (c->type) if (filter_thisaddress != NULL) { - if ((filter_test != NULL && debug_selector != 0) || + if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) { indent(); @@ -1578,7 +1595,7 @@ switch (c->type) case cond_matches: case cond_MATCHES: - if ((filter_test != NULL && debug_selector != 0) || + if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) { debug_printf("Match expanded arguments:\n"); @@ -1621,7 +1638,7 @@ switch (c->type) break; } -if ((filter_test != NULL && debug_selector != 0) || +if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) { indent(); @@ -1729,7 +1746,7 @@ while (commands != NULL) } filter_n[n[1]] += n[0]; - if (filter_test != NULL) printf("Add %d to n%d\n", n[0], n[1]); + if (filter_test != FTEST_NONE) printf("Add %d to n%d\n", n[0], n[1]); break; /* A deliver command's argument must be a valid address. Its optional @@ -1777,7 +1794,7 @@ while (commands != NULL) /* Test case: report what would happen */ - if (filter_test != NULL) + if (filter_test != FTEST_NONE) { indent(); printf("%seliver message to: %s%s%s%s\n", @@ -1804,7 +1821,7 @@ while (commands != NULL) set in a system filter and to the local address in user filters. */ addr = deliver_make_addr(expargs[0], TRUE); /* TRUE => copy s */ - addr->p.errors_address = (s == NULL)? + addr->prop.errors_address = (s == NULL)? s : string_copy(s); /* Default is NULL */ if (commands->noerror) setflag(addr, af_ignore_error); addr->next = *generated; @@ -1818,7 +1835,7 @@ while (commands != NULL) /* Test case: report what would happen */ - if (filter_test != NULL) + if (filter_test != FTEST_NONE) { indent(); if (mode < 0) @@ -1834,11 +1851,12 @@ while (commands != NULL) else { + if (s[0] != '/' && (filter_options & RDO_PREPEND_HOME) != 0 && + deliver_home != NULL && deliver_home[0] != 0) + s = string_sprintf("%s/%s", deliver_home, s); DEBUG(D_filter) debug_printf("Filter: %ssave message to: %s%s\n", (commands->seen)? "" : "unseen ", s, commands->noerror? " (noerror)" : ""); - if (s[0] != '/' && deliver_home != NULL && deliver_home[0] != 0) - s = string_sprintf("%s/%s", deliver_home, s); /* Create the new address and add it to the chain, setting the af_pfr and af_file flags, the af_ignore_error flag if necessary, and the @@ -1855,7 +1873,7 @@ while (commands != NULL) case pipe_command: s = string_copy(commands->args[0].u); - if (filter_test != NULL) + if (filter_test != FTEST_NONE) { indent(); printf("%sipe message to: %s%s\n", (commands->seen)? @@ -1907,11 +1925,11 @@ while (commands != NULL) if (log_mode == -1) log_mode = 0600; if (log_fd >= 0) { - close(log_fd); + (void)close(log_fd); log_fd = -1; } log_filename = expargs[0]; - if (filter_test != NULL) + if (filter_test != FTEST_NONE) { indent(); printf("%sogfile %s\n", (commands->seen)? "Seen l" : "L", log_filename); @@ -1921,7 +1939,7 @@ while (commands != NULL) case logwrite_command: s = expargs[0]; - if (filter_test != NULL) + if (filter_test != FTEST_NONE) { indent(); printf("%sogwrite \"%s\"\n", (commands->seen)? "Seen l" : "L", @@ -1983,7 +2001,7 @@ while (commands != NULL) int subtype = commands->args[1].i; s = expargs[0]; - if (filter_test != NULL) + if (filter_test != FTEST_NONE) printf("Headers %s \"%s\"\n", (subtype == TRUE)? "add" : (subtype == FALSE)? "remove" : "charset", string_printing(s)); @@ -2003,7 +2021,7 @@ while (commands != NULL) { int sep = 0; uschar *ss; - uschar *list = s; + const uschar *list = s; uschar buffer[128]; while ((ss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) @@ -2039,10 +2057,10 @@ while (commands != NULL) DEFERFREEZEFAIL: fmsg = expargs[0]; if (Ustrlen(fmsg) > 1024) Ustrcpy(fmsg + 1000, " ... (truncated)"); - fmsg = string_printing(fmsg); + fmsg = US string_printing(fmsg); *error_pointer = fmsg; - if (filter_test != NULL) + if (filter_test != FTEST_NONE) { indent(); printf("%c%s text \"%s\"\n", toupper(ff_name[0]), ff_name+1, fmsg); @@ -2051,7 +2069,7 @@ while (commands != NULL) return ff_ret; case finish_command: - if (filter_test != NULL) + if (filter_test != FTEST_NONE) { indent(); printf("%sinish\n", (commands->seen)? "Seen f" : "F"); @@ -2091,7 +2109,7 @@ while (commands != NULL) case vacation_command: if (return_path == NULL || return_path[0] == 0) { - if (filter_test != NULL) + if (filter_test != FTEST_NONE) printf("%s command ignored because return_path is empty\n", command_list[commands->command]); else DEBUG(D_filter) debug_printf("%s command ignored because return_path " @@ -2166,7 +2184,6 @@ while (commands != NULL) string_printing(s), command_list[commands->command]); return FF_ERROR; } - pp++; } p = pp; } @@ -2180,7 +2197,7 @@ while (commands != NULL) /* Proceed with mail or vacation command */ - if (filter_test != NULL) + if (filter_test != FTEST_NONE) { uschar *to = commands->args[mailarg_index_to].u; indent(); @@ -2207,12 +2224,17 @@ while (commands != NULL) else { uschar *tt; + uschar *log_addr = NULL; uschar *to = commands->args[mailarg_index_to].u; + int size = 0; + int ptr = 0; + int badflag = 0; + if (to == NULL) to = expand_string(US"$reply_address"); while (isspace(*to)) to++; - for (tt = to; *tt != 0; tt++) /* Get rid of newlines so that */ - if (*tt == '\n') *tt = ' '; /* the eventual log line is OK */ + for (tt = to; *tt != 0; tt++) /* Get rid of newlines */ + if (*tt == '\n') *tt = ' '; DEBUG(D_filter) { @@ -2235,10 +2257,59 @@ while (commands != NULL) } } - /* Create the "address" for the autoreply */ + /* Create the "address" for the autoreply. This is used only for logging, + as the actual recipients are extracted from the To: line by -t. We use the + same logic here to extract the working addresses (there may be more than + one). Just in case there are a vast number of addresses, stop when the + string gets too long. */ + + tt = to; + while (*tt != 0) + { + uschar *ss = parse_find_address_end(tt, FALSE); + uschar *recipient, *errmess; + int start, end, domain; + int temp = *ss; + + *ss = 0; + recipient = parse_extract_address(tt, &errmess, &start, &end, &domain, + FALSE); + *ss = temp; - addr = deliver_make_addr(string_sprintf(">%.256s", to), FALSE); - setflag(addr, af_pfr); + /* Ignore empty addresses and errors; an error will occur later if + there's something really bad. */ + + if (recipient != NULL) + { + log_addr = string_cat(log_addr, &size, &ptr, + (log_addr == NULL)? US">" : US",", 1); + log_addr = string_cat(log_addr, &size, &ptr, recipient, + Ustrlen(recipient)); + } + + /* Check size */ + + if (ptr > 256) + { + log_addr = string_cat(log_addr, &size, &ptr, US", ...", 5); + break; + } + + /* Move on past this address */ + + tt = ss + (*ss? 1:0); + while (isspace(*tt)) tt++; + } + + if (log_addr == NULL) + { + log_addr = string_sprintf(">**bad-reply**"); + badflag = af_bad_reply; + } + else log_addr[ptr] = 0; + + addr = deliver_make_addr(log_addr, FALSE); + setflag(addr, (af_pfr|badflag)); if (commands->noerror) setflag(addr, af_ignore_error); addr->next = *generated; *generated = addr; @@ -2278,10 +2349,10 @@ while (commands != NULL) break; case testprint_command: - if (filter_test != NULL || (debug_selector & D_filter) != 0) + if (filter_test != FTEST_NONE || (debug_selector & D_filter) != 0) { - uschar *s = string_printing(expargs[0]); - if (filter_test == NULL) + const uschar *s = string_printing(expargs[0]); + if (filter_test == FTEST_NONE) debug_printf("Filter: testprint: %s\n", s); else printf("Testprint: %s\n", s); @@ -2321,14 +2392,42 @@ header_line *h; int to_count = 2; int from_count = 9; -/* If any header line in the message starts with "List-", it is not -a personal message. */ +/* If any header line in the message is a defined "List-" header field, it is +not a personal message. We used to check for any header line that started with +"List-", but this was tightened up for release 4.54. The check is now for +"List-Id", defined in RFC 2929, or "List-Help", "List-Subscribe", "List- +Unsubscribe", "List-Post", "List-Owner" or "List-Archive", all of which are +defined in RFC 2369. We also scan for "Auto-Submitted"; if it is found to +contain any value other than "no", the message is not personal (RFC 3834). +Previously the test was for "auto-". */ for (h = header_list; h != NULL; h = h->next) { - if (h->type != htype_old && h->slen > 5 && - strncmpic(h->text, US"List-", 5) == 0) - return FALSE; + uschar *s; + if (h->type == htype_old) continue; + + if (strncmpic(h->text, US"List-", 5) == 0) + { + s = h->text + 5; + if (strncmpic(s, US"Id:", 3) == 0 || + strncmpic(s, US"Help:", 5) == 0 || + strncmpic(s, US"Subscribe:", 10) == 0 || + strncmpic(s, US"Unsubscribe:", 12) == 0 || + strncmpic(s, US"Post:", 5) == 0 || + strncmpic(s, US"Owner:", 6) == 0 || + strncmpic(s, US"Archive:", 8) == 0) + return FALSE; + } + + else if (strncmpic(h->text, US"Auto-submitted:", 15) == 0) + { + s = h->text + 15; + while (isspace(*s)) s++; + if (strncmpic(s, US"no", 2) != 0) return FALSE; + s += 2; + while (isspace(*s)) s++; + if (*s != 0) return FALSE; + } } /* Set up "my" address */ @@ -2384,7 +2483,6 @@ yield = "^daemon@", "^root@", "^listserv@", "^majordomo@", "^.*?-request@", "^owner-[^@]+@", self, self_from, psself, psself_from) && - header_match(US"auto-submitted:", FALSE, FALSE, NULL, 1, "auto-") && header_match(US"precedence:", FALSE, FALSE, NULL, 3, "bulk","list","junk") && (sender_address == NULL || sender_address[0] != 0); @@ -2460,7 +2558,7 @@ ptr = nextsigchar(ptr, TRUE); if (read_command_list(&ptr, &lastcmdptr, FALSE)) yield = interpret_commands(commands, generated); -if (filter_test != NULL || (debug_selector & D_filter) != 0) +if (filter_test != FTEST_NONE || (debug_selector & D_filter) != 0) { uschar *s = US""; switch(yield) @@ -2493,14 +2591,14 @@ if (filter_test != NULL || (debug_selector & D_filter) != 0) break; } - if (filter_test != NULL) printf("%s\n", CS s); + if (filter_test != FTEST_NONE) printf("%s\n", CS s); else debug_printf("%s\n", s); } /* Close the log file if it was opened, and kill off any numerical variables before returning. Reset the header decoding charset. */ -if (log_fd >= 0) close(log_fd); +if (log_fd >= 0) (void)close(log_fd); expand_nmax = -1; filter_running = FALSE; headers_charset = save_headers_charset;