Как отказаться от получателя в smtpd.SMTPServer.process_message?

1

Скажем, ваша обработка сообщения в переопределенном классе, например:

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) просто в конце концов отказывается и предупреждает пользователя много часов спустя. Документация кажется довольно редкой.

Теги:
email
smtp

3 ответа

5
Лучший ответ

Как указано только в источниках (извините!), process_message спецификации включают в себя:

Эта функция должна возвращать None, для нормальный ответ "250 Ok"; в противном случае он возвращает желаемую строку ответа в формате RFC 821.

Итак, вы можете "вернуть" 554 плохих получателей% s "% badrecipients" вместо использования этого оператора raise - не совсем удовлетворительно (неправильно учитывает сочетание хороших и плохих, что RFC 821 должно верните сообщение "250 Ok", но также отправьте сообщение с предупреждением позже), но похоже, что это эффект "отскок назад", который вы ищете с помощью raise.

  • 1
    +1 за ссылку на источники
1

Способ отклонения сообщения - вернуть строку с кодом ошибки из вашего метода process_message; например.

return '550 No such user here'

Однако RFC 821 не позволяет вернуть код ошибки 550 после передачи данных сообщения (он должен быть возвращен после команды RCPT), а модуль smtpd, к сожалению, не обеспечивает простой способ верните код ошибки на этом этапе. Кроме того, smtpd.py затрудняет подкласс классов, используя атрибуты double-underscore "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 valid
        return False
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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню