|
49 | 49 | import email.generator |
50 | 50 | import base64 |
51 | 51 | import hmac |
| 52 | +import copy |
52 | 53 | from email.base64mime import body_encode as encode_base64 |
53 | 54 | from sys import stderr |
54 | 55 |
|
@@ -676,7 +677,7 @@ def sendmail(self, from_addr, to_addrs, msg, mail_options=[], |
676 | 677 |
|
677 | 678 | msg may be a string containing characters in the ASCII range, or a byte |
678 | 679 | string. A string is encoded to bytes using the ascii codec, and lone |
679 | | - \r and \n characters are converted to \r\n characters. |
| 680 | + \\r and \\n characters are converted to \\r\\n characters. |
680 | 681 |
|
681 | 682 | If there has been no previous EHLO or HELO command this session, this |
682 | 683 | method tries ESMTP EHLO first. If the server does ESMTP, message size |
@@ -759,24 +760,49 @@ def send_message(self, msg, from_addr=None, to_addrs=None, |
759 | 760 | """Converts message to a bytestring and passes it to sendmail. |
760 | 761 |
|
761 | 762 | The arguments are as for sendmail, except that msg is an |
762 | | - email.message.Message object. If from_addr is None, the from_addr is |
763 | | - taken from the 'From' header of the Message. If to_addrs is None, its |
764 | | - value is composed from the addresses listed in the 'To', 'CC', and |
765 | | - 'Bcc' fields. Regardless of the values of from_addr and to_addr, any |
766 | | - Bcc field in the Message object is deleted. The Message object is then |
767 | | - serialized using email.generator.BytesGenerator and sendmail is called |
768 | | - to transmit the message. |
| 763 | + email.message.Message object. If from_addr is None or to_addrs is |
| 764 | + None, these arguments are taken from the headers of the Message as |
| 765 | + described in RFC 2822 (a ValueError is raised if there is more than |
| 766 | + one set of 'Resent-' headers). Regardless of the values of from_addr and |
| 767 | + to_addr, any Bcc field (or Resent-Bcc field, when the Message is a |
| 768 | + resent) of the Message object won't be transmitted. The Message |
| 769 | + object is then serialized using email.generator.BytesGenerator and |
| 770 | + sendmail is called to transmit the message. |
| 771 | +
|
769 | 772 | """ |
| 773 | + # 'Resent-Date' is a mandatory field if the Message is resent (RFC 2822 |
| 774 | + # Section 3.6.6). In such a case, we use the 'Resent-*' fields. However, |
| 775 | + # if there is more than one 'Resent-' block there's no way to |
| 776 | + # unambiguously determine which one is the most recent in all cases, |
| 777 | + # so rather than guess we raise a ValueError in that case. |
| 778 | + # |
| 779 | + # TODO implement heuristics to guess the correct Resent-* block with an |
| 780 | + # option allowing the user to enable the heuristics. (It should be |
| 781 | + # possible to guess correctly almost all of the time.) |
| 782 | + resent =msg.get_all('Resent-Date') |
| 783 | + if resent is None: |
| 784 | + header_prefix = '' |
| 785 | + elif len(resent) == 1: |
| 786 | + header_prefix = 'Resent-' |
| 787 | + else: |
| 788 | + raise ValueError("message has more than one 'Resent-' header block") |
770 | 789 | if from_addr is None: |
771 | | - from_addr = msg['From'] |
| 790 | + # Prefer the sender field per RFC 2822:3.6.2. |
| 791 | + from_addr = (msg[header_prefix+'Sender'] |
| 792 | + if (header_prefix+'Sender') in msg |
| 793 | + else msg[header_prefix+'From']) |
772 | 794 | if to_addrs is None: |
773 | | - addr_fields = [f for f in (msg['To'], msg['Bcc'], msg['CC']) |
774 | | - if f is not None] |
| 795 | + addr_fields = [f for f in (msg[header_prefix+'To'], |
| 796 | + msg[header_prefix+'Bcc'], |
| 797 | + msg[header_prefix+'Cc']) if f is not None] |
775 | 798 | to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)] |
776 | | - del msg['Bcc'] |
| 799 | + # Make a local copy so we can delete the bcc headers. |
| 800 | + msg_copy = copy.copy(msg) |
| 801 | + del msg_copy['Bcc'] |
| 802 | + del msg_copy['Resent-Bcc'] |
777 | 803 | with io.BytesIO() as bytesmsg: |
778 | 804 | g = email.generator.BytesGenerator(bytesmsg) |
779 | | - g.flatten(msg, linesep='\r\n') |
| 805 | + g.flatten(msg_copy, linesep='\r\n') |
780 | 806 | flatmsg = bytesmsg.getvalue() |
781 | 807 | return self.sendmail(from_addr, to_addrs, flatmsg, mail_options, |
782 | 808 | rcpt_options) |
|
0 commit comments