mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Better RFC 821 compliance (MAIL and RCPT, and CRLF in data)
by Carey Evans <c.evans@clear.net.nz>, for picky mail servers.
This commit is contained in:
parent
5c44027d8a
commit
69a79bca68
1 changed files with 37 additions and 15 deletions
|
@ -4,6 +4,8 @@
|
||||||
Author: The Dragon De Monsyne <dragondm@integral.org>
|
Author: The Dragon De Monsyne <dragondm@integral.org>
|
||||||
ESMTP support, test code and doc fixes added by
|
ESMTP support, test code and doc fixes added by
|
||||||
Eric S. Raymond <esr@thyrsus.com>
|
Eric S. Raymond <esr@thyrsus.com>
|
||||||
|
Better RFC 821 compliance (MAIL and RCPT, and CRLF in data)
|
||||||
|
by Carey Evans <c.evans@clear.net.nz>, for picky mail servers.
|
||||||
|
|
||||||
(This was modified from the Python 1.5 library HTTP lib.)
|
(This was modified from the Python 1.5 library HTTP lib.)
|
||||||
|
|
||||||
|
@ -43,6 +45,37 @@ SMTPSenderRefused="Sender address refused"
|
||||||
SMTPRecipientsRefused="All Recipients refused"
|
SMTPRecipientsRefused="All Recipients refused"
|
||||||
SMTPDataError="Error transmitting message data"
|
SMTPDataError="Error transmitting message data"
|
||||||
|
|
||||||
|
def quoteaddr(addr):
|
||||||
|
"""Quote a subset of the email addresses defined by RFC 821.
|
||||||
|
|
||||||
|
Technically, only a <mailbox> is allowed. In addition,
|
||||||
|
email addresses without a domain are permitted.
|
||||||
|
|
||||||
|
Addresses will not be modified if they are already quoted
|
||||||
|
(actually if they begin with '<' and end with '>'."""
|
||||||
|
if re.match('(?s)\A<.*>\Z', addr):
|
||||||
|
return addr
|
||||||
|
|
||||||
|
localpart = None
|
||||||
|
domain = ''
|
||||||
|
try:
|
||||||
|
at = string.rindex(addr, '@')
|
||||||
|
localpart = addr[:at]
|
||||||
|
domain = addr[at:]
|
||||||
|
except ValueError:
|
||||||
|
localpart = addr
|
||||||
|
|
||||||
|
pat = re.compile(r'([<>()\[\]\\,;:@\"\001-\037\177])')
|
||||||
|
return '<%s%s>' % (pat.sub(r'\\\1', localpart), domain)
|
||||||
|
|
||||||
|
def quotedata(data):
|
||||||
|
"""Quote data for email.
|
||||||
|
|
||||||
|
Double leading '.', and change Unix newline '\n' into
|
||||||
|
Internet CRLF end-of-line."""
|
||||||
|
return re.sub(r'(?m)^\.', '..',
|
||||||
|
re.sub(r'\r?\n', CRLF, data))
|
||||||
|
|
||||||
class SMTP:
|
class SMTP:
|
||||||
"""This class manages a connection to an SMTP or ESMTP server."""
|
"""This class manages a connection to an SMTP or ESMTP server."""
|
||||||
debuglevel = 0
|
debuglevel = 0
|
||||||
|
@ -208,36 +241,25 @@ class SMTP:
|
||||||
options = " " + string.joinfields(options, ' ')
|
options = " " + string.joinfields(options, ' ')
|
||||||
else:
|
else:
|
||||||
options = ''
|
options = ''
|
||||||
self.putcmd("mail from:", sender + options)
|
self.putcmd("mail", "from:" + quoteaddr(sender) + options)
|
||||||
return self.getreply()
|
return self.getreply()
|
||||||
|
|
||||||
def rcpt(self,recip):
|
def rcpt(self,recip):
|
||||||
""" SMTP 'rcpt' command. Indicates 1 recipient for this mail. """
|
""" SMTP 'rcpt' command. Indicates 1 recipient for this mail. """
|
||||||
self.putcmd("rcpt","to: %s" % recip)
|
self.putcmd("rcpt","to:%s" % quoteaddr(recip))
|
||||||
return self.getreply()
|
return self.getreply()
|
||||||
|
|
||||||
def data(self,msg):
|
def data(self,msg):
|
||||||
""" SMTP 'DATA' command. Sends message data to server.
|
""" SMTP 'DATA' command. Sends message data to server.
|
||||||
Automatically quotes lines beginning with a period per rfc821. """
|
Automatically quotes lines beginning with a period per rfc821. """
|
||||||
#quote periods in msg according to RFC821
|
|
||||||
# ps, I don't know why I have to do it this way... doing:
|
|
||||||
# quotepat=re.compile(r"^[.]",re.M)
|
|
||||||
# msg=re.sub(quotepat,"..",msg)
|
|
||||||
# should work, but it dosen't (it doubles the number of any
|
|
||||||
# contiguous series of .'s at the beginning of a line,
|
|
||||||
#instead of just adding one. )
|
|
||||||
quotepat=re.compile(r"^[.]+",re.M)
|
|
||||||
def m(pat):
|
|
||||||
return "."+pat.group(0)
|
|
||||||
msg=re.sub(quotepat,m,msg)
|
|
||||||
self.putcmd("data")
|
self.putcmd("data")
|
||||||
(code,repl)=self.getreply()
|
(code,repl)=self.getreply()
|
||||||
if self.debuglevel >0 : print "data:", (code,repl)
|
if self.debuglevel >0 : print "data:", (code,repl)
|
||||||
if code <> 354:
|
if code <> 354:
|
||||||
return -1
|
return -1
|
||||||
else:
|
else:
|
||||||
self.send(msg)
|
self.send(quotedata(msg))
|
||||||
self.send("\n.\n")
|
self.send("%s.%s" % (CRLF, CRLF))
|
||||||
(code,msg)=self.getreply()
|
(code,msg)=self.getreply()
|
||||||
if self.debuglevel >0 : print "data:", (code,msg)
|
if self.debuglevel >0 : print "data:", (code,msg)
|
||||||
return code
|
return code
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue