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

python 3 smtplib: binary attachment encodes incorrectly in flask when gnupg is active

我正在尝试使用 python 3.5.2 smtplib 通过 TLS 发送二进制附件。我的平台是 OSX,我正在使用从自制程序安装的 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 header,并添加 Content-Disposition header 和 none 似乎有所作为。

我已经确认我的电子邮件客户端在接收来自其他客户端的 base64 编码附件时没有问题。

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

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


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

更新 2:我已将这个问题缩小到与来自 https://github.com/isislovecruft/python-gnupg/ 的 gnupg 库的交互。我已经更新了我的最小示例以反映这一点。 python-gnupug 中 python 编解码器的 monkey-patch 是造成问题的原因:codecs.register_error('strict', codecs.replace_errors)

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

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