python (3.12.0)

(root)/
lib/
python3.12/
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, *, 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 context parameter,
     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 None:
     778                  context = ssl._create_stdlib_context()
     779              self.sock = context.wrap_socket(self.sock,
     780                                              server_hostname=self._host)
     781              self.file = None
     782              # RFC 3207:
     783              # The client MUST discard any knowledge obtained from
     784              # the server, such as the list of SMTP service extensions,
     785              # which was not obtained from the TLS negotiation itself.
     786              self.helo_resp = None
     787              self.ehlo_resp = None
     788              self.esmtp_features = {}
     789              self.does_esmtp = False
     790          else:
     791              # RFC 3207:
     792              # 501 Syntax error (no parameters allowed)
     793              # 454 TLS not available due to temporary reason
     794              raise SMTPResponseException(resp, reply)
     795          return (resp, reply)
     796  
     797      def sendmail(self, from_addr, to_addrs, msg, mail_options=(),
     798                   rcpt_options=()):
     799          """This command performs an entire mail transaction.
     800  
     801          The arguments are:
     802              - from_addr    : The address sending this mail.
     803              - to_addrs     : A list of addresses to send this mail to.  A bare
     804                               string will be treated as a list with 1 address.
     805              - msg          : The message to send.
     806              - mail_options : List of ESMTP options (such as 8bitmime) for the
     807                               mail command.
     808              - rcpt_options : List of ESMTP options (such as DSN commands) for
     809                               all the rcpt commands.
     810  
     811          msg may be a string containing characters in the ASCII range, or a byte
     812          string.  A string is encoded to bytes using the ascii codec, and lone
     813          \\r and \\n characters are converted to \\r\\n characters.
     814  
     815          If there has been no previous EHLO or HELO command this session, this
     816          method tries ESMTP EHLO first.  If the server does ESMTP, message size
     817          and each of the specified options will be passed to it.  If EHLO
     818          fails, HELO will be tried and ESMTP options suppressed.
     819  
     820          This method will return normally if the mail is accepted for at least
     821          one recipient.  It returns a dictionary, with one entry for each
     822          recipient that was refused.  Each entry contains a tuple of the SMTP
     823          error code and the accompanying error message sent by the server.
     824  
     825          This method may raise the following exceptions:
     826  
     827           SMTPHeloError          The server didn't reply properly to
     828                                  the helo greeting.
     829           SMTPRecipientsRefused  The server rejected ALL recipients
     830                                  (no mail was sent).
     831           SMTPSenderRefused      The server didn't accept the from_addr.
     832           SMTPDataError          The server replied with an unexpected
     833                                  error code (other than a refusal of
     834                                  a recipient).
     835           SMTPNotSupportedError  The mail_options parameter includes 'SMTPUTF8'
     836                                  but the SMTPUTF8 extension is not supported by
     837                                  the server.
     838  
     839          Note: the connection will be open even after an exception is raised.
     840  
     841          Example:
     842  
     843           >>> import smtplib
     844           >>> s=smtplib.SMTP("localhost")
     845           >>> tolist=["one@one.org","two@two.org","three@three.org","four@four.org"]
     846           >>> msg = '''\\
     847           ... From: Me@my.org
     848           ... Subject: testin'...
     849           ...
     850           ... This is a test '''
     851           >>> s.sendmail("me@my.org",tolist,msg)
     852           { "three@three.org" : ( 550 ,"User unknown" ) }
     853           >>> s.quit()
     854  
     855          In the above example, the message was accepted for delivery to three
     856          of the four addresses, and one was rejected, with the error code
     857          550.  If all addresses are accepted, then the method will return an
     858          empty dictionary.
     859  
     860          """
     861          self.ehlo_or_helo_if_needed()
     862          esmtp_opts = []
     863          if isinstance(msg, str):
     864              msg = _fix_eols(msg).encode('ascii')
     865          if self.does_esmtp:
     866              if self.has_extn('size'):
     867                  esmtp_opts.append("size=%d" % len(msg))
     868              for option in mail_options:
     869                  esmtp_opts.append(option)
     870          (code, resp) = self.mail(from_addr, esmtp_opts)
     871          if code != 250:
     872              if code == 421:
     873                  self.close()
     874              else:
     875                  self._rset()
     876              raise SMTPSenderRefused(code, resp, from_addr)
     877          senderrs = {}
     878          if isinstance(to_addrs, str):
     879              to_addrs = [to_addrs]
     880          for each in to_addrs:
     881              (code, resp) = self.rcpt(each, rcpt_options)
     882              if (code != 250) and (code != 251):
     883                  senderrs[each] = (code, resp)
     884              if code == 421:
     885                  self.close()
     886                  raise SMTPRecipientsRefused(senderrs)
     887          if len(senderrs) == len(to_addrs):
     888              # the server refused all our recipients
     889              self._rset()
     890              raise SMTPRecipientsRefused(senderrs)
     891          (code, resp) = self.data(msg)
     892          if code != 250:
     893              if code == 421:
     894                  self.close()
     895              else:
     896                  self._rset()
     897              raise SMTPDataError(code, resp)
     898          #if we got here then somebody got our mail
     899          return senderrs
     900  
     901      def send_message(self, msg, from_addr=None, to_addrs=None,
     902                       mail_options=(), rcpt_options=()):
     903          """Converts message to a bytestring and passes it to sendmail.
     904  
     905          The arguments are as for sendmail, except that msg is an
     906          email.message.Message object.  If from_addr is None or to_addrs is
     907          None, these arguments are taken from the headers of the Message as
     908          described in RFC 2822 (a ValueError is raised if there is more than
     909          one set of 'Resent-' headers).  Regardless of the values of from_addr and
     910          to_addr, any Bcc field (or Resent-Bcc field, when the Message is a
     911          resent) of the Message object won't be transmitted.  The Message
     912          object is then serialized using email.generator.BytesGenerator and
     913          sendmail is called to transmit the message.  If the sender or any of
     914          the recipient addresses contain non-ASCII and the server advertises the
     915          SMTPUTF8 capability, the policy is cloned with utf8 set to True for the
     916          serialization, and SMTPUTF8 and BODY=8BITMIME are asserted on the send.
     917          If the server does not support SMTPUTF8, an SMTPNotSupported error is
     918          raised.  Otherwise the generator is called without modifying the
     919          policy.
     920  
     921          """
     922          # 'Resent-Date' is a mandatory field if the Message is resent (RFC 2822
     923          # Section 3.6.6). In such a case, we use the 'Resent-*' fields.  However,
     924          # if there is more than one 'Resent-' block there's no way to
     925          # unambiguously determine which one is the most recent in all cases,
     926          # so rather than guess we raise a ValueError in that case.
     927          #
     928          # TODO implement heuristics to guess the correct Resent-* block with an
     929          # option allowing the user to enable the heuristics.  (It should be
     930          # possible to guess correctly almost all of the time.)
     931  
     932          self.ehlo_or_helo_if_needed()
     933          resent = msg.get_all('Resent-Date')
     934          if resent is None:
     935              header_prefix = ''
     936          elif len(resent) == 1:
     937              header_prefix = 'Resent-'
     938          else:
     939              raise ValueError("message has more than one 'Resent-' header block")
     940          if from_addr is None:
     941              # Prefer the sender field per RFC 2822:3.6.2.
     942              from_addr = (msg[header_prefix + 'Sender']
     943                             if (header_prefix + 'Sender') in msg
     944                             else msg[header_prefix + 'From'])
     945              from_addr = email.utils.getaddresses([from_addr])[0][1]
     946          if to_addrs is None:
     947              addr_fields = [f for f in (msg[header_prefix + 'To'],
     948                                         msg[header_prefix + 'Bcc'],
     949                                         msg[header_prefix + 'Cc'])
     950                             if f is not None]
     951              to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)]
     952          # Make a local copy so we can delete the bcc headers.
     953          msg_copy = copy.copy(msg)
     954          del msg_copy['Bcc']
     955          del msg_copy['Resent-Bcc']
     956          international = False
     957          try:
     958              ''.join([from_addr, *to_addrs]).encode('ascii')
     959          except UnicodeEncodeError:
     960              if not self.has_extn('smtputf8'):
     961                  raise SMTPNotSupportedError(
     962                      "One or more source or delivery addresses require"
     963                      " internationalized email support, but the server"
     964                      " does not advertise the required SMTPUTF8 capability")
     965              international = True
     966          with io.BytesIO() as bytesmsg:
     967              if international:
     968                  g = email.generator.BytesGenerator(
     969                      bytesmsg, policy=msg.policy.clone(utf8=True))
     970                  mail_options = (*mail_options, 'SMTPUTF8', 'BODY=8BITMIME')
     971              else:
     972                  g = email.generator.BytesGenerator(bytesmsg)
     973              g.flatten(msg_copy, linesep='\r\n')
     974              flatmsg = bytesmsg.getvalue()
     975          return self.sendmail(from_addr, to_addrs, flatmsg, mail_options,
     976                               rcpt_options)
     977  
     978      def close(self):
     979          """Close the connection to the SMTP server."""
     980          try:
     981              file = self.file
     982              self.file = None
     983              if file:
     984                  file.close()
     985          finally:
     986              sock = self.sock
     987              self.sock = None
     988              if sock:
     989                  sock.close()
     990  
     991      def quit(self):
     992          """Terminate the SMTP session."""
     993          res = self.docmd("quit")
     994          # A new EHLO is required after reconnecting with connect()
     995          self.ehlo_resp = self.helo_resp = None
     996          self.esmtp_features = {}
     997          self.does_esmtp = False
     998          self.close()
     999          return res
    1000  
    1001  if _have_ssl:
    1002  
    1003      class ESC[4;38;5;81mSMTP_SSL(ESC[4;38;5;149mSMTP):
    1004          """ This is a subclass derived from SMTP that connects over an SSL
    1005          encrypted socket (to use this class you need a socket module that was
    1006          compiled with SSL support). If host is not specified, '' (the local
    1007          host) is used. If port is omitted, the standard SMTP-over-SSL port
    1008          (465) is used.  local_hostname and source_address have the same meaning
    1009          as they do in the SMTP class.  context also optional, can contain a
    1010          SSLContext.
    1011  
    1012          """
    1013  
    1014          default_port = SMTP_SSL_PORT
    1015  
    1016          def __init__(self, host='', port=0, local_hostname=None,
    1017                       *, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
    1018                       source_address=None, context=None):
    1019              if context is None:
    1020                  context = ssl._create_stdlib_context()
    1021              self.context = context
    1022              SMTP.__init__(self, host, port, local_hostname, timeout,
    1023                            source_address)
    1024  
    1025          def _get_socket(self, host, port, timeout):
    1026              if self.debuglevel > 0:
    1027                  self._print_debug('connect:', (host, port))
    1028              new_socket = super()._get_socket(host, port, timeout)
    1029              new_socket = self.context.wrap_socket(new_socket,
    1030                                                    server_hostname=self._host)
    1031              return new_socket
    1032  
    1033      __all__.append("SMTP_SSL")
    1034  
    1035  #
    1036  # LMTP extension
    1037  #
    1038  LMTP_PORT = 2003
    1039  
    1040  class ESC[4;38;5;81mLMTP(ESC[4;38;5;149mSMTP):
    1041      """LMTP - Local Mail Transfer Protocol
    1042  
    1043      The LMTP protocol, which is very similar to ESMTP, is heavily based
    1044      on the standard SMTP client. It's common to use Unix sockets for
    1045      LMTP, so our connect() method must support that as well as a regular
    1046      host:port server.  local_hostname and source_address have the same
    1047      meaning as they do in the SMTP class.  To specify a Unix socket,
    1048      you must use an absolute path as the host, starting with a '/'.
    1049  
    1050      Authentication is supported, using the regular SMTP mechanism. When
    1051      using a Unix socket, LMTP generally don't support or require any
    1052      authentication, but your mileage might vary."""
    1053  
    1054      ehlo_msg = "lhlo"
    1055  
    1056      def __init__(self, host='', port=LMTP_PORT, local_hostname=None,
    1057                   source_address=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
    1058          """Initialize a new instance."""
    1059          super().__init__(host, port, local_hostname=local_hostname,
    1060                           source_address=source_address, timeout=timeout)
    1061  
    1062      def connect(self, host='localhost', port=0, source_address=None):
    1063          """Connect to the LMTP daemon, on either a Unix or a TCP socket."""
    1064          if host[0] != '/':
    1065              return super().connect(host, port, source_address=source_address)
    1066  
    1067          if self.timeout is not None and not self.timeout:
    1068              raise ValueError('Non-blocking socket (timeout=0) is not supported')
    1069  
    1070          # Handle Unix-domain sockets.
    1071          try:
    1072              self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    1073              if self.timeout is not socket._GLOBAL_DEFAULT_TIMEOUT:
    1074                  self.sock.settimeout(self.timeout)
    1075              self.file = None
    1076              self.sock.connect(host)
    1077          except OSError:
    1078              if self.debuglevel > 0:
    1079                  self._print_debug('connect fail:', host)
    1080              if self.sock:
    1081                  self.sock.close()
    1082              self.sock = None
    1083              raise
    1084          (code, msg) = self.getreply()
    1085          if self.debuglevel > 0:
    1086              self._print_debug('connect:', msg)
    1087          return (code, msg)
    1088  
    1089  
    1090  # Test the sendmail method, which tests most of the others.
    1091  # Note: This always sends to localhost.
    1092  if __name__ == '__main__':
    1093      def prompt(prompt):
    1094          sys.stdout.write(prompt + ": ")
    1095          sys.stdout.flush()
    1096          return sys.stdin.readline().strip()
    1097  
    1098      fromaddr = prompt("From")
    1099      toaddrs = prompt("To").split(',')
    1100      print("Enter message, end with ^D:")
    1101      msg = ''
    1102      while line := sys.stdin.readline():
    1103          msg = msg + line
    1104      print("Message length is %d" % len(msg))
    1105  
    1106      server = SMTP('localhost')
    1107      server.set_debuglevel(1)
    1108      server.sendmail(fromaddr, toaddrs, msg)
    1109      server.quit()