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