Sort out all mail coming from the scuba-dive mailing list into the
mailfolder scubafile (uses the locallockfile scubafile.lock). :0:
* ^TOscuba
scubafile
Forward all mail from peter about compilers to william (and keep a copy of
it here in petcompil).
:0
* ^From.*peter
* ^Subject:.*compilers
{
:0 c
! william@somewhere.edu
:0
petcompil
}
An equivalent solution that accomplishes the same:
:0 c
* ^From.*peter
* ^Subject:.*compilers
! william@somewhere.edu
:0 A
petcompil
An equivalent, but slightly slower solution that accomplishes the same:
:0 c
* ^From.*peter
* ^Subject:.*compilers
! william@somewhere.edu
:0
* ^From.*peter
* ^Subject:.*compilers
petcompil
If you are fairly new to procmail and plan to experiment a little bit it
often helps to have a safety net of some sort. Inserting the following
two recipes above all other recipes will make sure that of all arriving mail
always the last 32 messages will be preserved. In order for it to work as
intended, you have to create a directory named `backup' in $MAILDIR prior to
inserting these two recipes.
:0 c
backup
:0 ic
| cd backup && rm -f dummy `ls -t msg.* | sed -e 1,32d`
If your system doesn't generate or generates incorrect leading `From '
lines on every mail, you can fix this by calling up procmail with the -f-
option. To fix the same problem by different means, you could have inserted
the following two recipes above all other recipes in your rcfile. They will
filter the header of any mail through formail which will strip any leading
`From ', and automatically regenerates it subsequently.
:0 fhw
| formail -I "From " -a "From "
Add the headers of all messages that didn't come from the postmaster to
your private header collection (for statistics or mail debugging); and use the
lockfile `headc.lock'. In order to make sure the lockfile is not removed until
the pipe has finished, you have to specify option `w'; otherwise the lockfile
would be removed as soon as the pipe has accepted the mail.
:0 hwc:
* !^FROM_MAILER
| uncompress headc.Z; cat >>headc; compress headc
Or, if you would use the more efficient gzip instead of compress:
:0 hwc:
* !^FROM_MAILER
| gzip >>headc.gz
Forward all mails shorter than 1000 bytes to my home address (no lockfile
needed on this recipe).
:0
* < 1000
! myname@home
Split up incoming digests from the surfing mailing list into their
individual messages, and store them into surfing, using surfing.lock as the
locallockfile.
:0:
* ^Subject:.*surfing.*Digest
| formail +1 -ds >>surfing
Store everything coming from the postmaster or mailer-daemon (like bounced
mail) into the file postm, using postm.lock as the locallockfile.
:0:
* ^FROM_MAILER
postm
A simple autoreply recipe. It makes sure that neither mail from any daemon
(like bouncing mail or mail from mailing-lists), nor autoreplies coming from
yourself will be autoreplied to. If this precaution would not be taken,
disaster could result (`ringing' mail). In order for this recipe to autoreply
to all the incoming mail, you should of course insert it before all other
recipes in your rcfile. However, it is advisable to put it after any
recipes that process the mails from subscribed mailinglists; it generally is
not a good idea to generate autoreplies to mailinglists (yes, the
!^FROM_DAEMON regexp should already catch those, but if the mailinglist
doesn't follow accepted conventions, this might not be enough).
:0 h c
* !^FROM_DAEMON
* !^X-Loop: your@own.mail.address
| (formail -r -A"Precedence: junk" \
-A"X-Loop: your@own.mail.address" ; \
echo "Mail received.") | $SENDMAIL -t
A more complicated autoreply recipe that implements the functional
equivalent of the well known vacation(1) program. This recipe is based
on the same principles as the last one (prevent `ringing' mail). In addition
to that however, it maintains a vacation database by extracting the name of
the sender and inserting it in the vacation.cache file if the name was new
(the vacation.cache file is maintained by formail which will make sure that it
always contains the most recent names, the size of the file is limited to a
maximum of aproximately 8192 bytes).
If the name was new, an autoreply will be sent.
As you can see, the following recipe has comments between the
conditions. This is allowed. Do not put comments on the same line as a
condition though.
SHELL=/bin/sh # for other shells, this might need adjustment
:0 Whc: vacation.lock
# Perform a quick check to see if the mail was addressed to us
* $^To:.*\<$\LOGNAME\>
# Don't reply to daemons and mailinglists
* !^FROM_DAEMON
# Mail loops are evil
* !^X-Loop: your@own.mail.address
| formail -rD 8192 vacation.cache
:0 ehc # if the name was not in the cache
| (formail -rA"Precedence: junk" \
-A"X-Loop: your@own.mail.address" ; \
echo "I received your mail,"; \
echo "but I won't be back until Monday."; \
echo "-- "; cat $HOME/.signature \
) | $SENDMAIL -oi -t
Store all messages concerning TeX in separate, unique filenames, in a
directory named texmail (this directory has to exist); there is no need to use
lockfiles in this case, so we won't.
:0
* (^TO|^Subject:.*)TeX[^t]
texmail
The same as above, except now we store the mails in numbered files (MH mail
folder).
:0
* (^TO|^Subject:.*)TeX[^t]
texmail/.
Or you could file the mail in several directory folders at the same time.
The following recipe will deliver the mail to two MH-folders and one directory
folder. It is actually only one file with two extra hardlinks.
:0
* (^TO|^Subject:.*)TeX[^t]
texmail/. wordprocessing dtp/.
Store all the messages about meetings in a folder that is in a directory
that changes every month. E.g. if it were January 1994, the folder would have
the name `94-01/meeting' and the locallockfile would be `94-01/meeting.lock'.
:0:
* meeting
`date +%y-%m`/meeting
The same as above, but, if the `94-01' directory wouldn't have existed, it
is created automatically:
MONTHFOLDER=`date +%y-%m`
:0 Wic
* ? test ! -d $MONTHFOLDER
| mkdir $MONTHFOLDER
:0:
* meeting
${MONTHFOLDER}/meeting
The same as above, but now by slightly different means:
MONTHFOLDER=`date +%y-%m`
DUMMY=`test -d $MONTHFOLDER || mkdir $MONTHFOLDER`
:0:
* meeting
${MONTHFOLDER}/meeting
If you are subscribed to several mailinglists and people cross-post to some
of them, you usually receive several duplicate mails (one from every list).
The following simple recipe eliminates duplicate mails. It tells formail to
keep an 8KB cache file in which it will store the Message-IDs of the most
recent mails you received. Since Message-IDs are guaranteed to be unique for
every new mail, they are ideally suited to weed out duplicate mails. Simply
put the following recipe at the top of your rcfile, and no duplicate mail will
get past it.
:0 Wh: msgid.lock
| formail -D 8192 msgid.cache
Beware if you have delivery problems in recipes below this one and
procmail tries to requeue the mail, then on the next queue run, this mail will
be considered a duplicate and will be thrown away. For those not quite so
confident in their own scripting capabilities, you can use the following
recipe instead. It puts duplicates in a separate folder instead of throwing
them away. It is up to you to periodically empty the folder of course.
:0 Whc: msgid.lock
| formail -D 8192 msgid.cache
:0 a:
duplicates
Procmail can deliver to MH folders directly, but, it does not update the
unseen sequences the real MH manages. If you want procmail to update those as
well, use a recipe like the following which will file everything that contains
the word spam in the body of the mail into an MH folder called spamfold. Note
the local lockfile, which is needed because rcvstore steps on its own toes
(unlike procmail, when delivering to MH folders directly).
:0 :spamfold/$LOCKEXT
* B ?? spam
| rcvstore +spamfold
When delivering to emacs folders (i.e. mailfolders managed by any emacs
mail package, e.g. RMAIL or VM) directly, you should use emacs-compatible
lockfiles. The emacs mailers are a bit braindamaged in that respect, they get
very upset if someone delivers to mailfolders which they already have in their
internal buffers. The following recipe assumes that $HOME equals /home/john.
MAILDIR=Mail
:0:/usr/local/lib/emacs/lock/!home!john!Mail!mailbox
* ^Subject:.*whatever
mailbox
Alternatively, you can have procmail deliver into its own set of mailboxes,
which you then periodically empty and copy over to your emacs files using
movemail. Movemail uses mailbox.lock local lockfiles per mailbox. This
actually is the preferred mode of operation in conjunction with procmail.
To extract certain headers from a mail and put them into environment
variables you can use any of the following constructs:
SUBJECT=`formail -xSubject:` # regular field
FROM=`formail -rt -xTo:` # special case
:0 h # alternate method
KEYWORDS=| formail -xKeywords:
If you are using temporary files in a procmailrc file, and want to make
sure that they are removed just before procmail exits, you could use something
along the lines of:
TEMPORARY=$HOME/tmp/pmail.$$
TRAP="/bin/rm -f $TEMPORARY"
The TRAP keyword can also be used to change the exitcode of procmail. I.e.
if you want procmail to return an exitcode of `1' instead of its regular
exitcodes, you could use:
EXITCODE=""
TRAP="exit 1;" # The trailing semi-colon is important
# since exit is not a standalone program
Or, if the exitcode does not need to depend on the programs run from the
TRAP, you can use a mere:
EXITCODE=1
The following recipe prints every incoming mail that looks like a
postscript file.
:0 Bb
* ^^%!
| lpr
The following recipe does the same, but is a bit more selective. It only
prints the postscript file if it comes from the print-server. The first
condition matches only if it is found in the header. The second condition only
matches at the start of the body.
:0 b
* ^From[ :].*print-server
* B ?? ^^%!
| lpr
The same as above, but now by slightly different means:
:0
* ^From[ :].*print-server
{
:0 B b
* ^^%!
| lpr
}
Likewise:
:0 HB b
* ^^(.+$)*From[ :].*print-server
* ^^(.+$)*^%!
| lpr
Suppose you have two accounts, you use both accounts regularly, but they
are in very distinct places (i.e. you can only read mail that arrived at
either one of the accounts). You would like to forward mail arriving at
account one to account two, and the other way around. The first thing that
comes to mind is using .forward files at both sites; this won't work of
course, since you will be creating a mail loop. This mail loop can be avoided
by inserting the following recipe in front of all other recipes in the
$HOME/.procmailrc files on both sites. If you make sure that you add the same
X-Loop: field at both sites, mail can now safely be forwarded to the other
account from either of them.
:0 c
* !^X-Loop: yourname@your.main.mail.address
| formail -A "X-Loop: yourname@your.main.mail.address" | \
$SENDMAIL -oi yourname@the.other.account
If someone sends you a mail with the word `retrieve' in the subject, the
following will automatically send back the contents of info_file to the
sender. Like in all recipes where we send mail, we watch out for mail loops.
:0
* !^From +YOUR_USERNAME
* !^Subject:.*Re:
* !^FROM_DAEMON
* ^Subject:.*retrieve
| (formail -r ; cat info_file) | $SENDMAIL -oi -t
Now follows an example for a very simple fileserver accessible by mail. For
more demanding applications, I suggest you take a look at SmartList
(available from the same place as the procmail distribution). As listed, this
fileserver sends back at most one file per request, it ignores the body of
incoming mails, the Subject: line has to look like "Subject: send file
the_file_you_want" (the blanks are significant), it does not return files that
have names starting with a dot, nor does it allow files to be retrieved that
are outside the fileserver directory tree (if you decide to munge this
example, make sure you do not inadvertently loosen this last restriction).
:0
* ^Subject: send file [0-9a-z]
* !^X-Loop: yourname@your.main.mail.address
* !^Subject:.*Re:
* !^FROM_DAEMON
* !^Subject: send file .*[/.]\.
{
MAILDIR=$HOME/fileserver # chdir to the fileserver directory
:0 fhw # reverse mailheader and extract name
* ^Subject: send file \/[^ ]*
| formail -rA "X-Loop: yourname@your.main.mail.address"
FILE="$MATCH" # the requested filename
:0 ah
| cat - ./$FILE 2>&1 | $SENDMAIL -oi -t
}
The following example preconverts all plain-text mail arriving in certain
encoded MIME formats into a more compact 8-bit format which can be used and
displayed more easily by most programs. The mimencode(1) program is
part of Nathaniel Borenstein's metamail package.
:0
* ^Content-Type: *text/plain
{
:0 fbw
* ^Content-Transfer-Encoding: *quoted-printable
| mimencode -u -q
:0 Afhw
| formail -I "Content-Transfer-Encoding: 8bit"
:0 fbw
* ^Content-Transfer-Encoding: *base64
| mimencode -u -b
:0 Afhw
| formail -I "Content-Transfer-Encoding: 8bit"
}
The following one is rather exotic, but it only serves to demonstrate a
feature. Suppose you have a file in your HOME directory called ".urgent", and
the (one) person named in that file is the sender of an incoming mail, you'd
like that mail to be stored in $MAILDIR/urgent instead of in any of the normal
mailfolders it would have been sorted in. Then this is what you could do
(beware, the filelength of $HOME/.urgent should be well below $LINEBUF,
increase LINEBUF if necessary):
URGMATCH=`cat $HOME/.urgent`
:0:
* $^From.*${URGMATCH}
urgent
An entirely different application for procmail would be to conditionally
apply filters to a certain (outgoing) text or mail. A typical example would be
a filter through which you pipe all outgoing mail, in order to make sure that
it will be MIME encoded only if it needs to be. I.e. in this case you could
start procmail in the middle of a pipe like:
cat newtext | procmail ./mimeconvert | mail chris@where.ever
The mimeconvert rcfile could contain something like (the =0x80= and
=0xff= should be substituted with the real 8-bit characters):
DEFAULT=| # pipe to stdout instead of
# delivering mail as usual
:0 Bfbw
* [=0x80=-=0xff=]
| mimencode -q
:0 Afhw
| formail -I 'MIME-Version: 1.0' \
-I 'Content-Type: text/plain; charset=ISO-8859-1' \
-I 'Content-Transfer-Encoding: quoted-printable'