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