我从smtpd开始,以便处理邮件队列,解析入站电子邮件并将它们发送回收件人(使用)。我切换到了aiosmtpd,因为我需要多线程处理(而smtpd是单线程的,而且看起来已经停止了)。
顺便说一句,我对aiosmtpd对邮件信封内容的管理感到困惑,这看起来比以前更细了,如果您真的需要进行微调,那么它是非常好的,但是如果您只想处理主体而不修改其余内容,那么就有点大了。
作为一个例子,smtpd process_message方法只需要data_decode=True参数来处理和解码邮件体而不触及任何东西,而aiosmtpd HANDLE_data方法似乎无法自动解码邮件信封,并且经常给出嵌入图像、附件等的异常.
编辑添加了代码示例,smtpd首先:下面的代码将实例化smtp服务器,等待端口10025上的邮件,并通过smtplib (都是本地主机)传递到10027。使用数据变量(基本上执行字符串替换,我的目标)对所有类型的邮件(基于文本/html,带有嵌入式图像、附件.)是安全的。
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相同的示例大致如下:
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重新弹出邮件文本?
发布于 2020-01-31 12:04:33
您正在调用decode(),它的编码您不可能事先知道或预测。无论如何,修改原始的RFC5322消息是非常麻烦的,因为如果您想要修改内容,那么很难查看引用的内部--可打印部分或base64主体部分。还要注意RFC2047封装在人眼可见的头文件中,RFC2231中的文件名(或者一些令人讨厌的不兼容的变态--许多客户端甚至都不正确)等等。下面是一个例子。
相反,如果我猜对了您想要什么,我会将它解析为一个email对象,然后从那里获取它。
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对象)才能正确地操作它。
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--发布于 2021-07-18 08:59:03
OP错误地将这个文本添加到问题中,我将其作为一个(半)答案移到这里。
--解决了--
到目前为止,我还需要进行一些小的调整(主要是mime内容的单独处理和“重建”),但这解决了我的主要问题:在分离的线程上接收邮件,为文本处理腾出空间,在最终交付之前睡上固定的时间。感谢三叉戟的回答和评论,我找到了正确的方法。
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()https://stackoverflow.com/questions/59990647
复制相似问题