python 3 smtplib:当gnupg处于活动状态时,二进制附件在烧瓶中编码不正确

Posted

技术标签:

【中文标题】python 3 smtplib:当gnupg处于活动状态时,二进制附件在烧瓶中编码不正确【英文标题】:python 3 smtplib: binary attachment encodes incorrectly in flask when gnupg is active 【发布时间】:2017-02-28 08:03:02 【问题描述】:

我正在尝试使用 python 3.5.2 smtplib 通过 TLS 发送二进制附件。我的平台是 OSX,我使用的是从 homebrew 安装的 python。

当我收到附件时,编码似乎被修改了。而不是以这个十六进制开头的原始文件:

ffd8 ffe0 0010 4a46 4946 0001 0100 0001

i.e., <FF><D8><FF><E0>^@^PJFIF^@^A^A

我收到的附件的起始十六进制有一些奇怪的 base64 剩余:

5c75 6463 6666 5c75 6463 6438 5c75 6463 6666 5c75 6463 6530 0010 4a46 4946 0001

i.e., \udcff\udcd8\udcff\udce0^@^PJFIF

这是一个失败的最小案例,除了添加了 TLS 逻辑和 gnupg

import gnupg
gpg = gnupg.GPG('/path/to/gpg')

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage

def send_my_email():
    msg = MIMEMultipart()
    msg['Subject'] = 'subject'
    msg['From'] = 'XXXX@gmail.com'
    msg['To'] = 'YYYY@gmail.com'
    with open('/tmp/image.jpg', mode='rb') as image_file:
        image = MIMEImage(image_file.read())
    msg.attach(image)
    s = smtplib.SMTP('smtp.gmail.com', 587)
    s.starttls()
    s.login('XXXX@gmail.com', 'password')
    s.send_message(msg)
    s.quit()

基于此处的另一个问题,我尝试了这个而不是send_message(),但它也失败了:

    s.sendmail('XXXX@gmail.com', ['YYYY@gmail.com'], msg.as_string())

我还尝试在初始化MIMEImage 时显式添加_subtype='jpg',添加Content-Transfer-Encoding 标头,并添加Content-Disposition 标头,但这些似乎都没有任何区别。

我已验证我的电子邮件客户端在收到来自其他客户端的 base64 编码附件时没有问题。

我查看了 smtplib 源代码并注意到 smtplib 处理行分隔符的方式看起来有点奇怪,我想知道这是否可能相关。 (另见参考:https://bugs.python.org/issue14645)

我是否需要对某些内容进行不同的编码,为我的平台设置一些特殊的内容,或者这是一个小故障?谢谢!


更新:此问题仅在我运行 Flask 时存在,不会在 Flask 之外发生。我正在尝试隔离我的 Flask 环境中的问题。我认为它可能是 flask_mail 但删除它并没有解决问题。下面的代码在我的系统上从 Flask 运行时会失败,但如果我从同一虚拟环境和同一 python 二进制文件中的 shell 脚本运行它则不会。由于复杂性,我目前不希望有任何答案,但会留给后代。

更新 2:我已将此问题缩小到与来自 https://github.com/isislovecruft/python-gnupg/ 的 gnupg 库的交互。我已经更新了我的最小示例以反映这一点。 python-gnupug 中的这个 python 编解码器的猴子补丁负责这个问题:codecs.register_error('strict', codecs.replace_errors)

【问题讨论】:

看起来文件正在被读入 python 字符串(unicode)并在 utf-8 编码该字符串后被序列化。我怀疑 MIMEImage 需要一些帮助,但我没有在 3.x 上使用 email.mime 代码,所以我无法直接指出问题所在。 啊哈,当我读入文件时,我会看看是否需要做一些不同的事情。谢谢! 在将app.open_resource() 转换为open() 后,您的程序对我来说完美无缺。您能否提供一个简短的完整程序来演示该问题?请参阅minimal reproducible example 了解更多信息。 此外,将其集成到 Flask 应用程序中仍然不会使其失败。请提供minimal reproducible example。 啊,我忽略了从我的示例中清除 open_resource 代码。我确实在使用 Flask,并且使用此代码确实会出现这个问题。 image.jpg 位于我的 Flask 应用程序的根目录中。我将尝试直接打开,看看它是否与我的 Flask 调用有关。 【参考方案1】:

这是由于 gnupg 和 smtplib + MIME 之间的不良交互造成的。 gnupg 调用codecs.register_error('strict', codecs.replace_errors),会干扰其他包进行的编码。

参考:https://github.com/isislovecruft/python-gnupg/issues/49

【讨论】:

以上是关于python 3 smtplib:当gnupg处于活动状态时,二进制附件在烧瓶中编码不正确的主要内容,如果未能解决你的问题,请参考以下文章

python 3 smtplib 异常:“SSL:WRONG_VERSION_NUMBER”登录到 Outlook

python学习-smtplib模块

smtplib 在 Python 3.1 中发送带有 unicode 字符的邮件的问题

Python GNUPG - 信任导入的密钥?

smtplib之邮件发送

python 2.7 smtplib 和 mime 的电子邮件附件问题