1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2012 W. Trevor King <wking@tremily.us>
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 __future__ import absolute_import
20 from email.header import decode_header as _decode_header
21 from email.message import Message as _Message
22 from email.mime.text import MIMEText as _MIMEText
23 from email.parser import Parser as _Parser
24 from email.utils import formataddr as _formataddr
25 from email.utils import getaddresses as _getaddresses
29 #ENCODING = 'iso-8859-1'
32 def header_from_text(text):
33 r"""Simple wrapper for instantiating a ``Message`` from text.
36 ... ['From: me@big.edu','To: you@big.edu','Subject: testing'])
37 >>> header = header_from_text(text=text)
38 >>> print(header.as_string()) # doctest: +REPORT_UDIFF
47 return p.parsestr(text, headersonly=True)
49 def guess_encoding(text):
51 >>> guess_encoding('hi there')
53 >>> guess_encoding('✉')
56 for encoding in ['us-ascii', ENCODING, 'utf-8']:
59 except UnicodeEncodeError:
63 raise ValueError(text)
65 def encodedMIMEText(body, encoding=None):
66 """Wrap ``MIMEText`` with ``guess_encoding`` detection.
68 >>> message = encodedMIMEText('Hello')
69 >>> print(message.as_string()) # doctest: +REPORT_UDIFF
70 Content-Type: text/plain; charset="us-ascii"
72 Content-Transfer-Encoding: 7bit
73 Content-Disposition: inline
76 >>> message = encodedMIMEText('Джон Доу')
77 >>> print(message.as_string()) # doctest: +REPORT_UDIFF
78 Content-Type: text/plain; charset="utf-8"
80 Content-Transfer-Encoding: base64
81 Content-Disposition: inline
87 encoding = guess_encoding(body)
88 if encoding == 'us-ascii':
89 message = _MIMEText(body)
91 # Create the message ('plain' stands for Content-Type: text/plain)
92 message = _MIMEText(body, 'plain', encoding)
93 message.add_header('Content-Disposition', 'inline')
96 def strip_bcc(message):
97 """Remove the Bcc field from a ``Message`` in preparation for mailing
99 >>> message = encodedMIMEText('howdy!')
100 >>> message['To'] = 'John Doe <jdoe@a.gov.ru>'
101 >>> message['Bcc'] = 'Jack <jack@hill.org>, Jill <jill@hill.org>'
102 >>> message = strip_bcc(message)
103 >>> print(message.as_string()) # doctest: +REPORT_UDIFF
104 Content-Type: text/plain; charset="us-ascii"
106 Content-Transfer-Encoding: 7bit
107 Content-Disposition: inline
108 To: John Doe <jdoe@a.gov.ru>
113 del message['resent-bcc']
116 def append_text(text_part, new_text):
117 r"""Append text to the body of a ``plain/text`` part.
119 Updates encoding as necessary.
121 >>> message = encodedMIMEText('Hello')
122 >>> append_text(message, ' John Doe')
123 >>> print(message.as_string()) # doctest: +REPORT_UDIFF
124 Content-Type: text/plain; charset="us-ascii"
126 Content-Disposition: inline
127 Content-Transfer-Encoding: 7bit
130 >>> append_text(message, ', Джон Доу')
131 >>> print(message.as_string()) # doctest: +REPORT_UDIFF
133 Content-Disposition: inline
134 Content-Type: text/plain; charset="utf-8"
135 Content-Transfer-Encoding: base64
137 SGVsbG8gSm9obiBEb2UsINCU0LbQvtC9INCU0L7Rgw==
139 >>> append_text(message, ', and Jane Sixpack.')
140 >>> print(message.as_string()) # doctest: +REPORT_UDIFF
142 Content-Disposition: inline
143 Content-Type: text/plain; charset="utf-8"
144 Content-Transfer-Encoding: base64
146 SGVsbG8gSm9obiBEb2UsINCU0LbQvtC9INCU0L7RgywgYW5kIEphbmUgU2l4cGFjay4=
149 original_encoding = text_part.get_charset().input_charset
150 original_payload = str(
151 text_part.get_payload(decode=True), original_encoding)
152 new_payload = '{}{}'.format(original_payload, new_text)
153 new_encoding = guess_encoding(new_payload)
154 if text_part.get('content-transfer-encoding', None):
155 # clear CTE so set_payload will set it properly for the new encoding
156 del text_part['content-transfer-encoding']
157 text_part.set_payload(new_payload, new_encoding)
159 def attach_root(header, root_part):
160 r"""Copy headers from ``header`` onto ``root_part``.
162 >>> header = header_from_text('From: me@big.edu\n')
163 >>> body = encodedMIMEText('Hello')
164 >>> message = attach_root(header, body)
165 >>> print(message.as_string()) # doctest: +REPORT_UDIFF
166 Content-Type: text/plain; charset="us-ascii"
168 Content-Transfer-Encoding: 7bit
169 Content-Disposition: inline
174 for k,v in header.items():
178 def getaddresses(addresses):
179 """A decoding version of ``email.utils.getaddresses``.
181 >>> text = ('To: =?utf-8?b?0JTQttC+0L0g0JTQvtGD?= <jdoe@a.gov.ru>, '
182 ... 'Jack <jack@hill.org>')
183 >>> header = header_from_text(text=text)
184 >>> list(getaddresses(header.get_all('to', [])))
185 [('Джон Доу', 'jdoe@a.gov.ru'), ('Jack', 'jack@hill.org')]
187 for (name,address) in _getaddresses(addresses):
189 for b,encoding in _decode_header(name):
193 n.append(str(b, encoding))
194 yield (' '.join(n), address)
196 def email_sources(message):
197 """Extract author address from an email ``Message``
199 Search the header of an email Message instance to find the
200 senders' email addresses (or sender's address).
202 >>> text = ('From: =?utf-8?b?0JTQttC+0L0g0JTQvtGD?= <jdoe@a.gov.ru>, '
203 ... 'Jack <jack@hill.org>')
204 >>> header = header_from_text(text=text)
205 >>> list(email_sources(header))
206 [('Джон Доу', 'jdoe@a.gov.ru'), ('Jack', 'jack@hill.org')]
208 froms = message.get_all('from', [])
209 return getaddresses(froms) # [(name, address), ...]
211 def email_targets(message):
212 """Extract recipient addresses from an email ``Message``
214 Search the header of an email Message instance to find a
215 list of recipient's email addresses.
217 >>> text = ('To: =?utf-8?b?0JTQttC+0L0g0JTQvtGD?= <jdoe@a.gov.ru>, '
218 ... 'Jack <jack@hill.org>')
219 >>> header = header_from_text(text=text)
220 >>> list(email_targets(header))
221 [('Джон Доу', 'jdoe@a.gov.ru'), ('Jack', 'jack@hill.org')]
223 tos = message.get_all('to', [])
224 ccs = message.get_all('cc', [])
225 bccs = message.get_all('bcc', [])
226 resent_tos = message.get_all('resent-to', [])
227 resent_ccs = message.get_all('resent-cc', [])
228 resent_bccs = message.get_all('resent-bcc', [])
230 tos + ccs + bccs + resent_tos + resent_ccs + resent_bccs)