diff options
Diffstat (limited to 'Lib/smtplib.py')
| -rw-r--r-- | Lib/smtplib.py | 92 |
1 files changed, 50 insertions, 42 deletions
diff --git a/Lib/smtplib.py b/Lib/smtplib.py index 7b97a6a..c7ff821 100644 --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -133,24 +133,18 @@ class SMTPAuthenticationError(SMTPResponseException): combination provided. """ -def quoteaddr(addr): +def quoteaddr(addrstring): """Quote a subset of the email addresses defined by RFC 821. Should be able to handle anything email.utils.parseaddr can handle. """ - m = (None, None) - try: - m = email.utils.parseaddr(addr)[1] - except AttributeError: - pass - if m == (None, None): # Indicates parse failure or AttributeError - # something weird here.. punt -ddm - return "<%s>" % addr - elif m is None: - # the sender wants an empty return address - return "<>" - else: - return "<%s>" % m + displayname, addr = email.utils.parseaddr(addrstring) + if (displayname, addr) == ('', ''): + # parseaddr couldn't parse it, use it as is and hope for the best. + if addrstring.strip().startswith('<'): + return addrstring + return "<%s>" % addrstring + return "<%s>" % addr def _addr_only(addrstring): displayname, addr = email.utils.parseaddr(addrstring) @@ -180,27 +174,6 @@ try: except ImportError: _have_ssl = False else: - class SSLFakeFile: - """A fake file like object that really wraps a SSLObject. - - It only supports what is needed in smtplib. - """ - def __init__(self, sslobj): - self.sslobj = sslobj - - def readline(self): - str = b"" - chr = None - while chr != b"\n": - chr = self.sslobj.read(1) - if not chr: - break - str += chr - return str - - def close(self): - pass - _have_ssl = True @@ -277,6 +250,19 @@ class SMTP: pass self.local_hostname = '[%s]' % addr + def __enter__(self): + return self + + def __exit__(self, *args): + try: + code, message = self.docmd("QUIT") + if code != 221: + raise SMTPResponseException(code, message) + except SMTPServerDisconnected: + pass + finally: + self.close() + def set_debuglevel(self, debuglevel): """Set the debug output level. @@ -317,6 +303,7 @@ class SMTP: if self.debuglevel > 0: print('connect:', (host, port), file=stderr) self.sock = self._get_socket(host, port, self.timeout) + self.file = None (code, msg) = self.getreply() if self.debuglevel > 0: print("connect:", msg, file=stderr) @@ -630,7 +617,7 @@ class SMTP: # We could not login sucessfully. Return result of last attempt. raise SMTPAuthenticationError(code, resp) - def starttls(self, keyfile=None, certfile=None): + def starttls(self, keyfile=None, certfile=None, context=None): """Puts the connection to the SMTP server into TLS mode. If there has been no previous EHLO or HELO command this session, this @@ -654,8 +641,17 @@ class SMTP: if resp == 220: if not _have_ssl: raise RuntimeError("No SSL support included in this Python") - self.sock = ssl.wrap_socket(self.sock, keyfile, certfile) - self.file = SSLFakeFile(self.sock) + if context is not None and keyfile is not None: + raise ValueError("context and keyfile arguments are mutually " + "exclusive") + if context is not None and certfile is not None: + raise ValueError("context and certfile arguments are mutually " + "exclusive") + if context is not None: + self.sock = context.wrap_socket(self.sock) + else: + self.sock = ssl.wrap_socket(self.sock, keyfile, certfile) + self.file = None # RFC 3207: # The client MUST discard any knowledge obtained from # the server, such as the list of SMTP service extensions, @@ -835,24 +831,35 @@ if _have_ssl: support). If host is not specified, '' (the local host) is used. If port is omitted, the standard SMTP-over-SSL port (465) is used. keyfile and certfile are also optional - they can contain a PEM formatted private key and - certificate chain file for the SSL connection. + certificate chain file for the SSL connection. context also optional, can contain + a SSLContext, and is an alternative to keyfile and certfile; If it is specified both + keyfile and certfile must be None. """ default_port = SMTP_SSL_PORT def __init__(self, host='', port=0, local_hostname=None, keyfile=None, certfile=None, - timeout=socket._GLOBAL_DEFAULT_TIMEOUT): + timeout=socket._GLOBAL_DEFAULT_TIMEOUT, context=None): + if context is not None and keyfile is not None: + raise ValueError("context and keyfile arguments are mutually " + "exclusive") + if context is not None and certfile is not None: + raise ValueError("context and certfile arguments are mutually " + "exclusive") self.keyfile = keyfile self.certfile = certfile + self.context = context SMTP.__init__(self, host, port, local_hostname, timeout) def _get_socket(self, host, port, timeout): if self.debuglevel > 0: print('connect:', (host, port), file=stderr) new_socket = socket.create_connection((host, port), timeout) - new_socket = ssl.wrap_socket(new_socket, self.keyfile, self.certfile) - self.file = SSLFakeFile(new_socket) + if self.context is not None: + new_socket = self.context.wrap_socket(new_socket) + else: + new_socket = ssl.wrap_socket(new_socket, self.keyfile, self.certfile) return new_socket __all__.append("SMTP_SSL") @@ -889,6 +896,7 @@ class LMTP(SMTP): # Handle Unix-domain sockets. try: self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.file = None self.sock.connect(host) except socket.error as msg: if self.debuglevel > 0: |
