我猜你想说的是,有两种类型的传入消息。一个看起来像这样:
From: Sender <there@example.net>
To: You <AmyX@example.com>
Subject: plain text
ohmigod0
另一个是复杂的MIME multipart,内容相同:
From: Sender <there@example.net>
To: Amy X <AmyX@example.com>
Subject: MIME complexity
MIME-Version: 1.0
Content-Type: multipart/related; boundary=12345
--12345
Content-type: text/plain; charset="us-ascii"
Content-transfer-encoding: base64
Content-disposition: attachment; filename="text_0.txt"
Content-location: text_0.txt
b2htaWdvZDA=
--12345--
如果这是正确的,您可能需要创建一个配方来首先处理更复杂的情况,因为它具有更多功能 - 如果您的正则表达式命中,则不太可能是误报。如果不是,则回退到更简单的模式,并假设永远不会有任何误报(可能是因为此帐户只接收来自单个系统的电子邮件)。
# extract message in the attachment if this is a MIME message
:0B
* ^Content-Disposition: *attachment.*(($)[a-z0-9].*)*($))($)\/[a-z0-9+]+=*
{ msgID="$MATCH" } # hafta have spaces inside the braces
:0EB # else, do this: assume the first non-empty body line is msgID
* ^()\/[a-z]+[0-9]+[^\+]
{ msgID="$MATCH" } # still need spaces inside braces;
# ... and, as pointed out many times before, cannot have spaces
# around the equals sign
附件的正则表达式过于简单,但我已经向您展示了如何处理a previous question of yours 中的复杂 MIME 消息——如果您有多种情况(例如,base64 编码的附件,或者只是一个普通的-文本附件,或者没有 MIME),我会从更复杂的(意味着正则表达式中的更多特征)排列它们,然后依次退回到更简单的正则表达式,误报的可能性更高。您可以随意链接:0E(“else”)案例——如果一个正则表达式成功并且以下配方是:0E 配方,它们都将被跳过。
针对您的更新,您的尝试存在两个问题。正如您所注意到的,第一个是第一个正则表达式不匹配。冒号后没有空格,我猜您要匹配的消息中有一个空格。您需要了解正则表达式中的每个字符都需要完全匹配,但具有特殊含义的正则表达式元字符除外。您通常会在许多 Procmail 配方中看到类似的内容:
* ^Content-Type:[ ]*text/html;
其中方括号之间的空格是空格和制表符。字符类(方括号中的内容)匹配任一字符一次,星号* 表示重复此模式零次或多次。这允许冒号后的任意间距。方括号和星号是元字符。 (这是非常基本的东西,应该在你可能读过的任何 Procmail 介绍中。)
您的另一个问题是每个正则表达式都是孤立应用的。所以你的食谱说,如果Content-Type 标头出现在正文中anywhere,而Content-Location 标头出现anywhere else(通常在另一个 MIME 标头中)等换句话说,你的食谱很容易出现误报。这就是我之前提出的规则如此复杂的原因:它在单个块中,即在单个 MIME 标头中按顺序查找这些标头(尽管实际上没有什么可以确保上下文 is MIME 正文部分标题;稍后会详细介绍)。
因为我们要确保有四个不同的标头,以任何顺序,正则表达式将是巨大的:ABCD|ACDB|ACDB|ABDC|ADCB|BACD|... 其中 A 是 Content-Type 标头正则表达式,B 是 Content-Location 正则表达式,等等。您可以欺骗 little 位并制作一个匹配 same 标头识别正则表达式的四个匹配序列的单个正则表达式 - 这不太可能导致任何错误积极的(没有合理的理由拥有相同标题的两个副本)并显着简化代码,尽管它仍然很复杂。请注意:我们要创建一个匹配这四个标头中的任何一个的单个正则表达式。
^Content-(Type:[ ]text/plain;|\
Location:[ ]*text_0\.txt|\
Transfer-Encoding:[ ]*base64|\
Disposition:[ ]*attachment)
...后面是任何标题,重复四次,然后是 MIME 正文部分(您在 Content-Disposition 标题之后有它,稍微脱离上下文,但本身并没有错误)。
(您的代码有text/html,但如果附件不是HTML,则按照格式和文件名的建议,它应该是text/plain;所以我将使用它。)
在我们开始之前,我要指出,Procmail 中的 MIME 解析并没有做很多,正是因为它往往会爆炸成非常复杂的正则表达式。 MIME 有很多选项,您需要每个正则表达式来允许省略或包含每个可选元素。有关于如何编码事物的选项(base64,或quoted-printable,或根本不编码),以及在许多元素周围包含或省略引号的选项,以及使用带有一个或多个正文部分的多部分消息的选项,或者只是将数据放在正文中,就像我构造的第一个示例消息(从技术上讲,它仍然是 MIME 消息;它的隐含内容类型是 text/plain; charset="us-ascii",默认内容传输编码是 7bit,方便地恰好是之前的电子邮件MIME 总是必须看起来像)。
所以除非你是因为 (a) 你真的非常想了解 Procmail 的最深奥的秘密,或者 (b) 你在一个非常受限的系统上您必须在哪里,因为没有其他东西可以使用,我会认真建议您使用适当的 MIME 解析器迁移到一种语言。对此进行解码的 Python 脚本将只有六行左右,并且您可以为您很好地规范化和解码所有内容,而无需您重新发明带引号的可打印解码或字符集翻译。 (如果你愿意,你仍然可以从 Procmail 调用 Python 脚本。)
我还要在这里指出,适当的 MIME 解析器将从多部分消息的顶级标头中提取 boundary= 参数,并确保正文部分标头上的任何匹配仅发生在边界分隔符之后。以下 Procmail 代码不这样做,因此如果邮件在 MIME 正文部分标头中的其他位置包含匹配项(例如,如果退回邮件包含 MIME 标头的片段),我们可能会得到误报退回的消息;在这种情况下,您希望配方不匹配,但它会匹配)。
:0B
* ^(Content-(Type:[ ]text/plain;|\
Location:[ ]*text_0\.txt|\
Transfer-Encoding:[ ]*base64|\
Disposition:[ ]*attachment).*(($)[a-z0-9].*)*)($)\
(Content-(Type:[ ]text/plain;|\
Location:[ ]*text_0\.txt|\
Transfer-Encoding:[ ]*base64|\
Disposition:[ ]*attachment).*(($)[a-z0-9].*)*)($)\
(Content-(Type:[ ]text/plain;|\
Location:[ ]*text_0\.txt|\
Transfer-Encoding:[ ]*base64|\
Disposition:[ ]*attachment).*(($)[a-z0-9].*)*)($)\
(Content-(Type:[ ]text/plain;|\
Location:[ ]*text_0\.txt|\
Transfer-Encoding:[ ]*base64|\
Disposition:[ ]*attachment).*(($)[a-z0-9].*)*)($)\
($)\/[a-z0-9/+]+=*
{ msgid=`printf '%s' "$MATCH" | base64 -d` }
:0BE
* ^^\/[a-z]+[0-9]*[^\+]
{ msgid="$MATCH" }
(不幸的是,Procmail 的正则表达式引擎没有 {4} 重复运算符,所以我们必须将正则表达式从字面上重复四次!)
如前所述,不幸的是,Procmail 对 MIME 一无所知。就 Procmail 而言,最顶层的 headers 是 headers,其他的都是 body。已经有人尝试为 Procmail 编写 MIME 库或扩展,但它们并不会降低复杂性,只是将其随机化。