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

Delta Between Two Patch Sets: Lib/smtplib.py

Issue 10321: Add support for Message objects and binary data to smtplib.sendmail
Left Patch Set: Created 8 years, 8 months ago
Right Patch Set: Created 8 years, 8 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:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « Doc/whatsnew/3.2.rst ('k') | Lib/test/test_smtplib.py » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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 646 matching lines...) Expand 10 before | Expand all | Expand 10 after
657 - from_addr : The address sending this mail. 657 - from_addr : The address sending this mail.
658 - to_addrs : A list of addresses to send this mail to. A bare 658 - to_addrs : A list of addresses to send this mail to. A bare
659 string will be treated as a list with 1 address. 659 string will be treated as a list with 1 address.
660 - msg : The message to send. 660 - msg : The message to send.
661 - mail_options : List of ESMTP options (such as 8bitmime) for the 661 - mail_options : List of ESMTP options (such as 8bitmime) for the
662 mail command. 662 mail command.
663 - rcpt_options : List of ESMTP options (such as DSN commands) for 663 - rcpt_options : List of ESMTP options (such as DSN commands) for
664 all the rcpt commands. 664 all the rcpt commands.
665 665
666 msg may be a string containing characters in the ASCII range, or a byte 666 msg may be a string containing characters in the ASCII range, or a byte
667 string, or an email.message.Message object. A string is encoded to 667 string. A string is encoded to bytes using the ascii codec, and lone
668 bytes using the ascii codec, and lone \r and \n characters are 668 \r and \n characters are converted to \r\n characters.
669 converted to \r\n characters. A byte string is transmitted as is. A
670 Message object is serialized using email.generator.BytesGenerator.
671
672 When msg is a Message object, from_addr and to_addrs may be set to
673 None, in which case the from_addr is taken from the 'From' header of
674 the Message, and the to_addrs is composed from the addresses listed in
675 the 'To', 'CC', and 'Bcc' fields. Regardless, any Bcc field in
676 the Message object is deleted before it is serialized.
677 669
678 If there has been no previous EHLO or HELO command this session, this 670 If there has been no previous EHLO or HELO command this session, this
679 method tries ESMTP EHLO first. If the server does ESMTP, message size 671 method tries ESMTP EHLO first. If the server does ESMTP, message size
680 and each of the specified options will be passed to it. If EHLO 672 and each of the specified options will be passed to it. If EHLO
681 fails, HELO will be tried and ESMTP options suppressed. 673 fails, HELO will be tried and ESMTP options suppressed.
682 674
683 This method will return normally if the mail is accepted for at least 675 This method will return normally if the mail is accepted for at least
684 one recipient. It returns a dictionary, with one entry for each 676 one recipient. It returns a dictionary, with one entry for each
685 recipient that was refused. Each entry contains a tuple of the SMTP 677 recipient that was refused. Each entry contains a tuple of the SMTP
686 error code and the accompanying error message sent by the server. 678 error code and the accompanying error message sent by the server.
(...skipping 26 matching lines...) Expand all
713 >>> s.quit() 705 >>> s.quit()
714 706
715 In the above example, the message was accepted for delivery to three 707 In the above example, the message was accepted for delivery to three
716 of the four addresses, and one was rejected, with the error code 708 of the four addresses, and one was rejected, with the error code
717 550. If all addresses are accepted, then the method will return an 709 550. If all addresses are accepted, then the method will return an
718 empty dictionary. 710 empty dictionary.
719 711
720 """ 712 """
721 self.ehlo_or_helo_if_needed() 713 self.ehlo_or_helo_if_needed()
722 esmtp_opts = [] 714 esmtp_opts = []
723 if isinstance(msg, email.message.Message): 715 if isinstance(msg, str):
724 if from_addr is None: 716 msg = _fix_eols(msg).encode('ascii')
725 from_addr = msg['From']
726 if to_addrs is None:
727 addr_fields = [f for f in (msg['To'], msg['Bcc'], msg['CC'])
728 if f is not None]
729 to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)]
730 del msg['Bcc']
731 bytesmsg = io.BytesIO()
732 g = email.generator.BytesGenerator(bytesmsg)
733 g.flatten(msg, linesep='\r\n')
734 bytesmsg = bytesmsg.getvalue()
735 elif isinstance(msg, str):
736 bytesmsg = _fix_eols(msg).encode('ascii')
737 else:
738 bytesmsg = msg
739 if self.does_esmtp: 717 if self.does_esmtp:
740 # Hmmm? what's this? -ddm 718 # Hmmm? what's this? -ddm
741 # self.esmtp_features['7bit']="" 719 # self.esmtp_features['7bit']=""
742 if self.has_extn('size'): 720 if self.has_extn('size'):
743 esmtp_opts.append("size=%d" % len(msg)) 721 esmtp_opts.append("size=%d" % len(msg))
744 for option in mail_options: 722 for option in mail_options:
745 esmtp_opts.append(option) 723 esmtp_opts.append(option)
746
747 (code,resp) = self.mail(from_addr, esmtp_opts) 724 (code,resp) = self.mail(from_addr, esmtp_opts)
748 if code != 250: 725 if code != 250:
749 self.rset() 726 self.rset()
750 raise SMTPSenderRefused(code, resp, from_addr) 727 raise SMTPSenderRefused(code, resp, from_addr)
751 senderrs={} 728 senderrs={}
752 if isinstance(to_addrs, str): 729 if isinstance(to_addrs, str):
753 to_addrs = [to_addrs] 730 to_addrs = [to_addrs]
754 for each in to_addrs: 731 for each in to_addrs:
755 (code,resp)=self.rcpt(each, rcpt_options) 732 (code,resp)=self.rcpt(each, rcpt_options)
756 if (code != 250) and (code != 251): 733 if (code != 250) and (code != 251):
757 senderrs[each]=(code,resp) 734 senderrs[each]=(code,resp)
758 if len(senderrs)==len(to_addrs): 735 if len(senderrs)==len(to_addrs):
759 # the server refused all our recipients 736 # the server refused all our recipients
760 self.rset() 737 self.rset()
761 raise SMTPRecipientsRefused(senderrs) 738 raise SMTPRecipientsRefused(senderrs)
762 (code,resp) = self.data(bytesmsg) 739 (code,resp) = self.data(msg)
763 if code != 250: 740 if code != 250:
764 self.rset() 741 self.rset()
765 raise SMTPDataError(code, resp) 742 raise SMTPDataError(code, resp)
766 #if we got here then somebody got our mail 743 #if we got here then somebody got our mail
767 return senderrs 744 return senderrs
745
746 def send_message(self, msg, from_addr=None, to_addrs=None,
747 mail_options=[], rcpt_options={}):
748 """Converts message to a bytestring and passes it to sendmail.
749
750 The arguments are as for sendmail, except that msg is an
751 email.message.Message object. If from_addr is None, the from_addr is
752 taken from the 'From' header of the Message. If to_addrs is None, its
753 value is composed from the addresses listed in the 'To', 'CC', and
754 'Bcc' fields. Regardless of the values of from_addr and to_addr, any
755 Bcc field in the Message object is deleted. The Message object is then
756 serialized using email.generator.BytesGenerator and sendmail is called
757 to transmit the message.
758 """
759 if from_addr is None:
760 from_addr = msg['From']
761 if to_addrs is None:
762 addr_fields = [f for f in (msg['To'], msg['Bcc'], msg['CC'])
763 if f is not None]
764 to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)]
765 del msg['Bcc']
766 with io.BytesIO() as bytesmsg:
767 g = email.generator.BytesGenerator(bytesmsg)
768 g.flatten(msg, linesep='\r\n')
769 flatmsg = bytesmsg.getvalue()
770 return self.sendmail(from_addr, to_addrs, flatmsg, mail_options,
771 rcpt_options)
768 772
769 773
770 def close(self): 774 def close(self):
771 """Close the connection to the SMTP server.""" 775 """Close the connection to the SMTP server."""
772 if self.file: 776 if self.file:
773 self.file.close() 777 self.file.close()
774 self.file = None 778 self.file = None
775 if self.sock: 779 if self.sock:
776 self.sock.close() 780 self.sock.close()
777 self.sock = None 781 self.sock = None
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
871 line = sys.stdin.readline() 875 line = sys.stdin.readline()
872 if not line: 876 if not line:
873 break 877 break
874 msg = msg + line 878 msg = msg + line
875 print("Message length is %d" % len(msg)) 879 print("Message length is %d" % len(msg))
876 880
877 server = SMTP('localhost') 881 server = SMTP('localhost')
878 server.set_debuglevel(1) 882 server.set_debuglevel(1)
879 server.sendmail(fromaddr, toaddrs, msg) 883 server.sendmail(fromaddr, toaddrs, msg)
880 server.quit() 884 server.quit()
LEFTRIGHT

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