如何提取电子邮件正文和附件

时间:2016-03-30 07:17:32

标签: email procmail

我正在尝试从多部分电子邮件正文或附件中提取消息,因此我使用:0B尝试每个选项,如下所示:

msgID=""

#extract message in the attachment if it's plain text
:0B
* ^Content-Disposition: *attachment.*(($)[a-z0-9].*)*($)($)\/[a-z0-9+]+=*
{msgID="$MATCH"}

#extract message in the body if it's there
:0EB
* ^()\/[a-z]+[0-9]+[^\+]
{msgID = "$MATCH"}

但msgID得到了来自正文图像代码的正文中的相同消息,它有什么问题,谁知道过滤它的更好条件?

我还需要检测正文中的子标题是否为text和base64编码,然后对其进行解码,如何使用正则表达式进行规定:

 :0B
 * ^Content-Type:text/html;
 * ^Content-Location:text_0.txt
 * ^Content-Transfer-Encoding:base64
 * ^Content-Disposition: *attachment.*(($)[a-z0-9].*)*($)($)\/[a-z0-9+]+=*
 { msgID= msgId =`printf '%s' "$MATCH" | base64 -d` }

总是抱怨不匹配:^Content-Type:text/html;

1 个答案:

答案 0 :(得分:1)

我想猜测你试图说,有两种类型的传入消息。一个看起来像这样:

From: Sender <there@example.net>
To: You <AmyX@example.com>
Subject: plain text

ohmigod0

另一个是具有相同内容的复杂MIME多部分:

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(&#34; else&#34;)个案 - 如果正则表达式成功并且以下食谱是:0E食谱,则它们都将被跳过。< / p>

为了响应您的更新,您的尝试存在两个问题。首先,正如您所注意到的,第一个正则表达式并不匹配。冒号后面没有空格,我猜你在匹配的邮件中有一个空格。您需要了解正则表达式中的每个字符都需要完全匹配,除了具有特殊含义的正则表达式元字符。您通常会在许多Procmail配方中看到类似的内容:

* ^Content-Type:[   ]*text/html;

方括号之间的空格是空格和制表符。字符类(方括号中的内容)匹配任一字符一次,星号*表示重复此模式一次或多次。这允许冒号后的任意间距。方括号和星号是元字符。 (这是非常基本的东西,应该在你可能已阅读的任何Procmail介绍中。)

您的另一个问题是每个正则表达式都是单独应用的。所以你的食谱说,如果Content-Type标题在正文中出现任何地方Content-Location标题会出现 where else(通常在另一个MIME中)标题某处)等等。换句话说,你的食谱很容易出现误报。这就是我之前提出的规则如此复杂的原因:它在单个块中按顺序查找这些头,即在单个MIME头中(尽管实际上没有什么可以确保上下文是< / em>一个MIME正文部分标题;稍后更多内容。

因为我们要确保有四个不同的标头,按任何顺序,这个的正则表达式将是巨大的:ABCD|ACDB|ACDB|ABDC|ADCB|BACD|...其中A是Content-Type标头正则表达式,B是{ {1}}正则表达式等等你可以欺骗 little 位并制作一个正则表达式,它匹配相同标题正则表达式的四个匹配序列 - 这个不太可能导致任何误报(没有理由有两个相同标题的副本)并且显着简化了代码,尽管它仍然很复杂。请注意:我们要创建一个与这四个标题中的任何一个匹配的正则表达式。

Content-Location

...后跟任何标题,重复四次,然后是MIME正文部分(你在^Content-(Type:[ ]text/plain;|\ Location:[ ]*text_0\.txt|\ Transfer-Encoding:[ ]*base64|\ Disposition:[ ]*attachment) 标题之后,稍微脱离了上下文,但本身并没有错误。)

(您的代码有Content-Disposition,但如果附件不是HTML,则按照格式和文件名的建议,它应该是text/html;所以我就是这样做的来代替。)

在我们去那里之前,我会指出Procmail中的MIME解析并没有做很多,正是因为它往往会爆炸成极其复杂的正则表达式。 MIME有很多选项,你需要每个正则表达式允许遗漏或包含每个可选元素。可以选择如何编码事物(text/plainbase64,或根本不编码)和选项以包含或省略许多元素周围的引号,以及使用多部分消息与一个或多个选项的选项正文部分或只是将数据放入正文中,就像我构造的第一个示例消息(技术上仍然是MIME消息;其隐含内容类型为quoted-printable,默认内容传输编码为text/plain; charset="us-ascii",这很容易就像MIME之前的电子邮件一样。)

所以除非你是因为(a)你真的,真的想学习Procmail最深层的秘密或者(b)你是在一个非常有限的系统你拥有的地方,因为你没有别的东西可以使用,我会认真地建议你转移到一个带有正确MIME解析器的语言。一个解码它的Python脚本只有六行左右,你可以很好地为你做一切规范化和解码,而不需要你重新发明引用可打印的解码或字符集转换。 (如果愿意,您仍然可以从Procmail调用Python脚本。)

我还要指出,正确的MIME解析器会从多部分消息中的顶级标头中提取7bit参数,并确保在正文部分标头上的任何匹配仅在紧接着之后发生边界分隔符。以下Procmail代码不会这样做,因此如果消息包含除MIME主体部分标题之外的其他地方(例如,如果退回邮件包含MIME标头的片段),则可能会出现误报。退回的消息;在这种情况下,您希望配方不匹配,但它会。)

boundary=

(不幸的是,Procmail的正则表达式引擎没有: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对MIME没有任何了解。就Procmail而言,顶级标题是标题,其他一切都是正文。有人试图为Procmail编写MIME库或扩展,但它们不会降低复杂性,只是随意改变它。