proctut1 - Anatomy of a Procmail Recipe, Part I
This tutorial will discuss the three core components of a procmail recipe and how they are used.
As we discussed in Procmail Basics, procmail uses recipes to scan and decide what to do with mail. We looked at a simple recipe:
:0:
* ^From:.*joe@schmoe\.org
joe
We mentioned that any mail passing through this recipe will be scanned for the email address 'joe@schmoe.org' in the 'From:' header (i.e., the message will be from joe@schmoe.org). Any mail matching this criterion will be delivered to a file called joe.
What's this first line ':0:' business?
To be brief, all procmail recipes have three components: flags, conditions, and an action. To be precise, only flags and actions are necessary (conditions are optional). The procmailrc(5) manpage states:
A line starting with ':' marks the beginning of a recipe. It has
the following format:
:0 [flags] [ : [locallockfile] ]
<zero or more conditions (one per line)>
<exactly one action line>
But we prefer brevity over rigorous technical details[1]. (That's a little humor, folks.) We will be as accurate as we can for an introductory tutorial, but we may omit certain information until later tutorials. We will attempt to note those absences when they appear and flesh them out in later tutorials.
You've seen this in all procmail recipes:
:0
What does it mean? Simply put, the procmail program needs to look for something in your recipe file to distinguish between recipes. :0 is the official start of a procmail recipe[2].
All of your recipes should begin with (at least) :0.
After :0 it gets tricky. Here is where you decide, in general, what part(s) of the mail will be scanned[3]. For example, procmail distinguishes between the email header and the email body. Knowing this distinction is important.
The header is everything from the very first line beginning with 'From ' (without a colon) down to the first empty line. Here is a very simple email message:
From scott@perlcode.org Fri Aug 22 22:41:40 2003
Received: from localhost (localhost)
by perlcode.org (8.12.9/8.12.9) id h7N4emRF047843;
Fri, 22 Aug 2003 22:40:48 -0600 (MDT)
Date: Fri, 22 Aug 2003 22:40:47 -0600
From: Scott Wiersdorf <scott@perlcode.org>
To: Scott Wiersdorf <scott@perlcode.org>
Subject: test
Hi!
--
Scott Wiersdorf
The header consists of the top 'From '[4] line[5] all the way down to the line beginning 'Subject:' The body of the email message is the line beginning 'Hi!' down to the signature 'Scott Wiersdorf' (yes, your author is rather vain).
By default, procmail scans only the headers of the email message. This means that if you want your recipe to scan the body of the message, you'll need to use the B flag:
:0B:
* Hi!
/var/mail/scott
You may wish to put spaces after :0, like this:
:0 B:
* Hi!
/var/mail/scott
That's a little easier to read. Now we're scanning the body of the message (not the header). What if we want to scan both the body and the header?
:0 HB:
* Hi!
/var/mail/scott
Now any messages that have the word 'Hi!' in either the headers (for example, the Subject line) or the body of the message will match this recipe.
There are many other flags that we will discuss in other tutorials. See procmailrc(5) for details.
Now that we've covered what the flags do and how to use them, we need to move on to the meat of a procmail recipe. Procmail conditions are where you do all of your fancy footwork to make your recipes match just the messages you want (not too much, not too little).
Condition lines are optional, as we have previously seen[1]. A recipe that has no condition lines will match all email messages (you can think of having no conditions as being "unconditional" and the action of the recipe will always trigger[6]).
Most useful recipes have conditions, however, and all conditions begin with an asterisk: *. Here is an example of a single condition:
* ^Subject:
In the context of a recipe, it might appear like this:
:0
* ^Subject: delete me
/dev/null
If a recipe has multiple condition lines, all of the conditions must be true for the recipe to match. If any one of the conditions is not true, procmail skips the remaining conditions and moves to the next recipe. For example:
:0
* ^From: Unwanted Spammer
* ^Subject: delete me
/dev/null
If we receive an email with the phrase "delete me" as the subject, the first recipe above would dump the email message to /dev/null (deleting it forever).
The second recipe, however, also requires that the email message be sent from "Unwanted Spammer". If the message does not meet both of these requirements, the recipe will not match and procmail will move on to the next recipe (if there is one--if not, the mail will be delivered to its default mailbox as discussed in Procmail Basics).
More about conditions in procmailrc(5) and in later tutorials.
Every procmail recipe has exactly one action line[7]. Most often, the action is the name of a file to deliver the mail to if all the conditions are met:
:0:
* ^From: Joe Schmoe
$HOME/Mail/joe
The first line after the conditions (remember, all conditions start with a '*') is the action line. This action line will deliver mail from Joe Schmoe to the Mail/joe mailbox in my (the recipient's) home directory. We've seen other examples where mail was delivered to /dev/null, the bottomless pit of Unix.
There are a few other things you can do with an action line. For example, you can redirect an incoming email to someone else by inserting an exclamation point (!) at the start of the action line:
:0
! bob@perlcode.org
The exclamation point tells procmail that we are going to be forwarding this email message to the email address specified next. Any email I get will be sent along to Bob instead. You can get this same effect with sendmail's virtusertable feature. However, one thing you can't get with the virtusertable feature is scanning:
:0
* ^Subject: Send this to Bob
! bob@perlcode.org
Now I only forward selected messages to Bob. That's harder to do with just sendmail.
Many more examples of action lines, including blocks, pipes, and filters can be found in procmailrc(5) and procmailex(5) and later tutorials.
A procmail recipe is comprised of three basic components: a flags line (which always begin with :0), zero or more condition lines (which always begin with *), and an action line.
:0
/dev/null
:0:
/var/mail/foo
:0:
/var/mail/joe
:0:
/var/mail/bob
:0
* ^Subject: scan the body
{
:0 B
* delete this message!
/dev/null
}
Simple Regular Expressions, Part I
procmail(1), procmailrc(5), procmailex(5)
Scott Wiersdorf <scott@perlcode.org>
Copyright (c) 2003 Scott Wiersdorf. All rights reserved.
$Id: proctut1.pod,v 1.7 2003/09/17 13:16:08 deep Exp $