Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(5)

Side by Side Diff: Lib/smtplib.py

Issue 10639: reindent.py converts newlines to platform default
Patch Set: Created 8 years, 10 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Lib/shutil.py ('k') | Lib/ssl.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #! /usr/bin/env python3 1 #! /usr/bin/env python3
2 2
3 '''SMTP/ESMTP client class. 3 '''SMTP/ESMTP client class.
4 4
5 This should follow RFC 821 (SMTP), RFC 1869 (ESMTP), RFC 2554 (SMTP 5 This should follow RFC 821 (SMTP), RFC 1869 (ESMTP), RFC 2554 (SMTP
6 Authentication) and RFC 2487 (Secure SMTP over TLS). 6 Authentication) and RFC 2487 (Secure SMTP over TLS).
7 7
8 Notes: 8 Notes:
9 9
10 Please remember, when doing ESMTP, that the names of the SMTP service 10 Please remember, when doing ESMTP, that the names of the SMTP service
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
42 # This was modified from the Python 1.5 library HTTP lib. 42 # This was modified from the Python 1.5 library HTTP lib.
43 43
44 import socket 44 import socket
45 import io 45 import io
46 import re 46 import re
47 import email.utils 47 import email.utils
48 import email.message 48 import email.message
49 import email.generator 49 import email.generator
50 import base64 50 import base64
51 import hmac 51 import hmac
52 import copy
53 from email.base64mime import body_encode as encode_base64 52 from email.base64mime import body_encode as encode_base64
54 from sys import stderr 53 from sys import stderr
55 54
56 __all__ = ["SMTPException", "SMTPServerDisconnected", "SMTPResponseException", 55 __all__ = ["SMTPException", "SMTPServerDisconnected", "SMTPResponseException",
57 "SMTPSenderRefused", "SMTPRecipientsRefused", "SMTPDataError", 56 "SMTPSenderRefused", "SMTPRecipientsRefused", "SMTPDataError",
58 "SMTPConnectError", "SMTPHeloError", "SMTPAuthenticationError", 57 "SMTPConnectError", "SMTPHeloError", "SMTPAuthenticationError",
59 "quoteaddr", "quotedata", "SMTP"] 58 "quoteaddr", "quotedata", "SMTP"]
60 59
61 SMTP_PORT = 25 60 SMTP_PORT = 25
62 SMTP_SSL_PORT = 465 61 SMTP_SSL_PORT = 465
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
126 class SMTPHeloError(SMTPResponseException): 125 class SMTPHeloError(SMTPResponseException):
127 """The server refused our HELO reply.""" 126 """The server refused our HELO reply."""
128 127
129 class SMTPAuthenticationError(SMTPResponseException): 128 class SMTPAuthenticationError(SMTPResponseException):
130 """Authentication error. 129 """Authentication error.
131 130
132 Most probably the server didn't accept the username/password 131 Most probably the server didn't accept the username/password
133 combination provided. 132 combination provided.
134 """ 133 """
135 134
136 def quoteaddr(addrstring): 135 def quoteaddr(addr):
137 """Quote a subset of the email addresses defined by RFC 821. 136 """Quote a subset of the email addresses defined by RFC 821.
138 137
139 Should be able to handle anything email.utils.parseaddr can handle. 138 Should be able to handle anything email.utils.parseaddr can handle.
140 """ 139 """
141 displayname, addr = email.utils.parseaddr(addrstring) 140 m = (None, None)
142 if (displayname, addr) == ('', ''): 141 try:
143 # parseaddr couldn't parse it, use it as is and hope for the best. 142 m = email.utils.parseaddr(addr)[1]
144 if addrstring.strip().startswith('<'): 143 except AttributeError:
145 return addrstring 144 pass
146 return "<%s>" % addrstring 145 if m == (None, None): # Indicates parse failure or AttributeError
147 return "<%s>" % addr 146 # something weird here.. punt -ddm
148 147 return "<%s>" % addr
149 def _addr_only(addrstring): 148 elif m is None:
150 displayname, addr = email.utils.parseaddr(addrstring) 149 # the sender wants an empty return address
151 if (displayname, addr) == ('', ''): 150 return "<>"
152 # parseaddr couldn't parse it, so use it as is. 151 else:
153 return addrstring 152 return "<%s>" % m
154 return addr
155 153
156 # Legacy method kept for backward compatibility. 154 # Legacy method kept for backward compatibility.
157 def quotedata(data): 155 def quotedata(data):
158 """Quote data for email. 156 """Quote data for email.
159 157
160 Double leading '.', and change Unix newline '\\n', or Mac '\\r' into 158 Double leading '.', and change Unix newline '\\n', or Mac '\\r' into
161 Internet CRLF end-of-line. 159 Internet CRLF end-of-line.
162 """ 160 """
163 return re.sub(r'(?m)^\.', '..', 161 return re.sub(r'(?m)^\.', '..',
164 re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data)) 162 re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data))
(...skipping 336 matching lines...) Expand 10 before | Expand all | Expand 10 after
501 q = q + bCRLF 499 q = q + bCRLF
502 q = q + b"." + bCRLF 500 q = q + b"." + bCRLF
503 self.send(q) 501 self.send(q)
504 (code, msg) = self.getreply() 502 (code, msg) = self.getreply()
505 if self.debuglevel > 0: 503 if self.debuglevel > 0:
506 print("data:", (code, msg), file=stderr) 504 print("data:", (code, msg), file=stderr)
507 return (code, msg) 505 return (code, msg)
508 506
509 def verify(self, address): 507 def verify(self, address):
510 """SMTP 'verify' command -- checks for address validity.""" 508 """SMTP 'verify' command -- checks for address validity."""
511 self.putcmd("vrfy", _addr_only(address)) 509 self.putcmd("vrfy", quoteaddr(address))
512 return self.getreply() 510 return self.getreply()
513 # a.k.a. 511 # a.k.a.
514 vrfy = verify 512 vrfy = verify
515 513
516 def expn(self, address): 514 def expn(self, address):
517 """SMTP 'expn' command -- expands a mailing list.""" 515 """SMTP 'expn' command -- expands a mailing list."""
518 self.putcmd("expn", _addr_only(address)) 516 self.putcmd("expn", quoteaddr(address))
519 return self.getreply() 517 return self.getreply()
520 518
521 # some useful methods 519 # some useful methods
522 520
523 def ehlo_or_helo_if_needed(self): 521 def ehlo_or_helo_if_needed(self):
524 """Call self.ehlo() and/or self.helo() if needed. 522 """Call self.ehlo() and/or self.helo() if needed.
525 523
526 If there has been no previous EHLO or HELO command this session, this 524 If there has been no previous EHLO or HELO command this session, this
527 method tries ESMTP EHLO first. 525 method tries ESMTP EHLO first.
528 526
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
671 - to_addrs : A list of addresses to send this mail to. A bare 669 - to_addrs : A list of addresses to send this mail to. A bare
672 string will be treated as a list with 1 address. 670 string will be treated as a list with 1 address.
673 - msg : The message to send. 671 - msg : The message to send.
674 - mail_options : List of ESMTP options (such as 8bitmime) for the 672 - mail_options : List of ESMTP options (such as 8bitmime) for the
675 mail command. 673 mail command.
676 - rcpt_options : List of ESMTP options (such as DSN commands) for 674 - rcpt_options : List of ESMTP options (such as DSN commands) for
677 all the rcpt commands. 675 all the rcpt commands.
678 676
679 msg may be a string containing characters in the ASCII range, or a byte 677 msg may be a string containing characters in the ASCII range, or a byte
680 string. A string is encoded to bytes using the ascii codec, and lone 678 string. A string is encoded to bytes using the ascii codec, and lone
681 \\r and \\n characters are converted to \\r\\n characters. 679 \r and \n characters are converted to \r\n characters.
682 680
683 If there has been no previous EHLO or HELO command this session, this 681 If there has been no previous EHLO or HELO command this session, this
684 method tries ESMTP EHLO first. If the server does ESMTP, message size 682 method tries ESMTP EHLO first. If the server does ESMTP, message size
685 and each of the specified options will be passed to it. If EHLO 683 and each of the specified options will be passed to it. If EHLO
686 fails, HELO will be tried and ESMTP options suppressed. 684 fails, HELO will be tried and ESMTP options suppressed.
687 685
688 This method will return normally if the mail is accepted for at least 686 This method will return normally if the mail is accepted for at least
689 one recipient. It returns a dictionary, with one entry for each 687 one recipient. It returns a dictionary, with one entry for each
690 recipient that was refused. Each entry contains a tuple of the SMTP 688 recipient that was refused. Each entry contains a tuple of the SMTP
691 error code and the accompanying error message sent by the server. 689 error code and the accompanying error message sent by the server.
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
754 self.rset() 752 self.rset()
755 raise SMTPDataError(code, resp) 753 raise SMTPDataError(code, resp)
756 #if we got here then somebody got our mail 754 #if we got here then somebody got our mail
757 return senderrs 755 return senderrs
758 756
759 def send_message(self, msg, from_addr=None, to_addrs=None, 757 def send_message(self, msg, from_addr=None, to_addrs=None,
760 mail_options=[], rcpt_options={}): 758 mail_options=[], rcpt_options={}):
761 """Converts message to a bytestring and passes it to sendmail. 759 """Converts message to a bytestring and passes it to sendmail.
762 760
763 The arguments are as for sendmail, except that msg is an 761 The arguments are as for sendmail, except that msg is an
764 email.message.Message object. If from_addr is None or to_addrs is 762 email.message.Message object. If from_addr is None, the from_addr is
765 None, these arguments are taken from the headers of the Message as 763 taken from the 'From' header of the Message. If to_addrs is None, its
766 described in RFC 2822 (a ValueError is raised if there is more than 764 value is composed from the addresses listed in the 'To', 'CC', and
767 one set of 'Resent-' headers). Regardless of the values of from_addr an d 765 'Bcc' fields. Regardless of the values of from_addr and to_addr, any
768 to_addr, any Bcc field (or Resent-Bcc field, when the Message is a 766 Bcc field in the Message object is deleted. The Message object is then
769 resent) of the Message object won't be transmitted. The Message 767 serialized using email.generator.BytesGenerator and sendmail is called
770 object is then serialized using email.generator.BytesGenerator and 768 to transmit the message.
771 sendmail is called to transmit the message.
772
773 """ 769 """
774 # 'Resent-Date' is a mandatory field if the Message is resent (RFC 2822
775 # Section 3.6.6). In such a case, we use the 'Resent-*' fields. However ,
776 # if there is more than one 'Resent-' block there's no way to
777 # unambiguously determine which one is the most recent in all cases,
778 # so rather than guess we raise a ValueError in that case.
779 #
780 # TODO implement heuristics to guess the correct Resent-* block with an
781 # option allowing the user to enable the heuristics. (It should be
782 # possible to guess correctly almost all of the time.)
783 resent =msg.get_all('Resent-Date')
784 if resent is None:
785 header_prefix = ''
786 elif len(resent) == 1:
787 header_prefix = 'Resent-'
788 else:
789 raise ValueError("message has more than one 'Resent-' header block")
790 if from_addr is None: 770 if from_addr is None:
791 # Prefer the sender field per RFC 2822:3.6.2. 771 from_addr = msg['From']
792 from_addr = (msg[header_prefix+'Sender']
793 if (header_prefix+'Sender') in msg
794 else msg[header_prefix+'From'])
795 if to_addrs is None: 772 if to_addrs is None:
796 addr_fields = [f for f in (msg[header_prefix+'To'], 773 addr_fields = [f for f in (msg['To'], msg['Bcc'], msg['CC'])
797 msg[header_prefix+'Bcc'], 774 if f is not None]
798 msg[header_prefix+'Cc']) if f is not None ]
799 to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)] 775 to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)]
800 # Make a local copy so we can delete the bcc headers. 776 del msg['Bcc']
801 msg_copy = copy.copy(msg)
802 del msg_copy['Bcc']
803 del msg_copy['Resent-Bcc']
804 with io.BytesIO() as bytesmsg: 777 with io.BytesIO() as bytesmsg:
805 g = email.generator.BytesGenerator(bytesmsg) 778 g = email.generator.BytesGenerator(bytesmsg)
806 g.flatten(msg_copy, linesep='\r\n') 779 g.flatten(msg, linesep='\r\n')
807 flatmsg = bytesmsg.getvalue() 780 flatmsg = bytesmsg.getvalue()
808 return self.sendmail(from_addr, to_addrs, flatmsg, mail_options, 781 return self.sendmail(from_addr, to_addrs, flatmsg, mail_options,
809 rcpt_options) 782 rcpt_options)
810 783
811 def close(self): 784 def close(self):
812 """Close the connection to the SMTP server.""" 785 """Close the connection to the SMTP server."""
813 if self.file: 786 if self.file:
814 self.file.close() 787 self.file.close()
815 self.file = None 788 self.file = None
816 if self.sock: 789 if self.sock:
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
928 line = sys.stdin.readline() 901 line = sys.stdin.readline()
929 if not line: 902 if not line:
930 break 903 break
931 msg = msg + line 904 msg = msg + line
932 print("Message length is %d" % len(msg)) 905 print("Message length is %d" % len(msg))
933 906
934 server = SMTP('localhost') 907 server = SMTP('localhost')
935 server.set_debuglevel(1) 908 server.set_debuglevel(1)
936 server.sendmail(fromaddr, toaddrs, msg) 909 server.sendmail(fromaddr, toaddrs, msg)
937 server.quit() 910 server.quit()
OLDNEW
« no previous file with comments | « Lib/shutil.py ('k') | Lib/ssl.py » ('j') | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+