-/* $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 - 2009 */
/* See the file NOTICE for conditions of use and distribution. */
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",
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",
/* 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",
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"
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:
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:
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;
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 "
/* 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;
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;
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
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();
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");
break;
}
-if ((filter_test != NULL && debug_selector != 0) ||
+if ((filter_test != FTEST_NONE && debug_selector != 0) ||
(debug_selector & D_filter) != 0)
{
indent();
}
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
/* 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",
/* Test case: report what would happen */
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
{
indent();
if (mode < 0)
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
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)?
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);
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",
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));
fmsg = 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);
return ff_ret;
case finish_command:
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
{
indent();
printf("%sinish\n", (commands->seen)? "Seen f" : "F");
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 "
string_printing(s), command_list[commands->command]);
return FF_ERROR;
}
- pp++;
}
p = pp;
}
/* Proceed with mail or vacation command */
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
{
uschar *to = commands->args[mailarg_index_to].u;
indent();
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)
{
}
}
- /* 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;
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)
+ if (filter_test == FTEST_NONE)
debug_printf("Filter: testprint: %s\n", s);
else
printf("Testprint: %s\n", s);
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 */
"^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);
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)
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;