1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) The Exim Maintainers 2020 - 2023 */
6 /* Copyright (c) University of Cambridge 1995 - 2018 */
7 /* See the file NOTICE for conditions of use and distribution. */
8 /* SPDX-License-Identifier: GPL-2.0-or-later */
11 /* Code for mail filtering functions. */
16 /* Command arguments and left/right points in conditions can contain different
17 types of data, depending on the particular command or condition. Originally,
18 (void *) was used as "any old type", with casts, but this gives trouble and
19 warnings in some environments. So now it is done "properly", with a union. We
20 need to declare the structures first because some of them are recursive. */
23 struct condition_block;
26 struct string_item *a;
28 struct condition_block *c;
34 /* Local structures used in this module */
36 typedef struct filter_cmd {
37 struct filter_cmd *next;
41 union argtypes args[1];
44 typedef struct condition_block {
45 struct condition_block *parent;
52 /* Miscellaneous other declarations */
54 static uschar **error_pointer;
55 static const uschar *log_filename;
56 static int filter_options;
57 static int line_number;
58 static int expect_endif;
59 static int had_else_endif;
62 static int output_indent;
63 static BOOL filter_delivered;
64 static BOOL finish_obeyed;
65 static BOOL seen_force;
66 static BOOL seen_value;
67 static BOOL noerror_force;
69 enum { had_neither, had_else, had_elif, had_endif };
71 static BOOL read_command_list(const uschar **, filter_cmd ***, BOOL);
74 /* The string arguments for the mail command. The header line ones (that are
75 permitted to include \n followed by white space) first, and then the body text
76 one (it can have \n anywhere). Then the file names and once_repeat, which may
79 static const char *mailargs[] = { /* "to" must be first, and */
80 "to", /* "cc" and "bcc" must follow */
86 "extra_headers", /* miscellaneous added header lines */
94 /* The count of string arguments */
96 #define MAILARGS_STRING_COUNT (nelem(mailargs))
98 /* The count of string arguments that are actually passed over as strings
99 (once_repeat is converted to an int). */
101 #define mailargs_string_passed (MAILARGS_STRING_COUNT - 1)
103 /* This defines the offsets for the arguments; first the string ones, and
104 then the non-string ones. The order must be as above. */
106 enum { mailarg_index_to,
110 mailarg_index_reply_to,
111 mailarg_index_subject,
112 mailarg_index_headers, /* misc headers must be last */
113 mailarg_index_text, /* text is first after headers */
114 mailarg_index_file, /* between text and expand are filenames */
117 mailarg_index_once_repeat, /* a time string */
118 mailarg_index_expand, /* first non-string argument */
119 mailarg_index_return,
120 mailargs_total /* total number of arguments */
123 /* Offsets in the data structure for the string arguments (note that
124 once_repeat isn't a string argument at this point.) */
126 static int reply_offsets[] = { /* must be in same order as above */
127 offsetof(reply_item, to),
128 offsetof(reply_item, cc),
129 offsetof(reply_item, bcc),
130 offsetof(reply_item, from),
131 offsetof(reply_item, reply_to),
132 offsetof(reply_item, subject),
133 offsetof(reply_item, headers),
134 offsetof(reply_item, text),
135 offsetof(reply_item, file),
136 offsetof(reply_item, logfile),
137 offsetof(reply_item, oncelog),
140 /* Condition identities and names, with negated versions for some
143 enum { cond_and, cond_or, cond_personal, cond_begins, cond_BEGINS,
144 cond_ends, cond_ENDS, cond_is, cond_IS, cond_matches,
145 cond_MATCHES, cond_contains, cond_CONTAINS, cond_delivered,
146 cond_above, cond_below, cond_errormsg, cond_firsttime,
147 cond_manualthaw, cond_foranyaddress };
149 static const char *cond_names[] = {
150 "and", "or", "personal",
151 "begins", "BEGINS", "ends", "ENDS",
152 "is", "IS", "matches", "MATCHES", "contains",
153 "CONTAINS", "delivered", "above", "below", "error_message",
154 "first_delivery", "manually_thawed", "foranyaddress" };
156 static const char *cond_not_names[] = {
157 "", "", "not personal",
158 "does not begin", "does not BEGIN",
159 "does not end", "does not END",
160 "is not", "IS not", "does not match",
161 "does not MATCH", "does not contain", "does not CONTAIN",
162 "not delivered", "not above", "not below", "not error_message",
163 "not first_delivery", "not manually_thawed", "not foranyaddress" };
165 /* Tables of binary condition words and their corresponding types. Not easy
166 to amalgamate with the above because of the different variants. */
168 static const char *cond_words[] = {
190 static int cond_word_count = nelem(cond_words);
192 static int cond_types[] = { cond_BEGINS, cond_BEGINS, cond_CONTAINS,
193 cond_CONTAINS, cond_ENDS, cond_ENDS, cond_IS, cond_MATCHES, cond_MATCHES,
194 cond_above, cond_begins, cond_begins, cond_below, cond_contains,
195 cond_contains, cond_ends, cond_ends, cond_is, cond_matches, cond_matches };
197 /* Command identities */
199 enum { add_command, defer_command, deliver_command, elif_command, else_command,
200 endif_command, finish_command, fail_command, freeze_command,
201 headers_command, if_command, logfile_command, logwrite_command,
202 mail_command, noerror_command, pipe_command, save_command, seen_command,
203 testprint_command, unseen_command, vacation_command };
205 static const char * command_list[] = {
206 [add_command] = "add",
207 [defer_command] = "defer",
208 [deliver_command] = "deliver",
209 [elif_command] = "elif",
210 [else_command] = "else",
211 [endif_command] = "endif",
212 [finish_command] = "finish",
213 [fail_command] = "fail",
214 [freeze_command] = "freeze",
215 [headers_command] = "headers",
217 [logfile_command] = "logfile",
218 [logwrite_command] = "logwrite",
219 [mail_command] = "mail",
220 [noerror_command] = "noerror",
221 [pipe_command] = "pipe",
222 [save_command] = "save",
223 [seen_command] = "seen",
224 [testprint_command] = "testprint",
225 [unseen_command] = "unseen",
226 [vacation_command] = "vacation"
229 static int command_list_count = nelem(command_list);
231 /* This table contains the number of expanded arguments in the bottom 4 bits.
232 If the top bit is set, it means that the default for the command is "seen". */
234 static uschar command_exparg_count[] = {
237 [deliver_command] = 128+2,
241 [finish_command] = 0,
243 [freeze_command] = 1,
244 [headers_command] = 1,
246 [logfile_command] = 1,
247 [logwrite_command] = 1,
248 [mail_command] = MAILARGS_STRING_COUNT,
249 [noerror_command] = 0,
250 [pipe_command] = 128+0,
251 [save_command] = 128+1,
253 [testprint_command] = 1,
254 [unseen_command] = 0,
255 [vacation_command] = MAILARGS_STRING_COUNT
260 /*************************************************
261 * Find next significant uschar *
262 *************************************************/
264 /* Function to skip over white space and, optionally, comments.
267 ptr pointer to next character
268 comment_allowed if TRUE, comments (# to \n) are skipped
270 Returns: pointer to next non-whitespace character
273 static const uschar *
274 nextsigchar(const uschar *ptr, BOOL comment_allowed)
278 while (isspace(*ptr))
279 if (*ptr++ == '\n') line_number++;
280 if (comment_allowed && *ptr == '#')
281 while (*++ptr != '\n' && *ptr) ;
290 /*************************************************
292 *************************************************/
294 /* The terminator is white space unless bracket is TRUE, in which
295 case ( and ) terminate.
298 ptr pointer to next character
299 buffer where to put the word
301 bracket if TRUE, terminate on ( and ) as well as space
303 Returns: pointer to the next significant character after the word
306 static const uschar *
307 nextword(const uschar *ptr, uschar *buffer, int size, BOOL bracket)
309 uschar * bp = buffer;
310 while (*ptr && !isspace(*ptr) &&
311 (!bracket || (*ptr != '(' && *ptr != ')')))
312 if (bp - buffer < size - 1)
316 *error_pointer = string_sprintf("word is too long in line %d of "
317 "filter file (max = %d chars)", line_number, size);
322 return nextsigchar(ptr, TRUE);
327 /*************************************************
329 *************************************************/
331 /* Might be a word, or might be a quoted string; in the latter case
335 ptr pointer to next character
336 buffer where to put the item
338 bracket if TRUE, terminate non-quoted on ( and ) as well as space
340 Returns: the next significant character after the item
343 static const uschar *
344 nextitem(const uschar *ptr, uschar *buffer, int size, BOOL bracket)
347 if (*ptr != '\"') return nextword(ptr, buffer, size, bracket);
349 while (*++ptr && *ptr != '\"' && *ptr != '\n')
351 if (bp - buffer >= size - 1)
353 *error_pointer = string_sprintf("string is too long in line %d of "
354 "filter file (max = %d chars)", line_number, size);
358 if (*ptr != '\\') *bp++ = *ptr; else
360 if (isspace(ptr[1])) /* \<whitespace>NL<whitespace> ignored */
362 const uschar *p = ptr + 1;
363 while (*p != '\n' && isspace(*p)) p++;
368 while (ptr[1] != '\n' && isspace(ptr[1])) ptr++;
373 *bp++ = string_interpret_escape(CUSS &ptr);
377 if (*ptr == '\"') ptr++;
378 else if (*error_pointer == NULL)
379 *error_pointer = string_sprintf("quote missing at end of string "
380 "in line %d", line_number);
383 return nextsigchar(ptr, TRUE);
389 /*************************************************
390 * Convert a string + K|M to a number *
391 *************************************************/
395 s points to text string
396 OK set TRUE if a valid number was read
398 Returns: the number, or 0 on error (with *OK FALSE)
402 get_number(const uschar *s, BOOL *ok)
406 if (sscanf(CS s, "%i%n", &value, &count) != 1) return 0;
407 if (tolower(s[count]) == 'k') { value *= 1024; count++; }
408 if (tolower(s[count]) == 'm') { value *= 1024*1024; count++; }
409 while (isspace(s[count])) count++;
410 if (s[count]) return 0;
417 /*************************************************
418 * Read one condition *
419 *************************************************/
421 /* A complete condition must be terminated by "then"; bracketed internal
422 conditions must be terminated by a closing bracket. They are read by calling
423 this function recursively.
426 ptr points to start of condition
427 condition_block where to hang the created condition block
428 toplevel TRUE when called at the top level
430 Returns: points to next character after "then"
433 static const uschar *
434 read_condition(const uschar *ptr, condition_block **cond, BOOL toplevel)
438 condition_block *current_parent = NULL;
439 condition_block **current = cond;
443 /* Loop to read next condition */
449 /* reaching the end of the input is an error. */
453 *error_pointer = US"\"then\" missing at end of filter file";
457 /* Opening bracket at the start of a condition introduces a nested
458 condition, which must be terminated by a closing bracket. */
462 ptr = read_condition(nextsigchar(ptr+1, TRUE), &c, FALSE);
463 if (*error_pointer != NULL) break;
466 *error_pointer = string_sprintf("expected \")\" in line %d of "
467 "filter file", line_number);
472 c->testfor = !c->testfor;
475 ptr = nextsigchar(ptr+1, TRUE);
479 /* Closing bracket at the start of a condition is an error. Give an
480 explicit message, as otherwise "unknown condition" would be confusing. */
482 else if (*ptr == ')')
484 *error_pointer = string_sprintf("unexpected \")\" in line %d of "
485 "filter file", line_number);
489 /* Otherwise we expect a word or a string. */
493 ptr = nextitem(ptr, buffer, sizeof(buffer), TRUE);
494 if (*error_pointer) break;
496 /* "Then" at the start of a condition is an error */
498 if (Ustrcmp(buffer, "then") == 0)
500 *error_pointer = string_sprintf("unexpected \"then\" near line %d of "
501 "filter file", line_number);
505 /* "Not" at the start of a condition negates the testing condition. */
507 if (Ustrcmp(buffer, "not") == 0)
513 /* Build a condition block from the specific word. */
515 c = store_get(sizeof(condition_block), GET_UNTAINTED);
516 c->left.u = c->right.u = NULL;
517 c->testfor = testfor;
520 /* Check for conditions that start with a keyword */
522 if (Ustrcmp(buffer, "delivered") == 0) c->type = cond_delivered;
523 else if (Ustrcmp(buffer, "error_message") == 0) c->type = cond_errormsg;
524 else if (Ustrcmp(buffer, "first_delivery") == 0) c->type = cond_firsttime;
525 else if (Ustrcmp(buffer, "manually_thawed") == 0) c->type = cond_manualthaw;
527 /* Personal can be followed by any number of aliases */
529 else if (Ustrcmp(buffer, "personal") == 0)
531 c->type = cond_personal;
535 const uschar * saveptr = ptr;
536 ptr = nextword(ptr, buffer, sizeof(buffer), TRUE);
537 if (*error_pointer) break;
538 if (Ustrcmp(buffer, "alias") != 0)
543 ptr = nextitem(ptr, buffer, sizeof(buffer), TRUE);
544 if (*error_pointer) break;
545 aa = store_get(sizeof(string_item), GET_UNTAINTED);
546 aa->text = string_copy(buffer);
547 aa->next = c->left.a;
552 /* Foranyaddress must be followed by a string and a condition enclosed
553 in parentheses, which is handled as a subcondition. */
555 else if (Ustrcmp(buffer, "foranyaddress") == 0)
557 ptr = nextitem(ptr, buffer, sizeof(buffer), TRUE);
558 if (*error_pointer) break;
561 *error_pointer = string_sprintf("\"(\" expected after \"foranyaddress\" "
562 "near line %d of filter file", line_number);
566 c->type = cond_foranyaddress;
567 c->left.u = string_copy(buffer);
569 ptr = read_condition(nextsigchar(ptr+1, TRUE), &(c->right.c), FALSE);
570 if (*error_pointer) break;
573 *error_pointer = string_sprintf("expected \")\" in line %d of "
574 "filter file", line_number);
577 ptr = nextsigchar(ptr+1, TRUE);
580 /* If it's not a word we recognize, then it must be the lefthand
581 operand of one of the comparison words. */
586 const uschar *isptr = NULL;
588 c->left.u = string_copy(buffer);
589 ptr = nextword(ptr, buffer, sizeof(buffer), TRUE);
590 if (*error_pointer) break;
592 /* Handle "does|is [not]", preserving the pointer after "is" in
593 case it isn't that, but the form "is <string>". */
595 if (strcmpic(buffer, US"does") == 0 || strcmpic(buffer, US"is") == 0)
597 if (buffer[0] == 'i') { c->type = cond_is; isptr = ptr; }
598 if (buffer[0] == 'I') { c->type = cond_IS; isptr = ptr; }
600 ptr = nextword(ptr, buffer, sizeof(buffer), TRUE);
601 if (*error_pointer) break;
602 if (strcmpic(buffer, US"not") == 0)
604 c->testfor = !c->testfor;
605 if (isptr) isptr = ptr;
606 ptr = nextword(ptr, buffer, sizeof(buffer), TRUE);
607 if (*error_pointer) break;
611 for (i = 0; i < cond_word_count; i++)
613 if (Ustrcmp(buffer, cond_words[i]) == 0)
615 c->type = cond_types[i];
620 /* If an unknown word follows "is" or "is not"
621 it's actually the argument. Reset to read it. */
623 if (i >= cond_word_count)
627 *error_pointer = string_sprintf("unrecognized condition word \"%s\" "
628 "near line %d of filter file", buffer, line_number);
634 /* Get the RH argument. */
636 ptr = nextitem(ptr, buffer, sizeof(buffer), TRUE);
637 if (*error_pointer) break;
638 c->right.u = string_copy(buffer);
642 /* We have read some new condition and set it up in the condition block
643 c; point the current pointer at it, and then deal with what follows. */
647 /* Closing bracket terminates if this is a lower-level condition. Otherwise
653 *error_pointer = string_sprintf("unexpected \")\" in line %d of "
654 "filter file", line_number);
658 /* Opening bracket following a condition is an error; give an explicit
659 message to make it clearer what is wrong. */
661 else if (*ptr == '(')
663 *error_pointer = string_sprintf("unexpected \"(\" in line %d of "
664 "filter file", line_number);
668 /* Otherwise the next thing must be one of the words "and", "or" or "then" */
672 // const uschar *saveptr = ptr;
673 ptr = nextword(ptr, buffer, sizeof(buffer), FALSE);
674 if (*error_pointer) break;
676 /* "Then" terminates a toplevel condition; otherwise a closing bracket
677 has been omitted. Put a string terminator at the start of "then" so
678 that reflecting the condition can be done when testing. */
679 /*XXX This stops us doing a constification job in this file, unfortunately.
680 Comment it out and see if anything breaks.
681 With one addition down at DEFERFREEZEFAIL it passes the testsuite. */
683 if (Ustrcmp(buffer, "then") == 0)
685 // if (toplevel) *saveptr = 0;
688 *error_pointer = string_sprintf("missing \")\" at end of "
689 "condition near line %d of filter file", line_number);
693 /* "And" causes a new condition block to replace the one we have
694 just read, which becomes the left sub-condition. The current pointer
695 is reset to the pointer for the right sub-condition. We have to keep
696 track of the tree of sequential "ands", so as to traverse back up it
697 if an "or" is met. */
699 else if (Ustrcmp(buffer, "and") == 0)
701 condition_block * andc = store_get(sizeof(condition_block), GET_UNTAINTED);
702 andc->parent = current_parent;
703 andc->type = cond_and;
704 andc->testfor = TRUE;
706 andc->right.u = NULL; /* insurance */
708 current = &(andc->right.c);
709 current_parent = andc;
712 /* "Or" is similar, but has to be done a bit more carefully to
713 ensure that "and" is more binding. If there's a parent set, we
714 are following a sequence of "and"s and must track back to their
717 else if (Ustrcmp(buffer, "or") == 0)
719 condition_block * orc = store_get(sizeof(condition_block), GET_UNTAINTED);
720 condition_block * or_parent = NULL;
724 while (current_parent->parent &&
725 current_parent->parent->type == cond_and)
726 current_parent = current_parent->parent;
728 /* If the parent has a parent, it must be an "or" parent. */
730 if (current_parent->parent)
731 or_parent = current_parent->parent;
734 orc->parent = or_parent;
735 if (!or_parent) *cond = orc;
736 else or_parent->right.c = orc;
739 orc->left.c = (current_parent == NULL)? c : current_parent;
740 orc->right.c = NULL; /* insurance */
741 current = &(orc->right.c);
742 current_parent = orc;
745 /* Otherwise there is a disaster */
749 *error_pointer = string_sprintf("\"and\" or \"or\" or \"%s\" "
750 "expected near line %d of filter file, but found \"%s\"",
751 toplevel? "then" : ")", line_number, buffer);
757 return nextsigchar(ptr, TRUE);
762 /*************************************************
763 * Output the current indent *
764 *************************************************/
770 for (i = 0; i < output_indent; i++) debug_printf(" ");
775 /*************************************************
776 * Condition printer: for debugging *
777 *************************************************/
781 c the block at the top of the tree
782 toplevel TRUE at toplevel - stops overall brackets
788 print_condition(condition_block *c, BOOL toplevel)
790 const char *name = (c->testfor)? cond_names[c->type] : cond_not_names[c->type];
797 case cond_manualthaw:
798 debug_printf("%s", name);
813 debug_printf("%s %s %s", c->left.u, name, c->right.u);
817 if (!c->testfor) debug_printf("not (");
818 print_condition(c->left.c, FALSE);
819 debug_printf(" %s ", cond_names[c->type]);
820 print_condition(c->right.c, FALSE);
821 if (!c->testfor) debug_printf(")");
825 if (!c->testfor) debug_printf("not (");
826 else if (!toplevel) debug_printf("(");
827 print_condition(c->left.c, FALSE);
828 debug_printf(" %s ", cond_names[c->type]);
829 print_condition(c->right.c, FALSE);
830 if (!toplevel || !c->testfor) debug_printf(")");
833 case cond_foranyaddress:
834 debug_printf("%s %s (", name, c->left.u);
835 print_condition(c->right.c, FALSE);
844 /*************************************************
845 * Read one filtering command *
846 *************************************************/
850 pptr points to pointer to first character of command; the pointer
851 is updated to point after the last character read
852 lastcmdptr points to pointer to pointer to last command; used for hanging
853 on the newly read command
855 Returns: TRUE if command successfully read, else FALSE
859 read_command(const uschar **pptr, filter_cmd ***lastcmdptr)
861 int command, i, cmd_bit;
862 filter_cmd *new, **newlastcmdptr;
864 BOOL was_seen_or_unseen = FALSE;
865 BOOL was_noerror = FALSE;
867 const uschar *ptr = *pptr;
868 const uschar *saveptr;
871 /* Read the next word and find which command it is. Command words are normally
872 terminated by white space, but there are two exceptions, which are the "if" and
873 "elif" commands. We must allow for them to be terminated by an opening bracket,
874 as brackets are allowed in conditions and users will expect not to require
877 *buffer = '\0'; /* compiler quietening */
879 if (Ustrncmp(ptr, "if(", 3) == 0)
881 Ustrcpy(buffer, US"if");
884 else if (Ustrncmp(ptr, "elif(", 5) == 0)
886 Ustrcpy(buffer, US"elif");
891 ptr = nextword(ptr, buffer, sizeof(buffer), FALSE);
892 if (*error_pointer) return FALSE;
895 for (command = 0; command < command_list_count; command++)
896 if (Ustrcmp(buffer, command_list[command]) == 0) break;
898 /* Handle the individual commands */
902 /* Add takes two arguments, separated by the word "to". Headers has two
903 arguments, but the first must be "add", "remove", or "charset", and it gets
904 stored in the second argument slot. Neither may be preceded by seen, unseen
908 case headers_command:
909 if (seen_force || noerror_force)
911 *error_pointer = string_sprintf("\"seen\", \"unseen\", or \"noerror\" "
912 "found before an \"%s\" command near line %d",
913 command_list[command], line_number);
918 /* Logwrite, logfile, pipe, and testprint all take a single argument, save
919 and logfile can have an option second argument for the mode, and deliver can
920 have "errors_to <address>" in a system filter, or in a user filter if the
921 address is the current one. */
923 case deliver_command:
924 case logfile_command:
925 case logwrite_command:
928 case testprint_command:
930 ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE);
932 *error_pointer = string_sprintf("\"%s\" requires an argument "
933 "near line %d of filter file", command_list[command], line_number);
935 if (*error_pointer) yield = FALSE; else
937 union argtypes argument, second_argument;
939 argument.u = second_argument.u = NULL;
941 if (command == add_command)
943 argument.u = string_copy(buffer);
944 ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE);
945 if (!*buffer || Ustrcmp(buffer, "to") != 0)
946 *error_pointer = string_sprintf("\"to\" expected in \"add\" command "
947 "near line %d of filter file", line_number);
950 ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE);
952 *error_pointer = string_sprintf("value missing after \"to\" "
953 "near line %d of filter file", line_number);
954 else second_argument.u = string_copy(buffer);
958 else if (command == headers_command)
960 if (Ustrcmp(buffer, "add") == 0)
961 second_argument.b = TRUE;
963 if (Ustrcmp(buffer, "remove") == 0) second_argument.b = FALSE;
965 if (Ustrcmp(buffer, "charset") == 0)
966 second_argument.b = TRUE_UNSET;
969 *error_pointer = string_sprintf("\"add\", \"remove\", or \"charset\" "
970 "expected after \"headers\" near line %d of filter file",
975 if (!f.system_filtering && second_argument.b != TRUE_UNSET)
977 *error_pointer = string_sprintf("header addition and removal is "
978 "available only in system filters: near line %d of filter file",
986 ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE);
988 *error_pointer = string_sprintf("value missing after \"add\", "
989 "\"remove\", or \"charset\" near line %d of filter file",
991 else argument.u = string_copy(buffer);
995 /* The argument for the logwrite command must end in a newline, and the save
996 and logfile commands can have an optional mode argument. The deliver
997 command can have an optional "errors_to <address>" for a system filter,
998 or for a user filter if the address is the user's address. Accept the
999 syntax here - the check is later. */
1003 if (command == logwrite_command)
1005 int len = Ustrlen(buffer);
1006 if (len == 0 || buffer[len-1] != '\n') Ustrcat(buffer, US"\n");
1009 argument.u = string_copy(buffer);
1011 if (command == save_command || command == logfile_command)
1015 ptr = nextword(ptr, buffer, sizeof(buffer), FALSE);
1016 second_argument.i = (int)Ustrtol(buffer, NULL, 8);
1018 else second_argument.i = -1;
1021 else if (command == deliver_command)
1023 const uschar *save_ptr = ptr;
1024 ptr = nextword(ptr, buffer, sizeof(buffer), FALSE);
1025 if (Ustrcmp(buffer, "errors_to") == 0)
1027 ptr = nextword(ptr, buffer, sizeof(buffer), FALSE);
1028 second_argument.u = string_copy(buffer);
1030 else ptr = save_ptr;
1034 /* Set up the command block. Seen defaults TRUE for delivery commands,
1035 FALSE for logging commands, and it doesn't matter for testprint, as
1036 that doesn't change the "delivered" status. */
1038 if (*error_pointer) yield = FALSE;
1041 new = store_get(sizeof(filter_cmd) + sizeof(union argtypes), GET_UNTAINTED);
1044 *lastcmdptr = &(new->next);
1045 new->command = command;
1046 new->seen = seen_force? seen_value : command_exparg_count[command] >= 128;
1047 new->noerror = noerror_force;
1048 new->args[0] = argument;
1049 new->args[1] = second_argument;
1055 /* Elif, else and endif just set a flag if expected. */
1060 if (seen_force || noerror_force)
1062 *error_pointer = string_sprintf("\"seen\", \"unseen\", or \"noerror\" "
1063 "near line %d is not followed by a command", line_number);
1067 if (expect_endif > 0)
1068 had_else_endif = (command == elif_command)? had_elif :
1069 (command == else_command)? had_else : had_endif;
1072 *error_pointer = string_sprintf("unexpected \"%s\" command near "
1073 "line %d of filter file", buffer, line_number);
1079 /* Defer, freeze, and fail are available only if permitted. */
1082 cmd_bit = RDO_DEFER;
1083 goto DEFER_FREEZE_FAIL;
1087 goto DEFER_FREEZE_FAIL;
1089 case freeze_command:
1090 cmd_bit = RDO_FREEZE;
1093 if ((filter_options & cmd_bit) == 0)
1095 *error_pointer = string_sprintf("filtering command \"%s\" is disabled: "
1096 "near line %d of filter file", buffer, line_number);
1101 /* A text message can be provided after the "text" keyword, or
1102 as a string in quotes. */
1105 ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE);
1106 if (*saveptr != '\"' && (!*buffer || Ustrcmp(buffer, "text") != 0))
1113 if (*saveptr != '\"')
1114 ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE);
1115 fmsg = string_copy(buffer);
1118 /* Drop through and treat as "finish", but never set "seen". */
1122 /* Finish has no arguments; fmsg defaults to NULL */
1124 case finish_command:
1125 new = store_get(sizeof(filter_cmd), GET_UNTAINTED);
1128 *lastcmdptr = &(new->next);
1129 new->command = command;
1130 new->seen = seen_force ? seen_value : FALSE;
1131 new->args[0].u = fmsg;
1135 /* Seen, unseen, and noerror are not allowed before if, which takes a
1136 condition argument and then and else sub-commands. */
1139 if (seen_force || noerror_force)
1141 *error_pointer = string_sprintf("\"seen\", \"unseen\", or \"noerror\" "
1142 "found before an \"if\" command near line %d",
1147 /* Set up the command block for if */
1149 new = store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), GET_UNTAINTED);
1152 *lastcmdptr = &new->next;
1153 new->command = command;
1155 new->args[0].u = NULL;
1156 new->args[1].u = new->args[2].u = NULL;
1157 new->args[3].u = ptr;
1159 /* Read the condition */
1161 ptr = read_condition(ptr, &new->args[0].c, TRUE);
1162 if (*error_pointer) { yield = FALSE; break; }
1164 /* Read the commands to be obeyed if the condition is true */
1166 newlastcmdptr = &(new->args[1].f);
1167 if (!read_command_list(&ptr, &newlastcmdptr, TRUE)) yield = FALSE;
1169 /* If commands were successfully read, handle the various possible
1170 terminators. There may be a number of successive "elif" sections. */
1174 while (had_else_endif == had_elif)
1176 filter_cmd *newnew =
1177 store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), GET_UNTAINTED);
1178 new->args[2].f = newnew;
1181 new->command = command;
1183 new->args[0].u = NULL;
1184 new->args[1].u = new->args[2].u = NULL;
1185 new->args[3].u = ptr;
1187 ptr = read_condition(ptr, &new->args[0].c, TRUE);
1188 if (*error_pointer) { yield = FALSE; break; }
1189 newlastcmdptr = &(new->args[1].f);
1190 if (!read_command_list(&ptr, &newlastcmdptr, TRUE))
1194 if (yield == FALSE) break;
1196 /* Handle termination by "else", possibly following one or more
1197 "elsif" sections. */
1199 if (had_else_endif == had_else)
1201 newlastcmdptr = &(new->args[2].f);
1202 if (!read_command_list(&ptr, &newlastcmdptr, TRUE))
1204 else if (had_else_endif != had_endif)
1206 *error_pointer = string_sprintf("\"endif\" missing near line %d of "
1207 "filter file", line_number);
1212 /* Otherwise the terminator was "endif" - this is checked by
1213 read_command_list(). The pointer is already set to NULL. */
1216 /* Reset the terminator flag. */
1218 had_else_endif = had_neither;
1222 /* The mail & vacation commands have a whole slew of keyworded arguments.
1223 The final argument values are the file expand and return message booleans,
1224 whose offsets are defined in mailarg_index_{expand,return}. Although they
1225 are logically booleans, because they are stored in a uschar * value, we use
1226 NULL and not FALSE, to keep 64-bit compilers happy. */
1229 case vacation_command:
1230 new = store_get(sizeof(filter_cmd) + mailargs_total * sizeof(union argtypes), GET_UNTAINTED);
1232 new->command = command;
1233 new->seen = seen_force ? seen_value : FALSE;
1234 new->noerror = noerror_force;
1235 for (i = 0; i < mailargs_total; i++) new->args[i].u = NULL;
1237 /* Read keyword/value pairs until we hit one that isn't. The data
1238 must contain only printing chars plus tab, though the "text" value
1239 can also contain newlines. The "file" keyword can be preceded by the
1240 word "expand", and "return message" has no data. */
1244 const uschar *saveptr = ptr;
1245 ptr = nextword(ptr, buffer, sizeof(buffer), FALSE);
1247 { yield = FALSE; break; }
1249 /* Ensure "return" is followed by "message"; that's a complete option */
1251 if (Ustrcmp(buffer, "return") == 0)
1253 new->args[mailarg_index_return].u = US""; /* not NULL => TRUE */
1254 ptr = nextword(ptr, buffer, sizeof(buffer), FALSE);
1255 if (Ustrcmp(buffer, "message") != 0)
1257 *error_pointer = string_sprintf("\"return\" not followed by \"message\" "
1258 " near line %d of filter file", line_number);
1265 /* Ensure "expand" is followed by "file", then fall through to process the
1268 if (Ustrcmp(buffer, "expand") == 0)
1270 new->args[mailarg_index_expand].u = US""; /* not NULL => TRUE */
1271 ptr = nextword(ptr, buffer, sizeof(buffer), FALSE);
1272 if (Ustrcmp(buffer, "file") != 0)
1274 *error_pointer = string_sprintf("\"expand\" not followed by \"file\" "
1275 " near line %d of filter file", line_number);
1281 /* Scan for the keyword */
1283 for (i = 0; i < MAILARGS_STRING_COUNT; i++)
1284 if (Ustrcmp(buffer, mailargs[i]) == 0) break;
1286 /* Not found keyword; assume end of this command */
1288 if (i >= MAILARGS_STRING_COUNT)
1294 /* Found keyword, read the data item */
1296 ptr = nextitem(ptr, buffer, sizeof(buffer), FALSE);
1298 { yield = FALSE; break; }
1299 else new->args[i].u = string_copy(buffer);
1302 /* If this is the vacation command, apply some default settings to
1303 some of the arguments. */
1305 if (command == vacation_command)
1307 if (!new->args[mailarg_index_file].u)
1309 new->args[mailarg_index_file].u = string_copy(US".vacation.msg");
1310 new->args[mailarg_index_expand].u = US""; /* not NULL => TRUE */
1312 if (!new->args[mailarg_index_log].u)
1313 new->args[mailarg_index_log].u = string_copy(US".vacation.log");
1314 if (!new->args[mailarg_index_once].u)
1315 new->args[mailarg_index_once].u = string_copy(US".vacation");
1316 if (!new->args[mailarg_index_once_repeat].u)
1317 new->args[mailarg_index_once_repeat].u = string_copy(US"7d");
1318 if (!new->args[mailarg_index_subject].u)
1319 new->args[mailarg_index_subject].u = string_copy(US"On vacation");
1322 /* Join the address on to the chain of generated addresses */
1325 *lastcmdptr = &(new->next);
1329 /* Seen and unseen just set flags */
1332 case unseen_command:
1335 *error_pointer = string_sprintf("\"seen\" or \"unseen\" "
1336 "near line %d is not followed by a command", line_number);
1341 *error_pointer = string_sprintf("\"seen\" or \"unseen\" repeated "
1342 "near line %d", line_number);
1345 seen_value = (command == seen_command);
1347 was_seen_or_unseen = TRUE;
1351 /* So does noerror */
1353 case noerror_command:
1356 *error_pointer = string_sprintf("\"noerror\" "
1357 "near line %d is not followed by a command", line_number);
1360 noerror_force = TRUE;
1368 *error_pointer = string_sprintf("unknown filtering command \"%s\" "
1369 "near line %d of filter file", buffer, line_number);
1374 if (!was_seen_or_unseen && !was_noerror)
1377 noerror_force = FALSE;
1386 /*************************************************
1387 * Read a list of commands *
1388 *************************************************/
1390 /* If conditional is TRUE, the list must be terminated
1391 by the words "else" or "endif".
1394 pptr points to pointer to next character; the pointer is updated
1395 lastcmdptr points to pointer to pointer to previously-read command; used
1396 for hanging on the new command
1397 conditional TRUE if this command is the subject of a condition
1399 Returns: TRUE on success
1403 read_command_list(const uschar **pptr, filter_cmd ***lastcmdptr, BOOL conditional)
1405 if (conditional) expect_endif++;
1406 had_else_endif = had_neither;
1407 while (**pptr && had_else_endif == had_neither)
1409 if (!read_command(pptr, lastcmdptr)) return FALSE;
1410 *pptr = nextsigchar(*pptr, TRUE);
1415 if (had_else_endif == had_neither)
1417 *error_pointer = US"\"endif\" missing at end of filter file";
1427 /*************************************************
1428 * Test a condition *
1429 *************************************************/
1433 c points to the condition block; c->testfor indicated whether
1434 it's a positive or negative condition
1435 toplevel TRUE if called from "if" directly; FALSE otherwise
1437 Returns: TRUE if the condition is met
1441 test_condition(condition_block * c, BOOL toplevel)
1443 BOOL yield = FALSE, textonly_re;
1444 const uschar * exp[2], * p, * pp;
1447 if (!c) return TRUE; /* does this ever occur? */
1452 yield = test_condition(c->left.c, FALSE) &&
1453 *error_pointer == NULL &&
1454 test_condition(c->right.c, FALSE);
1458 yield = test_condition(c->left.c, FALSE) ||
1459 (*error_pointer == NULL &&
1460 test_condition(c->right.c, FALSE));
1463 /* The personal test is meaningless in a system filter. The tests are now in
1464 a separate function (so Sieve can use them). However, an Exim filter does not
1465 scan Cc: (hence the FALSE argument). */
1468 yield = f.system_filtering? FALSE : filter_personal(c->left.a, FALSE);
1471 case cond_delivered:
1472 yield = filter_delivered;
1475 /* Only TRUE if a message is actually being processed; FALSE for address
1476 testing and verification. */
1479 yield = message_id[0] && (!sender_address || !*sender_address);
1482 /* Only FALSE if a message is actually being processed; TRUE for address
1483 and filter testing and verification. */
1485 case cond_firsttime:
1486 yield = filter_test != FTEST_NONE || !message_id[0] || f.deliver_firsttime;
1489 /* Only TRUE if a message is actually being processed; FALSE for address
1490 testing and verification. */
1492 case cond_manualthaw:
1493 yield = message_id[0] && f.deliver_manual_thaw;
1496 /* The foranyaddress condition loops through a list of addresses */
1498 case cond_foranyaddress:
1500 if (!(pp = expand_cstring(p)))
1502 *error_pointer = string_sprintf("failed to expand \"%s\" in "
1503 "filter file: %s", p, expand_string_message);
1508 f.parse_allow_group = TRUE; /* Allow group syntax */
1513 int start, end, domain;
1516 p = parse_find_address_end(pp, FALSE);
1517 s = string_copyn(pp, p - pp);
1519 filter_thisaddress =
1520 parse_extract_address(s, &error, &start, &end, &domain, FALSE);
1522 if (filter_thisaddress)
1524 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
1525 (debug_selector & D_filter) != 0)
1528 debug_printf_indent("Extracted address %s\n", filter_thisaddress);
1530 yield = test_condition(c->right.c, FALSE);
1538 f.parse_allow_group = FALSE; /* Reset group syntax flags */
1539 f.parse_found_group = FALSE;
1542 /* All other conditions have left and right values that need expanding;
1543 on error, it doesn't matter what value is returned. */
1547 for (int i = 0; i < 2; i++)
1549 if (!(exp[i] = expand_string_2(p, &textonly_re)))
1551 *error_pointer = string_sprintf("failed to expand \"%s\" in "
1552 "filter file: %s", p, expand_string_message);
1558 /* Inner switch for the different cases */
1563 yield = strcmpic(exp[0], exp[1]) == 0;
1567 yield = Ustrcmp(exp[0], exp[1]) == 0;
1571 yield = strstric_c(exp[0], exp[1], FALSE) != NULL;
1575 yield = Ustrstr(exp[0], exp[1]) != NULL;
1579 yield = strncmpic(exp[0], exp[1], Ustrlen(exp[1])) == 0;
1583 yield = Ustrncmp(exp[0], exp[1], Ustrlen(exp[1])) == 0;
1589 int len = Ustrlen(exp[1]);
1590 const uschar *s = exp[0] + Ustrlen(exp[0]) - len;
1593 : (c->type == cond_ends ? strcmpic(s, exp[1]) : Ustrcmp(s, exp[1])) == 0;
1600 const pcre2_code * re;
1601 mcs_flags flags = textonly_re ? MCS_CACHEABLE : MCS_NOFLAGS;
1603 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
1604 (debug_selector & D_filter) != 0)
1606 debug_printf_indent("Match expanded arguments:\n");
1607 debug_printf_indent(" Subject = %s\n", exp[0]);
1608 debug_printf_indent(" Pattern = %s\n", exp[1]);
1611 if (c->type == cond_matches) flags |= MCS_CASELESS;
1612 if (!(re = regex_compile(exp[1], flags, error_pointer, pcre_gen_cmp_ctx)))
1615 yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1);
1619 /* For above and below, convert the strings to numbers */
1623 for (int i = 0; i < 2; i++)
1625 val[i] = get_number(exp[i], &yield);
1628 *error_pointer = string_sprintf("malformed numerical string \"%s\"",
1633 yield = c->type == cond_above ? (val[0] > val[1]) : (val[0] < val[1]);
1639 if ((filter_test != FTEST_NONE && debug_selector != 0) ||
1640 (debug_selector & D_filter) != 0)
1643 debug_printf_indent("%sondition is %s: ",
1644 toplevel? "C" : "Sub-c",
1645 (yield == c->testfor)? "true" : "false");
1646 print_condition(c, TRUE);
1647 debug_printf_indent("\n");
1650 return yield == c->testfor;
1655 /*************************************************
1656 * Interpret chain of commands *
1657 *************************************************/
1659 /* In testing state, just say what would be done rather than doing it. The
1660 testprint command just expands and outputs its argument in testing state, and
1661 does nothing otherwise.
1664 commands points to chain of commands to interpret
1665 generated where to hang newly-generated addresses
1667 Returns: FF_DELIVERED success, a significant action was taken
1668 FF_NOTDELIVERED success, no significant action
1669 FF_DEFER defer requested
1670 FF_FAIL fail requested
1671 FF_FREEZE freeze requested
1672 FF_ERROR there was a problem
1676 interpret_commands(filter_cmd *commands, address_item **generated)
1681 BOOL condition_value;
1686 uschar *fmsg, *ff_name;
1687 const uschar *expargs[MAILARGS_STRING_COUNT];
1691 /* Expand the relevant number of arguments for the command that are
1694 for (i = 0; i < (command_exparg_count[commands->command] & 15); i++)
1696 const uschar *ss = commands->args[i].u;
1699 else if (!(expargs[i] = expand_cstring(ss)))
1701 *error_pointer = string_sprintf("failed to expand \"%s\" in "
1702 "%s command: %s", ss, command_list[commands->command],
1703 expand_string_message);
1708 /* Now switch for each command, setting the "delivered" flag if any of them
1711 if (commands->seen) filter_delivered = TRUE;
1713 switch(commands->command)
1716 for (i = 0; i < 2; i++)
1718 const uschar *ss = expargs[i];
1721 if (i == 1 && (*ss++ != 'n' || ss[1] != 0))
1723 *error_pointer = string_sprintf("unknown variable \"%s\" in \"add\" "
1724 "command", expargs[i]);
1728 /* Allow for "--" at the start of the value (from -$n0) for example */
1729 if (i == 0) while (ss[0] == '-' && ss[1] == '-') ss += 2;
1731 n[i] = (int)Ustrtol(ss, &end, 0);
1734 *error_pointer = string_sprintf("malformed number \"%s\" in \"add\" "
1740 filter_n[n[1]] += n[0];
1741 if (filter_test != FTEST_NONE) printf("Add %d to n%d\n", n[0], n[1]);
1744 /* A deliver command's argument must be a valid address. Its optional
1745 second argument (system filter only) must also be a valid address. */
1747 case deliver_command:
1748 for (i = 0; i < 2; i++)
1753 int start, end, domain;
1755 uschar *ss = parse_extract_address(s, &error, &start, &end, &domain,
1758 expargs[i] = filter_options & RDO_REWRITE
1759 ? rewrite_address(ss, TRUE, FALSE, global_rewrite_rules,
1761 : rewrite_address_qualify(ss, TRUE);
1764 *error_pointer = string_sprintf("malformed address \"%s\" in "
1765 "filter file: %s", s, error);
1771 /* Stick the errors address into a simple variable, as it will
1772 be referenced a few times. Check that the caller is permitted to
1777 if (s != NULL && !f.system_filtering)
1779 uschar *ownaddress = expand_string(US"$local_part@$domain");
1780 if (strcmpic(ownaddress, s) != 0)
1782 *error_pointer = US"errors_to must point to the caller's address";
1787 /* Test case: report what would happen */
1789 if (filter_test != FTEST_NONE)
1792 printf("%seliver message to: %s%s%s%s\n",
1793 (commands->seen)? "D" : "Unseen d",
1795 commands->noerror? " (noerror)" : "",
1796 (s != NULL)? " errors_to " : "",
1797 (s != NULL)? s : US"");
1804 DEBUG(D_filter) debug_printf_indent("Filter: %sdeliver message to: %s%s%s%s\n",
1805 (commands->seen)? "" : "unseen ",
1807 commands->noerror? " (noerror)" : "",
1808 (s != NULL)? " errors_to " : "",
1809 (s != NULL)? s : US"");
1811 /* Create the new address and add it to the chain, setting the
1812 af_ignore_error flag if necessary, and the errors address, which can be
1813 set in a system filter and to the local address in user filters. */
1815 addr = deliver_make_addr(US expargs[0], TRUE); /* TRUE => copy s, so deconst ok */
1816 addr->prop.errors_address = !s ? NULL : string_copy(s); /* Default is NULL */
1817 if (commands->noerror) addr->prop.ignore_error = TRUE;
1818 addr->next = *generated;
1825 mode = commands->args[1].i;
1827 /* Test case: report what would happen */
1829 if (filter_test != FTEST_NONE)
1833 printf("%save message to: %s%s\n", (commands->seen)?
1834 "S" : "Unseen s", s, commands->noerror? " (noerror)" : "");
1836 printf("%save message to: %s %04o%s\n", (commands->seen)?
1837 "S" : "Unseen s", s, mode, commands->noerror? " (noerror)" : "");
1840 /* Real case: Ensure save argument starts with / if there is a home
1841 directory to prepend. */
1845 if (s[0] != '/' && (filter_options & RDO_PREPEND_HOME) != 0 &&
1846 deliver_home != NULL && deliver_home[0] != 0)
1847 s = string_sprintf("%s/%s", deliver_home, s);
1848 DEBUG(D_filter) debug_printf_indent("Filter: %ssave message to: %s%s\n",
1849 (commands->seen)? "" : "unseen ", s,
1850 commands->noerror? " (noerror)" : "");
1852 /* Create the new address and add it to the chain, setting the
1853 af_pfr and af_file flags, the af_ignore_error flag if necessary, and the
1856 addr = deliver_make_addr(US s, TRUE); /* TRUE => copy s, so deconst ok */
1857 setflag(addr, af_pfr);
1858 setflag(addr, af_file);
1859 if (commands->noerror) addr->prop.ignore_error = TRUE;
1861 addr->next = *generated;
1867 s = string_copy(commands->args[0].u);
1868 if (filter_test != FTEST_NONE)
1871 printf("%sipe message to: %s%s\n", (commands->seen)?
1872 "P" : "Unseen p", s, commands->noerror? " (noerror)" : "");
1874 else /* Ensure pipe command starts with | */
1876 DEBUG(D_filter) debug_printf_indent("Filter: %spipe message to: %s%s\n",
1877 commands->seen ? "" : "unseen ", s,
1878 commands->noerror ? " (noerror)" : "");
1879 if (s[0] != '|') s = string_sprintf("|%s", s);
1881 /* Create the new address and add it to the chain, setting the
1882 af_ignore_error flag if necessary. Set the af_expand_pipe flag so that
1883 each command argument is expanded in the transport after the command
1884 has been split up into separate arguments. */
1886 addr = deliver_make_addr(US s, TRUE); /* TRUE => copy s, so deconst ok */
1887 setflag(addr, af_pfr);
1888 setflag(addr, af_expand_pipe);
1889 if (commands->noerror) addr->prop.ignore_error = TRUE;
1890 addr->next = *generated;
1893 /* If there are any numeric variables in existence (e.g. after a regex
1894 condition), or if $thisaddress is set, take a copy for use in the
1895 expansion. Note that we can't pass NULL for filter_thisaddress, because
1896 NULL terminates the list. */
1898 if (expand_nmax >= 0 || filter_thisaddress != NULL)
1900 int ecount = expand_nmax >= 0 ? expand_nmax : -1;
1901 uschar ** ss = store_get(sizeof(uschar *) * (ecount + 3), GET_UNTAINTED);
1903 addr->pipe_expandn = ss;
1904 if (!filter_thisaddress) filter_thisaddress = US"";
1905 *ss++ = string_copy(filter_thisaddress);
1906 for (int i = 0; i <= expand_nmax; i++)
1907 *ss++ = string_copyn(expand_nstring[i], expand_nlength[i]);
1913 /* Set up the file name and mode, and close any previously open
1916 case logfile_command:
1917 log_mode = commands->args[1].i;
1918 if (log_mode == -1) log_mode = 0600;
1921 (void)close(log_fd);
1924 log_filename = expargs[0];
1925 if (filter_test != FTEST_NONE)
1928 printf("%sogfile %s\n", (commands->seen)? "Seen l" : "L", log_filename);
1932 case logwrite_command:
1935 if (filter_test != FTEST_NONE)
1938 printf("%sogwrite \"%s\"\n", (commands->seen)? "Seen l" : "L",
1939 string_printing(s));
1942 /* Attempt to write to a log file only if configured as permissible.
1943 Logging may be forcibly skipped for verifying or testing. */
1945 else if ((filter_options & RDO_LOG) != 0) /* Locked out */
1948 debug_printf_indent("filter log command aborted: euid=%ld\n",
1949 (long int)geteuid());
1950 *error_pointer = US"logwrite command forbidden";
1953 else if ((filter_options & RDO_REALLOG) != 0)
1956 DEBUG(D_filter) debug_printf_indent("writing filter log as euid %ld\n",
1957 (long int)geteuid());
1962 *error_pointer = US"attempt to obey \"logwrite\" command "
1963 "without a previous \"logfile\"";
1966 log_fd = Uopen(log_filename, O_CREAT|O_APPEND|O_WRONLY, log_mode);
1969 *error_pointer = string_open_failed("filter log file \"%s\"",
1975 if (write(log_fd, s, len) != len)
1977 *error_pointer = string_sprintf("write error on file \"%s\": %s",
1978 log_filename, strerror(errno));
1984 debug_printf_indent("skipping logwrite (verifying or testing)\n");
1987 /* Header addition and removal is available only in the system filter. The
1988 command is rejected at parse time otherwise. However "headers charset" is
1989 always permitted. */
1991 case headers_command:
1993 int subtype = commands->args[1].i;
1996 if (filter_test != FTEST_NONE)
1997 printf("Headers %s \"%s\"\n",
1998 subtype == TRUE ? "add"
1999 : subtype == FALSE ? "remove"
2001 string_printing(s));
2003 if (subtype == TRUE)
2005 if (Uskip_whitespace(&s))
2007 header_add(htype_other, "%s%s", s,
2008 s[Ustrlen(s)-1] == '\n' ? "" : "\n");
2009 header_last->type = header_checkname(header_last, FALSE);
2010 if (header_last->type >= 'a') header_last->type = htype_other;
2014 else if (subtype == FALSE)
2017 const uschar * list = s;
2019 for (uschar * ss; ss = string_nextinlist(&list, &sep, NULL, 0); )
2020 header_remove(0, ss);
2023 /* This setting lasts only while the filter is running; on exit, the
2024 variable is reset to the previous value. */
2026 else headers_charset = s;
2030 /* Defer, freeze, and fail are available only when explicitly permitted.
2031 These commands are rejected at parse time otherwise. The message can get
2032 very long by the inclusion of message headers; truncate if it is, and also
2033 ensure printing characters so as not to mess up log files. */
2036 ff_name = US"defer";
2038 goto DEFERFREEZEFAIL;
2043 goto DEFERFREEZEFAIL;
2045 case freeze_command:
2046 ff_name = US"freeze";
2050 *error_pointer = fmsg = US string_printing(Ustrlen(expargs[0]) > 1024
2051 ? string_sprintf("%.1000s ... (truncated)", expargs[0])
2052 : string_copy(expargs[0]));
2053 for(uschar * s = fmsg; *s; s++)
2054 if (!s[1] && *s == '\n') { *s = '\0'; break; } /* drop trailing newline */
2056 if (filter_test != FTEST_NONE)
2059 printf("%c%s text \"%s\"\n", toupper(ff_name[0]), ff_name+1, fmsg);
2062 DEBUG(D_filter) debug_printf_indent("Filter: %s \"%s\"\n", ff_name, fmsg);
2065 case finish_command:
2066 if (filter_test != FTEST_NONE)
2069 printf("%sinish\n", (commands->seen)? "Seen f" : "F");
2072 DEBUG(D_filter) debug_printf_indent("Filter: %sfinish\n",
2073 commands->seen ? " Seen " : "");
2074 finish_obeyed = TRUE;
2075 return filter_delivered ? FF_DELIVERED : FF_NOTDELIVERED;
2079 uschar *save_address = filter_thisaddress;
2080 int ok = FF_DELIVERED;
2081 condition_value = test_condition(commands->args[0].c, TRUE);
2087 ok = interpret_commands(commands->args[condition_value? 1:2].f,
2091 filter_thisaddress = save_address;
2092 if (finish_obeyed || ok != FF_DELIVERED && ok != FF_NOTDELIVERED)
2098 /* To try to catch runaway loops, do not generate mail if the
2099 return path is unset or if a non-trusted user supplied -f <>
2100 as the return path. */
2103 case vacation_command:
2104 if (!return_path || !*return_path)
2106 if (filter_test != FTEST_NONE)
2107 printf("%s command ignored because return_path is empty\n",
2108 command_list[commands->command]);
2109 else DEBUG(D_filter) debug_printf_indent("%s command ignored because return_path "
2110 "is empty\n", command_list[commands->command]);
2114 /* Check the contents of the strings. The type of string can be deduced
2115 from the value of i.
2117 . If i is equal to mailarg_index_text it's a text string for the body,
2118 where anything goes.
2120 . If i is > mailarg_index_text, we are dealing with a file name, which
2121 cannot contain non-printing characters.
2123 . If i is less than mailarg_index_headers we are dealing with something
2124 that will go in a single message header line, where newlines must be
2125 followed by white space.
2127 . If i is equal to mailarg_index_headers, we have a string that contains
2128 one or more headers. Newlines that are not followed by white space must
2129 be followed by a header name.
2132 for (i = 0; i < MAILARGS_STRING_COUNT; i++)
2134 const uschar *s = expargs[i];
2138 if (i != mailarg_index_text) for (const uschar * p = s; *p; p++)
2141 if (i > mailarg_index_text)
2143 if (!mac_isprint(c))
2145 *error_pointer = string_sprintf("non-printing character in \"%s\" "
2146 "in %s command", string_printing(s),
2147 command_list[commands->command]);
2152 /* i < mailarg_index_text */
2154 else if (c == '\n' && !isspace(p[1]))
2156 if (i < mailarg_index_headers)
2158 *error_pointer = string_sprintf("\\n not followed by space in "
2159 "\"%.1024s\" in %s command", string_printing(s),
2160 command_list[commands->command]);
2164 /* Check for the start of a new header line within the string */
2169 for (pp = p + 1;; pp++)
2172 if (c == ':' && pp != p + 1) break;
2173 if (!c || c == ':' || isspace(c))
2175 *error_pointer = string_sprintf("\\n not followed by space or "
2176 "valid header name in \"%.1024s\" in %s command",
2177 string_printing(s), command_list[commands->command]);
2184 } /* Loop to scan the string */
2186 /* The string is OK */
2188 commands->args[i].u = s;
2191 /* Proceed with mail or vacation command */
2193 if (filter_test != FTEST_NONE)
2195 const uschar *to = commands->args[mailarg_index_to].u;
2197 printf("%sail to: %s%s%s\n", (commands->seen)? "Seen m" : "M",
2198 to ? to : US"<default>",
2199 commands->command == vacation_command ? " (vacation)" : "",
2200 commands->noerror ? " (noerror)" : "");
2201 for (i = 1; i < MAILARGS_STRING_COUNT; i++)
2203 const uschar *arg = commands->args[i].u;
2206 int len = Ustrlen(mailargs[i]);
2207 int indent = (debug_selector != 0)? output_indent : 0;
2208 while (len++ < 7 + indent) printf(" ");
2209 printf("%s: %s%s\n", mailargs[i], string_printing(arg),
2210 (commands->args[mailarg_index_expand].u != NULL &&
2211 Ustrcmp(mailargs[i], "file") == 0)? " (expanded)" : "");
2214 if (commands->args[mailarg_index_return].u)
2215 printf("Return original message\n");
2220 const uschar *to = commands->args[mailarg_index_to].u;
2221 gstring * log_addr = NULL;
2223 if (!to) to = expand_string(US"$reply_address");
2224 Uskip_whitespace(&to);
2226 for (tt = to; *tt; tt++) /* Get rid of newlines */
2229 uschar * s = string_copy(to);
2230 for (uschar * ss = s; *ss; ss++)
2231 if (*ss == '\n') *ss = ' ';
2238 debug_printf_indent("Filter: %smail to: %s%s%s\n",
2239 commands->seen ? "seen " : "",
2241 commands->command == vacation_command ? " (vacation)" : "",
2242 commands->noerror ? " (noerror)" : "");
2243 for (i = 1; i < MAILARGS_STRING_COUNT; i++)
2245 const uschar *arg = commands->args[i].u;
2248 int len = Ustrlen(mailargs[i]);
2249 while (len++ < 15) debug_printf_indent(" ");
2250 debug_printf_indent("%s: %s%s\n", mailargs[i], string_printing(arg),
2251 (commands->args[mailarg_index_expand].u != NULL &&
2252 Ustrcmp(mailargs[i], "file") == 0)? " (expanded)" : "");
2257 /* Create the "address" for the autoreply. This is used only for logging,
2258 as the actual recipients are extracted from the To: line by -t. We use the
2259 same logic here to extract the working addresses (there may be more than
2260 one). Just in case there are a vast number of addresses, stop when the
2261 string gets too long. */
2266 uschar *ss = parse_find_address_end(tt, FALSE);
2267 uschar *recipient, *errmess;
2268 int start, end, domain;
2272 recipient = parse_extract_address(tt, &errmess, &start, &end, &domain,
2276 /* Ignore empty addresses and errors; an error will occur later if
2277 there's something really bad. */
2281 log_addr = string_catn(log_addr, log_addr ? US"," : US">", 1);
2282 log_addr = string_cat (log_addr, recipient);
2287 if (log_addr && log_addr->ptr > 256)
2289 log_addr = string_catn(log_addr, US", ...", 5);
2293 /* Move on past this address */
2295 tt = ss + (*ss ? 1 : 0);
2296 Uskip_whitespace(&tt);
2300 addr = deliver_make_addr(string_from_gstring(log_addr), FALSE);
2303 addr = deliver_make_addr(US ">**bad-reply**", FALSE);
2304 setflag(addr, af_bad_reply);
2307 setflag(addr, af_pfr);
2308 if (commands->noerror) addr->prop.ignore_error = TRUE;
2309 addr->next = *generated;
2312 addr->reply = store_get(sizeof(reply_item), GET_UNTAINTED);
2313 addr->reply->from = NULL;
2314 addr->reply->to = string_copy(to);
2315 addr->reply->file_expand =
2316 commands->args[mailarg_index_expand].u != NULL;
2317 addr->reply->expand_forbid = expand_forbid;
2318 addr->reply->return_message =
2319 commands->args[mailarg_index_return].u != NULL;
2320 addr->reply->once_repeat = 0;
2322 if (commands->args[mailarg_index_once_repeat].u != NULL)
2324 addr->reply->once_repeat =
2325 readconf_readtime(commands->args[mailarg_index_once_repeat].u, 0,
2327 if (addr->reply->once_repeat < 0)
2329 *error_pointer = string_sprintf("Bad time value for \"once_repeat\" "
2330 "in mail or vacation command: %s",
2331 commands->args[mailarg_index_once_repeat].u);
2336 /* Set up all the remaining string arguments (those other than "to") */
2338 for (i = 1; i < mailargs_string_passed; i++)
2340 const uschar *ss = commands->args[i].u;
2341 *(USS((US addr->reply) + reply_offsets[i])) =
2342 ss ? string_copy(ss) : NULL;
2347 case testprint_command:
2348 if (filter_test != FTEST_NONE || (debug_selector & D_filter) != 0)
2350 const uschar *s = string_printing(expargs[0]);
2351 if (filter_test == FTEST_NONE)
2352 debug_printf_indent("Filter: testprint: %s\n", s);
2354 printf("Testprint: %s\n", s);
2358 commands = commands->next;
2361 return filter_delivered ? FF_DELIVERED : FF_NOTDELIVERED;
2366 /*************************************************
2367 * Test for a personal message *
2368 *************************************************/
2370 /* This function is global so that it can also be called from the code that
2371 implements Sieve filters.
2374 aliases a chain of aliases
2375 scan_cc TRUE if Cc: and Bcc: are to be scanned (Exim filters do not)
2377 Returns: TRUE if the message is deemed to be personal
2381 filter_personal(string_item *aliases, BOOL scan_cc)
2383 const uschar *self, *self_from, *self_to;
2384 uschar *psself = NULL;
2385 const uschar *psself_from = NULL, *psself_to = NULL;
2386 rmark reset_point = store_mark();
2392 /* If any header line in the message is a defined "List-" header field, it is
2393 not a personal message. We used to check for any header line that started with
2394 "List-", but this was tightened up for release 4.54. The check is now for
2395 "List-Id", defined in RFC 2929, or "List-Help", "List-Subscribe", "List-
2396 Unsubscribe", "List-Post", "List-Owner" or "List-Archive", all of which are
2397 defined in RFC 2369. We also scan for "Auto-Submitted"; if it is found to
2398 contain any value other than "no", the message is not personal (RFC 3834).
2399 Previously the test was for "auto-". */
2401 for (h = header_list; h; h = h->next)
2403 if (h->type == htype_old) continue;
2405 if (strncmpic(h->text, US"List-", 5) == 0)
2407 uschar * s = h->text + 5;
2408 if (strncmpic(s, US"Id:", 3) == 0 ||
2409 strncmpic(s, US"Help:", 5) == 0 ||
2410 strncmpic(s, US"Subscribe:", 10) == 0 ||
2411 strncmpic(s, US"Unsubscribe:", 12) == 0 ||
2412 strncmpic(s, US"Post:", 5) == 0 ||
2413 strncmpic(s, US"Owner:", 6) == 0 ||
2414 strncmpic(s, US"Archive:", 8) == 0)
2418 else if (strncmpic(h->text, US"Auto-submitted:", 15) == 0)
2420 uschar * s = h->text + 15;
2421 Uskip_whitespace(&s);
2422 if (strncmpic(s, US"no", 2) != 0) return FALSE;
2424 Uskip_whitespace(&s);
2425 if (*s) return FALSE;
2429 /* Set up "my" address */
2431 self = string_sprintf("%s@%s", deliver_localpart, deliver_domain);
2432 self_from = rewrite_one(self, rewrite_from, NULL, FALSE, US"",
2433 global_rewrite_rules);
2434 self_to = rewrite_one(self, rewrite_to, NULL, FALSE, US"",
2435 global_rewrite_rules);
2438 if (!self_from) self_from = self;
2439 if (self_to) self_to = self;
2441 /* If there's a prefix or suffix set, we must include the prefixed/
2442 suffixed version of the local part in the tests. */
2444 if (deliver_localpart_prefix || deliver_localpart_suffix)
2446 psself = string_sprintf("%s%s%s@%s",
2447 deliver_localpart_prefix ? deliver_localpart_prefix : US"",
2449 deliver_localpart_suffix ? deliver_localpart_suffix : US"",
2451 psself_from = rewrite_one(psself, rewrite_from, NULL, FALSE, US"",
2452 global_rewrite_rules);
2453 psself_to = rewrite_one(psself, rewrite_to, NULL, FALSE, US"",
2454 global_rewrite_rules);
2455 if (psself_from == NULL) psself_from = psself;
2456 if (psself_to == NULL) psself_to = psself;
2461 /* Do all the necessary tests; the counts are adjusted for {pre,suf}fix */
2465 header_match(US"to:", TRUE, TRUE, aliases, to_count, self, self_to, psself,
2469 header_match(US"cc:", TRUE, TRUE, aliases, to_count, self, self_to,
2472 header_match(US"bcc:", TRUE, TRUE, aliases, to_count, self, self_to,
2478 header_match(US"from:", TRUE, FALSE, aliases, from_count, "^server@",
2479 "^daemon@", "^root@", "^listserv@", "^majordomo@", "^.*?-request@",
2480 "^owner-[^@]+@", self, self_from, psself, psself_from) &&
2482 header_match(US"precedence:", FALSE, FALSE, NULL, 3, "bulk","list","junk") &&
2484 (sender_address == NULL || sender_address[0] != 0);
2486 store_reset(reset_point);
2492 /*************************************************
2493 * Interpret a mail filter file *
2494 *************************************************/
2498 filter points to the entire file, read into store as a single string
2499 options controls whether various special things are allowed, and requests
2501 generated where to hang newly-generated addresses
2502 error where to pass back an error text
2504 Returns: FF_DELIVERED success, a significant action was taken
2505 FF_NOTDELIVERED success, no significant action
2506 FF_DEFER defer requested
2507 FF_FAIL fail requested
2508 FF_FREEZE freeze requested
2509 FF_ERROR there was a problem
2513 filter_interpret(const uschar *filter, int options, address_item **generated,
2517 int yield = FF_ERROR;
2518 const uschar *ptr = filter;
2519 const uschar *save_headers_charset = headers_charset;
2520 filter_cmd *commands = NULL;
2521 filter_cmd **lastcmdptr = &commands;
2523 DEBUG(D_route) debug_printf("Filter: start of processing\n");
2526 /* Initialize "not in an if command", set the global flag that is always TRUE
2527 while filtering, and zero the variables. */
2531 f.filter_running = TRUE;
2532 for (i = 0; i < FILTER_VARIABLE_COUNT; i++) filter_n[i] = 0;
2534 /* To save having to pass certain values about all the time, make them static.
2535 Also initialize the line number, for error messages, and the log file
2538 filter_options = options;
2539 filter_delivered = FALSE;
2540 finish_obeyed = FALSE;
2541 error_pointer = error;
2542 *error_pointer = NULL;
2546 log_filename = NULL;
2548 /* Scan filter file for syntax and build up an interpretation thereof, and
2549 interpret the compiled commands, and if testing, say whether we ended up
2550 delivered or not, unless something went wrong. */
2553 ptr = nextsigchar(ptr, TRUE);
2555 if (read_command_list(&ptr, &lastcmdptr, FALSE))
2556 yield = interpret_commands(commands, generated);
2558 if (filter_test != FTEST_NONE || (debug_selector & D_filter) != 0)
2564 s = US"Filtering ended by \"defer\".";
2568 s = US"Filtering ended by \"freeze\".";
2572 s = US"Filtering ended by \"fail\".";
2576 s = US"Filtering set up at least one significant delivery "
2577 "or other action.\n"
2578 "No other deliveries will occur.";
2581 case FF_NOTDELIVERED:
2582 s = US"Filtering did not set up a significant delivery.\n"
2583 "Normal delivery will occur.";
2587 s = string_sprintf("Filter error: %s", *error);
2591 if (filter_test != FTEST_NONE) printf("%s\n", CS s);
2592 else debug_printf_indent("%s\n", s);
2595 /* Close the log file if it was opened, and kill off any numerical variables
2596 before returning. Reset the header decoding charset. */
2598 if (log_fd >= 0) (void)close(log_fd);
2600 f.filter_running = FALSE;
2601 headers_charset = save_headers_charset;
2604 DEBUG(D_route) debug_printf("Filter: end of processing\n");
2609 /* End of filter.c */