Filter rules can be limited to a period: the rule dialog takes a start
and an end on the user's clock, the rule list shows whether a rule is
planned, active or expired, and the script tests the moment of
delivery with RFC 5260 currentdate in UTC. A rule is never written
without its period.
The vacation card becomes an out of office page: the period on top,
then the automatic reply and forwarding, each with its own switch.
Forwarding runs with or without the auto-reply, within the period,
never for spam, and keeps a copy here or not. Against the server's
redirect limit it counts as a rule ahead of all others, one that stops
the message unless it keeps a copy. The auto-reply can go to
internal or external senders only, told apart by the envelope sender
against the account's own domains. Both run from the filters script
that includes Stalwart's vacation script, which is put back in charge
when the server's own script takes over.
A script that uses any of this says metadata version 2, which older
builds leave alone instead of writing it back without these fields.
The rule editor had no way to say "every message that arrives"; the
workaround was a condition like "From contains @", which only works if
you know it. "All messages" is now a condition of its own, at the end of
the field list. It takes no comparator and no value, writes Sieve's
`true` test, and combines with other conditions as any condition does.
The rule list shows it on its own. A rule with it is never applied to
the messages already in a folder: it would act on all of them.
"Stop processing further rules" wrote no stop when the rule's last action
was "Delete silently" or "Reject". Both only cancel the implicit keep
(RFC 5228 4.4, RFC 5429): the script goes on, so a later rule still
filed the deleted or rejected message into a folder. Such a rule now
always writes a stop, unless its block has one already; rules below it
no longer run on its messages, which is what the option says.
Since 1.11.1 the "# Rule:" line carries the rule name with its runs of
whitespace collapsed, but the parser compared it with the name as
stored. A name like "Foo Bar", one with a tab or a trailing space, or
an empty one came back as someone else's block on every read, and every
save added one more copy. Both sides are now compared collapsed, and the
name is read from its own line only. That also drops the copies earlier
saves left, as long as the rule still exists under that name.
The generator wrote several rule fields into the script unescaped. A
`*/` in a rule name ended the `@metadata` comment, a line break ended
the `# Rule:` comment, a custom header name could close its string, and
a size value was written as-is, so each could add live commands such as
`redirect "attacker@..."` to the active script. Rules reach the
generator from plugins holding `filters:write` and from imported filter
sets, not only from the editor.
The metadata JSON now escapes `*/` (JSON reads `\/` back as `/`), the
rule-name comment is kept on one line, header names are escaped like
other strings, and a size that is not a number becomes 0.
The rule metadata is JSON inside a Sieve bracket comment, so a `*/` in a
rule name, a value or the vacation text ended the comment early and
broke the script. A line break in a rule name escaped the `# Rule:`
comment line. And a header name outside `[A-Za-z0-9_-]` made Stalwart
(0.16 and 1.0) refuse the whole script, every other rule with it.
- `*/` in the metadata JSON is written as `*\/`, which `JSON.parse`
reads back as `/`, so older versions still parse it.
- Line breaks in a rule name are flattened on the `# Rule:` line, and
the parser matches its own blocks the same way.
- The rule editor refuses to save an invalid header name, with a
translated message. The generator skips such a rule (it stays in the
metadata, so it can be fixed) instead of writing a script the server
refuses.
Stalwart only moves spam to Junk when nothing else decided where the
message goes. Any Bulwark rule that moved or copied mail to a folder
therefore delivered spam there too, e.g. a "subject contains invoice"
rule collected phishing. From 0.16.22 this also applies to the "Keep"
allow-list action, which older versions still send to Junk.
Move and copy rules now add
`not spamtest :percent :value "ge" :comparator "i;ascii-numeric" "50"`
to their condition when the server supports `spamtestplus` and
`relational`. 50 % is Stalwart's spam threshold on the graded scale
(0.16.19+), and older versions report 0 or 100. A new per-rule option
"Also move messages marked as spam" drops the guard, and "Keep" rules
are left unguarded as the explicit allow-list.
The Sieve generator wrote actions in the order they were picked in the
rule editor. `fileinto` stores the flags set at that moment, so "mark
read", "star" or "add label" placed after a move never reached the
moved message. Flag actions are now emitted first.
Moves and copies also targeted the folder by path only. Renaming the
folder silently sent the rule's mail to Inbox. Rules now store the
folder id and emit `fileinto :mailboxid "<id>" "<path>"` when the
server lists `mailboxid` (declaring `mailbox` too, which Stalwart
requires), with the path as the fallback. Saving a rule refreshes the
path from the id, so existing rules pick up the id on their next save.
Forward actions get a "Keep a copy" option (`redirect :copy`), and the
editor honours the server's `maxNumberRedirects` (1 on Stalwart):
forward is disabled once the limit is used up, with a hint when
existing rules already exceed it, because the server skips extra
redirects without telling anyone.
Stalwart allows one active Sieve script and turns the auto-reply on by
activating its own `vacation` script. Turning the auto-reply on switched
every Bulwark filter off (including junk and folder rules), and saving
any filter switched the auto-reply off again.
When the vacation script takes over from an existing filters script,
the filters script is now re-activated with
`include :personal :optional "vacation"`, so the server-built reply
(with its date window and HTML body) runs from there. Turning the
auto-reply off drops the include again. A filters save while the
vacation script is active keeps it through the same include, and the
vacation settings read the include as "on", because
VacationResponse.isEnabled reports false in that state.
Clients that only look at VacationResponse (Stalwart's web UI, phones)
still show the auto-reply as off while the include is active.
Renaming a tag changes its id - the id is a slug of the display name -
and with it the `$label:` keyword the tag is stored under. The rename
migrated the keyword on every message but left Sieve rules naming the old
id, in both the generated `addflag` line and the metadata block the visual
editor reads back. The rule then went on tagging new mail under an id no
definition names any more: the tag renders as its raw id in grey and a
search for the renamed tag misses everything that arrived since, which
reads as the filter having quietly stopped working.
`renameKeywordInRules` rewrites every `add_label` action naming the old id,
disabled rules included. An external rule reaches the script through its
verbatim `rawBlock`, so that is rewritten too - but only the operand of an
`addflag`, since a test comparing a header against the same literal is the
rule's own logic rather than the tag it applies.
The store fetches, rewrites and uploads the account's active script on its
own rather than through fetchFilters/saveFilters, so a rename from the tag
settings cannot disturb which account an open filter view is showing; that
view is updated in place when it shows the same account. The write still
runs through the same plugin pipeline a save does, or a plugin's managed
section would be dropped from the script this activates. A script that is
not the active one is left alone entirely: writing to it would activate it,
and a tag rename must not switch someone's filters back on.
Whatever still names the old tag in the script that ends up being written -
a rule kept verbatim, a test comparing against the keyword, a section a
plugin just grafted back on - is counted, and the tag settings warn that
some rules still use the old tag, because only the user can fix those.
Mail delivered between the message migration and the rewrite was still
tagged by the old rule, so a successful rewrite is followed by one more
migration sweep, which no rewritten rule can race any more.
Adds an "Attachment" condition field (is present / of type <ext>) backed by
the RFC 5703 Sieve mime extension, matching the filename in both
Content-Disposition and Content-Type headers so real-world senders that only
put the name in Content-Type (Microsoft SMTPSVC, etc.) are caught. Users type
extensions (pdf, doc) not MIME types.
Also makes each text condition accept comma-separated multiple values emitted
as a Sieve string list (OR within the condition), so "(domain1 OR domain2)
AND attachment pdf/xml" is expressible in one rule. value is now string |
string[] (single-value rules stay strings -> backward compatible). New filter
locale keys in all 17 locales.