带有自定义控制器和SMTP工厂的aiosmtpd古怪

2024-10-01 13:40:20 发布

您现在位置:Python中文网/ 问答频道 /正文

首先,我正在使用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预期的编程用法的方式在这里爆炸


Tags: importselfloop处理程序sslmessagedataenable
1条回答
网友
1楼 · 发布于 2024-10-01 13:40:20

实例化MessageHandler类的对象时缺少()

>>> server = StartTLSServer(MessageHandler(), ...)

当只传递MessageHandler而不传递()时,aiosmtpd将尝试调用正则函数MessageHandler.handle_DATA(...)(与绑定方法函数MessageHandler().handle_DATA(...)相反)

此常规函数接受四个参数:MessageHandler实例作为其第一个参数,后跟常规服务器、会话和信封参数。这解释了错误消息抱怨缺少位置参数的原因

请注意,您的handle_DATA实现是多余的,因为它与基类aiosmtpd.handlers.Message中的实现相同-因此您可以删除它,它仍然可以正常工作

相关问题 更多 >