电子邮件中的 UTF-8 编码,解析正文

Posted

技术标签:

【中文标题】电子邮件中的 UTF-8 编码,解析正文【英文标题】:UTF-8 encoding in emails, parsing the body 【发布时间】:2020-08-07 20:31:50 【问题描述】:

所以我真的不希望这个问题是特定于语言的,但是我怀疑 Go(我的语言选择)在这里发挥了作用。 我试图在原始电子邮件的正文中找到一个字符串。为此,我得到了编码,大多数情况是quoted-printable。 好的,那很好,我正在编码我的搜索查询引用可打印,然后搜索它。这样可行。 然而。在一种特定情况下,我在 gmail 中看到的原始电子邮件看起来不错,但是当我从 gmail API 检索原始电子邮件时,尽管编码和一切都是相同的,但它会将 " 编码为 =22 研究告诉我那是因为字符集是utf-8。 我还没有完全弄清楚那是编码 utf-8 然后引用打印或其他方式,但这也不是问题...... 如果我查看"=22 的电子邮件,我看到字符集为utf-8,而当我查看另一个未编码的电子邮件时,字符集为UTF-8(注意大小写)。我不敢相信这里的情况是导致这种情况发生的原因,但如果=22 实际上是=22 还是" 编码的utf-8,这似乎不是一个足够强大的方法。

我最初的想法是始终解码引用的可打印内容,然后在进行搜索之前对其进行重新编码,但我认为这不会是一种可靠的方法,并且认为其他人可能会有更好的建议?

结论,我正在尝试在原始电子邮件中查找字符串,但编码导致我无法让搜索字符串与正文的编码匹配

【问题讨论】:

【参考方案1】:

=22-type 编码实际上与字符集无关(无论是 utf-8 小写还是 UTF-8 大写或任何其他字符集)。

它是Content-Transfer-Encoding: quoted-printable 编码。

quoted-printable 编码只是十六进制编码八位字节的一种方式,通常仅限于可打印 ascii 范围之外的八位字节。以这种方式编码 DQUOTE 字符似乎很奇怪,但这样做是完全合法的。

如果您想在邮件正文中搜索字符串,您需要先对邮件正文进行解码。否则你不会成功。

我建议至少阅读rfc2045。

如果您最终想要在某个时候搜索标头,您可能还需要最终阅读 rfc2047,但这会变得......由于发送客户端存在的各种错误而变得棘手。

既然我已经被“触发”到对 MIME 的咆哮,让我解释一下为什么解码标头如此难以正确。我敢肯定,几乎每个曾经在电子邮件客户端上工作过的开发人员都会告诉你这一点,但我想我会成为这样做的人。

以下只是每个开发人员在为已根据 rfc2047 规范(理论上)编码的标头实现解码器时面临的问题的简短列表:

    首先,从技术上讲,rfc2047 指定了两种标头编码格式 - 一种用于短语,另一种用于非结构化文本字段。它们非常相似,但您不能使用相同的规则来标记它们。我提到这一点是因为似乎大多数 MIME 解析器都忽略了这个非常微妙的区别,因此,正如您可能想象的那样,大多数 MIME 生成器也是如此。见鬼,大多数 MIME 生成器可能从未听说过它的规范。

    这让我们:

    根据 rfc2822 和 rfc2047 的规则,MIME 标头无法标记化的方式有很多。你会遇到有趣的事情,例如:

    一个。编码字标记被非法嵌入到其他字标记中

    b.包含非法字符(例如空格、换行符等)的编码字标记有效地使标记器无法再标记它们(至少不容易)

    c。多字节字符序列在多个编码字标记之间拆分,这意味着无法单独解码所述编码字标记

    d。编码字令牌的有效负载被分成多个编码字令牌,通常在一个位置分裂,这使得无法单独解码有效负载

    你可以看到一些例子here。

    许多开发人员似乎忽略了一个事实,即每个编码字标记都允许采用不同的字符编码(您可能在 UTF-8 中有一个标记,在 ISO-8859-1 中有另一个,在 koi8 中有另一个-r)。通常,这没什么大不了的,因为您只需解码每个有效负载,然后通过 iconv() 或其他方式将指定的字符集转换为 UTF-8。然而,由于我在上面 (2c) 和 (2d) 中提到的乐趣破碎,这变得更加复杂。

    如果这还不足以让你想举起双手并喃喃几句脏话,还有更多......

    标题中未声明的 8 位文本。是的。一些邮寄者只是没有收到他们应该对非 ASCII 文本进行编码的备忘录。因此,现在您可以获得混合和匹配未声明的 8 位文本的有趣体验,即 God-only-knows what charset 以及(可能损坏的)编码词的内容。

如果你想看看如何处理这些问题,你可以看看我是如何在我的GMime 库中使用 C 实现的,这里:https://github.com/jstedfast/gmime/blob/master/gmime/gmime-utils.c#L1894(如果将来行偏移发生变化,请查看对于_g_mime_utils_header_decode_text() 以及它在该源文件中使用的各种内部方法 - 我已经编写了 cmets 来解释它如何处理上述问题)。

或者您可以在我的MimeKit 库中查看我是如何使用 C# 实现的,这里:https://github.com/jstedfast/MimeKit/blob/master/MimeKit/Utils/Rfc2047.cs

如需详细了解处理电子邮件为何及如何困难,请查看 Joshua Cramner 的博客系列:http://quetzalcoatal.blogspot.com/search/label/email-hard

【讨论】:

非常感谢@jstedfast 是的,我的经验是电子邮件是建立在快速沙子上的。所以我认为在这个阶段我不需要太深入地研究标题,因为我确信我可以从 gmail API 获得正文内容(只是没有以任何方式编码)。我也有来自 gmail API 的 quoted-printable 标头。我想要做的是在原始电子邮件中找到正文的开头,正如你所说,这意味着将我必须的正文编码为与原始电子邮件相同的类型并进行搜索.....TBC跨度> 但是我不知道为什么我使用的quoted-printable lib不编码“并且通常客户端也不编码,但在极少数情况下它已被编码。作为你说“这看起来很奇怪” - 问题是我无法检测到它是否会发生......没有一个确定的方法可以知道我想象的?我现在要看看你的代码 我建议的是解码消息正文,而不是编码搜索字符串。没有办法以允许任何合理的字符串匹配算法工作的方式对搜索字符串进行编码。 那里的问题是,我只想知道字符串在解码版本中的位置 - 我需要知道编码版本中的内容,但我想不出一种巧妙的方法来“映射两者”。也许我可以查找正文之前的标题并从那里使用索引,但这安全/可靠吗? 你想做的事是不可能的。

以上是关于电子邮件中的 UTF-8 编码,解析正文的主要内容,如果未能解决你的问题,请参考以下文章

Javamail 使用 7BIT 内容传输编码解析电子邮件正文

使用 7BIT 内容传输编码解析电子邮件正文 - PHP

mail 邮件内容出现随机乱码

使用 utf-8 文件名发送 MIME 编码的电子邮件附件

需要用 perl 解析 utf8 邮件

JavaMail 从字符串发送邮件附件 - 编码 UTF-8