1
2
3 """This module contains only the primary SMTP Dispatch class."""
4
5 import logging
6 log = logging.getLogger("turbomail.dispatch")
7
8 from smtplib import SMTP
9
10
11 __all__ = ['Dispatch']
12
13
15 """SMTP message dispatch class.
16
17 An instance of the Dispatch class is created for each SMTP
18 connection. Usually, this means one Dispatch instance per
19 running thread.
20
21 Example usage::
22
23 import turbomail
24 dispatch = turbomail.Dispatch("localhost")
25 message = turbomail.Message(
26 "from@localhost",
27 "to@localhost",
28 "Subject",
29 plain="Hello world!"
30 )
31 dispatch(message)
32 """
33
34 - def __init__(self, server, username=None, password=None, tls=None, debug=False):
35 """Initialize the Dispatch class.
36
37 Authentication is only performed if both I{username} and
38 I{password} are not None. An instance of Dispatch is callable.
39
40 @param server: The server (with optional port number) to
41 connect this instance to.
42 @type server: string
43
44 @param username: The username to use during authentication.
45 I{Optional.}
46 @type username: string
47
48 @param password: The password to use during authentaction.
49 I{Optional.}
50 @type password: string
51
52 @param debug: Enable SMTP verbose logging. This outputs all
53 communications between the client and server.
54 @type debug: bool
55 """
56
57 super(Dispatch, self).__init__()
58
59 self.server = server
60 self.username = username
61 self.password = password
62 self.tls = tls
63 self.debug = debug
64 self.log = None
65
66 log.debug("Creating SMTP object.")
67 self.connection = SMTP()
68 self.connection.set_debuglevel(debug)
69
71 """Return the current SMTP connection status."""
72
73 return getattr(self.connection, 'sock', None) is not None
74 connected = property(connected)
75
77 """Connect to the SMTP server if not already connected.
78
79 This process also automatically enables TLS, if available, and
80 authenticates against the username and password previousally
81 provided.
82 """
83
84 if self.connected: return
85
86 log.debug("Connecting to SMTP server %s." % self.server)
87 self.connection.connect(self.server)
88
89 if self.tls or self.tls is None:
90 self.connection.ehlo()
91
92 if self.connection.has_extn('STARTTLS') or self.tls:
93 self.connection.starttls()
94 self.connection.ehlo()
95 log.debug("TLS enabled on SMTP server.")
96 self.tls = True
97
98 else:
99 log.debug("TLS not available on SMTP server.")
100 self.tls = False
101
102 if self.username and self.password:
103 log.debug("Authenticating as %s." % self.username)
104 self.connection.login(self.username, self.password)
105
107 """Disconnect from the SMTP server if connected."""
108
109 if not self.connected: return
110
111 log.debug("Closing SMTP connection.")
112 self.connection.quit()
113
115 """Deliver a message via the current SMTP connection.
116
117 Calling an instance of the Dispatch class will automatically
118 connect, if needed, and will also automatically disconnect if
119 debug mode has been enabled.
120
121 @param message: This paramater must be a callable which returns
122 a tuple of (from, to, message), where all three
123 are strings, and message is valid content for an
124 e-mail message.
125 @type message: callable
126 """
127
128 self.connect()
129
130 if callable(message):
131 pack = message()
132 else:
133 pack = message
134
135 pack.update(dict(
136 user="",
137 server=self.server,
138 size=len(pack['message']),
139 ))
140 if self.username: pack['user'] = self.username + "@"
141
142 packUpdate = {}
143 if isinstance(pack['sender'], tuple):
144 packUpdate['addrfrom'] = '"%s" %s' % pack['sender']
145 packUpdate['smtpfrom'] = pack['sender'][1]
146 else:
147 packUpdate['addrfrom'] = '- %s' % pack['sender']
148 packUpdate['smtpfrom'] = pack['sender']
149
150 if 'smtpfrom' in pack:
151 if isinstance(pack['smtpfrom'], tuple):
152 packUpdate['smtpfrom'] = pack['smtpfrom'][1]
153 else:
154 packUpdate['smtpfrom'] = pack['smtpfrom']
155
156 pack.update(packUpdate)
157
158 if len(pack['to']) > 1:
159 pack.update(dict(addrto="- (%d)" % len(pack['to'])))
160 else:
161 if isinstance(pack['to'][0], tuple):
162 pack.update(dict(addrto='"%s" %s' % pack['to'][0]))
163 else:
164 pack.update(dict(addrto='- %s' % pack['to'][0]))
165
166 log.info(
167 "%(user)s%(server)s %(size)d %(addrfrom)s %(addrto)s - %(subject)s" % pack
168 )
169 self.connection.sendmail(pack['smtpfrom'], pack['recipients'], pack['message'])
170
171 if self.debug: self.disconnect()
172