blob: d8403730d4adf4a6a33432d1544ead111e817d10 [file] [log] [blame]
Barry Warsaw4c4bec81998-12-22 03:02:20 +00001#! /usr/bin/env python
2
Barry Warsawa1ae8842000-07-09 21:24:31 +00003'''SMTP/ESMTP client class.
Guido van Rossumbbe323e1998-01-29 17:24:40 +00004
Guido van Rossumf7fcf5e2001-09-14 16:08:44 +00005This should follow RFC 821 (SMTP), RFC 1869 (ESMTP), RFC 2554 (SMTP
6Authentication) and RFC 2487 (Secure SMTP over TLS).
Guido van Rossumbbe323e1998-01-29 17:24:40 +00007
Guido van Rossumfcfb6321998-08-04 15:29:54 +00008Notes:
9
10Please remember, when doing ESMTP, that the names of the SMTP service
Barry Warsaw4c4bec81998-12-22 03:02:20 +000011extensions are NOT the same thing as the option keywords for the RCPT
Guido van Rossumfcfb6321998-08-04 15:29:54 +000012and MAIL commands!
13
Guido van Rossumbbe323e1998-01-29 17:24:40 +000014Example:
15
Barry Warsawa7d9bdf1998-12-22 03:24:27 +000016 >>> import smtplib
17 >>> s=smtplib.SMTP("localhost")
18 >>> print s.help()
19 This is Sendmail version 8.8.4
20 Topics:
21 HELO EHLO MAIL RCPT DATA
22 RSET NOOP QUIT HELP VRFY
23 EXPN VERB ETRN DSN
24 For more info use "HELP <topic>".
25 To report bugs in the implementation send email to
26 sendmail-bugs@sendmail.org.
27 For local information send email to Postmaster at your site.
28 End of HELP info
29 >>> s.putcmd("vrfy","someone@here")
30 >>> s.getreply()
31 (250, "Somebody OverHere <somebody@here.my.org>")
32 >>> s.quit()
Barry Warsawa1ae8842000-07-09 21:24:31 +000033'''
Guido van Rossumbbe323e1998-01-29 17:24:40 +000034
Guido van Rossum98d9fd32000-02-28 15:12:25 +000035# Author: The Dragon De Monsyne <dragondm@integral.org>
36# ESMTP support, test code and doc fixes added by
37# Eric S. Raymond <esr@thyrsus.com>
38# Better RFC 821 compliance (MAIL and RCPT, and CRLF in data)
39# by Carey Evans <c.evans@clear.net.nz>, for picky mail servers.
Guido van Rossumae010462001-09-11 15:57:46 +000040# RFC 2554 (authentication) support by Gerhard Haering <gerhard@bigfoot.de>.
Tim Peters495ad3c2001-01-15 01:36:40 +000041#
Guido van Rossum98d9fd32000-02-28 15:12:25 +000042# This was modified from the Python 1.5 library HTTP lib.
43
Guido van Rossumbbe323e1998-01-29 17:24:40 +000044import socket
Barry Warsaw07201771998-12-22 20:37:36 +000045import re
Guido van Rossumfcfb6321998-08-04 15:29:54 +000046import rfc822
Jeremy Hylton31bb8ce1998-08-13 19:57:46 +000047import types
Guido van Rossumae010462001-09-11 15:57:46 +000048import base64
49import hmac
Guido van Rossumbbe323e1998-01-29 17:24:40 +000050
Skip Montanaro0de65802001-02-15 22:15:14 +000051__all__ = ["SMTPException","SMTPServerDisconnected","SMTPResponseException",
52 "SMTPSenderRefused","SMTPRecipientsRefused","SMTPDataError",
Guido van Rossumae010462001-09-11 15:57:46 +000053 "SMTPConnectError","SMTPHeloError","SMTPAuthenticationError",
54 "quoteaddr","quotedata","SMTP"]
Skip Montanaro0de65802001-02-15 22:15:14 +000055
Guido van Rossumbbe323e1998-01-29 17:24:40 +000056SMTP_PORT = 25
57CRLF="\r\n"
58
Tim Peters495ad3c2001-01-15 01:36:40 +000059# Exception classes used by this module.
Guido van Rossum296e1431999-04-07 15:03:39 +000060class SMTPException(Exception):
61 """Base class for all exceptions raised by this module."""
62
63class SMTPServerDisconnected(SMTPException):
64 """Not connected to any SMTP server.
65
66 This exception is raised when the server unexpectedly disconnects,
67 or when an attempt is made to use the SMTP instance before
68 connecting it to a server.
69 """
70
71class SMTPResponseException(SMTPException):
72 """Base class for all exceptions that include an SMTP error code.
73
74 These exceptions are generated in some instances when the SMTP
75 server returns an error code. The error code is stored in the
76 `smtp_code' attribute of the error, and the `smtp_error' attribute
77 is set to the error message.
78 """
79
80 def __init__(self, code, msg):
81 self.smtp_code = code
82 self.smtp_error = msg
83 self.args = (code, msg)
84
85class SMTPSenderRefused(SMTPResponseException):
86 """Sender address refused.
Guido van Rossumae010462001-09-11 15:57:46 +000087
Guido van Rossum296e1431999-04-07 15:03:39 +000088 In addition to the attributes set by on all SMTPResponseException
Barry Warsawd25c1b71999-11-28 17:11:06 +000089 exceptions, this sets `sender' to the string that the SMTP refused.
Guido van Rossum296e1431999-04-07 15:03:39 +000090 """
91
92 def __init__(self, code, msg, sender):
93 self.smtp_code = code
94 self.smtp_error = msg
95 self.sender = sender
96 self.args = (code, msg, sender)
97
Guido van Rossum20c92281999-04-21 16:52:20 +000098class SMTPRecipientsRefused(SMTPException):
Barry Warsawd25c1b71999-11-28 17:11:06 +000099 """All recipient addresses refused.
Guido van Rossumae010462001-09-11 15:57:46 +0000100
Thomas Wouters7e474022000-07-16 12:04:32 +0000101 The errors for each recipient are accessible through the attribute
Tim Peters495ad3c2001-01-15 01:36:40 +0000102 'recipients', which is a dictionary of exactly the same sort as
103 SMTP.sendmail() returns.
Guido van Rossum296e1431999-04-07 15:03:39 +0000104 """
105
106 def __init__(self, recipients):
107 self.recipients = recipients
108 self.args = ( recipients,)
109
110
Guido van Rossum296e1431999-04-07 15:03:39 +0000111class SMTPDataError(SMTPResponseException):
112 """The SMTP server didn't accept the data."""
113
114class SMTPConnectError(SMTPResponseException):
Barry Warsawd25c1b71999-11-28 17:11:06 +0000115 """Error during connection establishment."""
Guido van Rossum296e1431999-04-07 15:03:39 +0000116
117class SMTPHeloError(SMTPResponseException):
Barry Warsawd25c1b71999-11-28 17:11:06 +0000118 """The server refused our HELO reply."""
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000119
Guido van Rossumae010462001-09-11 15:57:46 +0000120class SMTPAuthenticationError(SMTPResponseException):
121 """Authentication error.
122
123 Most probably the server didn't accept the username/password
124 combination provided.
125 """
Peter Schneider-Kamp7bc82bb2000-08-10 14:02:23 +0000126
Guido van Rossumf7fcf5e2001-09-14 16:08:44 +0000127class SSLFakeSocket:
128 """A fake socket object that really wraps a SSLObject.
Tim Petersb64bec32001-09-18 02:26:39 +0000129
Guido van Rossumf7fcf5e2001-09-14 16:08:44 +0000130 It only supports what is needed in smtplib.
131 """
132 def __init__(self, realsock, sslobj):
133 self.realsock = realsock
134 self.sslobj = sslobj
135
136 def send(self, str):
137 self.sslobj.write(str)
138 return len(str)
139
Martin v. Löwis9ea6c192002-06-02 12:33:22 +0000140 sendall = send
141
Guido van Rossumf7fcf5e2001-09-14 16:08:44 +0000142 def close(self):
143 self.realsock.close()
144
145class SSLFakeFile:
146 """A fake file like object that really wraps a SSLObject.
Tim Petersb64bec32001-09-18 02:26:39 +0000147
Guido van Rossumf7fcf5e2001-09-14 16:08:44 +0000148 It only supports what is needed in smtplib.
149 """
150 def __init__( self, sslobj):
151 self.sslobj = sslobj
152
153 def readline(self):
154 str = ""
155 chr = None
156 while chr != "\n":
157 chr = self.sslobj.read(1)
158 str += chr
159 return str
160
161 def close(self):
162 pass
163
Guido van Rossum69a79bc1998-07-13 15:18:49 +0000164def quoteaddr(addr):
165 """Quote a subset of the email addresses defined by RFC 821.
166
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000167 Should be able to handle anything rfc822.parseaddr can handle.
168 """
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000169 m=None
Guido van Rossum69a79bc1998-07-13 15:18:49 +0000170 try:
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000171 m=rfc822.parseaddr(addr)[1]
172 except AttributeError:
173 pass
174 if not m:
175 #something weird here.. punt -ddm
176 return addr
177 else:
178 return "<%s>" % m
Guido van Rossum69a79bc1998-07-13 15:18:49 +0000179
180def quotedata(data):
181 """Quote data for email.
182
Barry Warsawd25c1b71999-11-28 17:11:06 +0000183 Double leading '.', and change Unix newline '\\n', or Mac '\\r' into
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000184 Internet CRLF end-of-line.
185 """
Guido van Rossum69a79bc1998-07-13 15:18:49 +0000186 return re.sub(r'(?m)^\.', '..',
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000187 re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data))
Guido van Rossum69a79bc1998-07-13 15:18:49 +0000188
Peter Schneider-Kamp7bc82bb2000-08-10 14:02:23 +0000189
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000190class SMTP:
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000191 """This class manages a connection to an SMTP or ESMTP server.
192 SMTP Objects:
Tim Peters495ad3c2001-01-15 01:36:40 +0000193 SMTP objects have the following attributes:
194 helo_resp
195 This is the message given by the server in response to the
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000196 most recent HELO command.
Tim Peters495ad3c2001-01-15 01:36:40 +0000197
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000198 ehlo_resp
Tim Peters495ad3c2001-01-15 01:36:40 +0000199 This is the message given by the server in response to the
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000200 most recent EHLO command. This is usually multiline.
201
Tim Peters495ad3c2001-01-15 01:36:40 +0000202 does_esmtp
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000203 This is a True value _after you do an EHLO command_, if the
204 server supports ESMTP.
205
Tim Peters495ad3c2001-01-15 01:36:40 +0000206 esmtp_features
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000207 This is a dictionary, which, if the server supports ESMTP,
Barry Warsawd25c1b71999-11-28 17:11:06 +0000208 will _after you do an EHLO command_, contain the names of the
209 SMTP service extensions this server supports, and their
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000210 parameters (if any).
Barry Warsawd25c1b71999-11-28 17:11:06 +0000211
Tim Peters495ad3c2001-01-15 01:36:40 +0000212 Note, all extension names are mapped to lower case in the
213 dictionary.
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000214
Barry Warsawd25c1b71999-11-28 17:11:06 +0000215 See each method's docstrings for details. In general, there is a
216 method of the same name to perform each SMTP command. There is also a
217 method called 'sendmail' that will do an entire mail transaction.
218 """
Guido van Rossum95e6f701998-06-25 02:15:50 +0000219 debuglevel = 0
220 file = None
221 helo_resp = None
222 ehlo_resp = None
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000223 does_esmtp = 0
Guido van Rossum95e6f701998-06-25 02:15:50 +0000224
Neil Schemenauer6730f262002-03-24 15:30:40 +0000225 def __init__(self, host = '', port = 0, local_hostname = None):
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000226 """Initialize a new instance.
227
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000228 If specified, `host' is the name of the remote host to which to
229 connect. If specified, `port' specifies the port to which to connect.
Barry Warsawd25c1b71999-11-28 17:11:06 +0000230 By default, smtplib.SMTP_PORT is used. An SMTPConnectError is raised
Neil Schemenauer6730f262002-03-24 15:30:40 +0000231 if the specified `host' doesn't respond correctly. If specified,
Tim Peters863ac442002-04-16 01:38:40 +0000232 `local_hostname` is used as the FQDN of the local host. By default,
233 the local hostname is found using socket.getfqdn().
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000234
235 """
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000236 self.esmtp_features = {}
Guido van Rossum296e1431999-04-07 15:03:39 +0000237 if host:
238 (code, msg) = self.connect(host, port)
239 if code != 220:
240 raise SMTPConnectError(code, msg)
Raymond Hettingerf13eb552002-06-02 00:40:05 +0000241 if local_hostname is not None:
Barry Warsaw13e34f72002-03-26 20:27:35 +0000242 self.local_hostname = local_hostname
Neil Schemenauer6730f262002-03-24 15:30:40 +0000243 else:
Barry Warsaw13e34f72002-03-26 20:27:35 +0000244 # RFC 2821 says we should use the fqdn in the EHLO/HELO verb, and
245 # if that can't be calculated, that we should use a domain literal
246 # instead (essentially an encoded IP address like [A.B.C.D]).
247 fqdn = socket.getfqdn()
248 if '.' in fqdn:
249 self.local_hostname = fqdn
250 else:
251 # We can't find an fqdn hostname, so use a domain literal
252 addr = socket.gethostbyname(socket.gethostname())
253 self.local_hostname = '[%s]' % addr
Tim Peters495ad3c2001-01-15 01:36:40 +0000254
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000255 def set_debuglevel(self, debuglevel):
256 """Set the debug output level.
257
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000258 A non-false value results in debug messages for connection and for all
259 messages sent to and received from the server.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000260
261 """
262 self.debuglevel = debuglevel
263
264 def connect(self, host='localhost', port = 0):
265 """Connect to a host on a given port.
Guido van Rossum95e6f701998-06-25 02:15:50 +0000266
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000267 If the hostname ends with a colon (`:') followed by a number, and
268 there is no port specified, that suffix will be stripped off and the
269 number interpreted as the port number to use.
Guido van Rossum95e6f701998-06-25 02:15:50 +0000270
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000271 Note: This method is automatically invoked by __init__, if a host is
272 specified during instantiation.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000273
274 """
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000275 if not port and (host.find(':') == host.rfind(':')):
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000276 i = host.rfind(':')
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000277 if i >= 0:
278 host, port = host[:i], host[i+1:]
Eric S. Raymondc013f302001-02-09 05:40:38 +0000279 try: port = int(port)
Eric S. Raymond8d876032001-02-09 10:14:53 +0000280 except ValueError:
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000281 raise socket.error, "nonnumeric port"
282 if not port: port = SMTP_PORT
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000283 if self.debuglevel > 0: print 'connect:', (host, port)
Martin v. Löwis2ad25692001-07-31 08:40:21 +0000284 msg = "getaddrinfo returns an empty list"
Martin v. Löwis322c0d12001-10-07 08:53:32 +0000285 self.sock = None
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000286 for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
287 af, socktype, proto, canonname, sa = res
288 try:
289 self.sock = socket.socket(af, socktype, proto)
290 if self.debuglevel > 0: print 'connect:', (host, port)
291 self.sock.connect(sa)
292 except socket.error, msg:
293 if self.debuglevel > 0: print 'connect fail:', (host, port)
Martin v. Löwis322c0d12001-10-07 08:53:32 +0000294 if self.sock:
295 self.sock.close()
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000296 self.sock = None
297 continue
298 break
299 if not self.sock:
300 raise socket.error, msg
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000301 (code, msg) = self.getreply()
302 if self.debuglevel > 0: print "connect:", msg
303 return (code, msg)
Tim Peters495ad3c2001-01-15 01:36:40 +0000304
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000305 def send(self, str):
306 """Send `str' to the server."""
307 if self.debuglevel > 0: print 'send:', `str`
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000308 if self.sock:
Guido van Rossum2880f6e1998-08-10 20:07:00 +0000309 try:
Martin v. Löwise12454f2002-02-16 23:06:19 +0000310 self.sock.sendall(str)
Guido van Rossum2880f6e1998-08-10 20:07:00 +0000311 except socket.error:
Barry Warsaw76750972001-12-14 20:34:20 +0000312 self.close()
Guido van Rossum40233ea1999-01-15 03:23:55 +0000313 raise SMTPServerDisconnected('Server not connected')
Guido van Rossumfc40a831998-01-29 17:26:45 +0000314 else:
Guido van Rossum40233ea1999-01-15 03:23:55 +0000315 raise SMTPServerDisconnected('please run connect() first')
Tim Peters495ad3c2001-01-15 01:36:40 +0000316
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000317 def putcmd(self, cmd, args=""):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000318 """Send a command to the server."""
Guido van Rossumdb23d3d1999-06-09 15:13:10 +0000319 if args == "":
320 str = '%s%s' % (cmd, CRLF)
321 else:
322 str = '%s %s%s' % (cmd, args, CRLF)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000323 self.send(str)
Tim Peters495ad3c2001-01-15 01:36:40 +0000324
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000325 def getreply(self):
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000326 """Get a reply from the server.
Tim Peters495ad3c2001-01-15 01:36:40 +0000327
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000328 Returns a tuple consisting of:
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000329
330 - server response code (e.g. '250', or such, if all goes well)
331 Note: returns -1 if it can't read response code.
332
333 - server response string corresponding to response code (multiline
334 responses are converted to a single, multiline string).
Guido van Rossumf123f841999-03-29 20:33:21 +0000335
336 Raises SMTPServerDisconnected if end-of-file is reached.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000337 """
338 resp=[]
Guido van Rossum296e1431999-04-07 15:03:39 +0000339 if self.file is None:
340 self.file = self.sock.makefile('rb')
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000341 while 1:
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000342 line = self.file.readline()
Guido van Rossum296e1431999-04-07 15:03:39 +0000343 if line == '':
344 self.close()
345 raise SMTPServerDisconnected("Connection unexpectedly closed")
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000346 if self.debuglevel > 0: print 'reply:', `line`
Eric S. Raymondc013f302001-02-09 05:40:38 +0000347 resp.append(line[4:].strip())
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000348 code=line[:3]
Guido van Rossum296e1431999-04-07 15:03:39 +0000349 # Check that the error code is syntactically correct.
350 # Don't attempt to read a continuation line if it is broken.
351 try:
Eric S. Raymondc013f302001-02-09 05:40:38 +0000352 errcode = int(code)
Guido van Rossum296e1431999-04-07 15:03:39 +0000353 except ValueError:
354 errcode = -1
355 break
Guido van Rossumf123f841999-03-29 20:33:21 +0000356 # Check if multiline response.
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000357 if line[3:4]!="-":
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000358 break
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000359
Eric S. Raymondc013f302001-02-09 05:40:38 +0000360 errmsg = "\n".join(resp)
Tim Peters495ad3c2001-01-15 01:36:40 +0000361 if self.debuglevel > 0:
Guido van Rossumfc40a831998-01-29 17:26:45 +0000362 print 'reply: retcode (%s); Msg: %s' % (errcode,errmsg)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000363 return errcode, errmsg
Tim Peters495ad3c2001-01-15 01:36:40 +0000364
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000365 def docmd(self, cmd, args=""):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000366 """Send a command, and return its response code."""
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000367 self.putcmd(cmd,args)
Guido van Rossum296e1431999-04-07 15:03:39 +0000368 return self.getreply()
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000369
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000370 # std smtp commands
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000371 def helo(self, name=''):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000372 """SMTP 'helo' command.
373 Hostname to send for this command defaults to the FQDN of the local
374 host.
375 """
Neil Schemenauer6730f262002-03-24 15:30:40 +0000376 self.putcmd("helo", name or self.local_hostname)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000377 (code,msg)=self.getreply()
378 self.helo_resp=msg
Guido van Rossum296e1431999-04-07 15:03:39 +0000379 return (code,msg)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000380
Guido van Rossum95e6f701998-06-25 02:15:50 +0000381 def ehlo(self, name=''):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000382 """ SMTP 'ehlo' command.
383 Hostname to send for this command defaults to the FQDN of the local
384 host.
385 """
Guido van Rossumf7fcf5e2001-09-14 16:08:44 +0000386 self.esmtp_features = {}
Neil Schemenauer6730f262002-03-24 15:30:40 +0000387 self.putcmd("ehlo", name or self.local_hostname)
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000388 (code,msg)=self.getreply()
Tim Peters495ad3c2001-01-15 01:36:40 +0000389 # According to RFC1869 some (badly written)
390 # MTA's will disconnect on an ehlo. Toss an exception if
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000391 # that happens -ddm
392 if code == -1 and len(msg) == 0:
Barry Warsaw76750972001-12-14 20:34:20 +0000393 self.close()
Guido van Rossum40233ea1999-01-15 03:23:55 +0000394 raise SMTPServerDisconnected("Server not connected")
Guido van Rossum95e6f701998-06-25 02:15:50 +0000395 self.ehlo_resp=msg
Fred Drake8152d322000-12-12 23:20:45 +0000396 if code != 250:
Guido van Rossum296e1431999-04-07 15:03:39 +0000397 return (code,msg)
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000398 self.does_esmtp=1
Thomas Wouters7e474022000-07-16 12:04:32 +0000399 #parse the ehlo response -ddm
Eric S. Raymondc013f302001-02-09 05:40:38 +0000400 resp=self.ehlo_resp.split('\n')
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000401 del resp[0]
Guido van Rossum2880f6e1998-08-10 20:07:00 +0000402 for each in resp:
Barry Warsawbe22ae62002-04-15 20:03:30 +0000403 # RFC 1869 requires a space between ehlo keyword and parameters.
404 # It's actually stricter, in that only spaces are allowed between
405 # parameters, but were not going to check for that here. Note
406 # that the space isn't present if there are no parameters.
407 m=re.match(r'(?P<feature>[A-Za-z0-9][A-Za-z0-9\-]*) ?',each)
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000408 if m:
Eric S. Raymondc013f302001-02-09 05:40:38 +0000409 feature=m.group("feature").lower()
410 params=m.string[m.end("feature"):].strip()
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000411 self.esmtp_features[feature]=params
Guido van Rossum296e1431999-04-07 15:03:39 +0000412 return (code,msg)
Guido van Rossum95e6f701998-06-25 02:15:50 +0000413
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000414 def has_extn(self, opt):
415 """Does the server support a given SMTP service extension?"""
Raymond Hettinger54f02222002-06-01 14:18:47 +0000416 return opt.lower() in self.esmtp_features
Guido van Rossum95e6f701998-06-25 02:15:50 +0000417
Guido van Rossum18586f41998-04-03 17:03:13 +0000418 def help(self, args=''):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000419 """SMTP 'help' command.
420 Returns help text from server."""
Guido van Rossum18586f41998-04-03 17:03:13 +0000421 self.putcmd("help", args)
Guido van Rossum296e1431999-04-07 15:03:39 +0000422 return self.getreply()
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000423
424 def rset(self):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000425 """SMTP 'rset' command -- resets session."""
Guido van Rossum296e1431999-04-07 15:03:39 +0000426 return self.docmd("rset")
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000427
428 def noop(self):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000429 """SMTP 'noop' command -- doesn't do anything :>"""
Guido van Rossum296e1431999-04-07 15:03:39 +0000430 return self.docmd("noop")
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000431
Guido van Rossum95e6f701998-06-25 02:15:50 +0000432 def mail(self,sender,options=[]):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000433 """SMTP 'mail' command -- begins mail xfer session."""
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000434 optionlist = ''
435 if options and self.does_esmtp:
Eric S. Raymondc013f302001-02-09 05:40:38 +0000436 optionlist = ' ' + ' '.join(options)
Guido van Rossumdb23d3d1999-06-09 15:13:10 +0000437 self.putcmd("mail", "FROM:%s%s" % (quoteaddr(sender) ,optionlist))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000438 return self.getreply()
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000439
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000440 def rcpt(self,recip,options=[]):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000441 """SMTP 'rcpt' command -- indicates 1 recipient for this mail."""
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000442 optionlist = ''
443 if options and self.does_esmtp:
Eric S. Raymondc013f302001-02-09 05:40:38 +0000444 optionlist = ' ' + ' '.join(options)
Guido van Rossum348fd061999-01-14 04:18:46 +0000445 self.putcmd("rcpt","TO:%s%s" % (quoteaddr(recip),optionlist))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000446 return self.getreply()
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000447
448 def data(self,msg):
Tim Peters495ad3c2001-01-15 01:36:40 +0000449 """SMTP 'DATA' command -- sends message data to server.
Guido van Rossum296e1431999-04-07 15:03:39 +0000450
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000451 Automatically quotes lines beginning with a period per rfc821.
Guido van Rossum296e1431999-04-07 15:03:39 +0000452 Raises SMTPDataError if there is an unexpected reply to the
453 DATA command; the return value from this method is the final
454 response code received when the all data is sent.
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000455 """
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000456 self.putcmd("data")
457 (code,repl)=self.getreply()
458 if self.debuglevel >0 : print "data:", (code,repl)
Fred Drake8152d322000-12-12 23:20:45 +0000459 if code != 354:
Guido van Rossum296e1431999-04-07 15:03:39 +0000460 raise SMTPDataError(code,repl)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000461 else:
Guido van Rossum20c92281999-04-21 16:52:20 +0000462 q = quotedata(msg)
463 if q[-2:] != CRLF:
464 q = q + CRLF
465 q = q + "." + CRLF
466 self.send(q)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000467 (code,msg)=self.getreply()
468 if self.debuglevel >0 : print "data:", (code,msg)
Guido van Rossum296e1431999-04-07 15:03:39 +0000469 return (code,msg)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000470
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000471 def verify(self, address):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000472 """SMTP 'verify' command -- checks for address validity."""
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000473 self.putcmd("vrfy", quoteaddr(address))
474 return self.getreply()
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000475 # a.k.a.
476 vrfy=verify
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000477
478 def expn(self, address):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000479 """SMTP 'verify' command -- checks for address validity."""
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000480 self.putcmd("expn", quoteaddr(address))
481 return self.getreply()
482
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000483 # some useful methods
Guido van Rossumae010462001-09-11 15:57:46 +0000484
485 def login(self, user, password):
486 """Log in on an SMTP server that requires authentication.
487
488 The arguments are:
489 - user: The user name to authenticate with.
490 - password: The password for the authentication.
491
492 If there has been no previous EHLO or HELO command this session, this
493 method tries ESMTP EHLO first.
494
495 This method will return normally if the authentication was successful.
496
497 This method may raise the following exceptions:
498
499 SMTPHeloError The server didn't reply properly to
500 the helo greeting.
501 SMTPAuthenticationError The server didn't accept the username/
502 password combination.
Fred Drake2f8f4d32001-10-13 18:35:32 +0000503 SMTPException No suitable authentication method was
Guido van Rossumae010462001-09-11 15:57:46 +0000504 found.
505 """
506
507 def encode_cram_md5(challenge, user, password):
508 challenge = base64.decodestring(challenge)
509 response = user + " " + hmac.HMAC(password, challenge).hexdigest()
510 return base64.encodestring(response)[:-1]
511
512 def encode_plain(user, password):
513 return base64.encodestring("%s\0%s\0%s" %
514 (user, user, password))[:-1]
515
516 AUTH_PLAIN = "PLAIN"
517 AUTH_CRAM_MD5 = "CRAM-MD5"
518
519 if self.helo_resp is None and self.ehlo_resp is None:
520 if not (200 <= self.ehlo()[0] <= 299):
521 (code, resp) = self.helo()
522 if not (200 <= code <= 299):
523 raise SMTPHeloError(code, resp)
524
525 if not self.has_extn("auth"):
526 raise SMTPException("SMTP AUTH extension not supported by server.")
527
528 # Authentication methods the server supports:
529 authlist = self.esmtp_features["auth"].split()
530
531 # List of authentication methods we support: from preferred to
532 # less preferred methods. Except for the purpose of testing the weaker
533 # ones, we prefer stronger methods like CRAM-MD5:
534 preferred_auths = [AUTH_CRAM_MD5, AUTH_PLAIN]
535 #preferred_auths = [AUTH_PLAIN, AUTH_CRAM_MD5]
536
537 # Determine the authentication method we'll use
538 authmethod = None
539 for method in preferred_auths:
540 if method in authlist:
541 authmethod = method
542 break
543 if self.debuglevel > 0: print "AuthMethod:", authmethod
Tim Petersb64bec32001-09-18 02:26:39 +0000544
Guido van Rossumae010462001-09-11 15:57:46 +0000545 if authmethod == AUTH_CRAM_MD5:
546 (code, resp) = self.docmd("AUTH", AUTH_CRAM_MD5)
547 if code == 503:
548 # 503 == 'Error: already authenticated'
549 return (code, resp)
550 (code, resp) = self.docmd(encode_cram_md5(resp, user, password))
551 elif authmethod == AUTH_PLAIN:
Tim Petersb64bec32001-09-18 02:26:39 +0000552 (code, resp) = self.docmd("AUTH",
Guido van Rossumae010462001-09-11 15:57:46 +0000553 AUTH_PLAIN + " " + encode_plain(user, password))
Raymond Hettinger7fdfc2d2002-05-31 17:49:10 +0000554 elif authmethod is None:
Fred Drake2f8f4d32001-10-13 18:35:32 +0000555 raise SMTPException("No suitable authentication method found.")
Guido van Rossumae010462001-09-11 15:57:46 +0000556 if code not in [235, 503]:
557 # 235 == 'Authentication successful'
558 # 503 == 'Error: already authenticated'
559 raise SMTPAuthenticationError(code, resp)
560 return (code, resp)
561
Guido van Rossumf7fcf5e2001-09-14 16:08:44 +0000562 def starttls(self, keyfile = None, certfile = None):
563 """Puts the connection to the SMTP server into TLS mode.
Tim Petersb64bec32001-09-18 02:26:39 +0000564
Guido van Rossumf7fcf5e2001-09-14 16:08:44 +0000565 If the server supports TLS, this will encrypt the rest of the SMTP
566 session. If you provide the keyfile and certfile parameters,
567 the identity of the SMTP server and client can be checked. This,
568 however, depends on whether the socket module really checks the
569 certificates.
570 """
Tim Petersb64bec32001-09-18 02:26:39 +0000571 (resp, reply) = self.docmd("STARTTLS")
Guido van Rossumf7fcf5e2001-09-14 16:08:44 +0000572 if resp == 220:
573 sslobj = socket.ssl(self.sock, keyfile, certfile)
574 self.sock = SSLFakeSocket(self.sock, sslobj)
575 self.file = SSLFakeFile(sslobj)
576 return (resp, reply)
Tim Petersb64bec32001-09-18 02:26:39 +0000577
Jeremy Hylton31bb8ce1998-08-13 19:57:46 +0000578 def sendmail(self, from_addr, to_addrs, msg, mail_options=[],
Tim Peters495ad3c2001-01-15 01:36:40 +0000579 rcpt_options=[]):
580 """This command performs an entire mail transaction.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000581
Tim Peters495ad3c2001-01-15 01:36:40 +0000582 The arguments are:
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000583 - from_addr : The address sending this mail.
584 - to_addrs : A list of addresses to send this mail to. A bare
585 string will be treated as a list with 1 address.
Tim Peters495ad3c2001-01-15 01:36:40 +0000586 - msg : The message to send.
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000587 - mail_options : List of ESMTP options (such as 8bitmime) for the
588 mail command.
589 - rcpt_options : List of ESMTP options (such as DSN commands) for
590 all the rcpt commands.
591
592 If there has been no previous EHLO or HELO command this session, this
593 method tries ESMTP EHLO first. If the server does ESMTP, message size
594 and each of the specified options will be passed to it. If EHLO
595 fails, HELO will be tried and ESMTP options suppressed.
596
597 This method will return normally if the mail is accepted for at least
Barry Warsawd25c1b71999-11-28 17:11:06 +0000598 one recipient. It returns a dictionary, with one entry for each
599 recipient that was refused. Each entry contains a tuple of the SMTP
600 error code and the accompanying error message sent by the server.
Guido van Rossum296e1431999-04-07 15:03:39 +0000601
602 This method may raise the following exceptions:
603
604 SMTPHeloError The server didn't reply properly to
Tim Peters495ad3c2001-01-15 01:36:40 +0000605 the helo greeting.
Barry Warsawd25c1b71999-11-28 17:11:06 +0000606 SMTPRecipientsRefused The server rejected ALL recipients
Guido van Rossum296e1431999-04-07 15:03:39 +0000607 (no mail was sent).
608 SMTPSenderRefused The server didn't accept the from_addr.
609 SMTPDataError The server replied with an unexpected
610 error code (other than a refusal of
611 a recipient).
612
613 Note: the connection will be open even after an exception is raised.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000614
Guido van Rossum95e6f701998-06-25 02:15:50 +0000615 Example:
Tim Peters495ad3c2001-01-15 01:36:40 +0000616
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000617 >>> import smtplib
618 >>> s=smtplib.SMTP("localhost")
Guido van Rossumfc40a831998-01-29 17:26:45 +0000619 >>> tolist=["one@one.org","two@two.org","three@three.org","four@four.org"]
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000620 >>> msg = '''
621 ... From: Me@my.org
622 ... Subject: testin'...
623 ...
624 ... This is a test '''
625 >>> s.sendmail("me@my.org",tolist,msg)
626 { "three@three.org" : ( 550 ,"User unknown" ) }
627 >>> s.quit()
Tim Peters495ad3c2001-01-15 01:36:40 +0000628
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000629 In the above example, the message was accepted for delivery to three
630 of the four addresses, and one was rejected, with the error code
Barry Warsawd25c1b71999-11-28 17:11:06 +0000631 550. If all addresses are accepted, then the method will return an
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000632 empty dictionary.
633
634 """
Guido van Rossum296e1431999-04-07 15:03:39 +0000635 if self.helo_resp is None and self.ehlo_resp is None:
636 if not (200 <= self.ehlo()[0] <= 299):
637 (code,resp) = self.helo()
638 if not (200 <= code <= 299):
639 raise SMTPHeloError(code, resp)
Guido van Rossum95e6f701998-06-25 02:15:50 +0000640 esmtp_opts = []
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000641 if self.does_esmtp:
642 # Hmmm? what's this? -ddm
643 # self.esmtp_features['7bit']=""
644 if self.has_extn('size'):
645 esmtp_opts.append("size=" + `len(msg)`)
646 for option in mail_options:
Guido van Rossum95e6f701998-06-25 02:15:50 +0000647 esmtp_opts.append(option)
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000648
Guido van Rossum95e6f701998-06-25 02:15:50 +0000649 (code,resp) = self.mail(from_addr, esmtp_opts)
Fred Drake8152d322000-12-12 23:20:45 +0000650 if code != 250:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000651 self.rset()
Guido van Rossum296e1431999-04-07 15:03:39 +0000652 raise SMTPSenderRefused(code, resp, from_addr)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000653 senderrs={}
Martin v. Löwis33567662002-02-24 15:07:24 +0000654 if isinstance(to_addrs, types.StringTypes):
Jeremy Hylton31bb8ce1998-08-13 19:57:46 +0000655 to_addrs = [to_addrs]
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000656 for each in to_addrs:
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000657 (code,resp)=self.rcpt(each, rcpt_options)
Fred Drake8152d322000-12-12 23:20:45 +0000658 if (code != 250) and (code != 251):
Guido van Rossumfc40a831998-01-29 17:26:45 +0000659 senderrs[each]=(code,resp)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000660 if len(senderrs)==len(to_addrs):
Guido van Rossum95e6f701998-06-25 02:15:50 +0000661 # the server refused all our recipients
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000662 self.rset()
Guido van Rossum296e1431999-04-07 15:03:39 +0000663 raise SMTPRecipientsRefused(senderrs)
Fred Drake8152d322000-12-12 23:20:45 +0000664 (code,resp) = self.data(msg)
665 if code != 250:
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000666 self.rset()
Guido van Rossum296e1431999-04-07 15:03:39 +0000667 raise SMTPDataError(code, resp)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000668 #if we got here then somebody got our mail
Tim Peters495ad3c2001-01-15 01:36:40 +0000669 return senderrs
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000670
671
672 def close(self):
673 """Close the connection to the SMTP server."""
674 if self.file:
675 self.file.close()
676 self.file = None
677 if self.sock:
678 self.sock.close()
679 self.sock = None
680
681
682 def quit(self):
Guido van Rossum95e6f701998-06-25 02:15:50 +0000683 """Terminate the SMTP session."""
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000684 self.docmd("quit")
685 self.close()
Guido van Rossum95e6f701998-06-25 02:15:50 +0000686
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000687
Guido van Rossum95e6f701998-06-25 02:15:50 +0000688# Test the sendmail method, which tests most of the others.
689# Note: This always sends to localhost.
690if __name__ == '__main__':
Andrew M. Kuchling6be424f2001-08-13 14:41:39 +0000691 import sys
Guido van Rossum95e6f701998-06-25 02:15:50 +0000692
693 def prompt(prompt):
694 sys.stdout.write(prompt + ": ")
Eric S. Raymondc013f302001-02-09 05:40:38 +0000695 return sys.stdin.readline().strip()
Guido van Rossum95e6f701998-06-25 02:15:50 +0000696
697 fromaddr = prompt("From")
Eric S. Raymond38151ed2001-02-09 07:40:17 +0000698 toaddrs = prompt("To").split(',')
Guido van Rossum95e6f701998-06-25 02:15:50 +0000699 print "Enter message, end with ^D:"
700 msg = ''
701 while 1:
702 line = sys.stdin.readline()
703 if not line:
704 break
705 msg = msg + line
706 print "Message length is " + `len(msg)`
707
708 server = SMTP('localhost')
709 server.set_debuglevel(1)
710 server.sendmail(fromaddr, toaddrs, msg)
711 server.quit()