首先,我正在使用aiosmtpd
,并试图编写一个类来封装它,以便使用StartTLS以编程方式启动SMTP服务器。现在,直到最近,这段代码在您可能传入的任何处理程序中都能正常工作,例如我编写的用于调整消息参数的基本消息处理程序,等等,并将其作为消息头的一部分传入
import asyncio
import aiosmtpd
import aiosmtpd.controller
import aiosmtpd.handlers
import aiosmtpd.smtp
import email
import regex
import logging
import ssl
EMPTYBYTES = b''
COMMASPACE = ', '
CRLF = b'\r\n'
NLCRE = regex.compile(br'\r\n|\r|\n')
class StartTLSServer(aiosmtpd.controller.Controller):
def __init__(self, handler, ssl_cert_file, ssl_key_file, loop=None, hostname=None,
port=8025, *, ready_timeout=1.0, enable_SMTPUTF8=True, decode_data=False,
require_starttls=True, smtp_ident=None, data_size_limit=10485760,
smtp_timeout=300):
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain(ssl_cert_file, ssl_key_file)
self.tls_context = context
self.require_starttls = require_starttls
self.enable_SMTPUTF8 = enable_SMTPUTF8
self.decode_data = decode_data
self.smtp_ident = smtp_ident
self.data_size_limit = data_size_limit
self.smtp_timeout = smtp_timeout
super().__init__(handler, loop=loop, hostname=hostname, port=port,
ready_timeout=ready_timeout, enable_SMTPUTF8=enable_SMTPUTF8)
def factory(self):
return aiosmtpd.smtp.SMTP(self.handler, data_size_limit=self.data_size_limit,
enable_SMTPUTF8=self.enable_SMTPUTF8,
decode_data=self.decode_data,
require_starttls=self.require_starttls,
hostname=self.smtp_ident,
ident=self.smtp_ident,
tls_context=self.tls_context,
timeout=self.smtp_timeout)
class MessageHandler(aiosmtpd.handlers.Message):
def __init__(self, message_class=None, *, loop=None):
super().__init__(message_class)
self.loop = loop or asyncio.get_event_loop()
async def handle_DATA(self, server, session, envelope):
message = self.prepare_message(session, envelope)
await self.handle_message(message)
return '250 OK'
def prepare_message(self, session, envelope):
# If the server was created with decode_data True, then data will be a
# str, otherwise it will be bytes.
data = envelope.content
if isinstance(data, bytes):
message = email.message_from_bytes(data, self.message_class)
else:
assert isinstance(data, str), (
'Expected str or bytes, got {}'.format(type(data)))
message = email.message_from_string(data, self.message_class)
message['X-Peer'] = str(session.peer)
message['X-Envelope-MailFrom'] = envelope.mail_from
message['X-Envelope-RcptTo'] = COMMASPACE.join(envelope.rcpt_tos)
return message # This is handed off to handle_message directly.
async def handle_message(self, message):
print(message.as_string())
return
这驻留在custom_handlers.py
中,随后通过Python控制台在测试中调用它,如下所示:
>>> from custom_handlers import StartTLSServer, MessageHandler
>>> server = StartTLSServer(MessageHandler, ssl_cert_file="valid cert path", ssl_key_file="valid key path", hostname="0.0.0.0", port=25, require_starttls=True, smtp_ident="StartTLSSMTPServer01")
>>> server.start()
当我想停止测试服务器时,我只需执行一个server.stop()
但是在处理任何消息的过程中,我们都会被这个错误阻止:
Traceback (most recent call last):
File "/home/sysadmin/.local/lib/python3.8/site-packages/aiosmtpd/smtp.py", line 728, in _handle_client
await method(arg)
File "/home/sysadmin/.local/lib/python3.8/site-packages/aiosmtpd/smtp.py", line 1438, in smtp_DATA
status = await self._call_handler_hook('DATA')
File "/home/sysadmin/.local/lib/python3.8/site-packages/aiosmtpd/smtp.py", line 465, in _call_handler_hook
status = await hook(self, self.session, self.envelope, *args)
TypeError: handle_DATA() missing 1 required positional argument: 'envelope'
现在,我可以使用传递到SMTP工厂的任何处理程序来复制它
但是,我无法使用带有调试处理程序的普通aiosmtpd复制此功能,如文档中定义的:
aiosmtpd -c aiosmtpd.handlers.Debugging stdout -l 0.0.0.0:8025
。。。这很好用。将调试处理程序传递到StartTLSServer会导致与自定义MessageHandler类相同的错误,即使使用调试处理程序也是如此
我是否遗漏了一些明显的关于我的类的东西,这些东西以不同于aiosmtpd预期的编程用法的方式在这里爆炸
实例化MessageHandler类的对象时缺少
()
:当只传递
MessageHandler
而不传递()
时,aiosmtpd将尝试调用正则函数MessageHandler.handle_DATA(...)
(与绑定方法函数MessageHandler().handle_DATA(...)
相反)此常规函数接受四个参数:MessageHandler实例作为其第一个参数,后跟常规服务器、会话和信封参数。这解释了错误消息抱怨缺少位置参数的原因
请注意,您的
handle_DATA
实现是多余的,因为它与基类aiosmtpd.handlers.Message
中的实现相同-因此您可以删除它,它仍然可以正常工作相关问题 更多 >
编程相关推荐