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