-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsmtp.py
More file actions
116 lines (107 loc) · 4 KB
/
smtp.py
File metadata and controls
116 lines (107 loc) · 4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
"""SMTP email backend class."""
from .. import smtplib
import socket
from .base import BaseEmailBackend
from ..utils import DNS_NAME
from ..message import sanitize_address
from tornado import gen
class EmailBackend(BaseEmailBackend):
"""
A wrapper that manages the SMTP network connection.
"""
def __init__(self, host=None, port=None, username=None, password=None,
use_tls=None, fail_silently=False, **kwargs):
super(EmailBackend, self).__init__(fail_silently=fail_silently)
self.host = host or '127.0.0.1'
self.port = port or 25
self.username = username or None
self.password = password or None
if use_tls is None:
self.use_tls = None
else:
self.use_tls = use_tls
self.connection = None
self.template_loader = kwargs.get('template_loader', None)
@gen.engine
def open(self, callback):
"""
Ensures we have a connection to the email server. Returns whether or
not a new connection was required (True or False).
"""
if self.connection:
# Nothing to do if the connection is already open.
callback(False)
try:
# If local_hostname is not specified, socket.getfqdn() gets used.
# For performance, we use the cached FQDN for local_hostname.
self.connection = smtplib.SMTP(self.host, self.port,
local_hostname=DNS_NAME.get_fqdn())
yield gen.Task(self.connection.connect, self.host, self.port)
if self.use_tls:
yield gen.Task(self.connection.ehlo)
yield gen.Task(self.connection.starttls)
yield gen.Task(self.connection.ehlo)
if self.username and self.password:
yield gen.Task(self.connection.login, self.username, self.password)
callback(True)
except:
if not self.fail_silently:
raise
@gen.engine
def close(self,callback=None):
"""Closes the connection to the email server."""
try:
try:
yield gen.Task(self.connection.quit)
except socket.sslerror:
# This happens when calling quit() on a TLS connection
# sometimes.
self.connection.close()
except:
if self.fail_silently:
return
raise
finally:
self.connection = None
if callback:callback(None)
@gen.engine
def send_messages(self, email_messages, callback=None):
"""
Sends one or more EmailMessage objects and returns the number of email
messages sent.
"""
if not email_messages:
return
new_conn_created = yield gen.Task(self.open)
if not self.connection:
# We failed silently on open().
# Trying to send would be pointless.
return
num_sent = 0
for message in email_messages:
sent = yield gen.Task(self._send, message)
if sent:
num_sent += 1
#if new_conn_created:
# yield gen.Task(self.close)
if callback:
callback(num_sent)
@gen.engine
def _send(self, email_message, callback=None):
"""A helper method that does the actual sending."""
if not email_message.recipients():
if callback:
callback(False)
from_email = sanitize_address(email_message.from_email, email_message.encoding)
recipients = [sanitize_address(addr, email_message.encoding)
for addr in email_message.recipients()]
try:
yield gen.Task(self.connection.sendmail, from_email, recipients,
email_message.message().as_string())
except:
if not self.fail_silently:
raise
if callback:
callback(False)
if callback:
callback(True)