mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Port various other bugfixes in smtplib.py from upstream
This commit is contained in:
parent
0b86d5175a
commit
415ebb7a6c
@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
from __future__ import print_function
|
||||
|
||||
'''SMTP/ESMTP client class.
|
||||
@ -47,8 +48,8 @@ import re
|
||||
import email.utils
|
||||
import base64
|
||||
import hmac
|
||||
import sys
|
||||
from email.base64mime import encode as encode_base64
|
||||
from sys import stderr
|
||||
from functools import partial
|
||||
|
||||
__all__ = ["SMTPException", "SMTPServerDisconnected", "SMTPResponseException",
|
||||
@ -59,9 +60,11 @@ __all__ = ["SMTPException","SMTPServerDisconnected","SMTPResponseException",
|
||||
SMTP_PORT = 25
|
||||
SMTP_SSL_PORT = 465
|
||||
CRLF = "\r\n"
|
||||
_MAXLINE = 8192 # more than 8 times larger than RFC 821, 4.5.3
|
||||
|
||||
OLDSTYLE_AUTH = re.compile(r"auth=(.*)", re.I)
|
||||
|
||||
|
||||
# Exception classes used by this module.
|
||||
class SMTPException(Exception):
|
||||
"""Base class for all exceptions raised by this module."""
|
||||
@ -130,6 +133,7 @@ class SMTPAuthenticationError(SMTPResponseException):
|
||||
combination provided.
|
||||
"""
|
||||
|
||||
|
||||
def quoteaddr(addr):
|
||||
"""Quote a subset of the email addresses defined by RFC 821.
|
||||
|
||||
@ -149,6 +153,13 @@ def quoteaddr(addr):
|
||||
else:
|
||||
return "<%s>" % m
|
||||
|
||||
def _addr_only(addrstring):
|
||||
displayname, addr = email.utils.parseaddr(addrstring)
|
||||
if (displayname, addr) == ('', ''):
|
||||
# parseaddr couldn't parse it, so use it as is.
|
||||
return addrstring
|
||||
return addr
|
||||
|
||||
def quotedata(data):
|
||||
"""Quote data for email.
|
||||
|
||||
@ -172,10 +183,14 @@ else:
|
||||
def __init__(self, sslobj):
|
||||
self.sslobj = sslobj
|
||||
|
||||
def readline(self):
|
||||
def readline(self, size=-1):
|
||||
if size < 0:
|
||||
size = None
|
||||
str = ""
|
||||
chr = None
|
||||
while chr != "\n":
|
||||
if size is not None and len(str) >= size:
|
||||
break
|
||||
chr = self.sslobj.read(1)
|
||||
if not chr:
|
||||
break
|
||||
@ -222,10 +237,11 @@ class SMTP:
|
||||
ehlo_msg = "ehlo"
|
||||
ehlo_resp = None
|
||||
does_esmtp = 0
|
||||
default_port = SMTP_PORT
|
||||
|
||||
def __init__(self, host='', port=0, local_hostname=None,
|
||||
timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
|
||||
debug_to=partial(print, file=sys.stderr)):
|
||||
debug_to=partial(print, file=stderr)):
|
||||
"""Initialize a new instance.
|
||||
|
||||
If specified, `host' is the name of the remote host to which to
|
||||
@ -241,7 +257,6 @@ class SMTP:
|
||||
self.timeout = timeout
|
||||
self.debug = debug_to
|
||||
self.esmtp_features = {}
|
||||
self.default_port = SMTP_PORT
|
||||
if host:
|
||||
(code, msg) = self.connect(host, port)
|
||||
if code != 220:
|
||||
@ -356,14 +371,18 @@ class SMTP:
|
||||
self.file = self.sock.makefile('rb')
|
||||
while True:
|
||||
try:
|
||||
line = self.file.readline()
|
||||
except socket.error:
|
||||
line = ''
|
||||
line = self.file.readline(_MAXLINE + 1)
|
||||
except socket.error as e:
|
||||
self.close()
|
||||
raise SMTPServerDisconnected("Connection unexpectedly closed: " +
|
||||
str(e))
|
||||
if line == '':
|
||||
self.close()
|
||||
raise SMTPServerDisconnected("Connection unexpectedly closed")
|
||||
if self.debuglevel > 0:
|
||||
self.debug('reply:', repr(line))
|
||||
if len(line) > _MAXLINE:
|
||||
raise SMTPResponseException(500, "Line too long.")
|
||||
resp.append(line[4:].strip())
|
||||
code = line[:3]
|
||||
# Check that the error code is syntactically correct.
|
||||
@ -509,14 +528,14 @@ class SMTP:
|
||||
|
||||
def verify(self, address):
|
||||
"""SMTP 'verify' command -- checks for address validity."""
|
||||
self.putcmd("vrfy", quoteaddr(address))
|
||||
self.putcmd("vrfy", _addr_only(address))
|
||||
return self.getreply()
|
||||
# a.k.a.
|
||||
vrfy = verify
|
||||
|
||||
def expn(self, address):
|
||||
"""SMTP 'expn' command -- expands a mailing list."""
|
||||
self.putcmd("expn", quoteaddr(address))
|
||||
self.putcmd("expn", _addr_only(address))
|
||||
return self.getreply()
|
||||
|
||||
# some useful methods
|
||||
@ -749,16 +768,24 @@ class SMTP:
|
||||
|
||||
def close(self):
|
||||
"""Close the connection to the SMTP server."""
|
||||
if self.file:
|
||||
self.file.close()
|
||||
try:
|
||||
file = self.file
|
||||
self.file = None
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
if file:
|
||||
file.close()
|
||||
finally:
|
||||
sock = self.sock
|
||||
self.sock = None
|
||||
if sock:
|
||||
sock.close()
|
||||
|
||||
def quit(self):
|
||||
"""Terminate the SMTP session."""
|
||||
res = self.docmd("quit")
|
||||
# A new EHLO is required after reconnecting with connect()
|
||||
self.ehlo_resp = self.helo_resp = None
|
||||
self.esmtp_features = {}
|
||||
self.does_esmtp = False
|
||||
self.close()
|
||||
return res
|
||||
|
||||
@ -772,15 +799,17 @@ if _have_ssl:
|
||||
are also optional - they can contain a PEM formatted private key and
|
||||
certificate chain file for the SSL connection.
|
||||
"""
|
||||
|
||||
default_port = SMTP_SSL_PORT
|
||||
|
||||
def __init__(self, host='', port=0, local_hostname=None,
|
||||
keyfile=None, certfile=None,
|
||||
timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
|
||||
debug_to=partial(print, file=sys.stderr)):
|
||||
debug_to=partial(print, file=stderr)):
|
||||
self.keyfile = keyfile
|
||||
self.certfile = certfile
|
||||
SMTP.__init__(self, host, port, local_hostname, timeout,
|
||||
debug_to=debug_to)
|
||||
self.default_port = SMTP_SSL_PORT
|
||||
|
||||
def _get_socket(self, host, port, timeout):
|
||||
if self.debuglevel > 0:
|
||||
@ -825,18 +854,40 @@ class LMTP(SMTP):
|
||||
try:
|
||||
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
self.sock.connect(host)
|
||||
except socket.error as msg:
|
||||
except socket.error:
|
||||
if self.debuglevel > 0:
|
||||
self.debug('connect fail:', host)
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
raise socket.error(msg)
|
||||
raise
|
||||
(code, msg) = self.getreply()
|
||||
if self.debuglevel > 0:
|
||||
self.debug("connect:", msg)
|
||||
return (code, msg)
|
||||
|
||||
|
||||
# Test the sendmail method, which tests most of the others.
|
||||
# Note: This always sends to localhost.
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
|
||||
def prompt(prompt):
|
||||
sys.stdout.write(prompt + ": ")
|
||||
return sys.stdin.readline().strip()
|
||||
|
||||
fromaddr = prompt("From")
|
||||
toaddrs = prompt("To").split(',')
|
||||
print ("Enter message, end with ^D:")
|
||||
msg = ''
|
||||
while 1:
|
||||
line = sys.stdin.readline()
|
||||
if not line:
|
||||
break
|
||||
msg = msg + line
|
||||
print ("Message length is %d" % len(msg))
|
||||
|
||||
server = SMTP('localhost')
|
||||
server.set_debuglevel(1)
|
||||
server.sendmail(fromaddr, toaddrs, msg)
|
||||
server.quit()
|
||||
|
Loading…
x
Reference in New Issue
Block a user