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 authentication.
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 previously
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 parameter 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 and pack['smtpfrom'] != None:
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 not pack.has_key('to'):
159 pack['to'] = pack['recipients']
160
161 if len(pack['to']) > 1:
162 pack.update(dict(addrto="- (%d)" % len(pack['to'])))
163 else:
164 if isinstance(pack['to'][0], tuple):
165 pack.update(dict(addrto='"%s" %s' % pack['to'][0]))
166 else:
167 pack.update(dict(addrto='- %s' % pack['to'][0]))
168
169 if not pack.has_key('subject'):
170 pack['subject'] = ''
171
172 log.info(
173 "%(user)s%(server)s %(size)d %(addrfrom)s %(addrto)s - %(subject)s" % pack
174 )
175 self.connection.sendmail(pack['smtpfrom'], pack['recipients'], pack['message'])
176
177 if self.debug: self.disconnect()
178