首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在aiosmtpd中解析和解码邮件文本,执行字符串替换,并重新弹出

在aiosmtpd中解析和解码邮件文本,执行字符串替换,并重新弹出
EN

Stack Overflow用户
提问于 2020-01-30 16:43:19
回答 2查看 1.6K关注 0票数 1

我从smtpd开始,以便处理邮件队列,解析入站电子邮件并将它们发送回收件人(使用)。我切换到了aiosmtpd,因为我需要多线程处理(而smtpd是单线程的,而且看起来已经停止了)。

顺便说一句,我对aiosmtpd对邮件信封内容的管理感到困惑,这看起来比以前更细了,如果您真的需要进行微调,那么它是非常好的,但是如果您只想处理主体而不修改其余内容,那么就有点大了。

作为一个例子,smtpd process_message方法只需要data_decode=True参数来处理和解码邮件体而不触及任何东西,而aiosmtpd HANDLE_data方法似乎无法自动解码邮件信封,并且经常给出嵌入图像、附件等的异常.

编辑添加了代码示例,smtpd首先:下面的代码将实例化smtp服务器,等待端口10025上的邮件,并通过smtplib (都是本地主机)传递到10027。使用数据变量(基本上执行字符串替换,我的目标)对所有类型的邮件(基于文本/html,带有嵌入式图像、附件.)是安全的。

代码语言:javascript
复制
class PROXY_SMTP(smtpd.SMTPServer):
        def process_message(self, peer, mailfrom, rcpttos, data, decode_data=True):
        server = smtplib.SMTP('localhost', 10027)
        server.sendmail(mailfrom, rcpttos, data)
        server.quit()
server = PROXY_SMTP(('127.0.0.1', 10025), None)
asyncore.loop()

以前的代码运行良好,但以单一线程的方式(= 1邮件一次),因此我切换到aiosmtpd进行并发邮件处理。与aiosmtpd相同的示例大致如下:

代码语言:javascript
复制
class MyHandler:
        async def handle_DATA(self, server, session, envelope):
                peer = session.peer
                mailfrom = envelope.mail_from
                rcpttos = envelope.rcpt_tos
                data = envelope.content.decode()
                server = smtplib.SMTP('localhost', 10027)
                server.sendmail(mailfrom, rcpttos, data)
                server.quit()

my_handler = MyHandler()

async def main(loop):
        my_controller = Controller(my_handler, hostname='127.0.0.1', port=10025)
        my_controller.start()
loop = asyncio.get_event_loop()
loop.create_task(main(loop=loop))
try:
     loop.run_forever()

此代码适用于文本电子邮件,但在对任何复杂邮件(mime内容、附件.)解码envelope.content时会出现异常。

我如何在aiosmtpd中解析和解码邮件文本,如何像我对smtpd那样执行字符串替换,以及如何通过smtplib重新弹出邮件文本?

EN

回答 2

Stack Overflow用户

发布于 2020-01-31 12:04:33

您正在调用decode(),它的编码您不可能事先知道或预测。无论如何,修改原始的RFC5322消息是非常麻烦的,因为如果您想要修改内容,那么很难查看引用的内部--可打印部分或base64主体部分。还要注意RFC2047封装在人眼可见的头文件中,RFC2231中的文件名(或者一些令人讨厌的不兼容的变态--许多客户端甚至都不正确)等等。下面是一个例子。

相反,如果我猜对了您想要什么,我会将它解析为一个email对象,然后从那里获取它。

代码语言:javascript
复制
from email import message_from_bytes
from email.policy import default

class MyHandler:
    async def handle_DATA(self, server, session, envelope):
        peer = session.peer
        mailfrom = envelope.mail_from
        rcpttos = envelope.rcpt_tos
        message = message_from_bytes(envelope.content, policy=default)
        # ... do things with the message,
        # maybe look into the .walk() method to traverse the MIME structure
        server = smtplib.SMTP('localhost', 10027)
        server.send_message(message, mailfrom, rcpttos)
        server.quit()
        return '250 OK'

policy参数选择现代的email.message.EmailMessage类,它取代了Python3.2和更早版本中遗留的email.message.Message类。(许多在线示例仍在推广遗留API;新的API更符合逻辑,用途更广,因此,如果可能的话,您希望将其作为目标。)

这还添加了缺少的return语句,每个处理程序都应该按照文件。提供这些语句。

这里有一个示例消息,其中包含两个位置中的字符串"Hello“。由于内容传输编码掩盖了内容,因此需要分析消息(例如将其解析为email对象)才能正确地操作它。

代码语言:javascript
复制
From: me <me@example.org>
To: you <recipient@example.net>
Subject: MIME encapsulation demo
Mime-Version: 1.0
Content-type: multipart/alternative; boundary="covfefe"

--covfefe
Content-type: text/plain; charset="utf-8"
Content-transfer-encoding: quoted-printable

You had me at "H=
ello."

--covfefe
Content-type: text/html; charset="utf-8"
Content-transfer-encoding: base64

PGh0bWw+PGhlYWQ+PHRpdGxlPkhlbGxvLCBpcyBpdCBtZSB5b3UncmUgbG9va2luZyBmb3I/PC
90aXRsZT48L2hlYWQ+PGJvZHk+PHA+VGhlIGNvdiBpbiB0aGUgZmUgZmU8L3A+PC9ib2R5Pjwv
aHRtbD4K

--covfefe--
票数 2
EN

Stack Overflow用户

发布于 2021-07-18 08:59:03

OP错误地将这个文本添加到问题中,我将其作为一个(半)答案移到这里。

--解决了--

到目前为止,我还需要进行一些小的调整(主要是mime内容的单独处理和“重建”),但这解决了我的主要问题:在分离的线程上接收邮件,为文本处理腾出空间,在最终交付之前睡上固定的时间。感谢三叉戟的回答和评论,我找到了正确的方法。

代码语言:javascript
复制
import asyncio
from aiosmtpd.controller import Controller
import smtplib
from email import message_from_bytes
from email.policy import default
class MyHandler:
    async def handle_DATA(self, server, session, envelope):
        peer = session.peer
        mailfrom = envelope.mail_from
        rcpttos = envelope.rcpt_tos
        message = message_from_bytes(envelope.content, policy=default)
        #HERE MAYBE WOULD BE SAFER TO WALK CONTENTS AND PARSE/MODIFY ONLY MAIL BODY, BUT NO SIDE EFFECTS UNTIL NOW WITH MIME, ATTACHMENTS...
        messagetostring = message.as_string() ### smtplib.sendmail WANTED BYTES or STRING, NOT email OBJECT.
        ### HERE HAPPENS TEXT PROCESSING, STRING SUBSTITUTIONS...
        ### THIS WAS MY CORE NEED, ASYNCWAIT ON EACH THREAD
        await asyncio.sleep(15)
        server = smtplib.SMTP('localhost', 10027)
        server.send_message(mailfrom, rcpttos, messagetostring) ### NEEDED TO INVERT ARGS ORDER
        server.quit()
        return '250 OK' ### ADDED RETURN
    
 my_handler = MyHandler()
    
 async def main(loop):
        my_controller = Controller(my_handler, hostname='127.0.0.1', port=10025)
        my_controller.start()
 loop = asyncio.get_event_loop()
 loop.create_task(main(loop=loop))
 try:
        loop.run_forever()
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59990647

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档