如何在smtpd.SMTPServer.process_message中拒绝收件人?

时间:2009-06-22 22:41:05

标签: python email smtp

假设您在重写的类中处理消息,例如:

class MailProcessorServer(smtpd.SMTPServer):
  def process_message(self, peer, sender, rcpttos, data):
    badrecipients = []
    for rcpt in rcpttos:
      badrecipients.append(rcpt)

    #Here I want to warn the sender via a bounced email
    # that the recipient does not exist
    raise smtplib.SMTPRecipientsRefused(badrecipients)
    #but this just crashes the process and eventually the sender times out,
    # not good enough

我只想立即回复发件人。相反,发送服务(比如GMail)最终会放弃,并在几小时后警告用户。 documentation看起来很稀疏。

3 个答案:

答案 0 :(得分:5)

仅在the sources中记录(抱歉!),process_message的规格包括:

  

此函数应返回None,for   正常的'250 Ok'响应;除此以外   它返回所需的响应字符串   RFC 821格式。

所以你可以“返回”554个坏收件人%s'%badrecipients“而不是使用raise语句 - 不完全令人满意(没有正确解释好坏的混合,这是由RFC 821 应该返回'250 Ok',但也会稍后发送警告邮件)但它似乎确实是你正在寻找raise的“立即反弹”效果。

答案 1 :(得分:1)

拒绝邮件的方法是返回一个包含process_message方法错误代码的字符串; e.g。

return '550 No such user here'

然而,RFC 821不允许在传输消息数据后返回错误代码550(它应该在RCPT命令之后返回),并且遗憾的是smtpd模块不容易提供在该阶段返回错误代码的方法。此外,smtpd.py使用auto-mangling“private”双下划线属性很难对其类进行子类化。

您可以使用以下smtpd类的自定义子类,但我还没有测试过这段代码:

class RecipientValidatingSMTPChannel(smtpd.SMTPChannel):
    def smtp_RCPT(self, arg):
        print >> smtpd.DEBUGSTREAM, '===> RCPT', arg
        if not self._SMTPChannel__mailfrom:
            self.push('503 Error: need MAIL command')
            return
        address = self._SMTPChannel__getaddr('TO:', arg)
        if not address:
            self.push('501 Syntax: RCPT TO: <address>')
            return
        if self._SMTPChannel__server.is_valid_recipient(address):
            self._SMTPChannel__rcpttos.append(address)
            print >> smtpd.DEBUGSTREAM, 'recips:', self._SMTPChannel__rcpttos
            self.push('250 Ok')
        else:
            self.push('550 No such user here')


class MailProcessorServer(smtpd.SMTPServer):
    def handle_accept(self):
        conn, addr = self.accept()
        print >> smtpd.DEBUGSTREAM, 'Incoming connection from %s' % repr(addr)
        channel = RecipientValidatingSMTPChannel(self, conn, addr)

    def is_valid_recipient(self, address):
        # insert your own tests here, return True if it's valid
        return False

答案 2 :(得分:0)

以下内容将丢弃邮件而不会退回邮件。

return '554-5.7.1'

问题:如果您拒绝邮件而没有退回邮件,则发件人MTA会尝试一次又一次重新发送邮件。

错误代码550将使电子邮件退回,这是一个坏主意,因为您不想向垃圾邮件发送者任何有关您的邮件服务器的信息。小心点。

return '550'

这两个错误都会引发smtplib.SMTPException。这是我用来处理此类异常的简化代码。

try:
    if bounce:
        return '550 Bad address'
    else:
        self.send_and_quit(sender, recipients, data)
except smtplib.SMTPException as e:
    raise e
except Exception as e:
    # Catch any other exception
    logging.error(traceback.format_exc())

    if not isinstance(e, smtplib.SMTPException):
        self.send_and_quit(sender, recipients, data)
    else:
        raise e