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