Скажем, ваша обработка сообщения в переопределенном классе, например:
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) просто в конце концов отказывается и предупреждает пользователя много часов спустя. Документация кажется довольно редкой.
Как указано только в источниках (извините!), process_message
спецификации включают в себя:
Эта функция должна возвращать None, для нормальный ответ "250 Ok"; в противном случае он возвращает желаемую строку ответа в формате RFC 821.
Итак, вы можете "вернуть" 554 плохих получателей% s "% badrecipients" вместо использования этого оператора raise
- не совсем удовлетворительно (неправильно учитывает сочетание хороших и плохих, что RFC 821 должно верните сообщение "250 Ok", но также отправьте сообщение с предупреждением позже), но похоже, что это эффект "отскок назад", который вы ищете с помощью raise
.
Способ отклонения сообщения - вернуть строку с кодом ошибки из вашего метода 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
Следующее будет отбрасывать почту, не возвращая ее.
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