Procmail

简介

Procmail 是一个邮件过滤工具,可以根据邮件的发信人、主题、长度以及关键字 等对邮件进行排序、分类、整理等工作,特别是在你订阅了邮件列表的时候显得 非常有用。

配置

基本配置

Procmail 的配置文件是 ~/.procmailrc 。配置非常简单,首先是一些选项,后 面则是一些过滤规则:

值得注意的是,邮箱末尾的 / 告诉 Procmail 使用 maildir 邮箱格式,而不是 mbox 格式。

关于 recipe

Procmail 使用 recipe 来决定处理哪些邮件以及如何处理他们。一个 recipe 有这种格式:

:0 [flags] [ : [locallockfile] ]
<zero or more conditions (one per line)>
<exactly one action line>

flags 具体可以参见 man procmailrc ,后面如果加上冒号则表示对文件进行锁 定,这样可以避免同时运行几个 Procmail 操纵相同的文件的时候造成混乱。

conditions* 开头,当然处理使用正则表达式进行匹配,还有其他可用的 命令,例如以 < 开头可以检查邮件的长度是否小于给定的值。其他具体可以参 见 man procmailrc

接下来是 action line 。如果以 ! 开头,则对邮件进行 forward 到指定的地 址;如果是以 | 开头则是启动相应的程序;以 { 开头则可以指定嵌套的 recipe ;其他情况则被视为本地邮箱,如果是一个目录,则表示 maildir 格式, 否则是 mbox 格式。

一个例子

最近 MSTC 内部组建了一个邮件列表,凡是发送到 allmstc@mstczju.org 的邮 件都会被自动投递给 MSTC 的每一个成员,可是萝卜时常在外网,不方便使用内 网邮箱,正好我这里有 VPN 连接,可以同时连接到内网和外网,所以我决定对 邮件进行 forward 。

首先是要把我受到的 allmstc 的邮件列表 forward 到萝卜的外网邮箱,观察 allmstc 的邮件列表的邮件头可以找出一个合适的匹配的正则表达式:

:0:
* ^X-MDMailing-List: allmstc@mstczju.org
{
	:0 c
	! yujiazi@gmail.com

	:0:
	mstc/
}

可以看到,所有匹配到的邮件进入嵌套的下一轮匹配,首先是 forward 到萝卜 的外网邮箱,这里使用了 c 这个标志,表示对邮件进行拷贝,这样可以保证接 下来的 recipe 能够继续处理这个邮件,否则我自己就收不到邮件了,于是下一 个 recipe 我让 Procmail 把邮件放到我的 mstc 这个本地邮箱里面。

同时,为了让萝卜能够参与邮件列表的讨论,他把邮件发送到我的外网邮箱,并 保证在主题里面有 [allmstc] 的字样,那么我就把这封邮件转发到 allmstc@mstczju.org 里面去:

:0
* ^To: pluskid.zju@gmail.com
* ^Subject.*\[allmstc\].*
! allmstc@mstczju.org

运行

要让发送给自己的邮件都经过 Procmail 过滤, Timo's procmail tips and recipes 上讲了一个方法,只需要在主目录下面建立 一个 .forward 文件,内容为:

"|IFS=' ' && exec /usr/bin/procmail || exit 75 #kid"

其中路径替换成自己 procmail 真正的路径,并把 kid 替换成自己真正的用户 名即可。注意主目录的属性,如果不小心打开了组或者其他人的写属性,那么邮 件服务器将会拒绝处理 .forward 文件。

这个应该是针对 Sendmail 的语法,我使用 Exim4 ,试验了一下并不能正常运 行,而且从 Exim4 的日志可以看出有错误。我在 google 上对日志里面的出错 信息进行了一番搜索以后得到了答案。

那怪异的语法是针对 Sendmail 的各种功能以及安全漏洞的,许多文档都给出那 样的例子,以至于那甚至被认为是所有 MTA 的“正常行为”了。对于 Sendmail ,如果两个用户的 .forward 文件的内容是一样的,就会出现问题。因为 Sendmail 会对邮件的 forward 进行优化,如果一个邮件同时发送到这两个用户, 并且他们的 forward 地址都是一样的,就删除一个重复的。这样的行为对于 .forward 内容为电子邮件地址的情况是好的,但是对于用管道输出到程序的情 况就不行了。因此人们总是在命令末尾加上一些“垃圾信息”(例如自己的用户名) 来避免重复。

而其他的 MTA 总是能够区分不同用户的管道命令,因为除了对命令行进行比较 以外,他们还对程序运行的 UID 进行比较。

Sendmail 调用一个 shell 来执行管道命令,这有可能出现安全漏洞,所以大家 添加 IFS=' ' 一类的东西,试图控制局面。 Exim4 自己进行命 令行解析,所以不需要这个。

因此,在使用 Exim4 的时候, .forward 里面的内容很简单:

|/usr/bin/procmail

资源

man 文档

在线资源