X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/4b233853511d46ecd2a39c06994e3a79ed944ed1..2d47f67729aecd3dcbacdfd303b719893f2d61fa:/src/src/filter.c diff --git a/src/src/filter.c b/src/src/filter.c index c9ff215dd..59ab3192f 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/filter.c,v 1.5 2005/10/03 11:26:21 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2005 */ +/* 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); } } @@ -521,14 +519,14 @@ for (;;) 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; @@ -542,7 +540,7 @@ for (;;) 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\" " @@ -554,18 +552,13 @@ for (;;) 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); } @@ -579,7 +572,7 @@ for (;;) 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 ". */ @@ -590,13 +583,13 @@ for (;;) 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; } } @@ -614,22 +607,19 @@ for (;;) 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); } } @@ -666,7 +656,7 @@ for (;;) { 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 @@ -675,7 +665,7 @@ for (;;) 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; } @@ -709,21 +699,21 @@ for (;;) 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; @@ -777,7 +767,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 +790,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 +1034,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 +1313,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 +1334,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 +1423,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; @@ -1804,7 +1813,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; @@ -1834,11 +1843,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 @@ -2003,7 +2013,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,7 +2049,7 @@ 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 != FTEST_NONE) @@ -2166,7 +2176,6 @@ while (commands != NULL) string_printing(s), command_list[commands->command]); return FF_ERROR; } - pp++; } p = pp; } @@ -2207,12 +2216,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 +2249,58 @@ 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; + + /* Ignore empty addresses and errors; an error will occur later if + there's something really bad. */ + + if (recipient != NULL) + { + log_addr = string_catn(log_addr, &size, &ptr, + log_addr ? US"," : US">", 1); + log_addr = string_cat(log_addr, &size, &ptr, recipient); + } + + /* Check size */ + + if (ptr > 256) + { + log_addr = string_catn(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(string_sprintf(">%.256s", to), FALSE); - setflag(addr, af_pfr); + 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; @@ -2280,7 +2342,7 @@ while (commands != NULL) case testprint_command: if (filter_test != FTEST_NONE || (debug_selector & D_filter) != 0) { - uschar *s = string_printing(expargs[0]); + const uschar *s = string_printing(expargs[0]); if (filter_test == FTEST_NONE) debug_printf("Filter: testprint: %s\n", s); else