157 lines
4.5 KiB
Python
157 lines
4.5 KiB
Python
# encoding: utf-8
|
|
|
|
__all__ = ['SMTPClientWithResponse', 'SMTPClientWithResponse_SSL']
|
|
|
|
import smtplib
|
|
from smtplib import _have_ssl, SMTP
|
|
import logging
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class SMTPClientWithResponse(SMTP):
|
|
|
|
def __init__(self, parent, **kwargs):
|
|
|
|
self._initialized = False
|
|
|
|
self.parent = parent
|
|
self.make_response = parent.make_response
|
|
self.tls = kwargs.pop('tls', False)
|
|
self.ssl = kwargs.pop('ssl', False)
|
|
self.debug = kwargs.pop('debug', 0)
|
|
self.user = kwargs.pop('user', None)
|
|
self.password = kwargs.pop('password', None)
|
|
|
|
SMTP.__init__(self, **kwargs)
|
|
|
|
self.initialize()
|
|
|
|
def initialize(self):
|
|
if not self._initialized:
|
|
self.set_debuglevel(self.debug)
|
|
if self.tls:
|
|
self.starttls()
|
|
if self.user:
|
|
self.login(user=self.user, password=self.password)
|
|
self.ehlo_or_helo_if_needed()
|
|
self.initialized = True
|
|
|
|
def quit(self):
|
|
"""Closes the connection to the email server."""
|
|
try:
|
|
SMTP.quit(self)
|
|
except (smtplib.SMTPServerDisconnected, ):
|
|
self.close()
|
|
|
|
def _rset(self):
|
|
try:
|
|
self.rset()
|
|
except smtplib.SMTPServerDisconnected:
|
|
pass
|
|
|
|
def sendmail(self, from_addr, to_addrs, msg, mail_options=None, rcpt_options=None):
|
|
|
|
if not to_addrs:
|
|
return None
|
|
|
|
rcpt_options = rcpt_options or []
|
|
mail_options = mail_options or []
|
|
esmtp_opts = []
|
|
if self.does_esmtp:
|
|
if self.has_extn('size'):
|
|
esmtp_opts.append("size=%d" % len(msg))
|
|
for option in mail_options:
|
|
esmtp_opts.append(option)
|
|
|
|
response = self.make_response()
|
|
|
|
response.from_addr = from_addr
|
|
response.esmtp_opts = esmtp_opts[:]
|
|
|
|
(code, resp) = self.mail(from_addr, esmtp_opts)
|
|
response.set_status('mail', code, resp)
|
|
|
|
if code != 250:
|
|
self._rset()
|
|
exc = smtplib.SMTPSenderRefused(code, resp, from_addr)
|
|
response.set_exception(exc)
|
|
return response
|
|
|
|
if not isinstance(to_addrs, (list, tuple)):
|
|
to_addrs = [to_addrs]
|
|
|
|
response.to_addrs = to_addrs
|
|
response.rcpt_options = rcpt_options[:]
|
|
response.refused_recipients = {}
|
|
|
|
for a in to_addrs:
|
|
(code, resp) = self.rcpt(a, rcpt_options)
|
|
response.set_status('rcpt', code, resp, recipient=a)
|
|
if (code != 250) and (code != 251):
|
|
response.refused_recipients[a] = (code, resp)
|
|
|
|
if len(response.refused_recipients) == len(to_addrs):
|
|
# the server refused all our recipients
|
|
self._rset()
|
|
exc = smtplib.SMTPRecipientsRefused(response.refused_recipients)
|
|
response.set_exception(exc)
|
|
return response
|
|
|
|
(code, resp) = self.data(msg)
|
|
response.set_status('data', code, resp)
|
|
if code != 250:
|
|
self._rset()
|
|
exc = smtplib.SMTPDataError(code, resp)
|
|
response.set_exception(exc)
|
|
return response
|
|
|
|
response._finished = True
|
|
return response
|
|
|
|
|
|
if _have_ssl:
|
|
|
|
from smtplib import SMTP_SSL
|
|
import ssl
|
|
|
|
class SMTPClientWithResponse_SSL(SMTP_SSL, SMTPClientWithResponse):
|
|
|
|
def __init__(self, **kw):
|
|
args = {}
|
|
for k in ('host', 'port', 'local_hostname', 'keyfile', 'certfile', 'timeout'):
|
|
if k in kw:
|
|
args[k] = kw[k]
|
|
SMTP_SSL.__init__(self, **args)
|
|
SMTPClientWithResponse.__init__(self, **kw)
|
|
|
|
def _rset(self):
|
|
try:
|
|
self.rset()
|
|
except (ssl.SSLError, smtplib.SMTPServerDisconnected):
|
|
pass
|
|
|
|
def quit(self):
|
|
"""Closes the connection to the email server."""
|
|
try:
|
|
SMTPClientWithResponse.quit(self)
|
|
except (ssl.SSLError, smtplib.SMTPServerDisconnected):
|
|
# This happens when calling quit() on a TLS connection
|
|
# sometimes, or when the connection was already disconnected
|
|
# by the server.
|
|
self.close()
|
|
|
|
def sendmail(self, *args, **kw):
|
|
return SMTPClientWithResponse.sendmail(self, *args, **kw)
|
|
|
|
else:
|
|
|
|
class SMTPClientWithResponse_SSL:
|
|
def __init__(self, *args, **kwargs):
|
|
# should raise import error here
|
|
import ssl
|
|
|
|
|
|
|