-/* $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 - 2018 */
/* 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",
/* The count of string arguments */
-#define MAILARGS_STRING_COUNT (sizeof(mailargs)/sizeof(uschar *))
+#define MAILARGS_STRING_COUNT (nelem(mailargs))
/* The count of string arguments that are actually passed over as strings
(once_repeat is converted to an int). */
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",
"match",
"matches"};
-static int cond_word_count = (sizeof(cond_words)/sizeof(uschar *));
+static int cond_word_count = nelem(cond_words);
static int cond_types[] = { cond_BEGINS, cond_BEGINS, cond_CONTAINS,
cond_CONTAINS, cond_ENDS, cond_ENDS, cond_IS, cond_MATCHES, cond_MATCHES,
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 int command_list_count = sizeof(command_list)/sizeof(uschar *);
+static int command_list_count = nelem(command_list);
/* This table contains the number of expanded arguments in the bottom 4 bits.
If the top bit is set, it means that the default for the command is "seen". */
}
}
- *bp++ = string_interpret_escape(&ptr);
+ *bp++ = string_interpret_escape(CUSS &ptr);
}
}
string_item *aa;
uschar *saveptr = ptr;
ptr = nextword(ptr, buffer, sizeof(buffer), TRUE);
- if (*error_pointer != NULL) break;
+ if (*error_pointer) break;
if (Ustrcmp(buffer, "alias") != 0)
{
ptr = saveptr;
break;
}
ptr = nextitem(ptr, buffer, sizeof(buffer), TRUE);
- if (*error_pointer != NULL) break;
+ if (*error_pointer) break;
aa = store_get(sizeof(string_item));
aa->text = string_copy(buffer);
aa->next = c->left.a;
else if (Ustrcmp(buffer, "foranyaddress") == 0)
{
ptr = nextitem(ptr, buffer, sizeof(buffer), TRUE);
- if (*error_pointer != NULL) break;
+ if (*error_pointer) break;
if (*ptr != '(')
{
*error_pointer = string_sprintf("\"(\" expected after \"foranyaddress\" "
c->left.u = string_copy(buffer);
ptr = read_condition(nextsigchar(ptr+1, TRUE), &(c->right.c), FALSE);
- if (*error_pointer != NULL) break;
+ if (*error_pointer) break;
if (*ptr != ')')
{
*error_pointer = string_sprintf("expected \")\" in line %d of "
"filter file", line_number);
break;
}
- if (!testfor)
- {
- c->testfor = !c->testfor;
- testfor = TRUE;
- }
ptr = nextsigchar(ptr+1, TRUE);
}
c->left.u = string_copy(buffer);
ptr = nextword(ptr, buffer, sizeof(buffer), TRUE);
- if (*error_pointer != NULL) break;
+ if (*error_pointer) break;
/* Handle "does|is [not]", preserving the pointer after "is" in
case it isn't that, but the form "is <string>". */
if (buffer[0] == 'I') { c->type = cond_IS; isptr = ptr; }
ptr = nextword(ptr, buffer, sizeof(buffer), TRUE);
- if (*error_pointer != NULL) break;
+ if (*error_pointer) break;
if (strcmpic(buffer, US"not") == 0)
{
c->testfor = !c->testfor;
- if (isptr != NULL) isptr = ptr;
+ if (isptr) isptr = ptr;
ptr = nextword(ptr, buffer, sizeof(buffer), TRUE);
- if (*error_pointer != NULL) break;
+ if (*error_pointer) break;
}
}
if (i >= cond_word_count)
{
- if (isptr != NULL)
- {
- ptr = isptr;
- }
- else
+ if (!isptr)
{
*error_pointer = string_sprintf("unrecognized condition word \"%s\" "
"near line %d of filter file", buffer, line_number);
break;
}
+ ptr = isptr;
}
/* Get the RH argument. */
ptr = nextitem(ptr, buffer, sizeof(buffer), TRUE);
- if (*error_pointer != NULL) break;
+ if (*error_pointer) break;
c->right.u = string_copy(buffer);
}
}
{
uschar *saveptr = ptr;
ptr = nextword(ptr, buffer, sizeof(buffer), FALSE);
- if (*error_pointer != NULL) break;
+ 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
if (Ustrcmp(buffer, "then") == 0)
{
if (toplevel) *saveptr = 0;
- else *error_pointer = string_sprintf("missing \")\" at end of "
+ else *error_pointer = string_sprintf("missing \")\" at end of "
"condition near line %d of filter file", line_number);
break;
}
condition_block *orc = store_get(sizeof(condition_block));
condition_block *or_parent = NULL;
- if (current_parent != NULL)
+ if (current_parent)
{
- while (current_parent->parent != NULL &&
+ while (current_parent->parent &&
current_parent->parent->type == cond_and)
current_parent = current_parent->parent;
/* If the parent has a parent, it must be an "or" parent. */
- if (current_parent->parent != NULL)
+ if (current_parent->parent)
or_parent = current_parent->parent;
}
orc->parent = or_parent;
- if (or_parent == NULL) *cond = orc; else
- or_parent->right.c = orc;
+ if (!or_parent) *cond = orc;
+ else or_parent->right.c = orc;
orc->type = cond_or;
orc->testfor = TRUE;
orc->left.c = (current_parent == NULL)? c : current_parent;
/*************************************************
-* Ouput the current indent *
+* Output the current indent *
*************************************************/
static void
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:
yield = FALSE;
}
- if (!system_filtering && second_argument.b != TRUE_UNSET)
+ if (!f.system_filtering && second_argument.b != TRUE_UNSET)
{
*error_pointer = string_sprintf("header addition and removal is "
"available only in system filters: near line %d of filter file",
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;
* Read a list of commands *
*************************************************/
-/* If condional is TRUE, the list must be terminated
+/* If conditional is TRUE, the list must be terminated
by the words "else" or "endif".
Arguments:
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;
scan Cc: (hence the FALSE argument). */
case cond_personal:
- yield = system_filtering? FALSE : filter_personal(c->left.a, FALSE);
+ yield = f.system_filtering? FALSE : filter_personal(c->left.a, FALSE);
break;
case cond_delivered:
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 || f.deliver_firsttime;
break;
/* Only TRUE if a message is actually being processed; FALSE for address
testing and verification. */
case cond_manualthaw:
- yield = message_id[0] != 0 && deliver_manual_thaw;
+ yield = message_id[0] != 0 && f.deliver_manual_thaw;
break;
/* The foranyaddress condition loops through a list of addresses */
}
yield = FALSE;
- parse_allow_group = TRUE; /* Allow group syntax */
+ f.parse_allow_group = TRUE; /* Allow group syntax */
while (*pp != 0)
{
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();
pp = p + 1;
}
- parse_allow_group = FALSE; /* Reset group syntax flags */
- parse_found_group = FALSE;
+ 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;
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
s = expargs[1];
- if (s != NULL && !system_filtering)
+ if (s != NULL && !f.system_filtering)
{
uschar *ownaddress = expand_string(US"$local_part@$domain");
if (strcmpic(ownaddress, s) != 0)
/* 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",
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);
+ if (commands->noerror) addr->prop.ignore_error = TRUE;
addr->next = *generated;
*generated = addr;
}
/* 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
mode value. */
addr = deliver_make_addr(s, TRUE); /* TRUE => copy s */
- setflag(addr, af_pfr|af_file);
- if (commands->noerror) setflag(addr, af_ignore_error);
+ setflag(addr, af_pfr);
+ setflag(addr, af_file);
+ if (commands->noerror) addr->prop.ignore_error = TRUE;
addr->mode = mode;
addr->next = *generated;
*generated = addr;
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)?
has been split up into separate arguments. */
addr = deliver_make_addr(s, TRUE); /* TRUE => copy s */
- setflag(addr, af_pfr|af_expand_pipe);
- if (commands->noerror) setflag(addr, af_ignore_error);
+ setflag(addr, af_pfr);
+ setflag(addr, af_expand_pipe);
+ if (commands->noerror) addr->prop.ignore_error = TRUE;
addr->next = *generated;
*generated = addr;
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));
{
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)
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);
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 mail_command:
case vacation_command:
- if (return_path == NULL || return_path[0] == 0)
- {
- if (filter_test != NULL)
- 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 "
- "is empty\n", command_list[commands->command]);
+ if (return_path == NULL || return_path[0] == 0)
+ {
+ 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 "
+ "is empty\n", command_list[commands->command]);
+ break;
+ }
+
+ /* Check the contents of the strings. The type of string can be deduced
+ from the value of i.
+
+ . If i is equal to mailarg_index_text it's a text string for the body,
+ where anything goes.
+
+ . If i is > mailarg_index_text, we are dealing with a file name, which
+ cannot contain non-printing characters.
+
+ . If i is less than mailarg_index_headers we are dealing with something
+ that will go in a single message header line, where newlines must be
+ followed by white space.
+
+ . If i is equal to mailarg_index_headers, we have a string that contains
+ one or more headers. Newlines that are not followed by white space must
+ be followed by a header name.
+ */
+
+ for (i = 0; i < MAILARGS_STRING_COUNT; i++)
+ {
+ uschar *p;
+ uschar *s = expargs[i];
+
+ if (s == NULL) continue;
+
+ if (i != mailarg_index_text) for (p = s; *p != 0; p++)
+ {
+ int c = *p;
+ if (i > mailarg_index_text)
+ {
+ if (!mac_isprint(c))
+ {
+ *error_pointer = string_sprintf("non-printing character in \"%s\" "
+ "in %s command", string_printing(s),
+ command_list[commands->command]);
+ return FF_ERROR;
+ }
+ }
+
+ /* i < mailarg_index_text */
+
+ else if (c == '\n' && !isspace(p[1]))
+ {
+ if (i < mailarg_index_headers)
+ {
+ *error_pointer = string_sprintf("\\n not followed by space in "
+ "\"%.1024s\" in %s command", string_printing(s),
+ command_list[commands->command]);
+ return FF_ERROR;
+ }
+
+ /* Check for the start of a new header line within the string */
+
+ else
+ {
+ uschar *pp;
+ for (pp = p + 1;; pp++)
+ {
+ c = *pp;
+ if (c == ':' && pp != p + 1) break;
+ if (c == 0 || c == ':' || isspace(*pp))
+ {
+ *error_pointer = string_sprintf("\\n not followed by space or "
+ "valid header name in \"%.1024s\" in %s command",
+ string_printing(s), command_list[commands->command]);
+ return FF_ERROR;
+ }
+ }
+ p = pp;
+ }
+ }
+ } /* Loop to scan the string */
+
+ /* The string is OK */
+
+ commands->args[i].u = s;
+ }
+
+ /* Proceed with mail or vacation command */
+
+ if (filter_test != FTEST_NONE)
+ {
+ 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->command == vacation_command ? " (vacation)" : "",
+ commands->noerror ? " (noerror)" : "");
+ for (i = 1; i < MAILARGS_STRING_COUNT; i++)
+ {
+ uschar *arg = commands->args[i].u;
+ if (arg)
+ {
+ int len = Ustrlen(mailargs[i]);
+ int indent = (debug_selector != 0)? output_indent : 0;
+ while (len++ < 7 + indent) printf(" ");
+ printf("%s: %s%s\n", mailargs[i], string_printing(arg),
+ (commands->args[mailarg_index_expand].u != NULL &&
+ Ustrcmp(mailargs[i], "file") == 0)? " (expanded)" : "");
+ }
+ }
+ if (commands->args[mailarg_index_return].u)
+ printf("Return original message\n");
+ }
+ else
+ {
+ uschar *tt;
+ 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 = ' ';
+
+ DEBUG(D_filter)
+ {
+ debug_printf("Filter: %smail to: %s%s%s\n",
+ commands->seen ? "seen " : "",
+ to,
+ commands->command == vacation_command ? " (vacation)" : "",
+ commands->noerror ? " (noerror)" : "");
+ for (i = 1; i < MAILARGS_STRING_COUNT; i++)
+ {
+ uschar *arg = commands->args[i].u;
+ if (arg != NULL)
+ {
+ int len = Ustrlen(mailargs[i]);
+ while (len++ < 15) debug_printf(" ");
+ debug_printf("%s: %s%s\n", mailargs[i], string_printing(arg),
+ (commands->args[mailarg_index_expand].u != NULL &&
+ Ustrcmp(mailargs[i], "file") == 0)? " (expanded)" : "");
+ }
+ }
+ }
+
+ /* 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;
+
+ /* Ignore empty addresses and errors; an error will occur later if
+ there's something really bad. */
+
+ if (recipient)
+ {
+ log_addr = string_catn(log_addr, log_addr ? US"," : US">", 1);
+ log_addr = string_cat (log_addr, recipient);
+ }
+
+ /* Check size */
+
+ if (log_addr && log_addr->ptr > 256)
+ {
+ log_addr = string_catn(log_addr, US", ...", 5);
+ break;
+ }
+
+ /* Move on past this address */
+
+ tt = ss + (*ss ? 1 : 0);
+ while (isspace(*tt)) tt++;
+ }
+
+ if (log_addr)
+ addr = deliver_make_addr(string_from_gstring(log_addr), FALSE);
+ else
+ {
+ addr = deliver_make_addr(US ">**bad-reply**", FALSE);
+ setflag(addr, af_bad_reply);
+ }
+
+ setflag(addr, af_pfr);
+ if (commands->noerror) addr->prop.ignore_error = TRUE;
+ addr->next = *generated;
+ *generated = addr;
+
+ addr->reply = store_get(sizeof(reply_item));
+ addr->reply->from = NULL;
+ addr->reply->to = string_copy(to);
+ addr->reply->file_expand =
+ commands->args[mailarg_index_expand].u != NULL;
+ addr->reply->expand_forbid = expand_forbid;
+ addr->reply->return_message =
+ commands->args[mailarg_index_return].u != NULL;
+ addr->reply->once_repeat = 0;
+
+ if (commands->args[mailarg_index_once_repeat].u != NULL)
+ {
+ addr->reply->once_repeat =
+ readconf_readtime(commands->args[mailarg_index_once_repeat].u, 0,
+ FALSE);
+ if (addr->reply->once_repeat < 0)
+ {
+ *error_pointer = string_sprintf("Bad time value for \"once_repeat\" "
+ "in mail or vacation command: %s",
+ commands->args[mailarg_index_once_repeat].u);
+ return FF_ERROR;
+ }
+ }
+
+ /* Set up all the remaining string arguments (those other than "to") */
+
+ for (i = 1; i < mailargs_string_passed; i++)
+ {
+ uschar *ss = commands->args[i].u;
+ *(USS((US addr->reply) + reply_offsets[i])) =
+ ss ? string_copy(ss) : NULL;
+ }
+ }
break;
- }
-
- /* Check the contents of the strings. The type of string can be deduced
- from the value of i.
-
- . If i is equal to mailarg_index_text it's a text string for the body,
- where anything goes.
-
- . If i is > mailarg_index_text, we are dealing with a file name, which
- cannot contain non-printing characters.
-
- . If i is less than mailarg_index_headers we are dealing with something
- that will go in a single message header line, where newlines must be
- followed by white space.
-
- . If i is equal to mailarg_index_headers, we have a string that contains
- one or more headers. Newlines that are not followed by white space must
- be followed by a header name.
- */
-
- for (i = 0; i < MAILARGS_STRING_COUNT; i++)
- {
- uschar *p;
- uschar *s = expargs[i];
-
- if (s == NULL) continue;
-
- if (i != mailarg_index_text) for (p = s; *p != 0; p++)
- {
- int c = *p;
- if (i > mailarg_index_text)
- {
- if (!mac_isprint(c))
- {
- *error_pointer = string_sprintf("non-printing character in \"%s\" "
- "in %s command", string_printing(s),
- command_list[commands->command]);
- return FF_ERROR;
- }
- }
-
- /* i < mailarg_index_text */
-
- else if (c == '\n' && !isspace(p[1]))
- {
- if (i < mailarg_index_headers)
- {
- *error_pointer = string_sprintf("\\n not followed by space in "
- "\"%.1024s\" in %s command", string_printing(s),
- command_list[commands->command]);
- return FF_ERROR;
- }
-
- /* Check for the start of a new header line within the string */
-
- else
- {
- uschar *pp;
- for (pp = p + 1;; pp++)
- {
- c = *pp;
- if (c == ':' && pp != p + 1) break;
- if (c == 0 || c == ':' || isspace(*pp))
- {
- *error_pointer = string_sprintf("\\n not followed by space or "
- "valid header name in \"%.1024s\" in %s command",
- string_printing(s), command_list[commands->command]);
- return FF_ERROR;
- }
- pp++;
- }
- p = pp;
- }
- }
- } /* Loop to scan the string */
-
- /* The string is OK */
-
- commands->args[i].u = s;
- }
-
- /* Proceed with mail or vacation command */
-
- if (filter_test != NULL)
- {
- uschar *to = commands->args[mailarg_index_to].u;
- indent();
- printf("%sail to: %s%s%s\n", (commands->seen)? "Seen m" : "M",
- (to == NULL)? US"<default>" : to,
- (commands->command == vacation_command)? " (vacation)" : "",
- (commands->noerror)? " (noerror)" : "");
- for (i = 1; i < MAILARGS_STRING_COUNT; i++)
- {
- uschar *arg = commands->args[i].u;
- if (arg != NULL)
- {
- int len = Ustrlen(mailargs[i]);
- int indent = (debug_selector != 0)? output_indent : 0;
- while (len++ < 7 + indent) printf(" ");
- printf("%s: %s%s\n", mailargs[i], string_printing(arg),
- (commands->args[mailarg_index_expand].u != NULL &&
- Ustrcmp(mailargs[i], "file") == 0)? " (expanded)" : "");
- }
- }
- if (commands->args[mailarg_index_return].u != NULL)
- printf("Return original message\n");
- }
- else
- {
- uschar *tt;
- uschar *to = commands->args[mailarg_index_to].u;
- 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 */
-
- DEBUG(D_filter)
- {
- debug_printf("Filter: %smail to: %s%s%s\n",
- (commands->seen)? "seen " : "",
- to,
- (commands->command == vacation_command)? " (vacation)" : "",
- (commands->noerror)? " (noerror)" : "");
- for (i = 1; i < MAILARGS_STRING_COUNT; i++)
- {
- uschar *arg = commands->args[i].u;
- if (arg != NULL)
- {
- int len = Ustrlen(mailargs[i]);
- while (len++ < 15) debug_printf(" ");
- debug_printf("%s: %s%s\n", mailargs[i], string_printing(arg),
- (commands->args[mailarg_index_expand].u != NULL &&
- Ustrcmp(mailargs[i], "file") == 0)? " (expanded)" : "");
- }
- }
- }
-
- /* Create the "address" for the autoreply */
-
- addr = deliver_make_addr(string_sprintf(">%.256s", to), FALSE);
- setflag(addr, af_pfr);
- if (commands->noerror) setflag(addr, af_ignore_error);
- addr->next = *generated;
- *generated = addr;
- addr->reply = store_get(sizeof(reply_item));
- addr->reply->from = NULL;
- addr->reply->to = string_copy(to);
- addr->reply->file_expand =
- commands->args[mailarg_index_expand].u != NULL;
- addr->reply->expand_forbid = expand_forbid;
- addr->reply->return_message =
- commands->args[mailarg_index_return].u != NULL;
- addr->reply->once_repeat = 0;
-
- if (commands->args[mailarg_index_once_repeat].u != NULL)
- {
- addr->reply->once_repeat =
- readconf_readtime(commands->args[mailarg_index_once_repeat].u, 0,
- FALSE);
- if (addr->reply->once_repeat < 0)
- {
- *error_pointer = string_sprintf("Bad time value for \"once_repeat\" "
- "in mail or vacation command: %s",
- commands->args[mailarg_index_once_repeat]);
- return FF_ERROR;
- }
- }
-
- /* Set up all the remaining string arguments (those other than "to") */
-
- for (i = 1; i < mailargs_string_passed; i++)
- {
- uschar *ss = commands->args[i].u;
- *((uschar **)(((uschar *)(addr->reply)) + reply_offsets[i])) =
- (ss == NULL)? NULL : string_copy(ss);
- }
- }
- break;
case testprint_command:
- if (filter_test != NULL || (debug_selector & D_filter) != 0)
- {
- uschar *s = string_printing(expargs[0]);
- if (filter_test == NULL)
- debug_printf("Filter: testprint: %s\n", s);
- else
- printf("Testprint: %s\n", s);
- }
+ if (filter_test != FTEST_NONE || (debug_selector & D_filter) != 0)
+ {
+ 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);
+ }
}
commands = commands->next;
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);
expect_endif = 0;
output_indent = 0;
-filter_running = TRUE;
+f.filter_running = TRUE;
for (i = 0; i < FILTER_VARIABLE_COUNT; i++) filter_n[i] = 0;
/* To save having to pass certain values about all the time, make them static.
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;
+f.filter_running = FALSE;
headers_charset = save_headers_charset;
DEBUG(D_route) debug_printf("Filter: end of processing\n");