Headers
Header fields carry the information about a message that mail clients and filters read: who sent it, its subject, the software that produced it. Changing them as the message passes through the server serves several common purposes. Outgoing mail can lose header fields that reveal internal details. The subject of mail from outside the organisation can be tagged. And a message can receive a header that a later filter or a mail client acts on.
These changes are made by a system Sieve script at the DATA stage, which runs before the message is queued. The script uses the editheader extension (RFC 5293) to add, remove and change header fields. It is a SieveSystemScript, selected by the script field of MtaStageData (found in the WebUI under Settings › MTA › Session › DATA Stage).
The extension provides two commands. addheader adds a field at the top of the header, or at the bottom when given :last. deleteheader removes every field with a given name, or, with the usual match types, only the fields whose value matches. Sieve has no command that changes a field in place, so a change is written as a deleteheader followed by an addheader.
Any of these changes can break a signature. When a received message carries a DKIM signature, changing a header field that the signature covers invalidates that signature.
Adding a header
Section titled “Adding a header”A header added by the server lets later steps tell kinds of mail apart. The following script marks every message received from an unauthenticated session:
require ["editheader", "variables"];
if string :is "${env.authenticated_as}" "" { addheader "X-Origin" "external";}Removing headers
Section titled “Removing headers”Some mail clients add header fields that reveal the user’s software or address. The following script removes those fields from messages sent by authenticated users.
The script cannot remove the Received header that Stalwart itself adds, with the client’s IP address, because that header is not visible to the script. That header is controlled instead by addReceivedHeader, whose default expression adds it only on port 25:
require ["editheader", "variables"];
if not string :is "${env.authenticated_as}" "" { deleteheader "X-Originating-IP"; deleteheader "User-Agent"; deleteheader "X-Mailer";}Changing a header
Section titled “Changing a header”The following script prefixes the subject of messages from unauthenticated sessions with [External]. It skips messages whose subject already carries the prefix:
require ["editheader", "variables"];
if allof (string :is "${env.authenticated_as}" "", header :matches "Subject" "*", not header :contains "Subject" "[External]") { set "subject" "${1}"; deleteheader "Subject"; addheader "Subject" "[External] ${subject}";}The :matches test captures the current subject in ${1}. The script saves that value in a variable before it deletes the field, and then adds the field again with the new value.