Postfix 07 - Notes on MDAs (procmail and maildrop)
Postfix Mail Server Learning · Previous: body_checks and header_checks · Next: EnGarde Secure Linux - installing a mail gateway
The Slovak original of this document: Postfix 07 - Poznámky k MDA (procmail a maildrop) (slovensky).
|=---------------------------=[ Procmail/Maildrop ]=-----------------------=| +---------------------------------------------------------------------------+ | | | Notes on MDAs (procmail and maildrop) | | | | | +---------------------------------------------------------------------------+ |=-------------------------------=[ BH 2007 ]=-----------------------------=|
1. Procmail
$HOME/.procmailrc
1.1 Rule syntax
:0 [flags] [ : [locallockfile] ] <zero or more conditions (one per line)> <exactly one action>
1.2 Flags
H = Egreps the header (default).
B = Egreps the body.
D = Sets the internal egrep to distinguish between upper and lower case
(the default is to ignore case)
A = This rule will not be executed unless the condition of the last preceding rule
(at the current nesting level) without the flag 'A' or 'a' matches. This lets
us chain actions that depend on a common condition.
a = This flag means the same as 'A', with the additional condition
that the rule immediately before it has to have finished successfully
before this rule runs.
E = This rule is executed if the rule immediately before this rule
was not executed. Executing this rule also disables any immediately
following rules with the 'E' flag. This lets us specify actions of the
'else if' type.
e = This rule is executed only if the previous rule failed
(e.g. the action line was attempted, but ended with an error).
h = Sends the header to the pipe, the file or the mail destination (the default).
b = Sends the body to the pipe, the file or the mail destination (the default).
f = Treats the pipe as a filter.
c = Generates a copy of this mail. This is meaningful in delivery rules
w = Waits for the filter or program to finish and checks its exit code
(normally ignores it); if the filter fails, the text is left unfiltered.
W = Means the same as the flag "w", but suppresses any "Program failure" message.
i = Ignores any write error in this rule
(for example, when the pipe is closed early).
r = Raw mode; makes no attempt to have the mail end with an empty line,
writes it as it is.1.3 Special conditions
! = Inverts the condition.
$ = Evaluates the rest of this condition according to the shell substitution rules
inside double quotes, skips whitespace and then reparses it.
? = Uses the exit code of the specified program.
< = Checks whether the total size of the mail is smaller than the given number of bytes (decimal).
> = Analogous to '<'
variable_name ?? = Matches the rest of this condition against the value of this environment variable
(which cannot be a pseudo-variable). A special case is when the variable name
matches 'B', 'H', 'HB' or 'BH'; this completely overrides the default
header/body search areas defined by the initial flags on
this rule.1.4 Extended regular expressions
^ = Start of a line.
$ = End of a line.
. = Any character except a newline. (e.g.: .aco => laco, maco)
a* = A sequence of zero or more 'a' characters (,a,aa,aaa). (e.g.: Lac* Hajzer => La Hajzer, Lac Hajzer, Lacccc Hajzer)
a+ = A sequence of one or more 'a' characters (a,aa,aaa). (e.g.: La(co)+ => Laco, Lacococo)
a? = Zero or one 'a' character. (e.g.: Laco? => Lac, Laco)
[^-a-d] = Any character that is not '-', 'a', 'b', 'c', 'd' or a newline.
[abcd] = A character class, meaning one of the listed characters.
de|abc = The sequence 'de' or 'abc' .
(abc)* = Zero or more 'abc' sequences.
\. = A single dot. To match any special character, we put the '\' character in front of it.
-----------------------------------------------------------------
Example 1: forwarding all e-mails to another e-mail address
-----------------------------------------------------------------
:0 H:
* ^To:.*mail@domena.sk
! inymail@inadomena.sk
-----------------------------------------------------------------
Example 2: forwarding all e-mails to another e-mail address + storing them
in the local mail folder
-----------------------------------------------------------------
:0 Hc:
* ^To:.*mail@domena.sk
! inymail@inadomena.sk
-----------------------------------------------------------------
Example 3: forwarding e-mails to another e-mail address by subject
-----------------------------------------------------------------
:0 H:
* ^Subject: .*UDALOST
! udalost@domena.sk
-----------------------------------------------------------------
Example 4: sorting e-mails into folders by subject
-----------------------------------------------------------------
:0 H:
* ^Subject: .*UDALOST
~/udalosti
-----------------------------------------------------------------
Example 5: an out-of-office auto-reply. The user sets it up themselves by sending an e-mail.
-----------------------------------------------------------------
# touch /home/user/.procmailrc
# chown user:user /home/user/.procmailrc
# chmod 700 /home/user/.procmailrc
We edit the file /home/user/.procmailrc
---------------------------------------
$VACATION_PASSWORD - the password for turning the auto-reply on/off
$VACATION_SENDER - the e-mail address we are setting the auto-reply for (user@domena.sk)
in case of problems we uncomment the following lines in the file /home/user/.procmailrc
------------------------------------------------------------------------------------------
#LOGFILE=/tmp/procmailvacation.log
#VERBOSE=on
Turning the auto-reply on
-------------------------
The user sends a mail with the subject: password vacation on (password is the password defined in .procmailrc)
The body of the mail becomes the text of the auto-reply.
Turning the auto-reply off
--------------------------
The user sends a mail with the subject: password vacation off (password is the password defined in .procmailrc)
From http://www.clarkconnect.com/wiki/index.php?title=Howtos_-_Procmail_Vacation_Auto-Reply_Recipe
------------------------------------ .procmailrc --------------------------------
# vim: ft=procmail
# User-managed vacation recipe for procmail
# Written by Jason Thaxter
# (http://www.google.com/search?q=jason+thaxter)
# To use from a shell:
#
# * Include this file in your procmail recipe.
# * Define $VACATION_SENDER in your procmail recipe: it will be "from" this
# address.
# * Put your message text in a file specified by $VACATION_MSG.
# (defaults to ".vacation_mesg")
# * Remove the $VACATION_MSG file to turn off vacation messages.
# For users without shell access:
#
# * Include this file in the procmail file.
# * Set $VACATION_PASSWORD. (for security, this is mandatory)
# * Define $VACATION_SENDER in your procmail recipe: it will be "from" this
# address.
# * E-mail a message with $VACATION_PASSWORD and $VACATION_ON in the subject
# line. The body of the message becomes the vacation message. $VACATION_ON
# can be set prior to the INCLUDERC, but it defaults to "vacation on".
# * To turn it off, e-mail a message with $VACATION_PASSWORD and $VACATION_OFF
# in the subject line. Likewise, $VACATION_OFF defaults to "vacation off".
# Note that you probably want this to execute *after* any mailing list or spam
# delivery recipes. You can set $VACATION_SKIP to disable vacation processing
# if it's inconvenient to skip this recipe.
# TODO:
# * configurable regex so we can reply "from" whatever they sent it "to" (if
# we can match on the regex, of course). kinda like mutt's "alternates".
# * optionally, send a vacation message for EVERY incoming message from a
# given email, not just the first one.
# * Duh: better documentation.
# * Nicer defaults for people who let mailing-list mail and spam hit
# this recipe (though we will *always* try to avoid replying to those).
# -----------------------------------------------------------------------------
# Configurable variables: These variables allow you to use this vacation recipe
# as an include and customize it from your main procmail file.
#
# lockfile:
VACATION_LOCK=${VACATION_LOCK:-".vacation$LOCKEXT"}
# cache file:
VACATION_CACHE=${VACATION_CACHE:-".vacation_cache"}
# cache size:
VACATION_CACHE_SZ=${VACATION_CACHE_SZ:-8192}
# message file
VACATION_MSG=${VACATION_MSG:-".vacation_mesg"}
# what to use as the xloop header
HOSTNAME=${HOSTNAME:-`hostname`}
VACATION_XLOOP=${VACATION_XLOOP:-"$LOGNAME@$HOSTNAME"}
# base token for default $VACATION_ON and $VACATION_OFF
# so you could set this and not those individually
VACATION_COOKIE=${VACATION_COOKIE:-"vacation"}
VACATION_ON=${VACATION_ON:-"$VACATION_COOKIE on"}
VACATION_OFF=${VACATION_OFF:-"$VACATION_COOKIE off"}
# UNSTABLE:
# get the sender for later use
VACATION_MSG_SEND_TO=`formail -rtx To: | expand | sed -e 's/^[ ]*//g' -e 's/[ ]*$//g'`
# not sure what to do for weird cases of these... yet
VACATION_SENDMAILFROM=${VACATION_SENDMAILFROM:-"-f$VACATION_SENDER"}
VACATION_SENDMAILFLAGS="-oi -t $VACATION_SENDMAILFROM"
# who are we? needs to be extracted with a regex using ALTERNATES
#VACATION_RECIPIENT=`formail -XTo: \ | expand | sed -e 's/^[ ]*//g' -e 's/[ ]*$//g'`
# -----------------------------------------------------------------------------
SENDMAIL_CMD="$SENDMAIL $VACATION_SENDMAILFLAGS"
SHELL=/bin/sh
# check if we should send vacation message, add user to cache
:0 Whc: $VACATION_LOCK
# if i haven't been instructed to skip processing
* ? test -z $VACATION_SKIP
# if i have a vacation message file
* ? test -f $VACATION_MSG
# and the message is not from a daemon or mailer
* !^FROM_DAEMON
* !^FROM_MAILER
# not declared spam by spamassassin
* !^X-Spam-Flag: YES
# not discernably in a mailing list
* !^List-
* !^(Mailing-List|Approved-By|BestServHost|Resent-(Message-ID|Sender)):
* !^X-[^:]*-List:
* !^X-(Sent-To|(Listprocessor|Mailman)-Version):
# and not x-loop
* !^X-Loop: $VACATION_XLOOP
# add it to the cache
| formail -rD $VACATION_CACHE_SZ $VACATION_CACHE
:0 ehc
# if the name was not in the cache
# if we can find who we're sending it to
# and who we are sending this "From"
* ? test -n ${VACATION_MSG_SEND_TO}
* ? test -n ${VACATION_SENDER}
| (formail -r \
-I"Precedence: junk" \
-A"From: $VACATION_SENDER" \
-A"X-Loop: $VACATION_XLOOP"; \
cat $VACATION_MSG ) | \
$SENDMAIL_CMD
# Add/remove vacation message
:0
# only do this if we have a password set
* ? test -n $VACATION_PASSWORD
# and it's in the subject line
* $^Subject:.*${VACATION_PASSWORD}
{
# VACATION ON
# if subject line matches magic cookie for ON:
:0
* $^Subject:.*${VACATION_ON}
{
# pipe the body into the vacation message file
:0c:$VACATION_LOCK
| formail -I "" > $VACATION_MSG
# add message to the body
:0f
| cat - ; \
echo; \
echo '---------- VACATION -----------------'; \
echo 'The above text was installed as your vacation message'
}
# VACATION OFF
# if subject line matches magic cookie for OFF:
# delete the vacation file and notify
:0f
* $^Subject:.*${VACATION_OFF}
| cat -; \
echo '---------- VACATION -----------------'; \
echo 'Removing message and cache: '; \
rm -vf $VACATION_MSG; \
rm -vf $VACATION_CACHE; \
echo ; \
echo "Removed vacation message."
}
------------------------------------ .procmailrc --------------------------------
----------
Examples
----------
http://www.erehwon.org/erehwon/procmailex.htmlMaildrop
Procmail vs Maildrop
Function procmail maildrop
-----------------------------------------------------------------------------------------------
Delivering a message | /cesta/k/programu to "|/cesta/k/programu"
by an external program or
cc "|/cesta/k/programu"
Filtering a message the same, but the start of the rule xfilter "/cesta/k/programu"
by an external program must contain the f flag:
:0 f
Filtering messages :0 if ($SIZE > 10000000)
by size * > 10000000 to spravy_nad_10MB
spravy_nad_10MB or
if ($LINES > 500)
to spravy_nad_500_riadkov
Reformatting messages the external program formail: the external program reformail:
(e.g. adding a header) :0 f xfilter "reformail -A'X-Envelope-From: $FROM'"
| formail -A"Message-ID:"
maildrop also contains the program reformime, which makes it possible to work with MIME messagesDOC
man procmail man procmailrc man procmailex man procmailsc
LINKZ
http://pm-doc.sourceforge.net/ http://www.perlcode.org/tutorials/procmail/proctut/ http://www.impsec.org/email-tools/procmail-security.html http://www.root.cz/clanky/procmail-vs-maildrop/ http://www.root.cz/clanky/procmail-pre-zaciatocnikov/ http://www.ii.com/internet/robots/procmail/qs/ http://partmaps.org/era/procmail/links.html http://lipas.uwasa.fi/~ts/info/proctips.html
Current practice (checked 2026-10)
noteThe article above is kept as it was written in 2007. This section lists what has changed since and what to do instead today.
- procmail: the syntax described above is unchanged and still correct, because upstream treats procmail as a static program that gets no new features. For new setups the standard filter language is Sieve (RFC 5228); with Dovecot it runs in the Pigeonhole plugin during LDA or LMTP delivery, and users upload their scripts over ManageSieve instead of editing a dot file in a shell account.
- Vacation recipe: switching the auto-reply on and off by a password in the
Subject:of a mail sends that password in clear with every such mail and keeps it readable in.procmailrc. The Sievevacationaction (RFC 5230) does the job without it: it remembers whom it has answered (:days), does not answer mailing lists or automatic mail, and answers only when the user's address is inTo:orCc:. - Auto-reply headers: the recipe marks its replies with
Precedence: junkandX-Looponly. RFC 3834 says an automatic response should carryAuto-Submitted: auto-repliedand that nothing should be sent in reply to a message whoseAuto-Submittedfield is other thanno; the recipe does neither. - Forwarding examples (
! address): a message forwarded unchanged keeps the original envelope sender, so the next server sees it coming from a host that the sender's SPF record does not list. Forwarding to large external providers fails or lands in spam for that reason; deliver locally and let the user fetch the mail, or forward from a server that rewrites the envelope sender. - Debug log in
/tmp:LOGFILE=/tmp/procmailvacation.logis a predictable name in a world-writable directory. Put the log into the user's home directory. - maildrop: it is still distributed by the Courier project, standalone and as part of the Courier mail server, and the comparison with procmail above still holds.
Sources: