This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
python-emails/emails/backend/smtp/backend.py

119 lines
3.3 KiB
Python

# encoding: utf-8
from __future__ import unicode_literals
__all__ = ['SMTPBackend', ]
import smtplib
import logging
from functools import wraps
from ..response import SMTPResponse
from .client import SMTPClientWithResponse, SMTPClientWithResponse_SSL
from ...utils import DNS_NAME
logger = logging.getLogger(__name__)
class SMTPBackend:
"""
SMTPBackend manages a smtp connection.
"""
DEFAULT_SOCKET_TIMEOUT = 5
connection_cls = SMTPClientWithResponse
connection_ssl_cls = SMTPClientWithResponse_SSL
response_cls = SMTPResponse
def __init__(self, ssl=False, fail_silently=True, **kwargs):
self.smtp_cls = ssl and self.connection_ssl_cls or self.connection_cls
self.ssl = ssl
self.tls = kwargs.get('tls')
if self.ssl and self.tls:
raise ValueError(
"ssl/tls are mutually exclusive, so only set "
"one of those settings to True.")
kwargs.setdefault('timeout', self.DEFAULT_SOCKET_TIMEOUT)
kwargs.setdefault('local_hostname', DNS_NAME.get_fqdn())
self.smtp_cls_kwargs = kwargs
self.host = kwargs.get('host')
self.port = kwargs.get('port')
self.fail_silently = fail_silently
self._client = None
def get_client(self):
if self._client is None:
self._client = self.smtp_cls(parent=self, **self.smtp_cls_kwargs)
return self._client
def close(self):
"""
Closes the connection to the email server.
"""
if self._client:
try:
self._client.close()
except:
if self.fail_silently:
return
raise
finally:
self._client = None
def make_response(self, exception=None):
return self.response_cls(backend=self, exception=exception)
def retry_on_disconnect(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except smtplib.SMTPServerDisconnected:
# If server disconected, clear old client
logging.debug('SMTPServerDisconnected, retry once')
self.close()
return func(*args, **kwargs)
return wrapper
def _send(self, **kwargs):
try:
client = self.get_client()
except (IOError, smtplib.SMTPException) as exc:
logger.exception("Error connecting smtp server")
response = self.make_response(exception=exc)
if not self.fail_silently:
response.raise_if_needed()
return response
return client.sendmail(**kwargs)
def sendmail(self, from_addr, to_addrs, msg, mail_options=None, rcpt_options=None):
if not to_addrs:
return None
if not isinstance(to_addrs, (list, tuple)):
to_addrs = [to_addrs, ]
send = self.retry_on_disconnect(self._send)
response = send(from_addr=from_addr,
to_addrs=to_addrs,
msg=msg.as_string(),
mail_options=mail_options,
rcpt_options=rcpt_options)
if not self.fail_silently:
response.raise_if_needed()
return response