SF patch #461413 (Gerhard Häring): Add STARTTLS feature to smtplib

This patch adds the features from RFC 2487 (Secure SMTP
   over TLS) to the smtplib module:

   - A starttls() function
   - Wrapper classes that simulate enough of sockets and
     files for smtplib, but really wrap a SSLObject
   - reset the list of known SMTP extensions at each call
     of ehlo(). This should have been the case anyway.
This commit is contained in:
Guido van Rossum 2001-09-14 16:08:44 +00:00
parent 5f5512d246
commit f7fcf5eea6
2 changed files with 62 additions and 2 deletions

View File

@ -178,6 +178,14 @@ or may raise the following exceptions:
\end{description}
\end{methoddesc}
\begin{methoddesc}{starttls}{\optional{keyfile, certfile}}
Put the SMTP connection in TLS (Transport Layer Security) mode. All SMTP
commands that follow will be encrypted. You should then call ehlo() again.
If \var{keyfile} and \var{certfile} are provided, these are passed to the
socket module's ssl function.
\end{methoddesc}
\begin{methoddesc}{sendmail}{from_addr, to_addrs, msg\optional{,
mail_options, rcpt_options}}
Send mail. The required arguments are an \rfc{822} from-address

View File

@ -2,8 +2,8 @@
'''SMTP/ESMTP client class.
This should follow RFC 821 (SMTP), RFC 1869 (ESMTP) and RFC 2554 (SMTP
Authentication).
This should follow RFC 821 (SMTP), RFC 1869 (ESMTP), RFC 2554 (SMTP
Authentication) and RFC 2487 (Secure SMTP over TLS).
Notes:
@ -124,6 +124,41 @@ class SMTPAuthenticationError(SMTPResponseException):
combination provided.
"""
class SSLFakeSocket:
"""A fake socket object that really wraps a SSLObject.
It only supports what is needed in smtplib.
"""
def __init__(self, realsock, sslobj):
self.realsock = realsock
self.sslobj = sslobj
def send(self, str):
self.sslobj.write(str)
return len(str)
def close(self):
self.realsock.close()
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 = ""
chr = None
while chr != "\n":
chr = self.sslobj.read(1)
str += chr
return str
def close(self):
pass
def quoteaddr(addr):
"""Quote a subset of the email addresses defined by RFC 821.
@ -333,6 +368,7 @@ class SMTP:
Hostname to send for this command defaults to the FQDN of the local
host.
"""
self.esmtp_features = {}
if name:
self.putcmd("ehlo", name)
else:
@ -506,6 +542,22 @@ class SMTP:
raise SMTPAuthenticationError(code, resp)
return (code, resp)
def starttls(self, keyfile = None, certfile = None):
"""Puts the connection to the SMTP server into TLS mode.
If the server supports TLS, this will encrypt the rest of the SMTP
session. If you provide the keyfile and certfile parameters,
the identity of the SMTP server and client can be checked. This,
however, depends on whether the socket module really checks the
certificates.
"""
(resp, reply) = self.docmd("STARTTLS")
if resp == 220:
sslobj = socket.ssl(self.sock, keyfile, certfile)
self.sock = SSLFakeSocket(self.sock, sslobj)
self.file = SSLFakeFile(sslobj)
return (resp, reply)
def sendmail(self, from_addr, to_addrs, msg, mail_options=[],
rcpt_options=[]):
"""This command performs an entire mail transaction.