xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/smtplib.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker#! /usr/bin/env python3
2*cda5da8dSAndroid Build Coastguard Worker
3*cda5da8dSAndroid Build Coastguard Worker'''SMTP/ESMTP client class.
4*cda5da8dSAndroid Build Coastguard Worker
5*cda5da8dSAndroid Build Coastguard WorkerThis should follow RFC 821 (SMTP), RFC 1869 (ESMTP), RFC 2554 (SMTP
6*cda5da8dSAndroid Build Coastguard WorkerAuthentication) and RFC 2487 (Secure SMTP over TLS).
7*cda5da8dSAndroid Build Coastguard Worker
8*cda5da8dSAndroid Build Coastguard WorkerNotes:
9*cda5da8dSAndroid Build Coastguard Worker
10*cda5da8dSAndroid Build Coastguard WorkerPlease remember, when doing ESMTP, that the names of the SMTP service
11*cda5da8dSAndroid Build Coastguard Workerextensions are NOT the same thing as the option keywords for the RCPT
12*cda5da8dSAndroid Build Coastguard Workerand MAIL commands!
13*cda5da8dSAndroid Build Coastguard Worker
14*cda5da8dSAndroid Build Coastguard WorkerExample:
15*cda5da8dSAndroid Build Coastguard Worker
16*cda5da8dSAndroid Build Coastguard Worker  >>> import smtplib
17*cda5da8dSAndroid Build Coastguard Worker  >>> s=smtplib.SMTP("localhost")
18*cda5da8dSAndroid Build Coastguard Worker  >>> print(s.help())
19*cda5da8dSAndroid Build Coastguard Worker  This is Sendmail version 8.8.4
20*cda5da8dSAndroid Build Coastguard Worker  Topics:
21*cda5da8dSAndroid Build Coastguard Worker      HELO    EHLO    MAIL    RCPT    DATA
22*cda5da8dSAndroid Build Coastguard Worker      RSET    NOOP    QUIT    HELP    VRFY
23*cda5da8dSAndroid Build Coastguard Worker      EXPN    VERB    ETRN    DSN
24*cda5da8dSAndroid Build Coastguard Worker  For more info use "HELP <topic>".
25*cda5da8dSAndroid Build Coastguard Worker  To report bugs in the implementation send email to
26*cda5da8dSAndroid Build Coastguard Worker      [email protected].
27*cda5da8dSAndroid Build Coastguard Worker  For local information send email to Postmaster at your site.
28*cda5da8dSAndroid Build Coastguard Worker  End of HELP info
29*cda5da8dSAndroid Build Coastguard Worker  >>> s.putcmd("vrfy","someone@here")
30*cda5da8dSAndroid Build Coastguard Worker  >>> s.getreply()
31*cda5da8dSAndroid Build Coastguard Worker  (250, "Somebody OverHere <[email protected]>")
32*cda5da8dSAndroid Build Coastguard Worker  >>> s.quit()
33*cda5da8dSAndroid Build Coastguard Worker'''
34*cda5da8dSAndroid Build Coastguard Worker
35*cda5da8dSAndroid Build Coastguard Worker# Author: The Dragon De Monsyne <[email protected]>
36*cda5da8dSAndroid Build Coastguard Worker# ESMTP support, test code and doc fixes added by
37*cda5da8dSAndroid Build Coastguard Worker#     Eric S. Raymond <[email protected]>
38*cda5da8dSAndroid Build Coastguard Worker# Better RFC 821 compliance (MAIL and RCPT, and CRLF in data)
39*cda5da8dSAndroid Build Coastguard Worker#     by Carey Evans <[email protected]>, for picky mail servers.
40*cda5da8dSAndroid Build Coastguard Worker# RFC 2554 (authentication) support by Gerhard Haering <[email protected]>.
41*cda5da8dSAndroid Build Coastguard Worker#
42*cda5da8dSAndroid Build Coastguard Worker# This was modified from the Python 1.5 library HTTP lib.
43*cda5da8dSAndroid Build Coastguard Worker
44*cda5da8dSAndroid Build Coastguard Workerimport socket
45*cda5da8dSAndroid Build Coastguard Workerimport io
46*cda5da8dSAndroid Build Coastguard Workerimport re
47*cda5da8dSAndroid Build Coastguard Workerimport email.utils
48*cda5da8dSAndroid Build Coastguard Workerimport email.message
49*cda5da8dSAndroid Build Coastguard Workerimport email.generator
50*cda5da8dSAndroid Build Coastguard Workerimport base64
51*cda5da8dSAndroid Build Coastguard Workerimport hmac
52*cda5da8dSAndroid Build Coastguard Workerimport copy
53*cda5da8dSAndroid Build Coastguard Workerimport datetime
54*cda5da8dSAndroid Build Coastguard Workerimport sys
55*cda5da8dSAndroid Build Coastguard Workerfrom email.base64mime import body_encode as encode_base64
56*cda5da8dSAndroid Build Coastguard Worker
57*cda5da8dSAndroid Build Coastguard Worker__all__ = ["SMTPException", "SMTPNotSupportedError", "SMTPServerDisconnected", "SMTPResponseException",
58*cda5da8dSAndroid Build Coastguard Worker           "SMTPSenderRefused", "SMTPRecipientsRefused", "SMTPDataError",
59*cda5da8dSAndroid Build Coastguard Worker           "SMTPConnectError", "SMTPHeloError", "SMTPAuthenticationError",
60*cda5da8dSAndroid Build Coastguard Worker           "quoteaddr", "quotedata", "SMTP"]
61*cda5da8dSAndroid Build Coastguard Worker
62*cda5da8dSAndroid Build Coastguard WorkerSMTP_PORT = 25
63*cda5da8dSAndroid Build Coastguard WorkerSMTP_SSL_PORT = 465
64*cda5da8dSAndroid Build Coastguard WorkerCRLF = "\r\n"
65*cda5da8dSAndroid Build Coastguard WorkerbCRLF = b"\r\n"
66*cda5da8dSAndroid Build Coastguard Worker_MAXLINE = 8192 # more than 8 times larger than RFC 821, 4.5.3
67*cda5da8dSAndroid Build Coastguard Worker_MAXCHALLENGE = 5  # Maximum number of AUTH challenges sent
68*cda5da8dSAndroid Build Coastguard Worker
69*cda5da8dSAndroid Build Coastguard WorkerOLDSTYLE_AUTH = re.compile(r"auth=(.*)", re.I)
70*cda5da8dSAndroid Build Coastguard Worker
71*cda5da8dSAndroid Build Coastguard Worker# Exception classes used by this module.
72*cda5da8dSAndroid Build Coastguard Workerclass SMTPException(OSError):
73*cda5da8dSAndroid Build Coastguard Worker    """Base class for all exceptions raised by this module."""
74*cda5da8dSAndroid Build Coastguard Worker
75*cda5da8dSAndroid Build Coastguard Workerclass SMTPNotSupportedError(SMTPException):
76*cda5da8dSAndroid Build Coastguard Worker    """The command or option is not supported by the SMTP server.
77*cda5da8dSAndroid Build Coastguard Worker
78*cda5da8dSAndroid Build Coastguard Worker    This exception is raised when an attempt is made to run a command or a
79*cda5da8dSAndroid Build Coastguard Worker    command with an option which is not supported by the server.
80*cda5da8dSAndroid Build Coastguard Worker    """
81*cda5da8dSAndroid Build Coastguard Worker
82*cda5da8dSAndroid Build Coastguard Workerclass SMTPServerDisconnected(SMTPException):
83*cda5da8dSAndroid Build Coastguard Worker    """Not connected to any SMTP server.
84*cda5da8dSAndroid Build Coastguard Worker
85*cda5da8dSAndroid Build Coastguard Worker    This exception is raised when the server unexpectedly disconnects,
86*cda5da8dSAndroid Build Coastguard Worker    or when an attempt is made to use the SMTP instance before
87*cda5da8dSAndroid Build Coastguard Worker    connecting it to a server.
88*cda5da8dSAndroid Build Coastguard Worker    """
89*cda5da8dSAndroid Build Coastguard Worker
90*cda5da8dSAndroid Build Coastguard Workerclass SMTPResponseException(SMTPException):
91*cda5da8dSAndroid Build Coastguard Worker    """Base class for all exceptions that include an SMTP error code.
92*cda5da8dSAndroid Build Coastguard Worker
93*cda5da8dSAndroid Build Coastguard Worker    These exceptions are generated in some instances when the SMTP
94*cda5da8dSAndroid Build Coastguard Worker    server returns an error code.  The error code is stored in the
95*cda5da8dSAndroid Build Coastguard Worker    `smtp_code' attribute of the error, and the `smtp_error' attribute
96*cda5da8dSAndroid Build Coastguard Worker    is set to the error message.
97*cda5da8dSAndroid Build Coastguard Worker    """
98*cda5da8dSAndroid Build Coastguard Worker
99*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, code, msg):
100*cda5da8dSAndroid Build Coastguard Worker        self.smtp_code = code
101*cda5da8dSAndroid Build Coastguard Worker        self.smtp_error = msg
102*cda5da8dSAndroid Build Coastguard Worker        self.args = (code, msg)
103*cda5da8dSAndroid Build Coastguard Worker
104*cda5da8dSAndroid Build Coastguard Workerclass SMTPSenderRefused(SMTPResponseException):
105*cda5da8dSAndroid Build Coastguard Worker    """Sender address refused.
106*cda5da8dSAndroid Build Coastguard Worker
107*cda5da8dSAndroid Build Coastguard Worker    In addition to the attributes set by on all SMTPResponseException
108*cda5da8dSAndroid Build Coastguard Worker    exceptions, this sets `sender' to the string that the SMTP refused.
109*cda5da8dSAndroid Build Coastguard Worker    """
110*cda5da8dSAndroid Build Coastguard Worker
111*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, code, msg, sender):
112*cda5da8dSAndroid Build Coastguard Worker        self.smtp_code = code
113*cda5da8dSAndroid Build Coastguard Worker        self.smtp_error = msg
114*cda5da8dSAndroid Build Coastguard Worker        self.sender = sender
115*cda5da8dSAndroid Build Coastguard Worker        self.args = (code, msg, sender)
116*cda5da8dSAndroid Build Coastguard Worker
117*cda5da8dSAndroid Build Coastguard Workerclass SMTPRecipientsRefused(SMTPException):
118*cda5da8dSAndroid Build Coastguard Worker    """All recipient addresses refused.
119*cda5da8dSAndroid Build Coastguard Worker
120*cda5da8dSAndroid Build Coastguard Worker    The errors for each recipient are accessible through the attribute
121*cda5da8dSAndroid Build Coastguard Worker    'recipients', which is a dictionary of exactly the same sort as
122*cda5da8dSAndroid Build Coastguard Worker    SMTP.sendmail() returns.
123*cda5da8dSAndroid Build Coastguard Worker    """
124*cda5da8dSAndroid Build Coastguard Worker
125*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, recipients):
126*cda5da8dSAndroid Build Coastguard Worker        self.recipients = recipients
127*cda5da8dSAndroid Build Coastguard Worker        self.args = (recipients,)
128*cda5da8dSAndroid Build Coastguard Worker
129*cda5da8dSAndroid Build Coastguard Worker
130*cda5da8dSAndroid Build Coastguard Workerclass SMTPDataError(SMTPResponseException):
131*cda5da8dSAndroid Build Coastguard Worker    """The SMTP server didn't accept the data."""
132*cda5da8dSAndroid Build Coastguard Worker
133*cda5da8dSAndroid Build Coastguard Workerclass SMTPConnectError(SMTPResponseException):
134*cda5da8dSAndroid Build Coastguard Worker    """Error during connection establishment."""
135*cda5da8dSAndroid Build Coastguard Worker
136*cda5da8dSAndroid Build Coastguard Workerclass SMTPHeloError(SMTPResponseException):
137*cda5da8dSAndroid Build Coastguard Worker    """The server refused our HELO reply."""
138*cda5da8dSAndroid Build Coastguard Worker
139*cda5da8dSAndroid Build Coastguard Workerclass SMTPAuthenticationError(SMTPResponseException):
140*cda5da8dSAndroid Build Coastguard Worker    """Authentication error.
141*cda5da8dSAndroid Build Coastguard Worker
142*cda5da8dSAndroid Build Coastguard Worker    Most probably the server didn't accept the username/password
143*cda5da8dSAndroid Build Coastguard Worker    combination provided.
144*cda5da8dSAndroid Build Coastguard Worker    """
145*cda5da8dSAndroid Build Coastguard Worker
146*cda5da8dSAndroid Build Coastguard Workerdef quoteaddr(addrstring):
147*cda5da8dSAndroid Build Coastguard Worker    """Quote a subset of the email addresses defined by RFC 821.
148*cda5da8dSAndroid Build Coastguard Worker
149*cda5da8dSAndroid Build Coastguard Worker    Should be able to handle anything email.utils.parseaddr can handle.
150*cda5da8dSAndroid Build Coastguard Worker    """
151*cda5da8dSAndroid Build Coastguard Worker    displayname, addr = email.utils.parseaddr(addrstring)
152*cda5da8dSAndroid Build Coastguard Worker    if (displayname, addr) == ('', ''):
153*cda5da8dSAndroid Build Coastguard Worker        # parseaddr couldn't parse it, use it as is and hope for the best.
154*cda5da8dSAndroid Build Coastguard Worker        if addrstring.strip().startswith('<'):
155*cda5da8dSAndroid Build Coastguard Worker            return addrstring
156*cda5da8dSAndroid Build Coastguard Worker        return "<%s>" % addrstring
157*cda5da8dSAndroid Build Coastguard Worker    return "<%s>" % addr
158*cda5da8dSAndroid Build Coastguard Worker
159*cda5da8dSAndroid Build Coastguard Workerdef _addr_only(addrstring):
160*cda5da8dSAndroid Build Coastguard Worker    displayname, addr = email.utils.parseaddr(addrstring)
161*cda5da8dSAndroid Build Coastguard Worker    if (displayname, addr) == ('', ''):
162*cda5da8dSAndroid Build Coastguard Worker        # parseaddr couldn't parse it, so use it as is.
163*cda5da8dSAndroid Build Coastguard Worker        return addrstring
164*cda5da8dSAndroid Build Coastguard Worker    return addr
165*cda5da8dSAndroid Build Coastguard Worker
166*cda5da8dSAndroid Build Coastguard Worker# Legacy method kept for backward compatibility.
167*cda5da8dSAndroid Build Coastguard Workerdef quotedata(data):
168*cda5da8dSAndroid Build Coastguard Worker    """Quote data for email.
169*cda5da8dSAndroid Build Coastguard Worker
170*cda5da8dSAndroid Build Coastguard Worker    Double leading '.', and change Unix newline '\\n', or Mac '\\r' into
171*cda5da8dSAndroid Build Coastguard Worker    internet CRLF end-of-line.
172*cda5da8dSAndroid Build Coastguard Worker    """
173*cda5da8dSAndroid Build Coastguard Worker    return re.sub(r'(?m)^\.', '..',
174*cda5da8dSAndroid Build Coastguard Worker        re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data))
175*cda5da8dSAndroid Build Coastguard Worker
176*cda5da8dSAndroid Build Coastguard Workerdef _quote_periods(bindata):
177*cda5da8dSAndroid Build Coastguard Worker    return re.sub(br'(?m)^\.', b'..', bindata)
178*cda5da8dSAndroid Build Coastguard Worker
179*cda5da8dSAndroid Build Coastguard Workerdef _fix_eols(data):
180*cda5da8dSAndroid Build Coastguard Worker    return  re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data)
181*cda5da8dSAndroid Build Coastguard Worker
182*cda5da8dSAndroid Build Coastguard Workertry:
183*cda5da8dSAndroid Build Coastguard Worker    import ssl
184*cda5da8dSAndroid Build Coastguard Workerexcept ImportError:
185*cda5da8dSAndroid Build Coastguard Worker    _have_ssl = False
186*cda5da8dSAndroid Build Coastguard Workerelse:
187*cda5da8dSAndroid Build Coastguard Worker    _have_ssl = True
188*cda5da8dSAndroid Build Coastguard Worker
189*cda5da8dSAndroid Build Coastguard Worker
190*cda5da8dSAndroid Build Coastguard Workerclass SMTP:
191*cda5da8dSAndroid Build Coastguard Worker    """This class manages a connection to an SMTP or ESMTP server.
192*cda5da8dSAndroid Build Coastguard Worker    SMTP Objects:
193*cda5da8dSAndroid Build Coastguard Worker        SMTP objects have the following attributes:
194*cda5da8dSAndroid Build Coastguard Worker            helo_resp
195*cda5da8dSAndroid Build Coastguard Worker                This is the message given by the server in response to the
196*cda5da8dSAndroid Build Coastguard Worker                most recent HELO command.
197*cda5da8dSAndroid Build Coastguard Worker
198*cda5da8dSAndroid Build Coastguard Worker            ehlo_resp
199*cda5da8dSAndroid Build Coastguard Worker                This is the message given by the server in response to the
200*cda5da8dSAndroid Build Coastguard Worker                most recent EHLO command. This is usually multiline.
201*cda5da8dSAndroid Build Coastguard Worker
202*cda5da8dSAndroid Build Coastguard Worker            does_esmtp
203*cda5da8dSAndroid Build Coastguard Worker                This is a True value _after you do an EHLO command_, if the
204*cda5da8dSAndroid Build Coastguard Worker                server supports ESMTP.
205*cda5da8dSAndroid Build Coastguard Worker
206*cda5da8dSAndroid Build Coastguard Worker            esmtp_features
207*cda5da8dSAndroid Build Coastguard Worker                This is a dictionary, which, if the server supports ESMTP,
208*cda5da8dSAndroid Build Coastguard Worker                will _after you do an EHLO command_, contain the names of the
209*cda5da8dSAndroid Build Coastguard Worker                SMTP service extensions this server supports, and their
210*cda5da8dSAndroid Build Coastguard Worker                parameters (if any).
211*cda5da8dSAndroid Build Coastguard Worker
212*cda5da8dSAndroid Build Coastguard Worker                Note, all extension names are mapped to lower case in the
213*cda5da8dSAndroid Build Coastguard Worker                dictionary.
214*cda5da8dSAndroid Build Coastguard Worker
215*cda5da8dSAndroid Build Coastguard Worker        See each method's docstrings for details.  In general, there is a
216*cda5da8dSAndroid Build Coastguard Worker        method of the same name to perform each SMTP command.  There is also a
217*cda5da8dSAndroid Build Coastguard Worker        method called 'sendmail' that will do an entire mail transaction.
218*cda5da8dSAndroid Build Coastguard Worker        """
219*cda5da8dSAndroid Build Coastguard Worker    debuglevel = 0
220*cda5da8dSAndroid Build Coastguard Worker
221*cda5da8dSAndroid Build Coastguard Worker    sock = None
222*cda5da8dSAndroid Build Coastguard Worker    file = None
223*cda5da8dSAndroid Build Coastguard Worker    helo_resp = None
224*cda5da8dSAndroid Build Coastguard Worker    ehlo_msg = "ehlo"
225*cda5da8dSAndroid Build Coastguard Worker    ehlo_resp = None
226*cda5da8dSAndroid Build Coastguard Worker    does_esmtp = False
227*cda5da8dSAndroid Build Coastguard Worker    default_port = SMTP_PORT
228*cda5da8dSAndroid Build Coastguard Worker
229*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, host='', port=0, local_hostname=None,
230*cda5da8dSAndroid Build Coastguard Worker                 timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
231*cda5da8dSAndroid Build Coastguard Worker                 source_address=None):
232*cda5da8dSAndroid Build Coastguard Worker        """Initialize a new instance.
233*cda5da8dSAndroid Build Coastguard Worker
234*cda5da8dSAndroid Build Coastguard Worker        If specified, `host` is the name of the remote host to which to
235*cda5da8dSAndroid Build Coastguard Worker        connect.  If specified, `port` specifies the port to which to connect.
236*cda5da8dSAndroid Build Coastguard Worker        By default, smtplib.SMTP_PORT is used.  If a host is specified the
237*cda5da8dSAndroid Build Coastguard Worker        connect method is called, and if it returns anything other than a
238*cda5da8dSAndroid Build Coastguard Worker        success code an SMTPConnectError is raised.  If specified,
239*cda5da8dSAndroid Build Coastguard Worker        `local_hostname` is used as the FQDN of the local host in the HELO/EHLO
240*cda5da8dSAndroid Build Coastguard Worker        command.  Otherwise, the local hostname is found using
241*cda5da8dSAndroid Build Coastguard Worker        socket.getfqdn(). The `source_address` parameter takes a 2-tuple (host,
242*cda5da8dSAndroid Build Coastguard Worker        port) for the socket to bind to as its source address before
243*cda5da8dSAndroid Build Coastguard Worker        connecting. If the host is '' and port is 0, the OS default behavior
244*cda5da8dSAndroid Build Coastguard Worker        will be used.
245*cda5da8dSAndroid Build Coastguard Worker
246*cda5da8dSAndroid Build Coastguard Worker        """
247*cda5da8dSAndroid Build Coastguard Worker        self._host = host
248*cda5da8dSAndroid Build Coastguard Worker        self.timeout = timeout
249*cda5da8dSAndroid Build Coastguard Worker        self.esmtp_features = {}
250*cda5da8dSAndroid Build Coastguard Worker        self.command_encoding = 'ascii'
251*cda5da8dSAndroid Build Coastguard Worker        self.source_address = source_address
252*cda5da8dSAndroid Build Coastguard Worker        self._auth_challenge_count = 0
253*cda5da8dSAndroid Build Coastguard Worker
254*cda5da8dSAndroid Build Coastguard Worker        if host:
255*cda5da8dSAndroid Build Coastguard Worker            (code, msg) = self.connect(host, port)
256*cda5da8dSAndroid Build Coastguard Worker            if code != 220:
257*cda5da8dSAndroid Build Coastguard Worker                self.close()
258*cda5da8dSAndroid Build Coastguard Worker                raise SMTPConnectError(code, msg)
259*cda5da8dSAndroid Build Coastguard Worker        if local_hostname is not None:
260*cda5da8dSAndroid Build Coastguard Worker            self.local_hostname = local_hostname
261*cda5da8dSAndroid Build Coastguard Worker        else:
262*cda5da8dSAndroid Build Coastguard Worker            # RFC 2821 says we should use the fqdn in the EHLO/HELO verb, and
263*cda5da8dSAndroid Build Coastguard Worker            # if that can't be calculated, that we should use a domain literal
264*cda5da8dSAndroid Build Coastguard Worker            # instead (essentially an encoded IP address like [A.B.C.D]).
265*cda5da8dSAndroid Build Coastguard Worker            fqdn = socket.getfqdn()
266*cda5da8dSAndroid Build Coastguard Worker            if '.' in fqdn:
267*cda5da8dSAndroid Build Coastguard Worker                self.local_hostname = fqdn
268*cda5da8dSAndroid Build Coastguard Worker            else:
269*cda5da8dSAndroid Build Coastguard Worker                # We can't find an fqdn hostname, so use a domain literal
270*cda5da8dSAndroid Build Coastguard Worker                addr = '127.0.0.1'
271*cda5da8dSAndroid Build Coastguard Worker                try:
272*cda5da8dSAndroid Build Coastguard Worker                    addr = socket.gethostbyname(socket.gethostname())
273*cda5da8dSAndroid Build Coastguard Worker                except socket.gaierror:
274*cda5da8dSAndroid Build Coastguard Worker                    pass
275*cda5da8dSAndroid Build Coastguard Worker                self.local_hostname = '[%s]' % addr
276*cda5da8dSAndroid Build Coastguard Worker
277*cda5da8dSAndroid Build Coastguard Worker    def __enter__(self):
278*cda5da8dSAndroid Build Coastguard Worker        return self
279*cda5da8dSAndroid Build Coastguard Worker
280*cda5da8dSAndroid Build Coastguard Worker    def __exit__(self, *args):
281*cda5da8dSAndroid Build Coastguard Worker        try:
282*cda5da8dSAndroid Build Coastguard Worker            code, message = self.docmd("QUIT")
283*cda5da8dSAndroid Build Coastguard Worker            if code != 221:
284*cda5da8dSAndroid Build Coastguard Worker                raise SMTPResponseException(code, message)
285*cda5da8dSAndroid Build Coastguard Worker        except SMTPServerDisconnected:
286*cda5da8dSAndroid Build Coastguard Worker            pass
287*cda5da8dSAndroid Build Coastguard Worker        finally:
288*cda5da8dSAndroid Build Coastguard Worker            self.close()
289*cda5da8dSAndroid Build Coastguard Worker
290*cda5da8dSAndroid Build Coastguard Worker    def set_debuglevel(self, debuglevel):
291*cda5da8dSAndroid Build Coastguard Worker        """Set the debug output level.
292*cda5da8dSAndroid Build Coastguard Worker
293*cda5da8dSAndroid Build Coastguard Worker        A non-false value results in debug messages for connection and for all
294*cda5da8dSAndroid Build Coastguard Worker        messages sent to and received from the server.
295*cda5da8dSAndroid Build Coastguard Worker
296*cda5da8dSAndroid Build Coastguard Worker        """
297*cda5da8dSAndroid Build Coastguard Worker        self.debuglevel = debuglevel
298*cda5da8dSAndroid Build Coastguard Worker
299*cda5da8dSAndroid Build Coastguard Worker    def _print_debug(self, *args):
300*cda5da8dSAndroid Build Coastguard Worker        if self.debuglevel > 1:
301*cda5da8dSAndroid Build Coastguard Worker            print(datetime.datetime.now().time(), *args, file=sys.stderr)
302*cda5da8dSAndroid Build Coastguard Worker        else:
303*cda5da8dSAndroid Build Coastguard Worker            print(*args, file=sys.stderr)
304*cda5da8dSAndroid Build Coastguard Worker
305*cda5da8dSAndroid Build Coastguard Worker    def _get_socket(self, host, port, timeout):
306*cda5da8dSAndroid Build Coastguard Worker        # This makes it simpler for SMTP_SSL to use the SMTP connect code
307*cda5da8dSAndroid Build Coastguard Worker        # and just alter the socket connection bit.
308*cda5da8dSAndroid Build Coastguard Worker        if timeout is not None and not timeout:
309*cda5da8dSAndroid Build Coastguard Worker            raise ValueError('Non-blocking socket (timeout=0) is not supported')
310*cda5da8dSAndroid Build Coastguard Worker        if self.debuglevel > 0:
311*cda5da8dSAndroid Build Coastguard Worker            self._print_debug('connect: to', (host, port), self.source_address)
312*cda5da8dSAndroid Build Coastguard Worker        return socket.create_connection((host, port), timeout,
313*cda5da8dSAndroid Build Coastguard Worker                                        self.source_address)
314*cda5da8dSAndroid Build Coastguard Worker
315*cda5da8dSAndroid Build Coastguard Worker    def connect(self, host='localhost', port=0, source_address=None):
316*cda5da8dSAndroid Build Coastguard Worker        """Connect to a host on a given port.
317*cda5da8dSAndroid Build Coastguard Worker
318*cda5da8dSAndroid Build Coastguard Worker        If the hostname ends with a colon (`:') followed by a number, and
319*cda5da8dSAndroid Build Coastguard Worker        there is no port specified, that suffix will be stripped off and the
320*cda5da8dSAndroid Build Coastguard Worker        number interpreted as the port number to use.
321*cda5da8dSAndroid Build Coastguard Worker
322*cda5da8dSAndroid Build Coastguard Worker        Note: This method is automatically invoked by __init__, if a host is
323*cda5da8dSAndroid Build Coastguard Worker        specified during instantiation.
324*cda5da8dSAndroid Build Coastguard Worker
325*cda5da8dSAndroid Build Coastguard Worker        """
326*cda5da8dSAndroid Build Coastguard Worker
327*cda5da8dSAndroid Build Coastguard Worker        if source_address:
328*cda5da8dSAndroid Build Coastguard Worker            self.source_address = source_address
329*cda5da8dSAndroid Build Coastguard Worker
330*cda5da8dSAndroid Build Coastguard Worker        if not port and (host.find(':') == host.rfind(':')):
331*cda5da8dSAndroid Build Coastguard Worker            i = host.rfind(':')
332*cda5da8dSAndroid Build Coastguard Worker            if i >= 0:
333*cda5da8dSAndroid Build Coastguard Worker                host, port = host[:i], host[i + 1:]
334*cda5da8dSAndroid Build Coastguard Worker                try:
335*cda5da8dSAndroid Build Coastguard Worker                    port = int(port)
336*cda5da8dSAndroid Build Coastguard Worker                except ValueError:
337*cda5da8dSAndroid Build Coastguard Worker                    raise OSError("nonnumeric port")
338*cda5da8dSAndroid Build Coastguard Worker        if not port:
339*cda5da8dSAndroid Build Coastguard Worker            port = self.default_port
340*cda5da8dSAndroid Build Coastguard Worker        sys.audit("smtplib.connect", self, host, port)
341*cda5da8dSAndroid Build Coastguard Worker        self.sock = self._get_socket(host, port, self.timeout)
342*cda5da8dSAndroid Build Coastguard Worker        self.file = None
343*cda5da8dSAndroid Build Coastguard Worker        (code, msg) = self.getreply()
344*cda5da8dSAndroid Build Coastguard Worker        if self.debuglevel > 0:
345*cda5da8dSAndroid Build Coastguard Worker            self._print_debug('connect:', repr(msg))
346*cda5da8dSAndroid Build Coastguard Worker        return (code, msg)
347*cda5da8dSAndroid Build Coastguard Worker
348*cda5da8dSAndroid Build Coastguard Worker    def send(self, s):
349*cda5da8dSAndroid Build Coastguard Worker        """Send `s' to the server."""
350*cda5da8dSAndroid Build Coastguard Worker        if self.debuglevel > 0:
351*cda5da8dSAndroid Build Coastguard Worker            self._print_debug('send:', repr(s))
352*cda5da8dSAndroid Build Coastguard Worker        if self.sock:
353*cda5da8dSAndroid Build Coastguard Worker            if isinstance(s, str):
354*cda5da8dSAndroid Build Coastguard Worker                # send is used by the 'data' command, where command_encoding
355*cda5da8dSAndroid Build Coastguard Worker                # should not be used, but 'data' needs to convert the string to
356*cda5da8dSAndroid Build Coastguard Worker                # binary itself anyway, so that's not a problem.
357*cda5da8dSAndroid Build Coastguard Worker                s = s.encode(self.command_encoding)
358*cda5da8dSAndroid Build Coastguard Worker            sys.audit("smtplib.send", self, s)
359*cda5da8dSAndroid Build Coastguard Worker            try:
360*cda5da8dSAndroid Build Coastguard Worker                self.sock.sendall(s)
361*cda5da8dSAndroid Build Coastguard Worker            except OSError:
362*cda5da8dSAndroid Build Coastguard Worker                self.close()
363*cda5da8dSAndroid Build Coastguard Worker                raise SMTPServerDisconnected('Server not connected')
364*cda5da8dSAndroid Build Coastguard Worker        else:
365*cda5da8dSAndroid Build Coastguard Worker            raise SMTPServerDisconnected('please run connect() first')
366*cda5da8dSAndroid Build Coastguard Worker
367*cda5da8dSAndroid Build Coastguard Worker    def putcmd(self, cmd, args=""):
368*cda5da8dSAndroid Build Coastguard Worker        """Send a command to the server."""
369*cda5da8dSAndroid Build Coastguard Worker        if args == "":
370*cda5da8dSAndroid Build Coastguard Worker            s = cmd
371*cda5da8dSAndroid Build Coastguard Worker        else:
372*cda5da8dSAndroid Build Coastguard Worker            s = f'{cmd} {args}'
373*cda5da8dSAndroid Build Coastguard Worker        if '\r' in s or '\n' in s:
374*cda5da8dSAndroid Build Coastguard Worker            s = s.replace('\n', '\\n').replace('\r', '\\r')
375*cda5da8dSAndroid Build Coastguard Worker            raise ValueError(
376*cda5da8dSAndroid Build Coastguard Worker                f'command and arguments contain prohibited newline characters: {s}'
377*cda5da8dSAndroid Build Coastguard Worker            )
378*cda5da8dSAndroid Build Coastguard Worker        self.send(f'{s}{CRLF}')
379*cda5da8dSAndroid Build Coastguard Worker
380*cda5da8dSAndroid Build Coastguard Worker    def getreply(self):
381*cda5da8dSAndroid Build Coastguard Worker        """Get a reply from the server.
382*cda5da8dSAndroid Build Coastguard Worker
383*cda5da8dSAndroid Build Coastguard Worker        Returns a tuple consisting of:
384*cda5da8dSAndroid Build Coastguard Worker
385*cda5da8dSAndroid Build Coastguard Worker          - server response code (e.g. '250', or such, if all goes well)
386*cda5da8dSAndroid Build Coastguard Worker            Note: returns -1 if it can't read response code.
387*cda5da8dSAndroid Build Coastguard Worker
388*cda5da8dSAndroid Build Coastguard Worker          - server response string corresponding to response code (multiline
389*cda5da8dSAndroid Build Coastguard Worker            responses are converted to a single, multiline string).
390*cda5da8dSAndroid Build Coastguard Worker
391*cda5da8dSAndroid Build Coastguard Worker        Raises SMTPServerDisconnected if end-of-file is reached.
392*cda5da8dSAndroid Build Coastguard Worker        """
393*cda5da8dSAndroid Build Coastguard Worker        resp = []
394*cda5da8dSAndroid Build Coastguard Worker        if self.file is None:
395*cda5da8dSAndroid Build Coastguard Worker            self.file = self.sock.makefile('rb')
396*cda5da8dSAndroid Build Coastguard Worker        while 1:
397*cda5da8dSAndroid Build Coastguard Worker            try:
398*cda5da8dSAndroid Build Coastguard Worker                line = self.file.readline(_MAXLINE + 1)
399*cda5da8dSAndroid Build Coastguard Worker            except OSError as e:
400*cda5da8dSAndroid Build Coastguard Worker                self.close()
401*cda5da8dSAndroid Build Coastguard Worker                raise SMTPServerDisconnected("Connection unexpectedly closed: "
402*cda5da8dSAndroid Build Coastguard Worker                                             + str(e))
403*cda5da8dSAndroid Build Coastguard Worker            if not line:
404*cda5da8dSAndroid Build Coastguard Worker                self.close()
405*cda5da8dSAndroid Build Coastguard Worker                raise SMTPServerDisconnected("Connection unexpectedly closed")
406*cda5da8dSAndroid Build Coastguard Worker            if self.debuglevel > 0:
407*cda5da8dSAndroid Build Coastguard Worker                self._print_debug('reply:', repr(line))
408*cda5da8dSAndroid Build Coastguard Worker            if len(line) > _MAXLINE:
409*cda5da8dSAndroid Build Coastguard Worker                self.close()
410*cda5da8dSAndroid Build Coastguard Worker                raise SMTPResponseException(500, "Line too long.")
411*cda5da8dSAndroid Build Coastguard Worker            resp.append(line[4:].strip(b' \t\r\n'))
412*cda5da8dSAndroid Build Coastguard Worker            code = line[:3]
413*cda5da8dSAndroid Build Coastguard Worker            # Check that the error code is syntactically correct.
414*cda5da8dSAndroid Build Coastguard Worker            # Don't attempt to read a continuation line if it is broken.
415*cda5da8dSAndroid Build Coastguard Worker            try:
416*cda5da8dSAndroid Build Coastguard Worker                errcode = int(code)
417*cda5da8dSAndroid Build Coastguard Worker            except ValueError:
418*cda5da8dSAndroid Build Coastguard Worker                errcode = -1
419*cda5da8dSAndroid Build Coastguard Worker                break
420*cda5da8dSAndroid Build Coastguard Worker            # Check if multiline response.
421*cda5da8dSAndroid Build Coastguard Worker            if line[3:4] != b"-":
422*cda5da8dSAndroid Build Coastguard Worker                break
423*cda5da8dSAndroid Build Coastguard Worker
424*cda5da8dSAndroid Build Coastguard Worker        errmsg = b"\n".join(resp)
425*cda5da8dSAndroid Build Coastguard Worker        if self.debuglevel > 0:
426*cda5da8dSAndroid Build Coastguard Worker            self._print_debug('reply: retcode (%s); Msg: %a' % (errcode, errmsg))
427*cda5da8dSAndroid Build Coastguard Worker        return errcode, errmsg
428*cda5da8dSAndroid Build Coastguard Worker
429*cda5da8dSAndroid Build Coastguard Worker    def docmd(self, cmd, args=""):
430*cda5da8dSAndroid Build Coastguard Worker        """Send a command, and return its response code."""
431*cda5da8dSAndroid Build Coastguard Worker        self.putcmd(cmd, args)
432*cda5da8dSAndroid Build Coastguard Worker        return self.getreply()
433*cda5da8dSAndroid Build Coastguard Worker
434*cda5da8dSAndroid Build Coastguard Worker    # std smtp commands
435*cda5da8dSAndroid Build Coastguard Worker    def helo(self, name=''):
436*cda5da8dSAndroid Build Coastguard Worker        """SMTP 'helo' command.
437*cda5da8dSAndroid Build Coastguard Worker        Hostname to send for this command defaults to the FQDN of the local
438*cda5da8dSAndroid Build Coastguard Worker        host.
439*cda5da8dSAndroid Build Coastguard Worker        """
440*cda5da8dSAndroid Build Coastguard Worker        self.putcmd("helo", name or self.local_hostname)
441*cda5da8dSAndroid Build Coastguard Worker        (code, msg) = self.getreply()
442*cda5da8dSAndroid Build Coastguard Worker        self.helo_resp = msg
443*cda5da8dSAndroid Build Coastguard Worker        return (code, msg)
444*cda5da8dSAndroid Build Coastguard Worker
445*cda5da8dSAndroid Build Coastguard Worker    def ehlo(self, name=''):
446*cda5da8dSAndroid Build Coastguard Worker        """ SMTP 'ehlo' command.
447*cda5da8dSAndroid Build Coastguard Worker        Hostname to send for this command defaults to the FQDN of the local
448*cda5da8dSAndroid Build Coastguard Worker        host.
449*cda5da8dSAndroid Build Coastguard Worker        """
450*cda5da8dSAndroid Build Coastguard Worker        self.esmtp_features = {}
451*cda5da8dSAndroid Build Coastguard Worker        self.putcmd(self.ehlo_msg, name or self.local_hostname)
452*cda5da8dSAndroid Build Coastguard Worker        (code, msg) = self.getreply()
453*cda5da8dSAndroid Build Coastguard Worker        # According to RFC1869 some (badly written)
454*cda5da8dSAndroid Build Coastguard Worker        # MTA's will disconnect on an ehlo. Toss an exception if
455*cda5da8dSAndroid Build Coastguard Worker        # that happens -ddm
456*cda5da8dSAndroid Build Coastguard Worker        if code == -1 and len(msg) == 0:
457*cda5da8dSAndroid Build Coastguard Worker            self.close()
458*cda5da8dSAndroid Build Coastguard Worker            raise SMTPServerDisconnected("Server not connected")
459*cda5da8dSAndroid Build Coastguard Worker        self.ehlo_resp = msg
460*cda5da8dSAndroid Build Coastguard Worker        if code != 250:
461*cda5da8dSAndroid Build Coastguard Worker            return (code, msg)
462*cda5da8dSAndroid Build Coastguard Worker        self.does_esmtp = True
463*cda5da8dSAndroid Build Coastguard Worker        #parse the ehlo response -ddm
464*cda5da8dSAndroid Build Coastguard Worker        assert isinstance(self.ehlo_resp, bytes), repr(self.ehlo_resp)
465*cda5da8dSAndroid Build Coastguard Worker        resp = self.ehlo_resp.decode("latin-1").split('\n')
466*cda5da8dSAndroid Build Coastguard Worker        del resp[0]
467*cda5da8dSAndroid Build Coastguard Worker        for each in resp:
468*cda5da8dSAndroid Build Coastguard Worker            # To be able to communicate with as many SMTP servers as possible,
469*cda5da8dSAndroid Build Coastguard Worker            # we have to take the old-style auth advertisement into account,
470*cda5da8dSAndroid Build Coastguard Worker            # because:
471*cda5da8dSAndroid Build Coastguard Worker            # 1) Else our SMTP feature parser gets confused.
472*cda5da8dSAndroid Build Coastguard Worker            # 2) There are some servers that only advertise the auth methods we
473*cda5da8dSAndroid Build Coastguard Worker            #    support using the old style.
474*cda5da8dSAndroid Build Coastguard Worker            auth_match = OLDSTYLE_AUTH.match(each)
475*cda5da8dSAndroid Build Coastguard Worker            if auth_match:
476*cda5da8dSAndroid Build Coastguard Worker                # This doesn't remove duplicates, but that's no problem
477*cda5da8dSAndroid Build Coastguard Worker                self.esmtp_features["auth"] = self.esmtp_features.get("auth", "") \
478*cda5da8dSAndroid Build Coastguard Worker                        + " " + auth_match.groups(0)[0]
479*cda5da8dSAndroid Build Coastguard Worker                continue
480*cda5da8dSAndroid Build Coastguard Worker
481*cda5da8dSAndroid Build Coastguard Worker            # RFC 1869 requires a space between ehlo keyword and parameters.
482*cda5da8dSAndroid Build Coastguard Worker            # It's actually stricter, in that only spaces are allowed between
483*cda5da8dSAndroid Build Coastguard Worker            # parameters, but were not going to check for that here.  Note
484*cda5da8dSAndroid Build Coastguard Worker            # that the space isn't present if there are no parameters.
485*cda5da8dSAndroid Build Coastguard Worker            m = re.match(r'(?P<feature>[A-Za-z0-9][A-Za-z0-9\-]*) ?', each)
486*cda5da8dSAndroid Build Coastguard Worker            if m:
487*cda5da8dSAndroid Build Coastguard Worker                feature = m.group("feature").lower()
488*cda5da8dSAndroid Build Coastguard Worker                params = m.string[m.end("feature"):].strip()
489*cda5da8dSAndroid Build Coastguard Worker                if feature == "auth":
490*cda5da8dSAndroid Build Coastguard Worker                    self.esmtp_features[feature] = self.esmtp_features.get(feature, "") \
491*cda5da8dSAndroid Build Coastguard Worker                            + " " + params
492*cda5da8dSAndroid Build Coastguard Worker                else:
493*cda5da8dSAndroid Build Coastguard Worker                    self.esmtp_features[feature] = params
494*cda5da8dSAndroid Build Coastguard Worker        return (code, msg)
495*cda5da8dSAndroid Build Coastguard Worker
496*cda5da8dSAndroid Build Coastguard Worker    def has_extn(self, opt):
497*cda5da8dSAndroid Build Coastguard Worker        """Does the server support a given SMTP service extension?"""
498*cda5da8dSAndroid Build Coastguard Worker        return opt.lower() in self.esmtp_features
499*cda5da8dSAndroid Build Coastguard Worker
500*cda5da8dSAndroid Build Coastguard Worker    def help(self, args=''):
501*cda5da8dSAndroid Build Coastguard Worker        """SMTP 'help' command.
502*cda5da8dSAndroid Build Coastguard Worker        Returns help text from server."""
503*cda5da8dSAndroid Build Coastguard Worker        self.putcmd("help", args)
504*cda5da8dSAndroid Build Coastguard Worker        return self.getreply()[1]
505*cda5da8dSAndroid Build Coastguard Worker
506*cda5da8dSAndroid Build Coastguard Worker    def rset(self):
507*cda5da8dSAndroid Build Coastguard Worker        """SMTP 'rset' command -- resets session."""
508*cda5da8dSAndroid Build Coastguard Worker        self.command_encoding = 'ascii'
509*cda5da8dSAndroid Build Coastguard Worker        return self.docmd("rset")
510*cda5da8dSAndroid Build Coastguard Worker
511*cda5da8dSAndroid Build Coastguard Worker    def _rset(self):
512*cda5da8dSAndroid Build Coastguard Worker        """Internal 'rset' command which ignores any SMTPServerDisconnected error.
513*cda5da8dSAndroid Build Coastguard Worker
514*cda5da8dSAndroid Build Coastguard Worker        Used internally in the library, since the server disconnected error
515*cda5da8dSAndroid Build Coastguard Worker        should appear to the application when the *next* command is issued, if
516*cda5da8dSAndroid Build Coastguard Worker        we are doing an internal "safety" reset.
517*cda5da8dSAndroid Build Coastguard Worker        """
518*cda5da8dSAndroid Build Coastguard Worker        try:
519*cda5da8dSAndroid Build Coastguard Worker            self.rset()
520*cda5da8dSAndroid Build Coastguard Worker        except SMTPServerDisconnected:
521*cda5da8dSAndroid Build Coastguard Worker            pass
522*cda5da8dSAndroid Build Coastguard Worker
523*cda5da8dSAndroid Build Coastguard Worker    def noop(self):
524*cda5da8dSAndroid Build Coastguard Worker        """SMTP 'noop' command -- doesn't do anything :>"""
525*cda5da8dSAndroid Build Coastguard Worker        return self.docmd("noop")
526*cda5da8dSAndroid Build Coastguard Worker
527*cda5da8dSAndroid Build Coastguard Worker    def mail(self, sender, options=()):
528*cda5da8dSAndroid Build Coastguard Worker        """SMTP 'mail' command -- begins mail xfer session.
529*cda5da8dSAndroid Build Coastguard Worker
530*cda5da8dSAndroid Build Coastguard Worker        This method may raise the following exceptions:
531*cda5da8dSAndroid Build Coastguard Worker
532*cda5da8dSAndroid Build Coastguard Worker         SMTPNotSupportedError  The options parameter includes 'SMTPUTF8'
533*cda5da8dSAndroid Build Coastguard Worker                                but the SMTPUTF8 extension is not supported by
534*cda5da8dSAndroid Build Coastguard Worker                                the server.
535*cda5da8dSAndroid Build Coastguard Worker        """
536*cda5da8dSAndroid Build Coastguard Worker        optionlist = ''
537*cda5da8dSAndroid Build Coastguard Worker        if options and self.does_esmtp:
538*cda5da8dSAndroid Build Coastguard Worker            if any(x.lower()=='smtputf8' for x in options):
539*cda5da8dSAndroid Build Coastguard Worker                if self.has_extn('smtputf8'):
540*cda5da8dSAndroid Build Coastguard Worker                    self.command_encoding = 'utf-8'
541*cda5da8dSAndroid Build Coastguard Worker                else:
542*cda5da8dSAndroid Build Coastguard Worker                    raise SMTPNotSupportedError(
543*cda5da8dSAndroid Build Coastguard Worker                        'SMTPUTF8 not supported by server')
544*cda5da8dSAndroid Build Coastguard Worker            optionlist = ' ' + ' '.join(options)
545*cda5da8dSAndroid Build Coastguard Worker        self.putcmd("mail", "FROM:%s%s" % (quoteaddr(sender), optionlist))
546*cda5da8dSAndroid Build Coastguard Worker        return self.getreply()
547*cda5da8dSAndroid Build Coastguard Worker
548*cda5da8dSAndroid Build Coastguard Worker    def rcpt(self, recip, options=()):
549*cda5da8dSAndroid Build Coastguard Worker        """SMTP 'rcpt' command -- indicates 1 recipient for this mail."""
550*cda5da8dSAndroid Build Coastguard Worker        optionlist = ''
551*cda5da8dSAndroid Build Coastguard Worker        if options and self.does_esmtp:
552*cda5da8dSAndroid Build Coastguard Worker            optionlist = ' ' + ' '.join(options)
553*cda5da8dSAndroid Build Coastguard Worker        self.putcmd("rcpt", "TO:%s%s" % (quoteaddr(recip), optionlist))
554*cda5da8dSAndroid Build Coastguard Worker        return self.getreply()
555*cda5da8dSAndroid Build Coastguard Worker
556*cda5da8dSAndroid Build Coastguard Worker    def data(self, msg):
557*cda5da8dSAndroid Build Coastguard Worker        """SMTP 'DATA' command -- sends message data to server.
558*cda5da8dSAndroid Build Coastguard Worker
559*cda5da8dSAndroid Build Coastguard Worker        Automatically quotes lines beginning with a period per rfc821.
560*cda5da8dSAndroid Build Coastguard Worker        Raises SMTPDataError if there is an unexpected reply to the
561*cda5da8dSAndroid Build Coastguard Worker        DATA command; the return value from this method is the final
562*cda5da8dSAndroid Build Coastguard Worker        response code received when the all data is sent.  If msg
563*cda5da8dSAndroid Build Coastguard Worker        is a string, lone '\\r' and '\\n' characters are converted to
564*cda5da8dSAndroid Build Coastguard Worker        '\\r\\n' characters.  If msg is bytes, it is transmitted as is.
565*cda5da8dSAndroid Build Coastguard Worker        """
566*cda5da8dSAndroid Build Coastguard Worker        self.putcmd("data")
567*cda5da8dSAndroid Build Coastguard Worker        (code, repl) = self.getreply()
568*cda5da8dSAndroid Build Coastguard Worker        if self.debuglevel > 0:
569*cda5da8dSAndroid Build Coastguard Worker            self._print_debug('data:', (code, repl))
570*cda5da8dSAndroid Build Coastguard Worker        if code != 354:
571*cda5da8dSAndroid Build Coastguard Worker            raise SMTPDataError(code, repl)
572*cda5da8dSAndroid Build Coastguard Worker        else:
573*cda5da8dSAndroid Build Coastguard Worker            if isinstance(msg, str):
574*cda5da8dSAndroid Build Coastguard Worker                msg = _fix_eols(msg).encode('ascii')
575*cda5da8dSAndroid Build Coastguard Worker            q = _quote_periods(msg)
576*cda5da8dSAndroid Build Coastguard Worker            if q[-2:] != bCRLF:
577*cda5da8dSAndroid Build Coastguard Worker                q = q + bCRLF
578*cda5da8dSAndroid Build Coastguard Worker            q = q + b"." + bCRLF
579*cda5da8dSAndroid Build Coastguard Worker            self.send(q)
580*cda5da8dSAndroid Build Coastguard Worker            (code, msg) = self.getreply()
581*cda5da8dSAndroid Build Coastguard Worker            if self.debuglevel > 0:
582*cda5da8dSAndroid Build Coastguard Worker                self._print_debug('data:', (code, msg))
583*cda5da8dSAndroid Build Coastguard Worker            return (code, msg)
584*cda5da8dSAndroid Build Coastguard Worker
585*cda5da8dSAndroid Build Coastguard Worker    def verify(self, address):
586*cda5da8dSAndroid Build Coastguard Worker        """SMTP 'verify' command -- checks for address validity."""
587*cda5da8dSAndroid Build Coastguard Worker        self.putcmd("vrfy", _addr_only(address))
588*cda5da8dSAndroid Build Coastguard Worker        return self.getreply()
589*cda5da8dSAndroid Build Coastguard Worker    # a.k.a.
590*cda5da8dSAndroid Build Coastguard Worker    vrfy = verify
591*cda5da8dSAndroid Build Coastguard Worker
592*cda5da8dSAndroid Build Coastguard Worker    def expn(self, address):
593*cda5da8dSAndroid Build Coastguard Worker        """SMTP 'expn' command -- expands a mailing list."""
594*cda5da8dSAndroid Build Coastguard Worker        self.putcmd("expn", _addr_only(address))
595*cda5da8dSAndroid Build Coastguard Worker        return self.getreply()
596*cda5da8dSAndroid Build Coastguard Worker
597*cda5da8dSAndroid Build Coastguard Worker    # some useful methods
598*cda5da8dSAndroid Build Coastguard Worker
599*cda5da8dSAndroid Build Coastguard Worker    def ehlo_or_helo_if_needed(self):
600*cda5da8dSAndroid Build Coastguard Worker        """Call self.ehlo() and/or self.helo() if needed.
601*cda5da8dSAndroid Build Coastguard Worker
602*cda5da8dSAndroid Build Coastguard Worker        If there has been no previous EHLO or HELO command this session, this
603*cda5da8dSAndroid Build Coastguard Worker        method tries ESMTP EHLO first.
604*cda5da8dSAndroid Build Coastguard Worker
605*cda5da8dSAndroid Build Coastguard Worker        This method may raise the following exceptions:
606*cda5da8dSAndroid Build Coastguard Worker
607*cda5da8dSAndroid Build Coastguard Worker         SMTPHeloError            The server didn't reply properly to
608*cda5da8dSAndroid Build Coastguard Worker                                  the helo greeting.
609*cda5da8dSAndroid Build Coastguard Worker        """
610*cda5da8dSAndroid Build Coastguard Worker        if self.helo_resp is None and self.ehlo_resp is None:
611*cda5da8dSAndroid Build Coastguard Worker            if not (200 <= self.ehlo()[0] <= 299):
612*cda5da8dSAndroid Build Coastguard Worker                (code, resp) = self.helo()
613*cda5da8dSAndroid Build Coastguard Worker                if not (200 <= code <= 299):
614*cda5da8dSAndroid Build Coastguard Worker                    raise SMTPHeloError(code, resp)
615*cda5da8dSAndroid Build Coastguard Worker
616*cda5da8dSAndroid Build Coastguard Worker    def auth(self, mechanism, authobject, *, initial_response_ok=True):
617*cda5da8dSAndroid Build Coastguard Worker        """Authentication command - requires response processing.
618*cda5da8dSAndroid Build Coastguard Worker
619*cda5da8dSAndroid Build Coastguard Worker        'mechanism' specifies which authentication mechanism is to
620*cda5da8dSAndroid Build Coastguard Worker        be used - the valid values are those listed in the 'auth'
621*cda5da8dSAndroid Build Coastguard Worker        element of 'esmtp_features'.
622*cda5da8dSAndroid Build Coastguard Worker
623*cda5da8dSAndroid Build Coastguard Worker        'authobject' must be a callable object taking a single argument:
624*cda5da8dSAndroid Build Coastguard Worker
625*cda5da8dSAndroid Build Coastguard Worker                data = authobject(challenge)
626*cda5da8dSAndroid Build Coastguard Worker
627*cda5da8dSAndroid Build Coastguard Worker        It will be called to process the server's challenge response; the
628*cda5da8dSAndroid Build Coastguard Worker        challenge argument it is passed will be a bytes.  It should return
629*cda5da8dSAndroid Build Coastguard Worker        an ASCII string that will be base64 encoded and sent to the server.
630*cda5da8dSAndroid Build Coastguard Worker
631*cda5da8dSAndroid Build Coastguard Worker        Keyword arguments:
632*cda5da8dSAndroid Build Coastguard Worker            - initial_response_ok: Allow sending the RFC 4954 initial-response
633*cda5da8dSAndroid Build Coastguard Worker              to the AUTH command, if the authentication methods supports it.
634*cda5da8dSAndroid Build Coastguard Worker        """
635*cda5da8dSAndroid Build Coastguard Worker        # RFC 4954 allows auth methods to provide an initial response.  Not all
636*cda5da8dSAndroid Build Coastguard Worker        # methods support it.  By definition, if they return something other
637*cda5da8dSAndroid Build Coastguard Worker        # than None when challenge is None, then they do.  See issue #15014.
638*cda5da8dSAndroid Build Coastguard Worker        mechanism = mechanism.upper()
639*cda5da8dSAndroid Build Coastguard Worker        initial_response = (authobject() if initial_response_ok else None)
640*cda5da8dSAndroid Build Coastguard Worker        if initial_response is not None:
641*cda5da8dSAndroid Build Coastguard Worker            response = encode_base64(initial_response.encode('ascii'), eol='')
642*cda5da8dSAndroid Build Coastguard Worker            (code, resp) = self.docmd("AUTH", mechanism + " " + response)
643*cda5da8dSAndroid Build Coastguard Worker            self._auth_challenge_count = 1
644*cda5da8dSAndroid Build Coastguard Worker        else:
645*cda5da8dSAndroid Build Coastguard Worker            (code, resp) = self.docmd("AUTH", mechanism)
646*cda5da8dSAndroid Build Coastguard Worker            self._auth_challenge_count = 0
647*cda5da8dSAndroid Build Coastguard Worker        # If server responds with a challenge, send the response.
648*cda5da8dSAndroid Build Coastguard Worker        while code == 334:
649*cda5da8dSAndroid Build Coastguard Worker            self._auth_challenge_count += 1
650*cda5da8dSAndroid Build Coastguard Worker            challenge = base64.decodebytes(resp)
651*cda5da8dSAndroid Build Coastguard Worker            response = encode_base64(
652*cda5da8dSAndroid Build Coastguard Worker                authobject(challenge).encode('ascii'), eol='')
653*cda5da8dSAndroid Build Coastguard Worker            (code, resp) = self.docmd(response)
654*cda5da8dSAndroid Build Coastguard Worker            # If server keeps sending challenges, something is wrong.
655*cda5da8dSAndroid Build Coastguard Worker            if self._auth_challenge_count > _MAXCHALLENGE:
656*cda5da8dSAndroid Build Coastguard Worker                raise SMTPException(
657*cda5da8dSAndroid Build Coastguard Worker                    "Server AUTH mechanism infinite loop. Last response: "
658*cda5da8dSAndroid Build Coastguard Worker                    + repr((code, resp))
659*cda5da8dSAndroid Build Coastguard Worker                )
660*cda5da8dSAndroid Build Coastguard Worker        if code in (235, 503):
661*cda5da8dSAndroid Build Coastguard Worker            return (code, resp)
662*cda5da8dSAndroid Build Coastguard Worker        raise SMTPAuthenticationError(code, resp)
663*cda5da8dSAndroid Build Coastguard Worker
664*cda5da8dSAndroid Build Coastguard Worker    def auth_cram_md5(self, challenge=None):
665*cda5da8dSAndroid Build Coastguard Worker        """ Authobject to use with CRAM-MD5 authentication. Requires self.user
666*cda5da8dSAndroid Build Coastguard Worker        and self.password to be set."""
667*cda5da8dSAndroid Build Coastguard Worker        # CRAM-MD5 does not support initial-response.
668*cda5da8dSAndroid Build Coastguard Worker        if challenge is None:
669*cda5da8dSAndroid Build Coastguard Worker            return None
670*cda5da8dSAndroid Build Coastguard Worker        return self.user + " " + hmac.HMAC(
671*cda5da8dSAndroid Build Coastguard Worker            self.password.encode('ascii'), challenge, 'md5').hexdigest()
672*cda5da8dSAndroid Build Coastguard Worker
673*cda5da8dSAndroid Build Coastguard Worker    def auth_plain(self, challenge=None):
674*cda5da8dSAndroid Build Coastguard Worker        """ Authobject to use with PLAIN authentication. Requires self.user and
675*cda5da8dSAndroid Build Coastguard Worker        self.password to be set."""
676*cda5da8dSAndroid Build Coastguard Worker        return "\0%s\0%s" % (self.user, self.password)
677*cda5da8dSAndroid Build Coastguard Worker
678*cda5da8dSAndroid Build Coastguard Worker    def auth_login(self, challenge=None):
679*cda5da8dSAndroid Build Coastguard Worker        """ Authobject to use with LOGIN authentication. Requires self.user and
680*cda5da8dSAndroid Build Coastguard Worker        self.password to be set."""
681*cda5da8dSAndroid Build Coastguard Worker        if challenge is None or self._auth_challenge_count < 2:
682*cda5da8dSAndroid Build Coastguard Worker            return self.user
683*cda5da8dSAndroid Build Coastguard Worker        else:
684*cda5da8dSAndroid Build Coastguard Worker            return self.password
685*cda5da8dSAndroid Build Coastguard Worker
686*cda5da8dSAndroid Build Coastguard Worker    def login(self, user, password, *, initial_response_ok=True):
687*cda5da8dSAndroid Build Coastguard Worker        """Log in on an SMTP server that requires authentication.
688*cda5da8dSAndroid Build Coastguard Worker
689*cda5da8dSAndroid Build Coastguard Worker        The arguments are:
690*cda5da8dSAndroid Build Coastguard Worker            - user:         The user name to authenticate with.
691*cda5da8dSAndroid Build Coastguard Worker            - password:     The password for the authentication.
692*cda5da8dSAndroid Build Coastguard Worker
693*cda5da8dSAndroid Build Coastguard Worker        Keyword arguments:
694*cda5da8dSAndroid Build Coastguard Worker            - initial_response_ok: Allow sending the RFC 4954 initial-response
695*cda5da8dSAndroid Build Coastguard Worker              to the AUTH command, if the authentication methods supports it.
696*cda5da8dSAndroid Build Coastguard Worker
697*cda5da8dSAndroid Build Coastguard Worker        If there has been no previous EHLO or HELO command this session, this
698*cda5da8dSAndroid Build Coastguard Worker        method tries ESMTP EHLO first.
699*cda5da8dSAndroid Build Coastguard Worker
700*cda5da8dSAndroid Build Coastguard Worker        This method will return normally if the authentication was successful.
701*cda5da8dSAndroid Build Coastguard Worker
702*cda5da8dSAndroid Build Coastguard Worker        This method may raise the following exceptions:
703*cda5da8dSAndroid Build Coastguard Worker
704*cda5da8dSAndroid Build Coastguard Worker         SMTPHeloError            The server didn't reply properly to
705*cda5da8dSAndroid Build Coastguard Worker                                  the helo greeting.
706*cda5da8dSAndroid Build Coastguard Worker         SMTPAuthenticationError  The server didn't accept the username/
707*cda5da8dSAndroid Build Coastguard Worker                                  password combination.
708*cda5da8dSAndroid Build Coastguard Worker         SMTPNotSupportedError    The AUTH command is not supported by the
709*cda5da8dSAndroid Build Coastguard Worker                                  server.
710*cda5da8dSAndroid Build Coastguard Worker         SMTPException            No suitable authentication method was
711*cda5da8dSAndroid Build Coastguard Worker                                  found.
712*cda5da8dSAndroid Build Coastguard Worker        """
713*cda5da8dSAndroid Build Coastguard Worker
714*cda5da8dSAndroid Build Coastguard Worker        self.ehlo_or_helo_if_needed()
715*cda5da8dSAndroid Build Coastguard Worker        if not self.has_extn("auth"):
716*cda5da8dSAndroid Build Coastguard Worker            raise SMTPNotSupportedError(
717*cda5da8dSAndroid Build Coastguard Worker                "SMTP AUTH extension not supported by server.")
718*cda5da8dSAndroid Build Coastguard Worker
719*cda5da8dSAndroid Build Coastguard Worker        # Authentication methods the server claims to support
720*cda5da8dSAndroid Build Coastguard Worker        advertised_authlist = self.esmtp_features["auth"].split()
721*cda5da8dSAndroid Build Coastguard Worker
722*cda5da8dSAndroid Build Coastguard Worker        # Authentication methods we can handle in our preferred order:
723*cda5da8dSAndroid Build Coastguard Worker        preferred_auths = ['CRAM-MD5', 'PLAIN', 'LOGIN']
724*cda5da8dSAndroid Build Coastguard Worker
725*cda5da8dSAndroid Build Coastguard Worker        # We try the supported authentications in our preferred order, if
726*cda5da8dSAndroid Build Coastguard Worker        # the server supports them.
727*cda5da8dSAndroid Build Coastguard Worker        authlist = [auth for auth in preferred_auths
728*cda5da8dSAndroid Build Coastguard Worker                    if auth in advertised_authlist]
729*cda5da8dSAndroid Build Coastguard Worker        if not authlist:
730*cda5da8dSAndroid Build Coastguard Worker            raise SMTPException("No suitable authentication method found.")
731*cda5da8dSAndroid Build Coastguard Worker
732*cda5da8dSAndroid Build Coastguard Worker        # Some servers advertise authentication methods they don't really
733*cda5da8dSAndroid Build Coastguard Worker        # support, so if authentication fails, we continue until we've tried
734*cda5da8dSAndroid Build Coastguard Worker        # all methods.
735*cda5da8dSAndroid Build Coastguard Worker        self.user, self.password = user, password
736*cda5da8dSAndroid Build Coastguard Worker        for authmethod in authlist:
737*cda5da8dSAndroid Build Coastguard Worker            method_name = 'auth_' + authmethod.lower().replace('-', '_')
738*cda5da8dSAndroid Build Coastguard Worker            try:
739*cda5da8dSAndroid Build Coastguard Worker                (code, resp) = self.auth(
740*cda5da8dSAndroid Build Coastguard Worker                    authmethod, getattr(self, method_name),
741*cda5da8dSAndroid Build Coastguard Worker                    initial_response_ok=initial_response_ok)
742*cda5da8dSAndroid Build Coastguard Worker                # 235 == 'Authentication successful'
743*cda5da8dSAndroid Build Coastguard Worker                # 503 == 'Error: already authenticated'
744*cda5da8dSAndroid Build Coastguard Worker                if code in (235, 503):
745*cda5da8dSAndroid Build Coastguard Worker                    return (code, resp)
746*cda5da8dSAndroid Build Coastguard Worker            except SMTPAuthenticationError as e:
747*cda5da8dSAndroid Build Coastguard Worker                last_exception = e
748*cda5da8dSAndroid Build Coastguard Worker
749*cda5da8dSAndroid Build Coastguard Worker        # We could not login successfully.  Return result of last attempt.
750*cda5da8dSAndroid Build Coastguard Worker        raise last_exception
751*cda5da8dSAndroid Build Coastguard Worker
752*cda5da8dSAndroid Build Coastguard Worker    def starttls(self, keyfile=None, certfile=None, context=None):
753*cda5da8dSAndroid Build Coastguard Worker        """Puts the connection to the SMTP server into TLS mode.
754*cda5da8dSAndroid Build Coastguard Worker
755*cda5da8dSAndroid Build Coastguard Worker        If there has been no previous EHLO or HELO command this session, this
756*cda5da8dSAndroid Build Coastguard Worker        method tries ESMTP EHLO first.
757*cda5da8dSAndroid Build Coastguard Worker
758*cda5da8dSAndroid Build Coastguard Worker        If the server supports TLS, this will encrypt the rest of the SMTP
759*cda5da8dSAndroid Build Coastguard Worker        session. If you provide the keyfile and certfile parameters,
760*cda5da8dSAndroid Build Coastguard Worker        the identity of the SMTP server and client can be checked. This,
761*cda5da8dSAndroid Build Coastguard Worker        however, depends on whether the socket module really checks the
762*cda5da8dSAndroid Build Coastguard Worker        certificates.
763*cda5da8dSAndroid Build Coastguard Worker
764*cda5da8dSAndroid Build Coastguard Worker        This method may raise the following exceptions:
765*cda5da8dSAndroid Build Coastguard Worker
766*cda5da8dSAndroid Build Coastguard Worker         SMTPHeloError            The server didn't reply properly to
767*cda5da8dSAndroid Build Coastguard Worker                                  the helo greeting.
768*cda5da8dSAndroid Build Coastguard Worker        """
769*cda5da8dSAndroid Build Coastguard Worker        self.ehlo_or_helo_if_needed()
770*cda5da8dSAndroid Build Coastguard Worker        if not self.has_extn("starttls"):
771*cda5da8dSAndroid Build Coastguard Worker            raise SMTPNotSupportedError(
772*cda5da8dSAndroid Build Coastguard Worker                "STARTTLS extension not supported by server.")
773*cda5da8dSAndroid Build Coastguard Worker        (resp, reply) = self.docmd("STARTTLS")
774*cda5da8dSAndroid Build Coastguard Worker        if resp == 220:
775*cda5da8dSAndroid Build Coastguard Worker            if not _have_ssl:
776*cda5da8dSAndroid Build Coastguard Worker                raise RuntimeError("No SSL support included in this Python")
777*cda5da8dSAndroid Build Coastguard Worker            if context is not None and keyfile is not None:
778*cda5da8dSAndroid Build Coastguard Worker                raise ValueError("context and keyfile arguments are mutually "
779*cda5da8dSAndroid Build Coastguard Worker                                 "exclusive")
780*cda5da8dSAndroid Build Coastguard Worker            if context is not None and certfile is not None:
781*cda5da8dSAndroid Build Coastguard Worker                raise ValueError("context and certfile arguments are mutually "
782*cda5da8dSAndroid Build Coastguard Worker                                 "exclusive")
783*cda5da8dSAndroid Build Coastguard Worker            if keyfile is not None or certfile is not None:
784*cda5da8dSAndroid Build Coastguard Worker                import warnings
785*cda5da8dSAndroid Build Coastguard Worker                warnings.warn("keyfile and certfile are deprecated, use a "
786*cda5da8dSAndroid Build Coastguard Worker                              "custom context instead", DeprecationWarning, 2)
787*cda5da8dSAndroid Build Coastguard Worker            if context is None:
788*cda5da8dSAndroid Build Coastguard Worker                context = ssl._create_stdlib_context(certfile=certfile,
789*cda5da8dSAndroid Build Coastguard Worker                                                     keyfile=keyfile)
790*cda5da8dSAndroid Build Coastguard Worker            self.sock = context.wrap_socket(self.sock,
791*cda5da8dSAndroid Build Coastguard Worker                                            server_hostname=self._host)
792*cda5da8dSAndroid Build Coastguard Worker            self.file = None
793*cda5da8dSAndroid Build Coastguard Worker            # RFC 3207:
794*cda5da8dSAndroid Build Coastguard Worker            # The client MUST discard any knowledge obtained from
795*cda5da8dSAndroid Build Coastguard Worker            # the server, such as the list of SMTP service extensions,
796*cda5da8dSAndroid Build Coastguard Worker            # which was not obtained from the TLS negotiation itself.
797*cda5da8dSAndroid Build Coastguard Worker            self.helo_resp = None
798*cda5da8dSAndroid Build Coastguard Worker            self.ehlo_resp = None
799*cda5da8dSAndroid Build Coastguard Worker            self.esmtp_features = {}
800*cda5da8dSAndroid Build Coastguard Worker            self.does_esmtp = False
801*cda5da8dSAndroid Build Coastguard Worker        else:
802*cda5da8dSAndroid Build Coastguard Worker            # RFC 3207:
803*cda5da8dSAndroid Build Coastguard Worker            # 501 Syntax error (no parameters allowed)
804*cda5da8dSAndroid Build Coastguard Worker            # 454 TLS not available due to temporary reason
805*cda5da8dSAndroid Build Coastguard Worker            raise SMTPResponseException(resp, reply)
806*cda5da8dSAndroid Build Coastguard Worker        return (resp, reply)
807*cda5da8dSAndroid Build Coastguard Worker
808*cda5da8dSAndroid Build Coastguard Worker    def sendmail(self, from_addr, to_addrs, msg, mail_options=(),
809*cda5da8dSAndroid Build Coastguard Worker                 rcpt_options=()):
810*cda5da8dSAndroid Build Coastguard Worker        """This command performs an entire mail transaction.
811*cda5da8dSAndroid Build Coastguard Worker
812*cda5da8dSAndroid Build Coastguard Worker        The arguments are:
813*cda5da8dSAndroid Build Coastguard Worker            - from_addr    : The address sending this mail.
814*cda5da8dSAndroid Build Coastguard Worker            - to_addrs     : A list of addresses to send this mail to.  A bare
815*cda5da8dSAndroid Build Coastguard Worker                             string will be treated as a list with 1 address.
816*cda5da8dSAndroid Build Coastguard Worker            - msg          : The message to send.
817*cda5da8dSAndroid Build Coastguard Worker            - mail_options : List of ESMTP options (such as 8bitmime) for the
818*cda5da8dSAndroid Build Coastguard Worker                             mail command.
819*cda5da8dSAndroid Build Coastguard Worker            - rcpt_options : List of ESMTP options (such as DSN commands) for
820*cda5da8dSAndroid Build Coastguard Worker                             all the rcpt commands.
821*cda5da8dSAndroid Build Coastguard Worker
822*cda5da8dSAndroid Build Coastguard Worker        msg may be a string containing characters in the ASCII range, or a byte
823*cda5da8dSAndroid Build Coastguard Worker        string.  A string is encoded to bytes using the ascii codec, and lone
824*cda5da8dSAndroid Build Coastguard Worker        \\r and \\n characters are converted to \\r\\n characters.
825*cda5da8dSAndroid Build Coastguard Worker
826*cda5da8dSAndroid Build Coastguard Worker        If there has been no previous EHLO or HELO command this session, this
827*cda5da8dSAndroid Build Coastguard Worker        method tries ESMTP EHLO first.  If the server does ESMTP, message size
828*cda5da8dSAndroid Build Coastguard Worker        and each of the specified options will be passed to it.  If EHLO
829*cda5da8dSAndroid Build Coastguard Worker        fails, HELO will be tried and ESMTP options suppressed.
830*cda5da8dSAndroid Build Coastguard Worker
831*cda5da8dSAndroid Build Coastguard Worker        This method will return normally if the mail is accepted for at least
832*cda5da8dSAndroid Build Coastguard Worker        one recipient.  It returns a dictionary, with one entry for each
833*cda5da8dSAndroid Build Coastguard Worker        recipient that was refused.  Each entry contains a tuple of the SMTP
834*cda5da8dSAndroid Build Coastguard Worker        error code and the accompanying error message sent by the server.
835*cda5da8dSAndroid Build Coastguard Worker
836*cda5da8dSAndroid Build Coastguard Worker        This method may raise the following exceptions:
837*cda5da8dSAndroid Build Coastguard Worker
838*cda5da8dSAndroid Build Coastguard Worker         SMTPHeloError          The server didn't reply properly to
839*cda5da8dSAndroid Build Coastguard Worker                                the helo greeting.
840*cda5da8dSAndroid Build Coastguard Worker         SMTPRecipientsRefused  The server rejected ALL recipients
841*cda5da8dSAndroid Build Coastguard Worker                                (no mail was sent).
842*cda5da8dSAndroid Build Coastguard Worker         SMTPSenderRefused      The server didn't accept the from_addr.
843*cda5da8dSAndroid Build Coastguard Worker         SMTPDataError          The server replied with an unexpected
844*cda5da8dSAndroid Build Coastguard Worker                                error code (other than a refusal of
845*cda5da8dSAndroid Build Coastguard Worker                                a recipient).
846*cda5da8dSAndroid Build Coastguard Worker         SMTPNotSupportedError  The mail_options parameter includes 'SMTPUTF8'
847*cda5da8dSAndroid Build Coastguard Worker                                but the SMTPUTF8 extension is not supported by
848*cda5da8dSAndroid Build Coastguard Worker                                the server.
849*cda5da8dSAndroid Build Coastguard Worker
850*cda5da8dSAndroid Build Coastguard Worker        Note: the connection will be open even after an exception is raised.
851*cda5da8dSAndroid Build Coastguard Worker
852*cda5da8dSAndroid Build Coastguard Worker        Example:
853*cda5da8dSAndroid Build Coastguard Worker
854*cda5da8dSAndroid Build Coastguard Worker         >>> import smtplib
855*cda5da8dSAndroid Build Coastguard Worker         >>> s=smtplib.SMTP("localhost")
856*cda5da8dSAndroid Build Coastguard Worker         >>> tolist=["[email protected]","[email protected]","[email protected]","[email protected]"]
857*cda5da8dSAndroid Build Coastguard Worker         >>> msg = '''\\
858*cda5da8dSAndroid Build Coastguard Worker         ... From: [email protected]
859*cda5da8dSAndroid Build Coastguard Worker         ... Subject: testin'...
860*cda5da8dSAndroid Build Coastguard Worker         ...
861*cda5da8dSAndroid Build Coastguard Worker         ... This is a test '''
862*cda5da8dSAndroid Build Coastguard Worker         >>> s.sendmail("[email protected]",tolist,msg)
863*cda5da8dSAndroid Build Coastguard Worker         { "[email protected]" : ( 550 ,"User unknown" ) }
864*cda5da8dSAndroid Build Coastguard Worker         >>> s.quit()
865*cda5da8dSAndroid Build Coastguard Worker
866*cda5da8dSAndroid Build Coastguard Worker        In the above example, the message was accepted for delivery to three
867*cda5da8dSAndroid Build Coastguard Worker        of the four addresses, and one was rejected, with the error code
868*cda5da8dSAndroid Build Coastguard Worker        550.  If all addresses are accepted, then the method will return an
869*cda5da8dSAndroid Build Coastguard Worker        empty dictionary.
870*cda5da8dSAndroid Build Coastguard Worker
871*cda5da8dSAndroid Build Coastguard Worker        """
872*cda5da8dSAndroid Build Coastguard Worker        self.ehlo_or_helo_if_needed()
873*cda5da8dSAndroid Build Coastguard Worker        esmtp_opts = []
874*cda5da8dSAndroid Build Coastguard Worker        if isinstance(msg, str):
875*cda5da8dSAndroid Build Coastguard Worker            msg = _fix_eols(msg).encode('ascii')
876*cda5da8dSAndroid Build Coastguard Worker        if self.does_esmtp:
877*cda5da8dSAndroid Build Coastguard Worker            if self.has_extn('size'):
878*cda5da8dSAndroid Build Coastguard Worker                esmtp_opts.append("size=%d" % len(msg))
879*cda5da8dSAndroid Build Coastguard Worker            for option in mail_options:
880*cda5da8dSAndroid Build Coastguard Worker                esmtp_opts.append(option)
881*cda5da8dSAndroid Build Coastguard Worker        (code, resp) = self.mail(from_addr, esmtp_opts)
882*cda5da8dSAndroid Build Coastguard Worker        if code != 250:
883*cda5da8dSAndroid Build Coastguard Worker            if code == 421:
884*cda5da8dSAndroid Build Coastguard Worker                self.close()
885*cda5da8dSAndroid Build Coastguard Worker            else:
886*cda5da8dSAndroid Build Coastguard Worker                self._rset()
887*cda5da8dSAndroid Build Coastguard Worker            raise SMTPSenderRefused(code, resp, from_addr)
888*cda5da8dSAndroid Build Coastguard Worker        senderrs = {}
889*cda5da8dSAndroid Build Coastguard Worker        if isinstance(to_addrs, str):
890*cda5da8dSAndroid Build Coastguard Worker            to_addrs = [to_addrs]
891*cda5da8dSAndroid Build Coastguard Worker        for each in to_addrs:
892*cda5da8dSAndroid Build Coastguard Worker            (code, resp) = self.rcpt(each, rcpt_options)
893*cda5da8dSAndroid Build Coastguard Worker            if (code != 250) and (code != 251):
894*cda5da8dSAndroid Build Coastguard Worker                senderrs[each] = (code, resp)
895*cda5da8dSAndroid Build Coastguard Worker            if code == 421:
896*cda5da8dSAndroid Build Coastguard Worker                self.close()
897*cda5da8dSAndroid Build Coastguard Worker                raise SMTPRecipientsRefused(senderrs)
898*cda5da8dSAndroid Build Coastguard Worker        if len(senderrs) == len(to_addrs):
899*cda5da8dSAndroid Build Coastguard Worker            # the server refused all our recipients
900*cda5da8dSAndroid Build Coastguard Worker            self._rset()
901*cda5da8dSAndroid Build Coastguard Worker            raise SMTPRecipientsRefused(senderrs)
902*cda5da8dSAndroid Build Coastguard Worker        (code, resp) = self.data(msg)
903*cda5da8dSAndroid Build Coastguard Worker        if code != 250:
904*cda5da8dSAndroid Build Coastguard Worker            if code == 421:
905*cda5da8dSAndroid Build Coastguard Worker                self.close()
906*cda5da8dSAndroid Build Coastguard Worker            else:
907*cda5da8dSAndroid Build Coastguard Worker                self._rset()
908*cda5da8dSAndroid Build Coastguard Worker            raise SMTPDataError(code, resp)
909*cda5da8dSAndroid Build Coastguard Worker        #if we got here then somebody got our mail
910*cda5da8dSAndroid Build Coastguard Worker        return senderrs
911*cda5da8dSAndroid Build Coastguard Worker
912*cda5da8dSAndroid Build Coastguard Worker    def send_message(self, msg, from_addr=None, to_addrs=None,
913*cda5da8dSAndroid Build Coastguard Worker                     mail_options=(), rcpt_options=()):
914*cda5da8dSAndroid Build Coastguard Worker        """Converts message to a bytestring and passes it to sendmail.
915*cda5da8dSAndroid Build Coastguard Worker
916*cda5da8dSAndroid Build Coastguard Worker        The arguments are as for sendmail, except that msg is an
917*cda5da8dSAndroid Build Coastguard Worker        email.message.Message object.  If from_addr is None or to_addrs is
918*cda5da8dSAndroid Build Coastguard Worker        None, these arguments are taken from the headers of the Message as
919*cda5da8dSAndroid Build Coastguard Worker        described in RFC 2822 (a ValueError is raised if there is more than
920*cda5da8dSAndroid Build Coastguard Worker        one set of 'Resent-' headers).  Regardless of the values of from_addr and
921*cda5da8dSAndroid Build Coastguard Worker        to_addr, any Bcc field (or Resent-Bcc field, when the Message is a
922*cda5da8dSAndroid Build Coastguard Worker        resent) of the Message object won't be transmitted.  The Message
923*cda5da8dSAndroid Build Coastguard Worker        object is then serialized using email.generator.BytesGenerator and
924*cda5da8dSAndroid Build Coastguard Worker        sendmail is called to transmit the message.  If the sender or any of
925*cda5da8dSAndroid Build Coastguard Worker        the recipient addresses contain non-ASCII and the server advertises the
926*cda5da8dSAndroid Build Coastguard Worker        SMTPUTF8 capability, the policy is cloned with utf8 set to True for the
927*cda5da8dSAndroid Build Coastguard Worker        serialization, and SMTPUTF8 and BODY=8BITMIME are asserted on the send.
928*cda5da8dSAndroid Build Coastguard Worker        If the server does not support SMTPUTF8, an SMTPNotSupported error is
929*cda5da8dSAndroid Build Coastguard Worker        raised.  Otherwise the generator is called without modifying the
930*cda5da8dSAndroid Build Coastguard Worker        policy.
931*cda5da8dSAndroid Build Coastguard Worker
932*cda5da8dSAndroid Build Coastguard Worker        """
933*cda5da8dSAndroid Build Coastguard Worker        # 'Resent-Date' is a mandatory field if the Message is resent (RFC 2822
934*cda5da8dSAndroid Build Coastguard Worker        # Section 3.6.6). In such a case, we use the 'Resent-*' fields.  However,
935*cda5da8dSAndroid Build Coastguard Worker        # if there is more than one 'Resent-' block there's no way to
936*cda5da8dSAndroid Build Coastguard Worker        # unambiguously determine which one is the most recent in all cases,
937*cda5da8dSAndroid Build Coastguard Worker        # so rather than guess we raise a ValueError in that case.
938*cda5da8dSAndroid Build Coastguard Worker        #
939*cda5da8dSAndroid Build Coastguard Worker        # TODO implement heuristics to guess the correct Resent-* block with an
940*cda5da8dSAndroid Build Coastguard Worker        # option allowing the user to enable the heuristics.  (It should be
941*cda5da8dSAndroid Build Coastguard Worker        # possible to guess correctly almost all of the time.)
942*cda5da8dSAndroid Build Coastguard Worker
943*cda5da8dSAndroid Build Coastguard Worker        self.ehlo_or_helo_if_needed()
944*cda5da8dSAndroid Build Coastguard Worker        resent = msg.get_all('Resent-Date')
945*cda5da8dSAndroid Build Coastguard Worker        if resent is None:
946*cda5da8dSAndroid Build Coastguard Worker            header_prefix = ''
947*cda5da8dSAndroid Build Coastguard Worker        elif len(resent) == 1:
948*cda5da8dSAndroid Build Coastguard Worker            header_prefix = 'Resent-'
949*cda5da8dSAndroid Build Coastguard Worker        else:
950*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("message has more than one 'Resent-' header block")
951*cda5da8dSAndroid Build Coastguard Worker        if from_addr is None:
952*cda5da8dSAndroid Build Coastguard Worker            # Prefer the sender field per RFC 2822:3.6.2.
953*cda5da8dSAndroid Build Coastguard Worker            from_addr = (msg[header_prefix + 'Sender']
954*cda5da8dSAndroid Build Coastguard Worker                           if (header_prefix + 'Sender') in msg
955*cda5da8dSAndroid Build Coastguard Worker                           else msg[header_prefix + 'From'])
956*cda5da8dSAndroid Build Coastguard Worker            from_addr = email.utils.getaddresses([from_addr])[0][1]
957*cda5da8dSAndroid Build Coastguard Worker        if to_addrs is None:
958*cda5da8dSAndroid Build Coastguard Worker            addr_fields = [f for f in (msg[header_prefix + 'To'],
959*cda5da8dSAndroid Build Coastguard Worker                                       msg[header_prefix + 'Bcc'],
960*cda5da8dSAndroid Build Coastguard Worker                                       msg[header_prefix + 'Cc'])
961*cda5da8dSAndroid Build Coastguard Worker                           if f is not None]
962*cda5da8dSAndroid Build Coastguard Worker            to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)]
963*cda5da8dSAndroid Build Coastguard Worker        # Make a local copy so we can delete the bcc headers.
964*cda5da8dSAndroid Build Coastguard Worker        msg_copy = copy.copy(msg)
965*cda5da8dSAndroid Build Coastguard Worker        del msg_copy['Bcc']
966*cda5da8dSAndroid Build Coastguard Worker        del msg_copy['Resent-Bcc']
967*cda5da8dSAndroid Build Coastguard Worker        international = False
968*cda5da8dSAndroid Build Coastguard Worker        try:
969*cda5da8dSAndroid Build Coastguard Worker            ''.join([from_addr, *to_addrs]).encode('ascii')
970*cda5da8dSAndroid Build Coastguard Worker        except UnicodeEncodeError:
971*cda5da8dSAndroid Build Coastguard Worker            if not self.has_extn('smtputf8'):
972*cda5da8dSAndroid Build Coastguard Worker                raise SMTPNotSupportedError(
973*cda5da8dSAndroid Build Coastguard Worker                    "One or more source or delivery addresses require"
974*cda5da8dSAndroid Build Coastguard Worker                    " internationalized email support, but the server"
975*cda5da8dSAndroid Build Coastguard Worker                    " does not advertise the required SMTPUTF8 capability")
976*cda5da8dSAndroid Build Coastguard Worker            international = True
977*cda5da8dSAndroid Build Coastguard Worker        with io.BytesIO() as bytesmsg:
978*cda5da8dSAndroid Build Coastguard Worker            if international:
979*cda5da8dSAndroid Build Coastguard Worker                g = email.generator.BytesGenerator(
980*cda5da8dSAndroid Build Coastguard Worker                    bytesmsg, policy=msg.policy.clone(utf8=True))
981*cda5da8dSAndroid Build Coastguard Worker                mail_options = (*mail_options, 'SMTPUTF8', 'BODY=8BITMIME')
982*cda5da8dSAndroid Build Coastguard Worker            else:
983*cda5da8dSAndroid Build Coastguard Worker                g = email.generator.BytesGenerator(bytesmsg)
984*cda5da8dSAndroid Build Coastguard Worker            g.flatten(msg_copy, linesep='\r\n')
985*cda5da8dSAndroid Build Coastguard Worker            flatmsg = bytesmsg.getvalue()
986*cda5da8dSAndroid Build Coastguard Worker        return self.sendmail(from_addr, to_addrs, flatmsg, mail_options,
987*cda5da8dSAndroid Build Coastguard Worker                             rcpt_options)
988*cda5da8dSAndroid Build Coastguard Worker
989*cda5da8dSAndroid Build Coastguard Worker    def close(self):
990*cda5da8dSAndroid Build Coastguard Worker        """Close the connection to the SMTP server."""
991*cda5da8dSAndroid Build Coastguard Worker        try:
992*cda5da8dSAndroid Build Coastguard Worker            file = self.file
993*cda5da8dSAndroid Build Coastguard Worker            self.file = None
994*cda5da8dSAndroid Build Coastguard Worker            if file:
995*cda5da8dSAndroid Build Coastguard Worker                file.close()
996*cda5da8dSAndroid Build Coastguard Worker        finally:
997*cda5da8dSAndroid Build Coastguard Worker            sock = self.sock
998*cda5da8dSAndroid Build Coastguard Worker            self.sock = None
999*cda5da8dSAndroid Build Coastguard Worker            if sock:
1000*cda5da8dSAndroid Build Coastguard Worker                sock.close()
1001*cda5da8dSAndroid Build Coastguard Worker
1002*cda5da8dSAndroid Build Coastguard Worker    def quit(self):
1003*cda5da8dSAndroid Build Coastguard Worker        """Terminate the SMTP session."""
1004*cda5da8dSAndroid Build Coastguard Worker        res = self.docmd("quit")
1005*cda5da8dSAndroid Build Coastguard Worker        # A new EHLO is required after reconnecting with connect()
1006*cda5da8dSAndroid Build Coastguard Worker        self.ehlo_resp = self.helo_resp = None
1007*cda5da8dSAndroid Build Coastguard Worker        self.esmtp_features = {}
1008*cda5da8dSAndroid Build Coastguard Worker        self.does_esmtp = False
1009*cda5da8dSAndroid Build Coastguard Worker        self.close()
1010*cda5da8dSAndroid Build Coastguard Worker        return res
1011*cda5da8dSAndroid Build Coastguard Worker
1012*cda5da8dSAndroid Build Coastguard Workerif _have_ssl:
1013*cda5da8dSAndroid Build Coastguard Worker
1014*cda5da8dSAndroid Build Coastguard Worker    class SMTP_SSL(SMTP):
1015*cda5da8dSAndroid Build Coastguard Worker        """ This is a subclass derived from SMTP that connects over an SSL
1016*cda5da8dSAndroid Build Coastguard Worker        encrypted socket (to use this class you need a socket module that was
1017*cda5da8dSAndroid Build Coastguard Worker        compiled with SSL support). If host is not specified, '' (the local
1018*cda5da8dSAndroid Build Coastguard Worker        host) is used. If port is omitted, the standard SMTP-over-SSL port
1019*cda5da8dSAndroid Build Coastguard Worker        (465) is used.  local_hostname and source_address have the same meaning
1020*cda5da8dSAndroid Build Coastguard Worker        as they do in the SMTP class.  keyfile and certfile are also optional -
1021*cda5da8dSAndroid Build Coastguard Worker        they can contain a PEM formatted private key and certificate chain file
1022*cda5da8dSAndroid Build Coastguard Worker        for the SSL connection. context also optional, can contain a
1023*cda5da8dSAndroid Build Coastguard Worker        SSLContext, and is an alternative to keyfile and certfile; If it is
1024*cda5da8dSAndroid Build Coastguard Worker        specified both keyfile and certfile must be None.
1025*cda5da8dSAndroid Build Coastguard Worker
1026*cda5da8dSAndroid Build Coastguard Worker        """
1027*cda5da8dSAndroid Build Coastguard Worker
1028*cda5da8dSAndroid Build Coastguard Worker        default_port = SMTP_SSL_PORT
1029*cda5da8dSAndroid Build Coastguard Worker
1030*cda5da8dSAndroid Build Coastguard Worker        def __init__(self, host='', port=0, local_hostname=None,
1031*cda5da8dSAndroid Build Coastguard Worker                     keyfile=None, certfile=None,
1032*cda5da8dSAndroid Build Coastguard Worker                     timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
1033*cda5da8dSAndroid Build Coastguard Worker                     source_address=None, context=None):
1034*cda5da8dSAndroid Build Coastguard Worker            if context is not None and keyfile is not None:
1035*cda5da8dSAndroid Build Coastguard Worker                raise ValueError("context and keyfile arguments are mutually "
1036*cda5da8dSAndroid Build Coastguard Worker                                 "exclusive")
1037*cda5da8dSAndroid Build Coastguard Worker            if context is not None and certfile is not None:
1038*cda5da8dSAndroid Build Coastguard Worker                raise ValueError("context and certfile arguments are mutually "
1039*cda5da8dSAndroid Build Coastguard Worker                                 "exclusive")
1040*cda5da8dSAndroid Build Coastguard Worker            if keyfile is not None or certfile is not None:
1041*cda5da8dSAndroid Build Coastguard Worker                import warnings
1042*cda5da8dSAndroid Build Coastguard Worker                warnings.warn("keyfile and certfile are deprecated, use a "
1043*cda5da8dSAndroid Build Coastguard Worker                              "custom context instead", DeprecationWarning, 2)
1044*cda5da8dSAndroid Build Coastguard Worker            self.keyfile = keyfile
1045*cda5da8dSAndroid Build Coastguard Worker            self.certfile = certfile
1046*cda5da8dSAndroid Build Coastguard Worker            if context is None:
1047*cda5da8dSAndroid Build Coastguard Worker                context = ssl._create_stdlib_context(certfile=certfile,
1048*cda5da8dSAndroid Build Coastguard Worker                                                     keyfile=keyfile)
1049*cda5da8dSAndroid Build Coastguard Worker            self.context = context
1050*cda5da8dSAndroid Build Coastguard Worker            SMTP.__init__(self, host, port, local_hostname, timeout,
1051*cda5da8dSAndroid Build Coastguard Worker                          source_address)
1052*cda5da8dSAndroid Build Coastguard Worker
1053*cda5da8dSAndroid Build Coastguard Worker        def _get_socket(self, host, port, timeout):
1054*cda5da8dSAndroid Build Coastguard Worker            if self.debuglevel > 0:
1055*cda5da8dSAndroid Build Coastguard Worker                self._print_debug('connect:', (host, port))
1056*cda5da8dSAndroid Build Coastguard Worker            new_socket = super()._get_socket(host, port, timeout)
1057*cda5da8dSAndroid Build Coastguard Worker            new_socket = self.context.wrap_socket(new_socket,
1058*cda5da8dSAndroid Build Coastguard Worker                                                  server_hostname=self._host)
1059*cda5da8dSAndroid Build Coastguard Worker            return new_socket
1060*cda5da8dSAndroid Build Coastguard Worker
1061*cda5da8dSAndroid Build Coastguard Worker    __all__.append("SMTP_SSL")
1062*cda5da8dSAndroid Build Coastguard Worker
1063*cda5da8dSAndroid Build Coastguard Worker#
1064*cda5da8dSAndroid Build Coastguard Worker# LMTP extension
1065*cda5da8dSAndroid Build Coastguard Worker#
1066*cda5da8dSAndroid Build Coastguard WorkerLMTP_PORT = 2003
1067*cda5da8dSAndroid Build Coastguard Worker
1068*cda5da8dSAndroid Build Coastguard Workerclass LMTP(SMTP):
1069*cda5da8dSAndroid Build Coastguard Worker    """LMTP - Local Mail Transfer Protocol
1070*cda5da8dSAndroid Build Coastguard Worker
1071*cda5da8dSAndroid Build Coastguard Worker    The LMTP protocol, which is very similar to ESMTP, is heavily based
1072*cda5da8dSAndroid Build Coastguard Worker    on the standard SMTP client. It's common to use Unix sockets for
1073*cda5da8dSAndroid Build Coastguard Worker    LMTP, so our connect() method must support that as well as a regular
1074*cda5da8dSAndroid Build Coastguard Worker    host:port server.  local_hostname and source_address have the same
1075*cda5da8dSAndroid Build Coastguard Worker    meaning as they do in the SMTP class.  To specify a Unix socket,
1076*cda5da8dSAndroid Build Coastguard Worker    you must use an absolute path as the host, starting with a '/'.
1077*cda5da8dSAndroid Build Coastguard Worker
1078*cda5da8dSAndroid Build Coastguard Worker    Authentication is supported, using the regular SMTP mechanism. When
1079*cda5da8dSAndroid Build Coastguard Worker    using a Unix socket, LMTP generally don't support or require any
1080*cda5da8dSAndroid Build Coastguard Worker    authentication, but your mileage might vary."""
1081*cda5da8dSAndroid Build Coastguard Worker
1082*cda5da8dSAndroid Build Coastguard Worker    ehlo_msg = "lhlo"
1083*cda5da8dSAndroid Build Coastguard Worker
1084*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, host='', port=LMTP_PORT, local_hostname=None,
1085*cda5da8dSAndroid Build Coastguard Worker                 source_address=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
1086*cda5da8dSAndroid Build Coastguard Worker        """Initialize a new instance."""
1087*cda5da8dSAndroid Build Coastguard Worker        super().__init__(host, port, local_hostname=local_hostname,
1088*cda5da8dSAndroid Build Coastguard Worker                         source_address=source_address, timeout=timeout)
1089*cda5da8dSAndroid Build Coastguard Worker
1090*cda5da8dSAndroid Build Coastguard Worker    def connect(self, host='localhost', port=0, source_address=None):
1091*cda5da8dSAndroid Build Coastguard Worker        """Connect to the LMTP daemon, on either a Unix or a TCP socket."""
1092*cda5da8dSAndroid Build Coastguard Worker        if host[0] != '/':
1093*cda5da8dSAndroid Build Coastguard Worker            return super().connect(host, port, source_address=source_address)
1094*cda5da8dSAndroid Build Coastguard Worker
1095*cda5da8dSAndroid Build Coastguard Worker        if self.timeout is not None and not self.timeout:
1096*cda5da8dSAndroid Build Coastguard Worker            raise ValueError('Non-blocking socket (timeout=0) is not supported')
1097*cda5da8dSAndroid Build Coastguard Worker
1098*cda5da8dSAndroid Build Coastguard Worker        # Handle Unix-domain sockets.
1099*cda5da8dSAndroid Build Coastguard Worker        try:
1100*cda5da8dSAndroid Build Coastguard Worker            self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
1101*cda5da8dSAndroid Build Coastguard Worker            if self.timeout is not socket._GLOBAL_DEFAULT_TIMEOUT:
1102*cda5da8dSAndroid Build Coastguard Worker                self.sock.settimeout(self.timeout)
1103*cda5da8dSAndroid Build Coastguard Worker            self.file = None
1104*cda5da8dSAndroid Build Coastguard Worker            self.sock.connect(host)
1105*cda5da8dSAndroid Build Coastguard Worker        except OSError:
1106*cda5da8dSAndroid Build Coastguard Worker            if self.debuglevel > 0:
1107*cda5da8dSAndroid Build Coastguard Worker                self._print_debug('connect fail:', host)
1108*cda5da8dSAndroid Build Coastguard Worker            if self.sock:
1109*cda5da8dSAndroid Build Coastguard Worker                self.sock.close()
1110*cda5da8dSAndroid Build Coastguard Worker            self.sock = None
1111*cda5da8dSAndroid Build Coastguard Worker            raise
1112*cda5da8dSAndroid Build Coastguard Worker        (code, msg) = self.getreply()
1113*cda5da8dSAndroid Build Coastguard Worker        if self.debuglevel > 0:
1114*cda5da8dSAndroid Build Coastguard Worker            self._print_debug('connect:', msg)
1115*cda5da8dSAndroid Build Coastguard Worker        return (code, msg)
1116*cda5da8dSAndroid Build Coastguard Worker
1117*cda5da8dSAndroid Build Coastguard Worker
1118*cda5da8dSAndroid Build Coastguard Worker# Test the sendmail method, which tests most of the others.
1119*cda5da8dSAndroid Build Coastguard Worker# Note: This always sends to localhost.
1120*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__':
1121*cda5da8dSAndroid Build Coastguard Worker    def prompt(prompt):
1122*cda5da8dSAndroid Build Coastguard Worker        sys.stdout.write(prompt + ": ")
1123*cda5da8dSAndroid Build Coastguard Worker        sys.stdout.flush()
1124*cda5da8dSAndroid Build Coastguard Worker        return sys.stdin.readline().strip()
1125*cda5da8dSAndroid Build Coastguard Worker
1126*cda5da8dSAndroid Build Coastguard Worker    fromaddr = prompt("From")
1127*cda5da8dSAndroid Build Coastguard Worker    toaddrs = prompt("To").split(',')
1128*cda5da8dSAndroid Build Coastguard Worker    print("Enter message, end with ^D:")
1129*cda5da8dSAndroid Build Coastguard Worker    msg = ''
1130*cda5da8dSAndroid Build Coastguard Worker    while 1:
1131*cda5da8dSAndroid Build Coastguard Worker        line = sys.stdin.readline()
1132*cda5da8dSAndroid Build Coastguard Worker        if not line:
1133*cda5da8dSAndroid Build Coastguard Worker            break
1134*cda5da8dSAndroid Build Coastguard Worker        msg = msg + line
1135*cda5da8dSAndroid Build Coastguard Worker    print("Message length is %d" % len(msg))
1136*cda5da8dSAndroid Build Coastguard Worker
1137*cda5da8dSAndroid Build Coastguard Worker    server = SMTP('localhost')
1138*cda5da8dSAndroid Build Coastguard Worker    server.set_debuglevel(1)
1139*cda5da8dSAndroid Build Coastguard Worker    server.sendmail(fromaddr, toaddrs, msg)
1140*cda5da8dSAndroid Build Coastguard Worker    server.quit()
1141