【问题标题】:How to extract email body and attachment如何提取电子邮件正文和附件
【发布时间】:2016-03-30 07:17:31
【问题描述】:

我正在尝试从多部分电子邮件正文或附件中提取消息,因此我使用 :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从body中得到了同样的消息,是inline image code,有什么问题,谁知道过滤它的更好条件?

我还需要检测body中的sub-header是否是文本和base64编码,然后解码,如何用regex规定:

 :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;

【问题讨论】:

  • 您的问题和标题说您想提取正文和附件,但您显示的代码并没有尝试做类似的事情。您最好包含一个不符合您预期的简单示例消息,并向我们展示您要提取的确切内容。
  • 另外,为什么你期望(几乎)相同的正则表达式不匹配相同的文本?
  • 正如之前反复指出的,(后半部分)我对您之前几乎相同的问题stackoverflow.com/a/32733374/874188 的回答解释了如何查找附件何时是 base64 编码以及如何解码提取的文本。
  • @tripleee 我在上面进一步解释了,但是我应该使用 :0B 以便从附件中获取它,是标题或正文的附件部分,实际使用什么 - :0fh 将正确的附件传递给$匹配?
  • 您的问题正在取得进展(格式终于正确!),但您需要解释更多您尝试过的内容(包括相关的、指向先前问题的链接;并解释为什么您更改了某些内容如果之前提供了有效的答案并且您没有逐字使用它)以及您遇到的问题。

标签: email procmail


【解决方案1】:

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

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 库或扩展,但它们并不会降低复杂性,只是将其随机化。

【讨论】:

  • 谢谢!如何检测 base64 编码的附件?查看修改后的帖子。我想最好通过 *^Content-Type:text/html 过滤它,然后是下一个 *^...,但是我从来没有通过内容类型的第一个条件,也没有通过你之前给我的四行正则表达式。 @tripleee
猜你喜欢
  • 2011-01-09
  • 1970-01-01
  • 2021-01-01
  • 1970-01-01
  • 2016-04-12
  • 1970-01-01
  • 2018-07-24
  • 1970-01-01
  • 2021-01-03
相关资源
最近更新 更多