1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2012 W. Trevor King <wking@drexel.edu>
4 # This file is part of pgp-mime.
6 # pgp-mime is free software: you can redistribute it and/or modify it under the
7 # terms of the GNU General Public License as published by the Free Software
8 # Foundation, either version 3 of the License, or (at your option) any later
11 # pgp-mime is distributed in the hope that it will be useful, but WITHOUT ANY
12 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License along with
16 # pgp-mime. If not, see <http://www.gnu.org/licenses/>.
18 from email.header import decode_header as _decode_header
19 from email.message import Message as _Message
20 from email.mime.text import MIMEText as _MIMEText
21 from email.parser import Parser as _Parser
22 from email.utils import formataddr as _formataddr
23 from email.utils import getaddresses as _getaddresses
27 #ENCODING = 'iso-8859-1'
30 def header_from_text(text):
31 r"""Simple wrapper for instantiating a ``Message`` from text.
34 ... ['From: me@big.edu','To: you@big.edu','Subject: testing'])
35 >>> header = header_from_text(text=text)
36 >>> print(header.as_string()) # doctest: +REPORT_UDIFF
45 return p.parsestr(text, headersonly=True)
47 def guess_encoding(text):
49 >>> guess_encoding('hi there')
51 >>> guess_encoding('✉')
54 for encoding in ['us-ascii', ENCODING, 'utf-8']:
57 except UnicodeEncodeError:
61 raise ValueError(text)
63 def encodedMIMEText(body, encoding=None):
64 """Wrap ``MIMEText`` with ``guess_encoding`` detection.
66 >>> message = encodedMIMEText('Hello')
67 >>> print(message.as_string()) # doctest: +REPORT_UDIFF
68 Content-Type: text/plain; charset="us-ascii"
70 Content-Transfer-Encoding: 7bit
71 Content-Disposition: inline
74 >>> message = encodedMIMEText('Джон Доу')
75 >>> print(message.as_string()) # doctest: +REPORT_UDIFF
76 Content-Type: text/plain; charset="utf-8"
78 Content-Transfer-Encoding: base64
79 Content-Disposition: inline
85 encoding = guess_encoding(body)
86 if encoding == 'us-ascii':
87 message = _MIMEText(body)
89 # Create the message ('plain' stands for Content-Type: text/plain)
90 message = _MIMEText(body, 'plain', encoding)
91 message.add_header('Content-Disposition', 'inline')
94 def strip_bcc(message):
95 """Remove the Bcc field from a ``Message`` in preparation for mailing
97 >>> message = encodedMIMEText('howdy!')
98 >>> message['To'] = 'John Doe <jdoe@a.gov.ru>'
99 >>> message['Bcc'] = 'Jack <jack@hill.org>, Jill <jill@hill.org>'
100 >>> message = strip_bcc(message)
101 >>> print(message.as_string()) # doctest: +REPORT_UDIFF
102 Content-Type: text/plain; charset="us-ascii"
104 Content-Transfer-Encoding: 7bit
105 Content-Disposition: inline
106 To: John Doe <jdoe@a.gov.ru>
111 del message['resent-bcc']
114 def append_text(text_part, new_text):
115 r"""Append text to the body of a ``plain/text`` part.
117 Updates encoding as necessary.
119 >>> message = encodedMIMEText('Hello')
120 >>> append_text(message, ' John Doe')
121 >>> print(message.as_string()) # doctest: +REPORT_UDIFF
122 Content-Type: text/plain; charset="us-ascii"
124 Content-Disposition: inline
125 Content-Transfer-Encoding: 7bit
128 >>> append_text(message, ', Джон Доу')
129 >>> print(message.as_string()) # doctest: +REPORT_UDIFF
131 Content-Disposition: inline
132 Content-Type: text/plain; charset="utf-8"
133 Content-Transfer-Encoding: base64
135 SGVsbG8gSm9obiBEb2UsINCU0LbQvtC9INCU0L7Rgw==
137 >>> append_text(message, ', and Jane Sixpack.')
138 >>> print(message.as_string()) # doctest: +REPORT_UDIFF
140 Content-Disposition: inline
141 Content-Type: text/plain; charset="utf-8"
142 Content-Transfer-Encoding: base64
144 SGVsbG8gSm9obiBEb2UsINCU0LbQvtC9INCU0L7RgywgYW5kIEphbmUgU2l4cGFjay4=
147 original_encoding = text_part.get_charset().input_charset
148 original_payload = str(
149 text_part.get_payload(decode=True), original_encoding)
150 new_payload = '{}{}'.format(original_payload, new_text)
151 new_encoding = guess_encoding(new_payload)
152 if text_part.get('content-transfer-encoding', None):
153 # clear CTE so set_payload will set it properly for the new encoding
154 del text_part['content-transfer-encoding']
155 text_part.set_payload(new_payload, new_encoding)
157 def attach_root(header, root_part):
158 r"""Copy headers from ``header`` onto ``root_part``.
160 >>> header = header_from_text('From: me@big.edu\n')
161 >>> body = encodedMIMEText('Hello')
162 >>> message = attach_root(header, body)
163 >>> print(message.as_string()) # doctest: +REPORT_UDIFF
164 Content-Type: text/plain; charset="us-ascii"
166 Content-Transfer-Encoding: 7bit
167 Content-Disposition: inline
172 for k,v in header.items():
176 def getaddresses(addresses):
177 """A decoding version of ``email.utils.getaddresses``.
179 >>> text = ('To: =?utf-8?b?0JTQttC+0L0g0JTQvtGD?= <jdoe@a.gov.ru>, '
180 ... 'Jack <jack@hill.org>')
181 >>> header = header_from_text(text=text)
182 >>> list(getaddresses(header.get_all('to', [])))
183 [('Джон Доу', 'jdoe@a.gov.ru'), ('Jack', 'jack@hill.org')]
185 for (name,address) in _getaddresses(addresses):
187 for b,encoding in _decode_header(name):
191 n.append(str(b, encoding))
192 yield (' '.join(n), address)
194 def email_sources(message):
195 """Extract author address from an email ``Message``
197 Search the header of an email Message instance to find the
198 senders' email addresses (or sender's address).
200 >>> text = ('From: =?utf-8?b?0JTQttC+0L0g0JTQvtGD?= <jdoe@a.gov.ru>, '
201 ... 'Jack <jack@hill.org>')
202 >>> header = header_from_text(text=text)
203 >>> list(email_sources(header))
204 [('Джон Доу', 'jdoe@a.gov.ru'), ('Jack', 'jack@hill.org')]
206 froms = message.get_all('from', [])
207 return getaddresses(froms) # [(name, address), ...]
209 def email_targets(message):
210 """Extract recipient addresses from an email ``Message``
212 Search the header of an email Message instance to find a
213 list of recipient's email addresses.
215 >>> text = ('To: =?utf-8?b?0JTQttC+0L0g0JTQvtGD?= <jdoe@a.gov.ru>, '
216 ... 'Jack <jack@hill.org>')
217 >>> header = header_from_text(text=text)
218 >>> list(email_targets(header))
219 [('Джон Доу', 'jdoe@a.gov.ru'), ('Jack', 'jack@hill.org')]
221 tos = message.get_all('to', [])
222 ccs = message.get_all('cc', [])
223 bccs = message.get_all('bcc', [])
224 resent_tos = message.get_all('resent-to', [])
225 resent_ccs = message.get_all('resent-cc', [])
226 resent_bccs = message.get_all('resent-bcc', [])
228 tos + ccs + bccs + resent_tos + resent_ccs + resent_bccs)