blob: 324a1c19f12afe52cfdc3e375d4b4157f3937c54 [file] [log] [blame]
Benjamin Peterson90f5ba52010-03-11 22:53:45 +00001#! /usr/bin/env python3
Barry Warsaw4c4bec81998-12-22 03:02:20 +00002
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")
Guido van Rossum7131f842007-02-09 20:13:25 +000018 >>> print(s.help())
Barry Warsawa7d9bdf1998-12-22 03:24:27 +000019 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
R. David Murray7dff9e02010-11-08 17:15:13 +000045import io
Barry Warsaw07201771998-12-22 20:37:36 +000046import re
Thomas Woutersb2137042007-02-01 18:02:27 +000047import email.utils
R. David Murray7dff9e02010-11-08 17:15:13 +000048import email.message
49import email.generator
Guido van Rossumae010462001-09-11 15:57:46 +000050import base64
51import hmac
R David Murrayac4e5ab2011-07-02 21:03:19 -040052import copy
R David Murray0c49b892015-04-16 17:14:42 -040053import datetime
54import sys
Barry Warsaw2cc1f6d2007-08-30 14:28:55 +000055from email.base64mime import body_encode as encode_base64
Guido van Rossumbbe323e1998-01-29 17:24:40 +000056
nde3faf8262019-10-05 02:30:58 +020057__all__ = ["SMTPException", "SMTPNotSupportedError", "SMTPServerDisconnected", "SMTPResponseException",
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +000058 "SMTPSenderRefused", "SMTPRecipientsRefused", "SMTPDataError",
59 "SMTPConnectError", "SMTPHeloError", "SMTPAuthenticationError",
60 "quoteaddr", "quotedata", "SMTP"]
Skip Montanaro0de65802001-02-15 22:15:14 +000061
Guido van Rossumbbe323e1998-01-29 17:24:40 +000062SMTP_PORT = 25
Thomas Wouters89f507f2006-12-13 04:49:30 +000063SMTP_SSL_PORT = 465
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +000064CRLF = "\r\n"
65bCRLF = b"\r\n"
Georg Brandlb38b5c42014-02-10 22:11:21 +010066_MAXLINE = 8192 # more than 8 times larger than RFC 821, 4.5.3
Pandu E POLUAN7591d942021-03-13 06:25:49 +070067_MAXCHALLENGE = 5 # Maximum number of AUTH challenges sent
Guido van Rossumbbe323e1998-01-29 17:24:40 +000068
Piers Lauder385a77a2002-07-27 00:38:30 +000069OLDSTYLE_AUTH = re.compile(r"auth=(.*)", re.I)
70
Tim Peters495ad3c2001-01-15 01:36:40 +000071# Exception classes used by this module.
R David Murray8a345962013-04-14 06:46:35 -040072class SMTPException(OSError):
Guido van Rossum296e1431999-04-07 15:03:39 +000073 """Base class for all exceptions raised by this module."""
74
R David Murraycee7cf62015-05-16 13:58:14 -040075class SMTPNotSupportedError(SMTPException):
76 """The command or option is not supported by the SMTP server.
77
78 This exception is raised when an attempt is made to run a command or a
79 command with an option which is not supported by the server.
80 """
81
Guido van Rossum296e1431999-04-07 15:03:39 +000082class SMTPServerDisconnected(SMTPException):
83 """Not connected to any SMTP server.
84
85 This exception is raised when the server unexpectedly disconnects,
86 or when an attempt is made to use the SMTP instance before
87 connecting it to a server.
88 """
89
90class SMTPResponseException(SMTPException):
91 """Base class for all exceptions that include an SMTP error code.
92
93 These exceptions are generated in some instances when the SMTP
94 server returns an error code. The error code is stored in the
95 `smtp_code' attribute of the error, and the `smtp_error' attribute
96 is set to the error message.
97 """
98
99 def __init__(self, code, msg):
100 self.smtp_code = code
101 self.smtp_error = msg
102 self.args = (code, msg)
103
104class SMTPSenderRefused(SMTPResponseException):
105 """Sender address refused.
Guido van Rossumae010462001-09-11 15:57:46 +0000106
Guido van Rossum296e1431999-04-07 15:03:39 +0000107 In addition to the attributes set by on all SMTPResponseException
Barry Warsawd25c1b71999-11-28 17:11:06 +0000108 exceptions, this sets `sender' to the string that the SMTP refused.
Guido van Rossum296e1431999-04-07 15:03:39 +0000109 """
110
111 def __init__(self, code, msg, sender):
112 self.smtp_code = code
113 self.smtp_error = msg
114 self.sender = sender
115 self.args = (code, msg, sender)
116
Guido van Rossum20c92281999-04-21 16:52:20 +0000117class SMTPRecipientsRefused(SMTPException):
Barry Warsawd25c1b71999-11-28 17:11:06 +0000118 """All recipient addresses refused.
Guido van Rossumae010462001-09-11 15:57:46 +0000119
Thomas Wouters7e474022000-07-16 12:04:32 +0000120 The errors for each recipient are accessible through the attribute
Tim Peters495ad3c2001-01-15 01:36:40 +0000121 'recipients', which is a dictionary of exactly the same sort as
122 SMTP.sendmail() returns.
Guido van Rossum296e1431999-04-07 15:03:39 +0000123 """
124
125 def __init__(self, recipients):
126 self.recipients = recipients
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000127 self.args = (recipients,)
Guido van Rossum296e1431999-04-07 15:03:39 +0000128
129
Guido van Rossum296e1431999-04-07 15:03:39 +0000130class SMTPDataError(SMTPResponseException):
131 """The SMTP server didn't accept the data."""
132
133class SMTPConnectError(SMTPResponseException):
Barry Warsawd25c1b71999-11-28 17:11:06 +0000134 """Error during connection establishment."""
Guido van Rossum296e1431999-04-07 15:03:39 +0000135
136class SMTPHeloError(SMTPResponseException):
Barry Warsawd25c1b71999-11-28 17:11:06 +0000137 """The server refused our HELO reply."""
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000138
Guido van Rossumae010462001-09-11 15:57:46 +0000139class SMTPAuthenticationError(SMTPResponseException):
140 """Authentication error.
141
142 Most probably the server didn't accept the username/password
143 combination provided.
144 """
Peter Schneider-Kamp7bc82bb2000-08-10 14:02:23 +0000145
R David Murray4c14bba2011-07-18 21:59:53 -0400146def quoteaddr(addrstring):
Guido van Rossum69a79bc1998-07-13 15:18:49 +0000147 """Quote a subset of the email addresses defined by RFC 821.
148
Georg Brandl9f0f9602008-06-12 22:23:59 +0000149 Should be able to handle anything email.utils.parseaddr can handle.
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000150 """
R David Murray4c14bba2011-07-18 21:59:53 -0400151 displayname, addr = email.utils.parseaddr(addrstring)
152 if (displayname, addr) == ('', ''):
153 # parseaddr couldn't parse it, use it as is and hope for the best.
154 if addrstring.strip().startswith('<'):
155 return addrstring
156 return "<%s>" % addrstring
157 return "<%s>" % addr
Guido van Rossum69a79bc1998-07-13 15:18:49 +0000158
R David Murray46346762011-07-18 21:38:54 -0400159def _addr_only(addrstring):
160 displayname, addr = email.utils.parseaddr(addrstring)
161 if (displayname, addr) == ('', ''):
162 # parseaddr couldn't parse it, so use it as is.
163 return addrstring
164 return addr
165
R. David Murray7dff9e02010-11-08 17:15:13 +0000166# Legacy method kept for backward compatibility.
Guido van Rossum69a79bc1998-07-13 15:18:49 +0000167def quotedata(data):
168 """Quote data for email.
169
Barry Warsawd25c1b71999-11-28 17:11:06 +0000170 Double leading '.', and change Unix newline '\\n', or Mac '\\r' into
Miss Islington (bot)6fc1efa2021-07-26 15:34:32 -0700171 internet CRLF end-of-line.
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000172 """
Guido van Rossum69a79bc1998-07-13 15:18:49 +0000173 return re.sub(r'(?m)^\.', '..',
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000174 re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data))
Guido van Rossum69a79bc1998-07-13 15:18:49 +0000175
R. David Murray7dff9e02010-11-08 17:15:13 +0000176def _quote_periods(bindata):
R David Murray0f663d02011-06-09 15:05:57 -0400177 return re.sub(br'(?m)^\.', b'..', bindata)
R. David Murray7dff9e02010-11-08 17:15:13 +0000178
179def _fix_eols(data):
180 return re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data)
181
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000182try:
183 import ssl
Brett Cannoncd171c82013-07-04 17:43:24 -0400184except ImportError:
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000185 _have_ssl = False
186else:
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000187 _have_ssl = True
188
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
Romuald Brunet7b313972018-10-09 16:31:55 +0200220
221 sock = None
Guido van Rossum95e6f701998-06-25 02:15:50 +0000222 file = None
223 helo_resp = None
Guido van Rossumd8faa362007-04-27 19:54:29 +0000224 ehlo_msg = "ehlo"
Guido van Rossum95e6f701998-06-25 02:15:50 +0000225 ehlo_resp = None
Ville Skyttäda51ba42020-05-23 03:50:58 +0300226 does_esmtp = False
Antoine Pitrouc1d52062011-05-07 19:39:37 +0200227 default_port = SMTP_PORT
Guido van Rossum95e6f701998-06-25 02:15:50 +0000228
Georg Brandlf78e02b2008-06-10 17:40:04 +0000229 def __init__(self, host='', port=0, local_hostname=None,
Senthil Kumaran3d23fd62011-07-30 10:56:50 +0800230 timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
231 source_address=None):
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000232 """Initialize a new instance.
233
uy-rrodriguezb3dec6f2021-04-26 02:56:37 +0100234 If specified, `host` is the name of the remote host to which to
235 connect. If specified, `port` specifies the port to which to connect.
R David Murray14ee3cf2013-04-13 14:40:33 -0400236 By default, smtplib.SMTP_PORT is used. If a host is specified the
R David Murray021362d2013-06-23 16:05:44 -0400237 connect method is called, and if it returns anything other than a
238 success code an SMTPConnectError is raised. If specified,
239 `local_hostname` is used as the FQDN of the local host in the HELO/EHLO
240 command. Otherwise, the local hostname is found using
241 socket.getfqdn(). The `source_address` parameter takes a 2-tuple (host,
242 port) for the socket to bind to as its source address before
243 connecting. If the host is '' and port is 0, the OS default behavior
244 will be used.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000245
246 """
Christian Heimesa5768f72013-12-02 20:44:17 +0100247 self._host = host
Guido van Rossumd8faa362007-04-27 19:54:29 +0000248 self.timeout = timeout
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000249 self.esmtp_features = {}
R David Murraycee7cf62015-05-16 13:58:14 -0400250 self.command_encoding = 'ascii'
Senthil Kumaran3d23fd62011-07-30 10:56:50 +0800251 self.source_address = source_address
Pandu E POLUAN7591d942021-03-13 06:25:49 +0700252 self._auth_challenge_count = 0
Senthil Kumaran3d23fd62011-07-30 10:56:50 +0800253
Guido van Rossum296e1431999-04-07 15:03:39 +0000254 if host:
255 (code, msg) = self.connect(host, port)
256 if code != 220:
Joel Hillacre9e98cd02017-05-23 23:14:50 -0600257 self.close()
Guido van Rossum296e1431999-04-07 15:03:39 +0000258 raise SMTPConnectError(code, msg)
Raymond Hettingerf13eb552002-06-02 00:40:05 +0000259 if local_hostname is not None:
Barry Warsaw13e34f72002-03-26 20:27:35 +0000260 self.local_hostname = local_hostname
Neil Schemenauer6730f262002-03-24 15:30:40 +0000261 else:
Barry Warsaw13e34f72002-03-26 20:27:35 +0000262 # RFC 2821 says we should use the fqdn in the EHLO/HELO verb, and
263 # if that can't be calculated, that we should use a domain literal
264 # instead (essentially an encoded IP address like [A.B.C.D]).
265 fqdn = socket.getfqdn()
266 if '.' in fqdn:
267 self.local_hostname = fqdn
268 else:
269 # We can't find an fqdn hostname, so use a domain literal
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000270 addr = '127.0.0.1'
271 try:
272 addr = socket.gethostbyname(socket.gethostname())
273 except socket.gaierror:
274 pass
Barry Warsaw13e34f72002-03-26 20:27:35 +0000275 self.local_hostname = '[%s]' % addr
Tim Peters495ad3c2001-01-15 01:36:40 +0000276
Barry Warsaw1f5c9582011-03-15 15:04:44 -0400277 def __enter__(self):
278 return self
279
280 def __exit__(self, *args):
281 try:
282 code, message = self.docmd("QUIT")
283 if code != 221:
284 raise SMTPResponseException(code, message)
285 except SMTPServerDisconnected:
286 pass
287 finally:
288 self.close()
289
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000290 def set_debuglevel(self, debuglevel):
291 """Set the debug output level.
292
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000293 A non-false value results in debug messages for connection and for all
294 messages sent to and received from the server.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000295
296 """
297 self.debuglevel = debuglevel
298
R David Murray0c49b892015-04-16 17:14:42 -0400299 def _print_debug(self, *args):
300 if self.debuglevel > 1:
301 print(datetime.datetime.now().time(), *args, file=sys.stderr)
302 else:
303 print(*args, file=sys.stderr)
304
Barry Warsawc9181712008-03-19 14:25:51 +0000305 def _get_socket(self, host, port, timeout):
Thomas Wouters89f507f2006-12-13 04:49:30 +0000306 # This makes it simpler for SMTP_SSL to use the SMTP connect code
307 # and just alter the socket connection bit.
Dong-hee Na62e39732020-01-14 16:49:59 +0900308 if timeout is not None and not timeout:
309 raise ValueError('Non-blocking socket (timeout=0) is not supported')
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000310 if self.debuglevel > 0:
R David Murray0c49b892015-04-16 17:14:42 -0400311 self._print_debug('connect: to', (host, port), self.source_address)
Senthil Kumaran3d23fd62011-07-30 10:56:50 +0800312 return socket.create_connection((host, port), timeout,
313 self.source_address)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000314
Senthil Kumaran3d23fd62011-07-30 10:56:50 +0800315 def connect(self, host='localhost', port=0, source_address=None):
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000316 """Connect to a host on a given port.
Guido van Rossum95e6f701998-06-25 02:15:50 +0000317
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000318 If the hostname ends with a colon (`:') followed by a number, and
319 there is no port specified, that suffix will be stripped off and the
320 number interpreted as the port number to use.
Guido van Rossum95e6f701998-06-25 02:15:50 +0000321
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000322 Note: This method is automatically invoked by __init__, if a host is
323 specified during instantiation.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000324
325 """
Senthil Kumaranb351a482011-07-31 09:14:17 +0800326
327 if source_address:
328 self.source_address = source_address
329
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000330 if not port and (host.find(':') == host.rfind(':')):
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000331 i = host.rfind(':')
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000332 if i >= 0:
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000333 host, port = host[:i], host[i + 1:]
334 try:
335 port = int(port)
Eric S. Raymond8d876032001-02-09 10:14:53 +0000336 except ValueError:
Andrew Svetlov2ade6f22012-12-17 18:57:16 +0200337 raise OSError("nonnumeric port")
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000338 if not port:
339 port = self.default_port
Steve Dower44f91c32019-06-27 10:47:59 -0700340 sys.audit("smtplib.connect", self, host, port)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000341 self.sock = self._get_socket(host, port, self.timeout)
Antoine Pitrouf068ab82011-06-06 19:17:09 +0200342 self.file = None
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000343 (code, msg) = self.getreply()
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000344 if self.debuglevel > 0:
R David Murray37f1ba92015-04-16 18:54:56 -0400345 self._print_debug('connect:', repr(msg))
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000346 return (code, msg)
Tim Peters495ad3c2001-01-15 01:36:40 +0000347
Guido van Rossum8a392d72007-11-21 22:09:45 +0000348 def send(self, s):
349 """Send `s' to the server."""
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000350 if self.debuglevel > 0:
R David Murray0c49b892015-04-16 17:14:42 -0400351 self._print_debug('send:', repr(s))
Romuald Brunet7b313972018-10-09 16:31:55 +0200352 if self.sock:
Guido van Rossum8a392d72007-11-21 22:09:45 +0000353 if isinstance(s, str):
R David Murraycee7cf62015-05-16 13:58:14 -0400354 # send is used by the 'data' command, where command_encoding
355 # should not be used, but 'data' needs to convert the string to
356 # binary itself anyway, so that's not a problem.
357 s = s.encode(self.command_encoding)
Steve Dower44f91c32019-06-27 10:47:59 -0700358 sys.audit("smtplib.send", self, s)
Guido van Rossum2880f6e1998-08-10 20:07:00 +0000359 try:
Guido van Rossum8a392d72007-11-21 22:09:45 +0000360 self.sock.sendall(s)
Andrew Svetlov2ade6f22012-12-17 18:57:16 +0200361 except OSError:
Barry Warsaw76750972001-12-14 20:34:20 +0000362 self.close()
Guido van Rossum40233ea1999-01-15 03:23:55 +0000363 raise SMTPServerDisconnected('Server not connected')
Guido van Rossumfc40a831998-01-29 17:26:45 +0000364 else:
Guido van Rossum40233ea1999-01-15 03:23:55 +0000365 raise SMTPServerDisconnected('please run connect() first')
Tim Peters495ad3c2001-01-15 01:36:40 +0000366
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000367 def putcmd(self, cmd, args=""):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000368 """Send a command to the server."""
Guido van Rossumdb23d3d1999-06-09 15:13:10 +0000369 if args == "":
Miss Islington (bot)9e6c3172021-08-29 07:43:39 -0700370 s = cmd
Guido van Rossumdb23d3d1999-06-09 15:13:10 +0000371 else:
Miss Islington (bot)9e6c3172021-08-29 07:43:39 -0700372 s = f'{cmd} {args}'
373 if '\r' in s or '\n' in s:
374 s = s.replace('\n', '\\n').replace('\r', '\\r')
375 raise ValueError(
376 f'command and arguments contain prohibited newline characters: {s}'
377 )
378 self.send(f'{s}{CRLF}')
Tim Peters495ad3c2001-01-15 01:36:40 +0000379
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000380 def getreply(self):
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000381 """Get a reply from the server.
Tim Peters495ad3c2001-01-15 01:36:40 +0000382
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000383 Returns a tuple consisting of:
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000384
385 - server response code (e.g. '250', or such, if all goes well)
386 Note: returns -1 if it can't read response code.
387
388 - server response string corresponding to response code (multiline
389 responses are converted to a single, multiline string).
Guido van Rossumf123f841999-03-29 20:33:21 +0000390
391 Raises SMTPServerDisconnected if end-of-file is reached.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000392 """
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000393 resp = []
Guido van Rossum296e1431999-04-07 15:03:39 +0000394 if self.file is None:
395 self.file = self.sock.makefile('rb')
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000396 while 1:
Benjamin Petersone1cdfd72009-01-18 21:02:37 +0000397 try:
Georg Brandlb38b5c42014-02-10 22:11:21 +0100398 line = self.file.readline(_MAXLINE + 1)
Andrew Svetlov2ade6f22012-12-17 18:57:16 +0200399 except OSError as e:
Antoine Pitrou6b2e1602011-08-28 01:20:42 +0200400 self.close()
401 raise SMTPServerDisconnected("Connection unexpectedly closed: "
402 + str(e))
Guido van Rossum806c2462007-08-06 23:33:07 +0000403 if not line:
Guido van Rossum296e1431999-04-07 15:03:39 +0000404 self.close()
405 raise SMTPServerDisconnected("Connection unexpectedly closed")
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000406 if self.debuglevel > 0:
R David Murray0c49b892015-04-16 17:14:42 -0400407 self._print_debug('reply:', repr(line))
Georg Brandlb38b5c42014-02-10 22:11:21 +0100408 if len(line) > _MAXLINE:
Senthil Kumaran4ce118e2014-06-03 07:24:54 -0700409 self.close()
Georg Brandlb38b5c42014-02-10 22:11:21 +0100410 raise SMTPResponseException(500, "Line too long.")
Guido van Rossum806c2462007-08-06 23:33:07 +0000411 resp.append(line[4:].strip(b' \t\r\n'))
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000412 code = line[:3]
Guido van Rossum296e1431999-04-07 15:03:39 +0000413 # Check that the error code is syntactically correct.
414 # Don't attempt to read a continuation line if it is broken.
415 try:
Eric S. Raymondc013f302001-02-09 05:40:38 +0000416 errcode = int(code)
Guido van Rossum296e1431999-04-07 15:03:39 +0000417 except ValueError:
418 errcode = -1
419 break
Guido van Rossumf123f841999-03-29 20:33:21 +0000420 # Check if multiline response.
Guido van Rossum806c2462007-08-06 23:33:07 +0000421 if line[3:4] != b"-":
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000422 break
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000423
Guido van Rossumc43e79f2007-06-18 18:26:36 +0000424 errmsg = b"\n".join(resp)
Tim Peters495ad3c2001-01-15 01:36:40 +0000425 if self.debuglevel > 0:
R David Murray37f1ba92015-04-16 18:54:56 -0400426 self._print_debug('reply: retcode (%s); Msg: %a' % (errcode, errmsg))
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000427 return errcode, errmsg
Tim Peters495ad3c2001-01-15 01:36:40 +0000428
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000429 def docmd(self, cmd, args=""):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000430 """Send a command, and return its response code."""
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000431 self.putcmd(cmd, args)
Guido van Rossum296e1431999-04-07 15:03:39 +0000432 return self.getreply()
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000433
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000434 # std smtp commands
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000435 def helo(self, name=''):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000436 """SMTP 'helo' command.
437 Hostname to send for this command defaults to the FQDN of the local
438 host.
439 """
Neil Schemenauer6730f262002-03-24 15:30:40 +0000440 self.putcmd("helo", name or self.local_hostname)
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000441 (code, msg) = self.getreply()
442 self.helo_resp = msg
443 return (code, msg)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000444
Guido van Rossum95e6f701998-06-25 02:15:50 +0000445 def ehlo(self, name=''):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000446 """ SMTP 'ehlo' command.
447 Hostname to send for this command defaults to the FQDN of the local
448 host.
449 """
Guido van Rossumf7fcf5e2001-09-14 16:08:44 +0000450 self.esmtp_features = {}
Guido van Rossumd8faa362007-04-27 19:54:29 +0000451 self.putcmd(self.ehlo_msg, name or self.local_hostname)
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000452 (code, msg) = self.getreply()
Tim Peters495ad3c2001-01-15 01:36:40 +0000453 # According to RFC1869 some (badly written)
454 # MTA's will disconnect on an ehlo. Toss an exception if
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000455 # that happens -ddm
456 if code == -1 and len(msg) == 0:
Barry Warsaw76750972001-12-14 20:34:20 +0000457 self.close()
Guido van Rossum40233ea1999-01-15 03:23:55 +0000458 raise SMTPServerDisconnected("Server not connected")
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000459 self.ehlo_resp = msg
Fred Drake8152d322000-12-12 23:20:45 +0000460 if code != 250:
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000461 return (code, msg)
Ville Skyttäda51ba42020-05-23 03:50:58 +0300462 self.does_esmtp = True
Thomas Wouters7e474022000-07-16 12:04:32 +0000463 #parse the ehlo response -ddm
Guido van Rossum04110fb2007-08-24 16:32:05 +0000464 assert isinstance(self.ehlo_resp, bytes), repr(self.ehlo_resp)
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000465 resp = self.ehlo_resp.decode("latin-1").split('\n')
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000466 del resp[0]
Guido van Rossum2880f6e1998-08-10 20:07:00 +0000467 for each in resp:
Piers Lauder385a77a2002-07-27 00:38:30 +0000468 # To be able to communicate with as many SMTP servers as possible,
469 # we have to take the old-style auth advertisement into account,
470 # because:
471 # 1) Else our SMTP feature parser gets confused.
472 # 2) There are some servers that only advertise the auth methods we
473 # support using the old style.
474 auth_match = OLDSTYLE_AUTH.match(each)
475 if auth_match:
476 # This doesn't remove duplicates, but that's no problem
477 self.esmtp_features["auth"] = self.esmtp_features.get("auth", "") \
478 + " " + auth_match.groups(0)[0]
479 continue
480
Barry Warsawbe22ae62002-04-15 20:03:30 +0000481 # RFC 1869 requires a space between ehlo keyword and parameters.
482 # It's actually stricter, in that only spaces are allowed between
483 # parameters, but were not going to check for that here. Note
484 # that the space isn't present if there are no parameters.
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000485 m = re.match(r'(?P<feature>[A-Za-z0-9][A-Za-z0-9\-]*) ?', each)
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000486 if m:
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000487 feature = m.group("feature").lower()
488 params = m.string[m.end("feature"):].strip()
Piers Lauder385a77a2002-07-27 00:38:30 +0000489 if feature == "auth":
490 self.esmtp_features[feature] = self.esmtp_features.get(feature, "") \
491 + " " + params
492 else:
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000493 self.esmtp_features[feature] = params
494 return (code, msg)
Guido van Rossum95e6f701998-06-25 02:15:50 +0000495
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000496 def has_extn(self, opt):
497 """Does the server support a given SMTP service extension?"""
Raymond Hettinger54f02222002-06-01 14:18:47 +0000498 return opt.lower() in self.esmtp_features
Guido van Rossum95e6f701998-06-25 02:15:50 +0000499
Guido van Rossum18586f41998-04-03 17:03:13 +0000500 def help(self, args=''):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000501 """SMTP 'help' command.
502 Returns help text from server."""
Guido van Rossum18586f41998-04-03 17:03:13 +0000503 self.putcmd("help", args)
Kurt B. Kaiser58bd1902005-06-26 18:27:36 +0000504 return self.getreply()[1]
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000505
506 def rset(self):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000507 """SMTP 'rset' command -- resets session."""
R David Murraycee7cf62015-05-16 13:58:14 -0400508 self.command_encoding = 'ascii'
Guido van Rossum296e1431999-04-07 15:03:39 +0000509 return self.docmd("rset")
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000510
R David Murrayafb151a2014-04-14 18:21:38 -0400511 def _rset(self):
512 """Internal 'rset' command which ignores any SMTPServerDisconnected error.
513
514 Used internally in the library, since the server disconnected error
515 should appear to the application when the *next* command is issued, if
516 we are doing an internal "safety" reset.
517 """
518 try:
519 self.rset()
520 except SMTPServerDisconnected:
521 pass
522
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000523 def noop(self):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000524 """SMTP 'noop' command -- doesn't do anything :>"""
Guido van Rossum296e1431999-04-07 15:03:39 +0000525 return self.docmd("noop")
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000526
Pablo Aguiard5fbe9b2018-09-08 00:04:48 +0200527 def mail(self, sender, options=()):
R David Murraycee7cf62015-05-16 13:58:14 -0400528 """SMTP 'mail' command -- begins mail xfer session.
529
530 This method may raise the following exceptions:
531
532 SMTPNotSupportedError The options parameter includes 'SMTPUTF8'
533 but the SMTPUTF8 extension is not supported by
534 the server.
535 """
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000536 optionlist = ''
537 if options and self.does_esmtp:
R David Murraycee7cf62015-05-16 13:58:14 -0400538 if any(x.lower()=='smtputf8' for x in options):
539 if self.has_extn('smtputf8'):
540 self.command_encoding = 'utf-8'
541 else:
542 raise SMTPNotSupportedError(
543 'SMTPUTF8 not supported by server')
Eric S. Raymondc013f302001-02-09 05:40:38 +0000544 optionlist = ' ' + ' '.join(options)
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000545 self.putcmd("mail", "FROM:%s%s" % (quoteaddr(sender), optionlist))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000546 return self.getreply()
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000547
Pablo Aguiard5fbe9b2018-09-08 00:04:48 +0200548 def rcpt(self, recip, options=()):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000549 """SMTP 'rcpt' command -- indicates 1 recipient for this mail."""
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000550 optionlist = ''
551 if options and self.does_esmtp:
Eric S. Raymondc013f302001-02-09 05:40:38 +0000552 optionlist = ' ' + ' '.join(options)
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000553 self.putcmd("rcpt", "TO:%s%s" % (quoteaddr(recip), optionlist))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000554 return self.getreply()
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000555
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000556 def data(self, msg):
Tim Peters495ad3c2001-01-15 01:36:40 +0000557 """SMTP 'DATA' command -- sends message data to server.
Guido van Rossum296e1431999-04-07 15:03:39 +0000558
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000559 Automatically quotes lines beginning with a period per rfc821.
Guido van Rossum296e1431999-04-07 15:03:39 +0000560 Raises SMTPDataError if there is an unexpected reply to the
561 DATA command; the return value from this method is the final
R. David Murray7dff9e02010-11-08 17:15:13 +0000562 response code received when the all data is sent. If msg
Serhiy Storchaka9f8a8912015-04-03 18:12:41 +0300563 is a string, lone '\\r' and '\\n' characters are converted to
564 '\\r\\n' characters. If msg is bytes, it is transmitted as is.
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000565 """
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000566 self.putcmd("data")
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000567 (code, repl) = self.getreply()
568 if self.debuglevel > 0:
R David Murray0c49b892015-04-16 17:14:42 -0400569 self._print_debug('data:', (code, repl))
Fred Drake8152d322000-12-12 23:20:45 +0000570 if code != 354:
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000571 raise SMTPDataError(code, repl)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000572 else:
R. David Murray7dff9e02010-11-08 17:15:13 +0000573 if isinstance(msg, str):
574 msg = _fix_eols(msg).encode('ascii')
575 q = _quote_periods(msg)
576 if q[-2:] != bCRLF:
577 q = q + bCRLF
578 q = q + b"." + bCRLF
Guido van Rossum20c92281999-04-21 16:52:20 +0000579 self.send(q)
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000580 (code, msg) = self.getreply()
581 if self.debuglevel > 0:
R David Murray0c49b892015-04-16 17:14:42 -0400582 self._print_debug('data:', (code, msg))
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000583 return (code, msg)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000584
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000585 def verify(self, address):
Barry Warsawa7d9bdf1998-12-22 03:24:27 +0000586 """SMTP 'verify' command -- checks for address validity."""
R David Murray46346762011-07-18 21:38:54 -0400587 self.putcmd("vrfy", _addr_only(address))
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000588 return self.getreply()
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000589 # a.k.a.
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000590 vrfy = verify
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000591
592 def expn(self, address):
Christian Heimes90c3d9b2008-02-23 13:18:03 +0000593 """SMTP 'expn' command -- expands a mailing list."""
R David Murray46346762011-07-18 21:38:54 -0400594 self.putcmd("expn", _addr_only(address))
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000595 return self.getreply()
596
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000597 # some useful methods
Guido van Rossumae010462001-09-11 15:57:46 +0000598
Christian Heimes679db4a2008-01-18 09:56:22 +0000599 def ehlo_or_helo_if_needed(self):
600 """Call self.ehlo() and/or self.helo() if needed.
601
602 If there has been no previous EHLO or HELO command this session, this
603 method tries ESMTP EHLO first.
604
605 This method may raise the following exceptions:
606
607 SMTPHeloError The server didn't reply properly to
608 the helo greeting.
609 """
610 if self.helo_resp is None and self.ehlo_resp is None:
611 if not (200 <= self.ehlo()[0] <= 299):
612 (code, resp) = self.helo()
613 if not (200 <= code <= 299):
614 raise SMTPHeloError(code, resp)
615
Barry Warsawc5ea7542015-07-09 10:39:55 -0400616 def auth(self, mechanism, authobject, *, initial_response_ok=True):
R David Murray76e13c12014-07-03 14:47:46 -0400617 """Authentication command - requires response processing.
618
619 'mechanism' specifies which authentication mechanism is to
620 be used - the valid values are those listed in the 'auth'
621 element of 'esmtp_features'.
622
623 'authobject' must be a callable object taking a single argument:
624
625 data = authobject(challenge)
626
627 It will be called to process the server's challenge response; the
628 challenge argument it is passed will be a bytes. It should return
Sebastian Rittau78deb7f2018-09-10 19:29:43 +0200629 an ASCII string that will be base64 encoded and sent to the server.
R David Murray76e13c12014-07-03 14:47:46 -0400630
Barry Warsawc5ea7542015-07-09 10:39:55 -0400631 Keyword arguments:
632 - initial_response_ok: Allow sending the RFC 4954 initial-response
633 to the AUTH command, if the authentication methods supports it.
634 """
635 # RFC 4954 allows auth methods to provide an initial response. Not all
636 # methods support it. By definition, if they return something other
637 # than None when challenge is None, then they do. See issue #15014.
R David Murray76e13c12014-07-03 14:47:46 -0400638 mechanism = mechanism.upper()
Barry Warsawc5ea7542015-07-09 10:39:55 -0400639 initial_response = (authobject() if initial_response_ok else None)
640 if initial_response is not None:
641 response = encode_base64(initial_response.encode('ascii'), eol='')
642 (code, resp) = self.docmd("AUTH", mechanism + " " + response)
Pandu E POLUAN7591d942021-03-13 06:25:49 +0700643 self._auth_challenge_count = 1
Barry Warsawc5ea7542015-07-09 10:39:55 -0400644 else:
645 (code, resp) = self.docmd("AUTH", mechanism)
Pandu E POLUAN7591d942021-03-13 06:25:49 +0700646 self._auth_challenge_count = 0
R David Murrayb0deeb42015-11-08 01:03:52 -0500647 # If server responds with a challenge, send the response.
Pandu E POLUAN7591d942021-03-13 06:25:49 +0700648 while code == 334:
649 self._auth_challenge_count += 1
R David Murrayb0deeb42015-11-08 01:03:52 -0500650 challenge = base64.decodebytes(resp)
651 response = encode_base64(
652 authobject(challenge).encode('ascii'), eol='')
653 (code, resp) = self.docmd(response)
Pandu E POLUAN7591d942021-03-13 06:25:49 +0700654 # If server keeps sending challenges, something is wrong.
655 if self._auth_challenge_count > _MAXCHALLENGE:
656 raise SMTPException(
657 "Server AUTH mechanism infinite loop. Last response: "
658 + repr((code, resp))
659 )
Barry Warsawc5ea7542015-07-09 10:39:55 -0400660 if code in (235, 503):
661 return (code, resp)
R David Murray76e13c12014-07-03 14:47:46 -0400662 raise SMTPAuthenticationError(code, resp)
663
Barry Warsawc5ea7542015-07-09 10:39:55 -0400664 def auth_cram_md5(self, challenge=None):
R David Murray76e13c12014-07-03 14:47:46 -0400665 """ Authobject to use with CRAM-MD5 authentication. Requires self.user
666 and self.password to be set."""
Barry Warsawc5ea7542015-07-09 10:39:55 -0400667 # CRAM-MD5 does not support initial-response.
668 if challenge is None:
669 return None
R David Murray76e13c12014-07-03 14:47:46 -0400670 return self.user + " " + hmac.HMAC(
671 self.password.encode('ascii'), challenge, 'md5').hexdigest()
672
Barry Warsawc5ea7542015-07-09 10:39:55 -0400673 def auth_plain(self, challenge=None):
R David Murray76e13c12014-07-03 14:47:46 -0400674 """ Authobject to use with PLAIN authentication. Requires self.user and
675 self.password to be set."""
676 return "\0%s\0%s" % (self.user, self.password)
677
Barry Warsawc5ea7542015-07-09 10:39:55 -0400678 def auth_login(self, challenge=None):
R David Murray76e13c12014-07-03 14:47:46 -0400679 """ Authobject to use with LOGIN authentication. Requires self.user and
680 self.password to be set."""
Pandu E POLUAN7591d942021-03-13 06:25:49 +0700681 if challenge is None or self._auth_challenge_count < 2:
R David Murrayb0deeb42015-11-08 01:03:52 -0500682 return self.user
683 else:
R David Murray76e13c12014-07-03 14:47:46 -0400684 return self.password
R David Murray76e13c12014-07-03 14:47:46 -0400685
Barry Warsawc5ea7542015-07-09 10:39:55 -0400686 def login(self, user, password, *, initial_response_ok=True):
Guido van Rossumae010462001-09-11 15:57:46 +0000687 """Log in on an SMTP server that requires authentication.
688
689 The arguments are:
R David Murray76e13c12014-07-03 14:47:46 -0400690 - user: The user name to authenticate with.
691 - password: The password for the authentication.
Guido van Rossumae010462001-09-11 15:57:46 +0000692
Barry Warsawc5ea7542015-07-09 10:39:55 -0400693 Keyword arguments:
694 - initial_response_ok: Allow sending the RFC 4954 initial-response
695 to the AUTH command, if the authentication methods supports it.
696
Guido van Rossumae010462001-09-11 15:57:46 +0000697 If there has been no previous EHLO or HELO command this session, this
698 method tries ESMTP EHLO first.
699
700 This method will return normally if the authentication was successful.
701
702 This method may raise the following exceptions:
703
704 SMTPHeloError The server didn't reply properly to
705 the helo greeting.
706 SMTPAuthenticationError The server didn't accept the username/
707 password combination.
R David Murraycee7cf62015-05-16 13:58:14 -0400708 SMTPNotSupportedError The AUTH command is not supported by the
709 server.
Fred Drake2f8f4d32001-10-13 18:35:32 +0000710 SMTPException No suitable authentication method was
Guido van Rossumae010462001-09-11 15:57:46 +0000711 found.
712 """
713
Christian Heimes679db4a2008-01-18 09:56:22 +0000714 self.ehlo_or_helo_if_needed()
Guido van Rossumae010462001-09-11 15:57:46 +0000715 if not self.has_extn("auth"):
R David Murraycee7cf62015-05-16 13:58:14 -0400716 raise SMTPNotSupportedError(
717 "SMTP AUTH extension not supported by server.")
Guido van Rossumae010462001-09-11 15:57:46 +0000718
Gerhard Häring1c5471f2010-08-05 14:08:44 +0000719 # Authentication methods the server claims to support
720 advertised_authlist = self.esmtp_features["auth"].split()
Guido van Rossumae010462001-09-11 15:57:46 +0000721
R David Murray76e13c12014-07-03 14:47:46 -0400722 # Authentication methods we can handle in our preferred order:
723 preferred_auths = ['CRAM-MD5', 'PLAIN', 'LOGIN']
Guido van Rossumae010462001-09-11 15:57:46 +0000724
R David Murray76e13c12014-07-03 14:47:46 -0400725 # We try the supported authentications in our preferred order, if
726 # the server supports them.
727 authlist = [auth for auth in preferred_auths
728 if auth in advertised_authlist]
Gerhard Häring1c5471f2010-08-05 14:08:44 +0000729 if not authlist:
Fred Drake2f8f4d32001-10-13 18:35:32 +0000730 raise SMTPException("No suitable authentication method found.")
Gerhard Häring1c5471f2010-08-05 14:08:44 +0000731
732 # Some servers advertise authentication methods they don't really
733 # support, so if authentication fails, we continue until we've tried
734 # all methods.
R David Murray76e13c12014-07-03 14:47:46 -0400735 self.user, self.password = user, password
Gerhard Häring1c5471f2010-08-05 14:08:44 +0000736 for authmethod in authlist:
R David Murray76e13c12014-07-03 14:47:46 -0400737 method_name = 'auth_' + authmethod.lower().replace('-', '_')
738 try:
Barry Warsawc5ea7542015-07-09 10:39:55 -0400739 (code, resp) = self.auth(
740 authmethod, getattr(self, method_name),
741 initial_response_ok=initial_response_ok)
R David Murray76e13c12014-07-03 14:47:46 -0400742 # 235 == 'Authentication successful'
743 # 503 == 'Error: already authenticated'
744 if code in (235, 503):
745 return (code, resp)
746 except SMTPAuthenticationError as e:
747 last_exception = e
Gerhard Häring1c5471f2010-08-05 14:08:44 +0000748
R David Murray76e13c12014-07-03 14:47:46 -0400749 # We could not login successfully. Return result of last attempt.
750 raise last_exception
Guido van Rossumae010462001-09-11 15:57:46 +0000751
Antoine Pitroue0650202011-05-18 18:03:09 +0200752 def starttls(self, keyfile=None, certfile=None, context=None):
Guido van Rossumf7fcf5e2001-09-14 16:08:44 +0000753 """Puts the connection to the SMTP server into TLS mode.
Tim Petersb64bec32001-09-18 02:26:39 +0000754
Christian Heimes679db4a2008-01-18 09:56:22 +0000755 If there has been no previous EHLO or HELO command this session, this
756 method tries ESMTP EHLO first.
757
Guido van Rossumf7fcf5e2001-09-14 16:08:44 +0000758 If the server supports TLS, this will encrypt the rest of the SMTP
759 session. If you provide the keyfile and certfile parameters,
760 the identity of the SMTP server and client can be checked. This,
761 however, depends on whether the socket module really checks the
762 certificates.
Christian Heimes679db4a2008-01-18 09:56:22 +0000763
764 This method may raise the following exceptions:
765
766 SMTPHeloError The server didn't reply properly to
767 the helo greeting.
Guido van Rossumf7fcf5e2001-09-14 16:08:44 +0000768 """
Christian Heimes679db4a2008-01-18 09:56:22 +0000769 self.ehlo_or_helo_if_needed()
770 if not self.has_extn("starttls"):
R David Murraycee7cf62015-05-16 13:58:14 -0400771 raise SMTPNotSupportedError(
772 "STARTTLS extension not supported by server.")
Tim Petersb64bec32001-09-18 02:26:39 +0000773 (resp, reply) = self.docmd("STARTTLS")
Guido van Rossumf7fcf5e2001-09-14 16:08:44 +0000774 if resp == 220:
Thomas Wouters47b49bf2007-08-30 22:15:33 +0000775 if not _have_ssl:
776 raise RuntimeError("No SSL support included in this Python")
Antoine Pitroue0650202011-05-18 18:03:09 +0200777 if context is not None and keyfile is not None:
778 raise ValueError("context and keyfile arguments are mutually "
779 "exclusive")
780 if context is not None and certfile is not None:
781 raise ValueError("context and certfile arguments are mutually "
782 "exclusive")
Christian Heimesd0486372016-09-10 23:23:33 +0200783 if keyfile is not None or certfile is not None:
784 import warnings
Pablo Aguiar4b5e62d2018-11-01 11:33:35 +0100785 warnings.warn("keyfile and certfile are deprecated, use a "
Christian Heimesd0486372016-09-10 23:23:33 +0200786 "custom context instead", DeprecationWarning, 2)
Christian Heimes67986f92013-11-23 22:43:47 +0100787 if context is None:
788 context = ssl._create_stdlib_context(certfile=certfile,
789 keyfile=keyfile)
Christian Heimesa5768f72013-12-02 20:44:17 +0100790 self.sock = context.wrap_socket(self.sock,
Benjamin Peterson7243b572014-11-23 17:04:34 -0600791 server_hostname=self._host)
Antoine Pitrouf068ab82011-06-06 19:17:09 +0200792 self.file = None
Christian Heimes679db4a2008-01-18 09:56:22 +0000793 # RFC 3207:
794 # The client MUST discard any knowledge obtained from
795 # the server, such as the list of SMTP service extensions,
796 # which was not obtained from the TLS negotiation itself.
797 self.helo_resp = None
798 self.ehlo_resp = None
799 self.esmtp_features = {}
Ville Skyttäda51ba42020-05-23 03:50:58 +0300800 self.does_esmtp = False
Benjamin Peterson46b32f32016-06-11 13:16:42 -0700801 else:
802 # RFC 3207:
803 # 501 Syntax error (no parameters allowed)
804 # 454 TLS not available due to temporary reason
805 raise SMTPResponseException(resp, reply)
Guido van Rossumf7fcf5e2001-09-14 16:08:44 +0000806 return (resp, reply)
Tim Petersb64bec32001-09-18 02:26:39 +0000807
Pablo Aguiard5fbe9b2018-09-08 00:04:48 +0200808 def sendmail(self, from_addr, to_addrs, msg, mail_options=(),
809 rcpt_options=()):
Tim Peters495ad3c2001-01-15 01:36:40 +0000810 """This command performs an entire mail transaction.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000811
Tim Peters495ad3c2001-01-15 01:36:40 +0000812 The arguments are:
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000813 - from_addr : The address sending this mail.
814 - to_addrs : A list of addresses to send this mail to. A bare
815 string will be treated as a list with 1 address.
Tim Peters495ad3c2001-01-15 01:36:40 +0000816 - msg : The message to send.
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000817 - mail_options : List of ESMTP options (such as 8bitmime) for the
818 mail command.
819 - rcpt_options : List of ESMTP options (such as DSN commands) for
820 all the rcpt commands.
821
R. David Murray7dff9e02010-11-08 17:15:13 +0000822 msg may be a string containing characters in the ASCII range, or a byte
823 string. A string is encoded to bytes using the ascii codec, and lone
R David Murrayac4e5ab2011-07-02 21:03:19 -0400824 \\r and \\n characters are converted to \\r\\n characters.
R. David Murray7dff9e02010-11-08 17:15:13 +0000825
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000826 If there has been no previous EHLO or HELO command this session, this
827 method tries ESMTP EHLO first. If the server does ESMTP, message size
828 and each of the specified options will be passed to it. If EHLO
829 fails, HELO will be tried and ESMTP options suppressed.
830
831 This method will return normally if the mail is accepted for at least
Barry Warsawd25c1b71999-11-28 17:11:06 +0000832 one recipient. It returns a dictionary, with one entry for each
833 recipient that was refused. Each entry contains a tuple of the SMTP
834 error code and the accompanying error message sent by the server.
Guido van Rossum296e1431999-04-07 15:03:39 +0000835
836 This method may raise the following exceptions:
837
838 SMTPHeloError The server didn't reply properly to
Tim Peters495ad3c2001-01-15 01:36:40 +0000839 the helo greeting.
Barry Warsawd25c1b71999-11-28 17:11:06 +0000840 SMTPRecipientsRefused The server rejected ALL recipients
Guido van Rossum296e1431999-04-07 15:03:39 +0000841 (no mail was sent).
842 SMTPSenderRefused The server didn't accept the from_addr.
843 SMTPDataError The server replied with an unexpected
844 error code (other than a refusal of
845 a recipient).
R David Murraycee7cf62015-05-16 13:58:14 -0400846 SMTPNotSupportedError The mail_options parameter includes 'SMTPUTF8'
847 but the SMTPUTF8 extension is not supported by
848 the server.
Guido van Rossum296e1431999-04-07 15:03:39 +0000849
850 Note: the connection will be open even after an exception is raised.
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000851
Guido van Rossum95e6f701998-06-25 02:15:50 +0000852 Example:
Tim Peters495ad3c2001-01-15 01:36:40 +0000853
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000854 >>> import smtplib
855 >>> s=smtplib.SMTP("localhost")
Guido van Rossumfc40a831998-01-29 17:26:45 +0000856 >>> tolist=["one@one.org","two@two.org","three@three.org","four@four.org"]
Martin v. Löwis301b1cd2002-07-28 16:52:01 +0000857 >>> msg = '''\\
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000858 ... From: Me@my.org
859 ... Subject: testin'...
860 ...
861 ... This is a test '''
862 >>> s.sendmail("me@my.org",tolist,msg)
863 { "three@three.org" : ( 550 ,"User unknown" ) }
864 >>> s.quit()
Tim Peters495ad3c2001-01-15 01:36:40 +0000865
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000866 In the above example, the message was accepted for delivery to three
867 of the four addresses, and one was rejected, with the error code
Barry Warsawd25c1b71999-11-28 17:11:06 +0000868 550. If all addresses are accepted, then the method will return an
Barry Warsaw4c4bec81998-12-22 03:02:20 +0000869 empty dictionary.
870
871 """
Christian Heimes679db4a2008-01-18 09:56:22 +0000872 self.ehlo_or_helo_if_needed()
Guido van Rossum95e6f701998-06-25 02:15:50 +0000873 esmtp_opts = []
R. David Murray7dff9e02010-11-08 17:15:13 +0000874 if isinstance(msg, str):
875 msg = _fix_eols(msg).encode('ascii')
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000876 if self.does_esmtp:
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000877 if self.has_extn('size'):
Walter Dörwald70a6b492004-02-12 17:35:32 +0000878 esmtp_opts.append("size=%d" % len(msg))
Guido van Rossumfcfb6321998-08-04 15:29:54 +0000879 for option in mail_options:
Guido van Rossum95e6f701998-06-25 02:15:50 +0000880 esmtp_opts.append(option)
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000881 (code, resp) = self.mail(from_addr, esmtp_opts)
Fred Drake8152d322000-12-12 23:20:45 +0000882 if code != 250:
R David Murrayd312c742013-03-20 20:36:14 -0400883 if code == 421:
884 self.close()
885 else:
R David Murrayafb151a2014-04-14 18:21:38 -0400886 self._rset()
Guido van Rossum296e1431999-04-07 15:03:39 +0000887 raise SMTPSenderRefused(code, resp, from_addr)
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000888 senderrs = {}
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000889 if isinstance(to_addrs, str):
Jeremy Hylton31bb8ce1998-08-13 19:57:46 +0000890 to_addrs = [to_addrs]
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000891 for each in to_addrs:
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000892 (code, resp) = self.rcpt(each, rcpt_options)
Fred Drake8152d322000-12-12 23:20:45 +0000893 if (code != 250) and (code != 251):
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000894 senderrs[each] = (code, resp)
R David Murrayd312c742013-03-20 20:36:14 -0400895 if code == 421:
896 self.close()
897 raise SMTPRecipientsRefused(senderrs)
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000898 if len(senderrs) == len(to_addrs):
Guido van Rossum95e6f701998-06-25 02:15:50 +0000899 # the server refused all our recipients
R David Murrayafb151a2014-04-14 18:21:38 -0400900 self._rset()
Guido van Rossum296e1431999-04-07 15:03:39 +0000901 raise SMTPRecipientsRefused(senderrs)
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +0000902 (code, resp) = self.data(msg)
Fred Drake8152d322000-12-12 23:20:45 +0000903 if code != 250:
R David Murrayd312c742013-03-20 20:36:14 -0400904 if code == 421:
905 self.close()
906 else:
R David Murrayafb151a2014-04-14 18:21:38 -0400907 self._rset()
Guido van Rossum296e1431999-04-07 15:03:39 +0000908 raise SMTPDataError(code, resp)
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000909 #if we got here then somebody got our mail
Tim Peters495ad3c2001-01-15 01:36:40 +0000910 return senderrs
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000911
R. David Murray7dff9e02010-11-08 17:15:13 +0000912 def send_message(self, msg, from_addr=None, to_addrs=None,
Pablo Aguiard5fbe9b2018-09-08 00:04:48 +0200913 mail_options=(), rcpt_options=()):
R. David Murray7dff9e02010-11-08 17:15:13 +0000914 """Converts message to a bytestring and passes it to sendmail.
915
916 The arguments are as for sendmail, except that msg is an
R David Murrayac4e5ab2011-07-02 21:03:19 -0400917 email.message.Message object. If from_addr is None or to_addrs is
918 None, these arguments are taken from the headers of the Message as
919 described in RFC 2822 (a ValueError is raised if there is more than
920 one set of 'Resent-' headers). Regardless of the values of from_addr and
921 to_addr, any Bcc field (or Resent-Bcc field, when the Message is a
922 resent) of the Message object won't be transmitted. The Message
923 object is then serialized using email.generator.BytesGenerator and
R David Murray83084442015-05-17 19:27:22 -0400924 sendmail is called to transmit the message. If the sender or any of
925 the recipient addresses contain non-ASCII and the server advertises the
926 SMTPUTF8 capability, the policy is cloned with utf8 set to True for the
927 serialization, and SMTPUTF8 and BODY=8BITMIME are asserted on the send.
Berker Peksage39682b2016-07-01 12:17:05 +0300928 If the server does not support SMTPUTF8, an SMTPNotSupported error is
R David Murray83084442015-05-17 19:27:22 -0400929 raised. Otherwise the generator is called without modifying the
930 policy.
R David Murrayac4e5ab2011-07-02 21:03:19 -0400931
R. David Murray7dff9e02010-11-08 17:15:13 +0000932 """
R David Murrayac4e5ab2011-07-02 21:03:19 -0400933 # 'Resent-Date' is a mandatory field if the Message is resent (RFC 2822
934 # Section 3.6.6). In such a case, we use the 'Resent-*' fields. However,
935 # if there is more than one 'Resent-' block there's no way to
936 # unambiguously determine which one is the most recent in all cases,
937 # so rather than guess we raise a ValueError in that case.
938 #
939 # TODO implement heuristics to guess the correct Resent-* block with an
940 # option allowing the user to enable the heuristics. (It should be
941 # possible to guess correctly almost all of the time.)
Senthil Kumaranb351a482011-07-31 09:14:17 +0800942
R David Murray83084442015-05-17 19:27:22 -0400943 self.ehlo_or_helo_if_needed()
Senthil Kumaranb351a482011-07-31 09:14:17 +0800944 resent = msg.get_all('Resent-Date')
R David Murrayac4e5ab2011-07-02 21:03:19 -0400945 if resent is None:
946 header_prefix = ''
947 elif len(resent) == 1:
948 header_prefix = 'Resent-'
949 else:
950 raise ValueError("message has more than one 'Resent-' header block")
R. David Murray7dff9e02010-11-08 17:15:13 +0000951 if from_addr is None:
R David Murrayac4e5ab2011-07-02 21:03:19 -0400952 # Prefer the sender field per RFC 2822:3.6.2.
Senthil Kumaranb351a482011-07-31 09:14:17 +0800953 from_addr = (msg[header_prefix + 'Sender']
954 if (header_prefix + 'Sender') in msg
955 else msg[header_prefix + 'From'])
Stéphane Wirtel8d83e4b2018-01-31 01:02:51 +0100956 from_addr = email.utils.getaddresses([from_addr])[0][1]
R. David Murray7dff9e02010-11-08 17:15:13 +0000957 if to_addrs is None:
Senthil Kumaranb351a482011-07-31 09:14:17 +0800958 addr_fields = [f for f in (msg[header_prefix + 'To'],
959 msg[header_prefix + 'Bcc'],
R David Murray83084442015-05-17 19:27:22 -0400960 msg[header_prefix + 'Cc'])
961 if f is not None]
R. David Murray7dff9e02010-11-08 17:15:13 +0000962 to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)]
R David Murrayac4e5ab2011-07-02 21:03:19 -0400963 # Make a local copy so we can delete the bcc headers.
964 msg_copy = copy.copy(msg)
965 del msg_copy['Bcc']
966 del msg_copy['Resent-Bcc']
R David Murray83084442015-05-17 19:27:22 -0400967 international = False
968 try:
969 ''.join([from_addr, *to_addrs]).encode('ascii')
970 except UnicodeEncodeError:
971 if not self.has_extn('smtputf8'):
972 raise SMTPNotSupportedError(
973 "One or more source or delivery addresses require"
974 " internationalized email support, but the server"
975 " does not advertise the required SMTPUTF8 capability")
976 international = True
R. David Murray7dff9e02010-11-08 17:15:13 +0000977 with io.BytesIO() as bytesmsg:
R David Murray83084442015-05-17 19:27:22 -0400978 if international:
979 g = email.generator.BytesGenerator(
980 bytesmsg, policy=msg.policy.clone(utf8=True))
Pablo Aguiard5fbe9b2018-09-08 00:04:48 +0200981 mail_options = (*mail_options, 'SMTPUTF8', 'BODY=8BITMIME')
R David Murray83084442015-05-17 19:27:22 -0400982 else:
983 g = email.generator.BytesGenerator(bytesmsg)
R David Murrayac4e5ab2011-07-02 21:03:19 -0400984 g.flatten(msg_copy, linesep='\r\n')
R. David Murray7dff9e02010-11-08 17:15:13 +0000985 flatmsg = bytesmsg.getvalue()
986 return self.sendmail(from_addr, to_addrs, flatmsg, mail_options,
987 rcpt_options)
988
Guido van Rossumbbe323e1998-01-29 17:24:40 +0000989 def close(self):
990 """Close the connection to the SMTP server."""
Serhiy Storchaka7e7a3db2015-04-10 13:24:41 +0300991 try:
992 file = self.file
993 self.file = None
994 if file:
995 file.close()
996 finally:
997 sock = self.sock
998 self.sock = None
999 if sock:
1000 sock.close()
Guido van Rossumbbe323e1998-01-29 17:24:40 +00001001
Guido van Rossumbbe323e1998-01-29 17:24:40 +00001002 def quit(self):
Guido van Rossum95e6f701998-06-25 02:15:50 +00001003 """Terminate the SMTP session."""
Christian Heimesba4af492008-03-28 00:55:15 +00001004 res = self.docmd("quit")
R David Murray0cff49f2014-08-30 16:51:59 -04001005 # A new EHLO is required after reconnecting with connect()
1006 self.ehlo_resp = self.helo_resp = None
1007 self.esmtp_features = {}
1008 self.does_esmtp = False
Guido van Rossum45e2fbc1998-03-26 21:13:24 +00001009 self.close()
Christian Heimesba4af492008-03-28 00:55:15 +00001010 return res
Guido van Rossum95e6f701998-06-25 02:15:50 +00001011
Thomas Wouters47b49bf2007-08-30 22:15:33 +00001012if _have_ssl:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001013
Thomas Wouters47b49bf2007-08-30 22:15:33 +00001014 class SMTP_SSL(SMTP):
R David Murray021362d2013-06-23 16:05:44 -04001015 """ This is a subclass derived from SMTP that connects over an SSL
1016 encrypted socket (to use this class you need a socket module that was
1017 compiled with SSL support). If host is not specified, '' (the local
1018 host) is used. If port is omitted, the standard SMTP-over-SSL port
1019 (465) is used. local_hostname and source_address have the same meaning
1020 as they do in the SMTP class. keyfile and certfile are also optional -
1021 they can contain a PEM formatted private key and certificate chain file
1022 for the SSL connection. context also optional, can contain a
1023 SSLContext, and is an alternative to keyfile and certfile; If it is
1024 specified both keyfile and certfile must be None.
1025
Thomas Wouters47b49bf2007-08-30 22:15:33 +00001026 """
Antoine Pitrouc1d52062011-05-07 19:39:37 +02001027
1028 default_port = SMTP_SSL_PORT
1029
Thomas Wouters47b49bf2007-08-30 22:15:33 +00001030 def __init__(self, host='', port=0, local_hostname=None,
Georg Brandlf78e02b2008-06-10 17:40:04 +00001031 keyfile=None, certfile=None,
Senthil Kumaran3d23fd62011-07-30 10:56:50 +08001032 timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
1033 source_address=None, context=None):
Antoine Pitroue0650202011-05-18 18:03:09 +02001034 if context is not None and keyfile is not None:
1035 raise ValueError("context and keyfile arguments are mutually "
1036 "exclusive")
1037 if context is not None and certfile is not None:
1038 raise ValueError("context and certfile arguments are mutually "
1039 "exclusive")
Christian Heimesd0486372016-09-10 23:23:33 +02001040 if keyfile is not None or certfile is not None:
1041 import warnings
Pablo Aguiar4b5e62d2018-11-01 11:33:35 +01001042 warnings.warn("keyfile and certfile are deprecated, use a "
Christian Heimesd0486372016-09-10 23:23:33 +02001043 "custom context instead", DeprecationWarning, 2)
Thomas Wouters47b49bf2007-08-30 22:15:33 +00001044 self.keyfile = keyfile
1045 self.certfile = certfile
Christian Heimes67986f92013-11-23 22:43:47 +01001046 if context is None:
1047 context = ssl._create_stdlib_context(certfile=certfile,
1048 keyfile=keyfile)
Antoine Pitroue0650202011-05-18 18:03:09 +02001049 self.context = context
Senthil Kumaran3d23fd62011-07-30 10:56:50 +08001050 SMTP.__init__(self, host, port, local_hostname, timeout,
Dong-hee Na62e39732020-01-14 16:49:59 +09001051 source_address)
Thomas Wouters47b49bf2007-08-30 22:15:33 +00001052
1053 def _get_socket(self, host, port, timeout):
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +00001054 if self.debuglevel > 0:
R David Murray0c49b892015-04-16 17:14:42 -04001055 self._print_debug('connect:', (host, port))
Dong-hee Na62e39732020-01-14 16:49:59 +09001056 new_socket = super()._get_socket(host, port, timeout)
Christian Heimesa5768f72013-12-02 20:44:17 +01001057 new_socket = self.context.wrap_socket(new_socket,
Benjamin Peterson7243b572014-11-23 17:04:34 -06001058 server_hostname=self._host)
R. David Murray87e20742009-05-23 01:30:26 +00001059 return new_socket
Thomas Wouters47b49bf2007-08-30 22:15:33 +00001060
1061 __all__.append("SMTP_SSL")
Barry Warsaw4c4bec81998-12-22 03:02:20 +00001062
Guido van Rossumd8faa362007-04-27 19:54:29 +00001063#
1064# LMTP extension
1065#
1066LMTP_PORT = 2003
1067
1068class LMTP(SMTP):
1069 """LMTP - Local Mail Transfer Protocol
1070
1071 The LMTP protocol, which is very similar to ESMTP, is heavily based
R David Murray021362d2013-06-23 16:05:44 -04001072 on the standard SMTP client. It's common to use Unix sockets for
1073 LMTP, so our connect() method must support that as well as a regular
R David Murray36beb662013-06-23 15:47:50 -04001074 host:port server. local_hostname and source_address have the same
R David Murray021362d2013-06-23 16:05:44 -04001075 meaning as they do in the SMTP class. To specify a Unix socket,
1076 you must use an absolute path as the host, starting with a '/'.
Guido van Rossumd8faa362007-04-27 19:54:29 +00001077
1078 Authentication is supported, using the regular SMTP mechanism. When
1079 using a Unix socket, LMTP generally don't support or require any
1080 authentication, but your mileage might vary."""
1081
1082 ehlo_msg = "lhlo"
1083
Senthil Kumaran3d23fd62011-07-30 10:56:50 +08001084 def __init__(self, host='', port=LMTP_PORT, local_hostname=None,
Dong-hee Na65a5ce22020-01-15 06:42:09 +09001085 source_address=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001086 """Initialize a new instance."""
Dong-hee Na62e39732020-01-14 16:49:59 +09001087 super().__init__(host, port, local_hostname=local_hostname,
Dong-hee Na65a5ce22020-01-15 06:42:09 +09001088 source_address=source_address, timeout=timeout)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001089
Senthil Kumaran3d23fd62011-07-30 10:56:50 +08001090 def connect(self, host='localhost', port=0, source_address=None):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001091 """Connect to the LMTP daemon, on either a Unix or a TCP socket."""
1092 if host[0] != '/':
Dong-hee Na62e39732020-01-14 16:49:59 +09001093 return super().connect(host, port, source_address=source_address)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001094
Dong-hee Na65a5ce22020-01-15 06:42:09 +09001095 if self.timeout is not None and not self.timeout:
1096 raise ValueError('Non-blocking socket (timeout=0) is not supported')
1097
Guido van Rossumd8faa362007-04-27 19:54:29 +00001098 # Handle Unix-domain sockets.
1099 try:
1100 self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Ross3bf05322021-01-01 17:20:25 +00001101 if self.timeout is not socket._GLOBAL_DEFAULT_TIMEOUT:
1102 self.sock.settimeout(self.timeout)
Antoine Pitrouf068ab82011-06-06 19:17:09 +02001103 self.file = None
Guido van Rossumd8faa362007-04-27 19:54:29 +00001104 self.sock.connect(host)
Andrew Svetlov2ade6f22012-12-17 18:57:16 +02001105 except OSError:
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +00001106 if self.debuglevel > 0:
R David Murray0c49b892015-04-16 17:14:42 -04001107 self._print_debug('connect fail:', host)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001108 if self.sock:
1109 self.sock.close()
1110 self.sock = None
Andrew Svetlovb6693c42012-12-17 18:54:53 +02001111 raise
Guido van Rossumd8faa362007-04-27 19:54:29 +00001112 (code, msg) = self.getreply()
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +00001113 if self.debuglevel > 0:
R David Murray0c49b892015-04-16 17:14:42 -04001114 self._print_debug('connect:', msg)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001115 return (code, msg)
1116
1117
Guido van Rossum95e6f701998-06-25 02:15:50 +00001118# Test the sendmail method, which tests most of the others.
1119# Note: This always sends to localhost.
1120if __name__ == '__main__':
Guido van Rossum95e6f701998-06-25 02:15:50 +00001121 def prompt(prompt):
1122 sys.stdout.write(prompt + ": ")
Ezio Melotti6bfecd12011-10-18 13:20:07 +03001123 sys.stdout.flush()
Eric S. Raymondc013f302001-02-09 05:40:38 +00001124 return sys.stdin.readline().strip()
Guido van Rossum95e6f701998-06-25 02:15:50 +00001125
1126 fromaddr = prompt("From")
Giampaolo Rodolàbd258bd2011-02-22 15:56:20 +00001127 toaddrs = prompt("To").split(',')
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001128 print("Enter message, end with ^D:")
Guido van Rossum95e6f701998-06-25 02:15:50 +00001129 msg = ''
1130 while 1:
1131 line = sys.stdin.readline()
1132 if not line:
1133 break
1134 msg = msg + line
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001135 print("Message length is %d" % len(msg))
Guido van Rossum95e6f701998-06-25 02:15:50 +00001136
1137 server = SMTP('localhost')
1138 server.set_debuglevel(1)
1139 server.sendmail(fromaddr, toaddrs, msg)
1140 server.quit()