1 | n/a | # Copyright (C) 2001-2010 Python Software Foundation |
---|
2 | n/a | # Contact: email-sig@python.org |
---|
3 | n/a | # email package unit tests |
---|
4 | n/a | |
---|
5 | n/a | import re |
---|
6 | n/a | import time |
---|
7 | n/a | import base64 |
---|
8 | n/a | import unittest |
---|
9 | n/a | import textwrap |
---|
10 | n/a | |
---|
11 | n/a | from io import StringIO, BytesIO |
---|
12 | n/a | from itertools import chain |
---|
13 | n/a | from random import choice |
---|
14 | n/a | from socket import getfqdn |
---|
15 | n/a | try: |
---|
16 | n/a | from threading import Thread |
---|
17 | n/a | except ImportError: |
---|
18 | n/a | from dummy_threading import Thread |
---|
19 | n/a | |
---|
20 | n/a | import email |
---|
21 | n/a | import email.policy |
---|
22 | n/a | |
---|
23 | n/a | from email.charset import Charset |
---|
24 | n/a | from email.header import Header, decode_header, make_header |
---|
25 | n/a | from email.parser import Parser, HeaderParser |
---|
26 | n/a | from email.generator import Generator, DecodedGenerator, BytesGenerator |
---|
27 | n/a | from email.message import Message |
---|
28 | n/a | from email.mime.application import MIMEApplication |
---|
29 | n/a | from email.mime.audio import MIMEAudio |
---|
30 | n/a | from email.mime.text import MIMEText |
---|
31 | n/a | from email.mime.image import MIMEImage |
---|
32 | n/a | from email.mime.base import MIMEBase |
---|
33 | n/a | from email.mime.message import MIMEMessage |
---|
34 | n/a | from email.mime.multipart import MIMEMultipart |
---|
35 | n/a | from email.mime.nonmultipart import MIMENonMultipart |
---|
36 | n/a | from email import utils |
---|
37 | n/a | from email import errors |
---|
38 | n/a | from email import encoders |
---|
39 | n/a | from email import iterators |
---|
40 | n/a | from email import base64mime |
---|
41 | n/a | from email import quoprimime |
---|
42 | n/a | |
---|
43 | n/a | from test.support import unlink, start_threads |
---|
44 | n/a | from test.test_email import openfile, TestEmailBase |
---|
45 | n/a | |
---|
46 | n/a | # These imports are documented to work, but we are testing them using a |
---|
47 | n/a | # different path, so we import them here just to make sure they are importable. |
---|
48 | n/a | from email.parser import FeedParser, BytesFeedParser |
---|
49 | n/a | |
---|
50 | n/a | NL = '\n' |
---|
51 | n/a | EMPTYSTRING = '' |
---|
52 | n/a | SPACE = ' ' |
---|
53 | n/a | |
---|
54 | n/a | |
---|
55 | n/a | # Test various aspects of the Message class's API |
---|
56 | n/a | class TestMessageAPI(TestEmailBase): |
---|
57 | n/a | def test_get_all(self): |
---|
58 | n/a | eq = self.assertEqual |
---|
59 | n/a | msg = self._msgobj('msg_20.txt') |
---|
60 | n/a | eq(msg.get_all('cc'), ['ccc@zzz.org', 'ddd@zzz.org', 'eee@zzz.org']) |
---|
61 | n/a | eq(msg.get_all('xx', 'n/a'), 'n/a') |
---|
62 | n/a | |
---|
63 | n/a | def test_getset_charset(self): |
---|
64 | n/a | eq = self.assertEqual |
---|
65 | n/a | msg = Message() |
---|
66 | n/a | eq(msg.get_charset(), None) |
---|
67 | n/a | charset = Charset('iso-8859-1') |
---|
68 | n/a | msg.set_charset(charset) |
---|
69 | n/a | eq(msg['mime-version'], '1.0') |
---|
70 | n/a | eq(msg.get_content_type(), 'text/plain') |
---|
71 | n/a | eq(msg['content-type'], 'text/plain; charset="iso-8859-1"') |
---|
72 | n/a | eq(msg.get_param('charset'), 'iso-8859-1') |
---|
73 | n/a | eq(msg['content-transfer-encoding'], 'quoted-printable') |
---|
74 | n/a | eq(msg.get_charset().input_charset, 'iso-8859-1') |
---|
75 | n/a | # Remove the charset |
---|
76 | n/a | msg.set_charset(None) |
---|
77 | n/a | eq(msg.get_charset(), None) |
---|
78 | n/a | eq(msg['content-type'], 'text/plain') |
---|
79 | n/a | # Try adding a charset when there's already MIME headers present |
---|
80 | n/a | msg = Message() |
---|
81 | n/a | msg['MIME-Version'] = '2.0' |
---|
82 | n/a | msg['Content-Type'] = 'text/x-weird' |
---|
83 | n/a | msg['Content-Transfer-Encoding'] = 'quinted-puntable' |
---|
84 | n/a | msg.set_charset(charset) |
---|
85 | n/a | eq(msg['mime-version'], '2.0') |
---|
86 | n/a | eq(msg['content-type'], 'text/x-weird; charset="iso-8859-1"') |
---|
87 | n/a | eq(msg['content-transfer-encoding'], 'quinted-puntable') |
---|
88 | n/a | |
---|
89 | n/a | def test_set_charset_from_string(self): |
---|
90 | n/a | eq = self.assertEqual |
---|
91 | n/a | msg = Message() |
---|
92 | n/a | msg.set_charset('us-ascii') |
---|
93 | n/a | eq(msg.get_charset().input_charset, 'us-ascii') |
---|
94 | n/a | eq(msg['content-type'], 'text/plain; charset="us-ascii"') |
---|
95 | n/a | |
---|
96 | n/a | def test_set_payload_with_charset(self): |
---|
97 | n/a | msg = Message() |
---|
98 | n/a | charset = Charset('iso-8859-1') |
---|
99 | n/a | msg.set_payload('This is a string payload', charset) |
---|
100 | n/a | self.assertEqual(msg.get_charset().input_charset, 'iso-8859-1') |
---|
101 | n/a | |
---|
102 | n/a | def test_set_payload_with_8bit_data_and_charset(self): |
---|
103 | n/a | data = b'\xd0\x90\xd0\x91\xd0\x92' |
---|
104 | n/a | charset = Charset('utf-8') |
---|
105 | n/a | msg = Message() |
---|
106 | n/a | msg.set_payload(data, charset) |
---|
107 | n/a | self.assertEqual(msg['content-transfer-encoding'], 'base64') |
---|
108 | n/a | self.assertEqual(msg.get_payload(decode=True), data) |
---|
109 | n/a | self.assertEqual(msg.get_payload(), '0JDQkdCS\n') |
---|
110 | n/a | |
---|
111 | n/a | def test_set_payload_with_non_ascii_and_charset_body_encoding_none(self): |
---|
112 | n/a | data = b'\xd0\x90\xd0\x91\xd0\x92' |
---|
113 | n/a | charset = Charset('utf-8') |
---|
114 | n/a | charset.body_encoding = None # Disable base64 encoding |
---|
115 | n/a | msg = Message() |
---|
116 | n/a | msg.set_payload(data.decode('utf-8'), charset) |
---|
117 | n/a | self.assertEqual(msg['content-transfer-encoding'], '8bit') |
---|
118 | n/a | self.assertEqual(msg.get_payload(decode=True), data) |
---|
119 | n/a | |
---|
120 | n/a | def test_set_payload_with_8bit_data_and_charset_body_encoding_none(self): |
---|
121 | n/a | data = b'\xd0\x90\xd0\x91\xd0\x92' |
---|
122 | n/a | charset = Charset('utf-8') |
---|
123 | n/a | charset.body_encoding = None # Disable base64 encoding |
---|
124 | n/a | msg = Message() |
---|
125 | n/a | msg.set_payload(data, charset) |
---|
126 | n/a | self.assertEqual(msg['content-transfer-encoding'], '8bit') |
---|
127 | n/a | self.assertEqual(msg.get_payload(decode=True), data) |
---|
128 | n/a | |
---|
129 | n/a | def test_set_payload_to_list(self): |
---|
130 | n/a | msg = Message() |
---|
131 | n/a | msg.set_payload([]) |
---|
132 | n/a | self.assertEqual(msg.get_payload(), []) |
---|
133 | n/a | |
---|
134 | n/a | def test_attach_when_payload_is_string(self): |
---|
135 | n/a | msg = Message() |
---|
136 | n/a | msg['Content-Type'] = 'multipart/mixed' |
---|
137 | n/a | msg.set_payload('string payload') |
---|
138 | n/a | sub_msg = MIMEMessage(Message()) |
---|
139 | n/a | self.assertRaisesRegex(TypeError, "[Aa]ttach.*non-multipart", |
---|
140 | n/a | msg.attach, sub_msg) |
---|
141 | n/a | |
---|
142 | n/a | def test_get_charsets(self): |
---|
143 | n/a | eq = self.assertEqual |
---|
144 | n/a | |
---|
145 | n/a | msg = self._msgobj('msg_08.txt') |
---|
146 | n/a | charsets = msg.get_charsets() |
---|
147 | n/a | eq(charsets, [None, 'us-ascii', 'iso-8859-1', 'iso-8859-2', 'koi8-r']) |
---|
148 | n/a | |
---|
149 | n/a | msg = self._msgobj('msg_09.txt') |
---|
150 | n/a | charsets = msg.get_charsets('dingbat') |
---|
151 | n/a | eq(charsets, ['dingbat', 'us-ascii', 'iso-8859-1', 'dingbat', |
---|
152 | n/a | 'koi8-r']) |
---|
153 | n/a | |
---|
154 | n/a | msg = self._msgobj('msg_12.txt') |
---|
155 | n/a | charsets = msg.get_charsets() |
---|
156 | n/a | eq(charsets, [None, 'us-ascii', 'iso-8859-1', None, 'iso-8859-2', |
---|
157 | n/a | 'iso-8859-3', 'us-ascii', 'koi8-r']) |
---|
158 | n/a | |
---|
159 | n/a | def test_get_filename(self): |
---|
160 | n/a | eq = self.assertEqual |
---|
161 | n/a | |
---|
162 | n/a | msg = self._msgobj('msg_04.txt') |
---|
163 | n/a | filenames = [p.get_filename() for p in msg.get_payload()] |
---|
164 | n/a | eq(filenames, ['msg.txt', 'msg.txt']) |
---|
165 | n/a | |
---|
166 | n/a | msg = self._msgobj('msg_07.txt') |
---|
167 | n/a | subpart = msg.get_payload(1) |
---|
168 | n/a | eq(subpart.get_filename(), 'dingusfish.gif') |
---|
169 | n/a | |
---|
170 | n/a | def test_get_filename_with_name_parameter(self): |
---|
171 | n/a | eq = self.assertEqual |
---|
172 | n/a | |
---|
173 | n/a | msg = self._msgobj('msg_44.txt') |
---|
174 | n/a | filenames = [p.get_filename() for p in msg.get_payload()] |
---|
175 | n/a | eq(filenames, ['msg.txt', 'msg.txt']) |
---|
176 | n/a | |
---|
177 | n/a | def test_get_boundary(self): |
---|
178 | n/a | eq = self.assertEqual |
---|
179 | n/a | msg = self._msgobj('msg_07.txt') |
---|
180 | n/a | # No quotes! |
---|
181 | n/a | eq(msg.get_boundary(), 'BOUNDARY') |
---|
182 | n/a | |
---|
183 | n/a | def test_set_boundary(self): |
---|
184 | n/a | eq = self.assertEqual |
---|
185 | n/a | # This one has no existing boundary parameter, but the Content-Type: |
---|
186 | n/a | # header appears fifth. |
---|
187 | n/a | msg = self._msgobj('msg_01.txt') |
---|
188 | n/a | msg.set_boundary('BOUNDARY') |
---|
189 | n/a | header, value = msg.items()[4] |
---|
190 | n/a | eq(header.lower(), 'content-type') |
---|
191 | n/a | eq(value, 'text/plain; charset="us-ascii"; boundary="BOUNDARY"') |
---|
192 | n/a | # This one has a Content-Type: header, with a boundary, stuck in the |
---|
193 | n/a | # middle of its headers. Make sure the order is preserved; it should |
---|
194 | n/a | # be fifth. |
---|
195 | n/a | msg = self._msgobj('msg_04.txt') |
---|
196 | n/a | msg.set_boundary('BOUNDARY') |
---|
197 | n/a | header, value = msg.items()[4] |
---|
198 | n/a | eq(header.lower(), 'content-type') |
---|
199 | n/a | eq(value, 'multipart/mixed; boundary="BOUNDARY"') |
---|
200 | n/a | # And this one has no Content-Type: header at all. |
---|
201 | n/a | msg = self._msgobj('msg_03.txt') |
---|
202 | n/a | self.assertRaises(errors.HeaderParseError, |
---|
203 | n/a | msg.set_boundary, 'BOUNDARY') |
---|
204 | n/a | |
---|
205 | n/a | def test_make_boundary(self): |
---|
206 | n/a | msg = MIMEMultipart('form-data') |
---|
207 | n/a | # Note that when the boundary gets created is an implementation |
---|
208 | n/a | # detail and might change. |
---|
209 | n/a | self.assertEqual(msg.items()[0][1], 'multipart/form-data') |
---|
210 | n/a | # Trigger creation of boundary |
---|
211 | n/a | msg.as_string() |
---|
212 | n/a | self.assertEqual(msg.items()[0][1][:33], |
---|
213 | n/a | 'multipart/form-data; boundary="==') |
---|
214 | n/a | # XXX: there ought to be tests of the uniqueness of the boundary, too. |
---|
215 | n/a | |
---|
216 | n/a | def test_message_rfc822_only(self): |
---|
217 | n/a | # Issue 7970: message/rfc822 not in multipart parsed by |
---|
218 | n/a | # HeaderParser caused an exception when flattened. |
---|
219 | n/a | with openfile('msg_46.txt') as fp: |
---|
220 | n/a | msgdata = fp.read() |
---|
221 | n/a | parser = HeaderParser() |
---|
222 | n/a | msg = parser.parsestr(msgdata) |
---|
223 | n/a | out = StringIO() |
---|
224 | n/a | gen = Generator(out, True, 0) |
---|
225 | n/a | gen.flatten(msg, False) |
---|
226 | n/a | self.assertEqual(out.getvalue(), msgdata) |
---|
227 | n/a | |
---|
228 | n/a | def test_byte_message_rfc822_only(self): |
---|
229 | n/a | # Make sure new bytes header parser also passes this. |
---|
230 | n/a | with openfile('msg_46.txt') as fp: |
---|
231 | n/a | msgdata = fp.read().encode('ascii') |
---|
232 | n/a | parser = email.parser.BytesHeaderParser() |
---|
233 | n/a | msg = parser.parsebytes(msgdata) |
---|
234 | n/a | out = BytesIO() |
---|
235 | n/a | gen = email.generator.BytesGenerator(out) |
---|
236 | n/a | gen.flatten(msg) |
---|
237 | n/a | self.assertEqual(out.getvalue(), msgdata) |
---|
238 | n/a | |
---|
239 | n/a | def test_get_decoded_payload(self): |
---|
240 | n/a | eq = self.assertEqual |
---|
241 | n/a | msg = self._msgobj('msg_10.txt') |
---|
242 | n/a | # The outer message is a multipart |
---|
243 | n/a | eq(msg.get_payload(decode=True), None) |
---|
244 | n/a | # Subpart 1 is 7bit encoded |
---|
245 | n/a | eq(msg.get_payload(0).get_payload(decode=True), |
---|
246 | n/a | b'This is a 7bit encoded message.\n') |
---|
247 | n/a | # Subpart 2 is quopri |
---|
248 | n/a | eq(msg.get_payload(1).get_payload(decode=True), |
---|
249 | n/a | b'\xa1This is a Quoted Printable encoded message!\n') |
---|
250 | n/a | # Subpart 3 is base64 |
---|
251 | n/a | eq(msg.get_payload(2).get_payload(decode=True), |
---|
252 | n/a | b'This is a Base64 encoded message.') |
---|
253 | n/a | # Subpart 4 is base64 with a trailing newline, which |
---|
254 | n/a | # used to be stripped (issue 7143). |
---|
255 | n/a | eq(msg.get_payload(3).get_payload(decode=True), |
---|
256 | n/a | b'This is a Base64 encoded message.\n') |
---|
257 | n/a | # Subpart 5 has no Content-Transfer-Encoding: header. |
---|
258 | n/a | eq(msg.get_payload(4).get_payload(decode=True), |
---|
259 | n/a | b'This has no Content-Transfer-Encoding: header.\n') |
---|
260 | n/a | |
---|
261 | n/a | def test_get_decoded_uu_payload(self): |
---|
262 | n/a | eq = self.assertEqual |
---|
263 | n/a | msg = Message() |
---|
264 | n/a | msg.set_payload('begin 666 -\n+:&5L;&\\@=V]R;&0 \n \nend\n') |
---|
265 | n/a | for cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'): |
---|
266 | n/a | msg['content-transfer-encoding'] = cte |
---|
267 | n/a | eq(msg.get_payload(decode=True), b'hello world') |
---|
268 | n/a | # Now try some bogus data |
---|
269 | n/a | msg.set_payload('foo') |
---|
270 | n/a | eq(msg.get_payload(decode=True), b'foo') |
---|
271 | n/a | |
---|
272 | n/a | def test_get_payload_n_raises_on_non_multipart(self): |
---|
273 | n/a | msg = Message() |
---|
274 | n/a | self.assertRaises(TypeError, msg.get_payload, 1) |
---|
275 | n/a | |
---|
276 | n/a | def test_decoded_generator(self): |
---|
277 | n/a | eq = self.assertEqual |
---|
278 | n/a | msg = self._msgobj('msg_07.txt') |
---|
279 | n/a | with openfile('msg_17.txt') as fp: |
---|
280 | n/a | text = fp.read() |
---|
281 | n/a | s = StringIO() |
---|
282 | n/a | g = DecodedGenerator(s) |
---|
283 | n/a | g.flatten(msg) |
---|
284 | n/a | eq(s.getvalue(), text) |
---|
285 | n/a | |
---|
286 | n/a | def test__contains__(self): |
---|
287 | n/a | msg = Message() |
---|
288 | n/a | msg['From'] = 'Me' |
---|
289 | n/a | msg['to'] = 'You' |
---|
290 | n/a | # Check for case insensitivity |
---|
291 | n/a | self.assertIn('from', msg) |
---|
292 | n/a | self.assertIn('From', msg) |
---|
293 | n/a | self.assertIn('FROM', msg) |
---|
294 | n/a | self.assertIn('to', msg) |
---|
295 | n/a | self.assertIn('To', msg) |
---|
296 | n/a | self.assertIn('TO', msg) |
---|
297 | n/a | |
---|
298 | n/a | def test_as_string(self): |
---|
299 | n/a | msg = self._msgobj('msg_01.txt') |
---|
300 | n/a | with openfile('msg_01.txt') as fp: |
---|
301 | n/a | text = fp.read() |
---|
302 | n/a | self.assertEqual(text, str(msg)) |
---|
303 | n/a | fullrepr = msg.as_string(unixfrom=True) |
---|
304 | n/a | lines = fullrepr.split('\n') |
---|
305 | n/a | self.assertTrue(lines[0].startswith('From ')) |
---|
306 | n/a | self.assertEqual(text, NL.join(lines[1:])) |
---|
307 | n/a | |
---|
308 | n/a | def test_as_string_policy(self): |
---|
309 | n/a | msg = self._msgobj('msg_01.txt') |
---|
310 | n/a | newpolicy = msg.policy.clone(linesep='\r\n') |
---|
311 | n/a | fullrepr = msg.as_string(policy=newpolicy) |
---|
312 | n/a | s = StringIO() |
---|
313 | n/a | g = Generator(s, policy=newpolicy) |
---|
314 | n/a | g.flatten(msg) |
---|
315 | n/a | self.assertEqual(fullrepr, s.getvalue()) |
---|
316 | n/a | |
---|
317 | n/a | def test_as_bytes(self): |
---|
318 | n/a | msg = self._msgobj('msg_01.txt') |
---|
319 | n/a | with openfile('msg_01.txt') as fp: |
---|
320 | n/a | data = fp.read().encode('ascii') |
---|
321 | n/a | self.assertEqual(data, bytes(msg)) |
---|
322 | n/a | fullrepr = msg.as_bytes(unixfrom=True) |
---|
323 | n/a | lines = fullrepr.split(b'\n') |
---|
324 | n/a | self.assertTrue(lines[0].startswith(b'From ')) |
---|
325 | n/a | self.assertEqual(data, b'\n'.join(lines[1:])) |
---|
326 | n/a | |
---|
327 | n/a | def test_as_bytes_policy(self): |
---|
328 | n/a | msg = self._msgobj('msg_01.txt') |
---|
329 | n/a | newpolicy = msg.policy.clone(linesep='\r\n') |
---|
330 | n/a | fullrepr = msg.as_bytes(policy=newpolicy) |
---|
331 | n/a | s = BytesIO() |
---|
332 | n/a | g = BytesGenerator(s,policy=newpolicy) |
---|
333 | n/a | g.flatten(msg) |
---|
334 | n/a | self.assertEqual(fullrepr, s.getvalue()) |
---|
335 | n/a | |
---|
336 | n/a | # test_headerregistry.TestContentTypeHeader.bad_params |
---|
337 | n/a | def test_bad_param(self): |
---|
338 | n/a | msg = email.message_from_string("Content-Type: blarg; baz; boo\n") |
---|
339 | n/a | self.assertEqual(msg.get_param('baz'), '') |
---|
340 | n/a | |
---|
341 | n/a | def test_missing_filename(self): |
---|
342 | n/a | msg = email.message_from_string("From: foo\n") |
---|
343 | n/a | self.assertEqual(msg.get_filename(), None) |
---|
344 | n/a | |
---|
345 | n/a | def test_bogus_filename(self): |
---|
346 | n/a | msg = email.message_from_string( |
---|
347 | n/a | "Content-Disposition: blarg; filename\n") |
---|
348 | n/a | self.assertEqual(msg.get_filename(), '') |
---|
349 | n/a | |
---|
350 | n/a | def test_missing_boundary(self): |
---|
351 | n/a | msg = email.message_from_string("From: foo\n") |
---|
352 | n/a | self.assertEqual(msg.get_boundary(), None) |
---|
353 | n/a | |
---|
354 | n/a | def test_get_params(self): |
---|
355 | n/a | eq = self.assertEqual |
---|
356 | n/a | msg = email.message_from_string( |
---|
357 | n/a | 'X-Header: foo=one; bar=two; baz=three\n') |
---|
358 | n/a | eq(msg.get_params(header='x-header'), |
---|
359 | n/a | [('foo', 'one'), ('bar', 'two'), ('baz', 'three')]) |
---|
360 | n/a | msg = email.message_from_string( |
---|
361 | n/a | 'X-Header: foo; bar=one; baz=two\n') |
---|
362 | n/a | eq(msg.get_params(header='x-header'), |
---|
363 | n/a | [('foo', ''), ('bar', 'one'), ('baz', 'two')]) |
---|
364 | n/a | eq(msg.get_params(), None) |
---|
365 | n/a | msg = email.message_from_string( |
---|
366 | n/a | 'X-Header: foo; bar="one"; baz=two\n') |
---|
367 | n/a | eq(msg.get_params(header='x-header'), |
---|
368 | n/a | [('foo', ''), ('bar', 'one'), ('baz', 'two')]) |
---|
369 | n/a | |
---|
370 | n/a | # test_headerregistry.TestContentTypeHeader.spaces_around_param_equals |
---|
371 | n/a | def test_get_param_liberal(self): |
---|
372 | n/a | msg = Message() |
---|
373 | n/a | msg['Content-Type'] = 'Content-Type: Multipart/mixed; boundary = "CPIMSSMTPC06p5f3tG"' |
---|
374 | n/a | self.assertEqual(msg.get_param('boundary'), 'CPIMSSMTPC06p5f3tG') |
---|
375 | n/a | |
---|
376 | n/a | def test_get_param(self): |
---|
377 | n/a | eq = self.assertEqual |
---|
378 | n/a | msg = email.message_from_string( |
---|
379 | n/a | "X-Header: foo=one; bar=two; baz=three\n") |
---|
380 | n/a | eq(msg.get_param('bar', header='x-header'), 'two') |
---|
381 | n/a | eq(msg.get_param('quuz', header='x-header'), None) |
---|
382 | n/a | eq(msg.get_param('quuz'), None) |
---|
383 | n/a | msg = email.message_from_string( |
---|
384 | n/a | 'X-Header: foo; bar="one"; baz=two\n') |
---|
385 | n/a | eq(msg.get_param('foo', header='x-header'), '') |
---|
386 | n/a | eq(msg.get_param('bar', header='x-header'), 'one') |
---|
387 | n/a | eq(msg.get_param('baz', header='x-header'), 'two') |
---|
388 | n/a | # XXX: We are not RFC-2045 compliant! We cannot parse: |
---|
389 | n/a | # msg["Content-Type"] = 'text/plain; weird="hey; dolly? [you] @ <\\"home\\">?"' |
---|
390 | n/a | # msg.get_param("weird") |
---|
391 | n/a | # yet. |
---|
392 | n/a | |
---|
393 | n/a | # test_headerregistry.TestContentTypeHeader.spaces_around_semis |
---|
394 | n/a | def test_get_param_funky_continuation_lines(self): |
---|
395 | n/a | msg = self._msgobj('msg_22.txt') |
---|
396 | n/a | self.assertEqual(msg.get_payload(1).get_param('name'), 'wibble.JPG') |
---|
397 | n/a | |
---|
398 | n/a | # test_headerregistry.TestContentTypeHeader.semis_inside_quotes |
---|
399 | n/a | def test_get_param_with_semis_in_quotes(self): |
---|
400 | n/a | msg = email.message_from_string( |
---|
401 | n/a | 'Content-Type: image/pjpeg; name="Jim&&Jill"\n') |
---|
402 | n/a | self.assertEqual(msg.get_param('name'), 'Jim&&Jill') |
---|
403 | n/a | self.assertEqual(msg.get_param('name', unquote=False), |
---|
404 | n/a | '"Jim&&Jill"') |
---|
405 | n/a | |
---|
406 | n/a | # test_headerregistry.TestContentTypeHeader.quotes_inside_rfc2231_value |
---|
407 | n/a | def test_get_param_with_quotes(self): |
---|
408 | n/a | msg = email.message_from_string( |
---|
409 | n/a | 'Content-Type: foo; bar*0="baz\\"foobar"; bar*1="\\"baz"') |
---|
410 | n/a | self.assertEqual(msg.get_param('bar'), 'baz"foobar"baz') |
---|
411 | n/a | msg = email.message_from_string( |
---|
412 | n/a | "Content-Type: foo; bar*0=\"baz\\\"foobar\"; bar*1=\"\\\"baz\"") |
---|
413 | n/a | self.assertEqual(msg.get_param('bar'), 'baz"foobar"baz') |
---|
414 | n/a | |
---|
415 | n/a | def test_field_containment(self): |
---|
416 | n/a | msg = email.message_from_string('Header: exists') |
---|
417 | n/a | self.assertIn('header', msg) |
---|
418 | n/a | self.assertIn('Header', msg) |
---|
419 | n/a | self.assertIn('HEADER', msg) |
---|
420 | n/a | self.assertNotIn('headerx', msg) |
---|
421 | n/a | |
---|
422 | n/a | def test_set_param(self): |
---|
423 | n/a | eq = self.assertEqual |
---|
424 | n/a | msg = Message() |
---|
425 | n/a | msg.set_param('charset', 'iso-2022-jp') |
---|
426 | n/a | eq(msg.get_param('charset'), 'iso-2022-jp') |
---|
427 | n/a | msg.set_param('importance', 'high value') |
---|
428 | n/a | eq(msg.get_param('importance'), 'high value') |
---|
429 | n/a | eq(msg.get_param('importance', unquote=False), '"high value"') |
---|
430 | n/a | eq(msg.get_params(), [('text/plain', ''), |
---|
431 | n/a | ('charset', 'iso-2022-jp'), |
---|
432 | n/a | ('importance', 'high value')]) |
---|
433 | n/a | eq(msg.get_params(unquote=False), [('text/plain', ''), |
---|
434 | n/a | ('charset', '"iso-2022-jp"'), |
---|
435 | n/a | ('importance', '"high value"')]) |
---|
436 | n/a | msg.set_param('charset', 'iso-9999-xx', header='X-Jimmy') |
---|
437 | n/a | eq(msg.get_param('charset', header='X-Jimmy'), 'iso-9999-xx') |
---|
438 | n/a | |
---|
439 | n/a | def test_del_param(self): |
---|
440 | n/a | eq = self.assertEqual |
---|
441 | n/a | msg = self._msgobj('msg_05.txt') |
---|
442 | n/a | eq(msg.get_params(), |
---|
443 | n/a | [('multipart/report', ''), ('report-type', 'delivery-status'), |
---|
444 | n/a | ('boundary', 'D1690A7AC1.996856090/mail.example.com')]) |
---|
445 | n/a | old_val = msg.get_param("report-type") |
---|
446 | n/a | msg.del_param("report-type") |
---|
447 | n/a | eq(msg.get_params(), |
---|
448 | n/a | [('multipart/report', ''), |
---|
449 | n/a | ('boundary', 'D1690A7AC1.996856090/mail.example.com')]) |
---|
450 | n/a | msg.set_param("report-type", old_val) |
---|
451 | n/a | eq(msg.get_params(), |
---|
452 | n/a | [('multipart/report', ''), |
---|
453 | n/a | ('boundary', 'D1690A7AC1.996856090/mail.example.com'), |
---|
454 | n/a | ('report-type', old_val)]) |
---|
455 | n/a | |
---|
456 | n/a | def test_del_param_on_other_header(self): |
---|
457 | n/a | msg = Message() |
---|
458 | n/a | msg.add_header('Content-Disposition', 'attachment', filename='bud.gif') |
---|
459 | n/a | msg.del_param('filename', 'content-disposition') |
---|
460 | n/a | self.assertEqual(msg['content-disposition'], 'attachment') |
---|
461 | n/a | |
---|
462 | n/a | def test_del_param_on_nonexistent_header(self): |
---|
463 | n/a | msg = Message() |
---|
464 | n/a | # Deleting param on empty msg should not raise exception. |
---|
465 | n/a | msg.del_param('filename', 'content-disposition') |
---|
466 | n/a | |
---|
467 | n/a | def test_del_nonexistent_param(self): |
---|
468 | n/a | msg = Message() |
---|
469 | n/a | msg.add_header('Content-Type', 'text/plain', charset='utf-8') |
---|
470 | n/a | existing_header = msg['Content-Type'] |
---|
471 | n/a | msg.del_param('foobar', header='Content-Type') |
---|
472 | n/a | self.assertEqual(msg['Content-Type'], existing_header) |
---|
473 | n/a | |
---|
474 | n/a | def test_set_type(self): |
---|
475 | n/a | eq = self.assertEqual |
---|
476 | n/a | msg = Message() |
---|
477 | n/a | self.assertRaises(ValueError, msg.set_type, 'text') |
---|
478 | n/a | msg.set_type('text/plain') |
---|
479 | n/a | eq(msg['content-type'], 'text/plain') |
---|
480 | n/a | msg.set_param('charset', 'us-ascii') |
---|
481 | n/a | eq(msg['content-type'], 'text/plain; charset="us-ascii"') |
---|
482 | n/a | msg.set_type('text/html') |
---|
483 | n/a | eq(msg['content-type'], 'text/html; charset="us-ascii"') |
---|
484 | n/a | |
---|
485 | n/a | def test_set_type_on_other_header(self): |
---|
486 | n/a | msg = Message() |
---|
487 | n/a | msg['X-Content-Type'] = 'text/plain' |
---|
488 | n/a | msg.set_type('application/octet-stream', 'X-Content-Type') |
---|
489 | n/a | self.assertEqual(msg['x-content-type'], 'application/octet-stream') |
---|
490 | n/a | |
---|
491 | n/a | def test_get_content_type_missing(self): |
---|
492 | n/a | msg = Message() |
---|
493 | n/a | self.assertEqual(msg.get_content_type(), 'text/plain') |
---|
494 | n/a | |
---|
495 | n/a | def test_get_content_type_missing_with_default_type(self): |
---|
496 | n/a | msg = Message() |
---|
497 | n/a | msg.set_default_type('message/rfc822') |
---|
498 | n/a | self.assertEqual(msg.get_content_type(), 'message/rfc822') |
---|
499 | n/a | |
---|
500 | n/a | def test_get_content_type_from_message_implicit(self): |
---|
501 | n/a | msg = self._msgobj('msg_30.txt') |
---|
502 | n/a | self.assertEqual(msg.get_payload(0).get_content_type(), |
---|
503 | n/a | 'message/rfc822') |
---|
504 | n/a | |
---|
505 | n/a | def test_get_content_type_from_message_explicit(self): |
---|
506 | n/a | msg = self._msgobj('msg_28.txt') |
---|
507 | n/a | self.assertEqual(msg.get_payload(0).get_content_type(), |
---|
508 | n/a | 'message/rfc822') |
---|
509 | n/a | |
---|
510 | n/a | def test_get_content_type_from_message_text_plain_implicit(self): |
---|
511 | n/a | msg = self._msgobj('msg_03.txt') |
---|
512 | n/a | self.assertEqual(msg.get_content_type(), 'text/plain') |
---|
513 | n/a | |
---|
514 | n/a | def test_get_content_type_from_message_text_plain_explicit(self): |
---|
515 | n/a | msg = self._msgobj('msg_01.txt') |
---|
516 | n/a | self.assertEqual(msg.get_content_type(), 'text/plain') |
---|
517 | n/a | |
---|
518 | n/a | def test_get_content_maintype_missing(self): |
---|
519 | n/a | msg = Message() |
---|
520 | n/a | self.assertEqual(msg.get_content_maintype(), 'text') |
---|
521 | n/a | |
---|
522 | n/a | def test_get_content_maintype_missing_with_default_type(self): |
---|
523 | n/a | msg = Message() |
---|
524 | n/a | msg.set_default_type('message/rfc822') |
---|
525 | n/a | self.assertEqual(msg.get_content_maintype(), 'message') |
---|
526 | n/a | |
---|
527 | n/a | def test_get_content_maintype_from_message_implicit(self): |
---|
528 | n/a | msg = self._msgobj('msg_30.txt') |
---|
529 | n/a | self.assertEqual(msg.get_payload(0).get_content_maintype(), 'message') |
---|
530 | n/a | |
---|
531 | n/a | def test_get_content_maintype_from_message_explicit(self): |
---|
532 | n/a | msg = self._msgobj('msg_28.txt') |
---|
533 | n/a | self.assertEqual(msg.get_payload(0).get_content_maintype(), 'message') |
---|
534 | n/a | |
---|
535 | n/a | def test_get_content_maintype_from_message_text_plain_implicit(self): |
---|
536 | n/a | msg = self._msgobj('msg_03.txt') |
---|
537 | n/a | self.assertEqual(msg.get_content_maintype(), 'text') |
---|
538 | n/a | |
---|
539 | n/a | def test_get_content_maintype_from_message_text_plain_explicit(self): |
---|
540 | n/a | msg = self._msgobj('msg_01.txt') |
---|
541 | n/a | self.assertEqual(msg.get_content_maintype(), 'text') |
---|
542 | n/a | |
---|
543 | n/a | def test_get_content_subtype_missing(self): |
---|
544 | n/a | msg = Message() |
---|
545 | n/a | self.assertEqual(msg.get_content_subtype(), 'plain') |
---|
546 | n/a | |
---|
547 | n/a | def test_get_content_subtype_missing_with_default_type(self): |
---|
548 | n/a | msg = Message() |
---|
549 | n/a | msg.set_default_type('message/rfc822') |
---|
550 | n/a | self.assertEqual(msg.get_content_subtype(), 'rfc822') |
---|
551 | n/a | |
---|
552 | n/a | def test_get_content_subtype_from_message_implicit(self): |
---|
553 | n/a | msg = self._msgobj('msg_30.txt') |
---|
554 | n/a | self.assertEqual(msg.get_payload(0).get_content_subtype(), 'rfc822') |
---|
555 | n/a | |
---|
556 | n/a | def test_get_content_subtype_from_message_explicit(self): |
---|
557 | n/a | msg = self._msgobj('msg_28.txt') |
---|
558 | n/a | self.assertEqual(msg.get_payload(0).get_content_subtype(), 'rfc822') |
---|
559 | n/a | |
---|
560 | n/a | def test_get_content_subtype_from_message_text_plain_implicit(self): |
---|
561 | n/a | msg = self._msgobj('msg_03.txt') |
---|
562 | n/a | self.assertEqual(msg.get_content_subtype(), 'plain') |
---|
563 | n/a | |
---|
564 | n/a | def test_get_content_subtype_from_message_text_plain_explicit(self): |
---|
565 | n/a | msg = self._msgobj('msg_01.txt') |
---|
566 | n/a | self.assertEqual(msg.get_content_subtype(), 'plain') |
---|
567 | n/a | |
---|
568 | n/a | def test_get_content_maintype_error(self): |
---|
569 | n/a | msg = Message() |
---|
570 | n/a | msg['Content-Type'] = 'no-slash-in-this-string' |
---|
571 | n/a | self.assertEqual(msg.get_content_maintype(), 'text') |
---|
572 | n/a | |
---|
573 | n/a | def test_get_content_subtype_error(self): |
---|
574 | n/a | msg = Message() |
---|
575 | n/a | msg['Content-Type'] = 'no-slash-in-this-string' |
---|
576 | n/a | self.assertEqual(msg.get_content_subtype(), 'plain') |
---|
577 | n/a | |
---|
578 | n/a | def test_replace_header(self): |
---|
579 | n/a | eq = self.assertEqual |
---|
580 | n/a | msg = Message() |
---|
581 | n/a | msg.add_header('First', 'One') |
---|
582 | n/a | msg.add_header('Second', 'Two') |
---|
583 | n/a | msg.add_header('Third', 'Three') |
---|
584 | n/a | eq(msg.keys(), ['First', 'Second', 'Third']) |
---|
585 | n/a | eq(msg.values(), ['One', 'Two', 'Three']) |
---|
586 | n/a | msg.replace_header('Second', 'Twenty') |
---|
587 | n/a | eq(msg.keys(), ['First', 'Second', 'Third']) |
---|
588 | n/a | eq(msg.values(), ['One', 'Twenty', 'Three']) |
---|
589 | n/a | msg.add_header('First', 'Eleven') |
---|
590 | n/a | msg.replace_header('First', 'One Hundred') |
---|
591 | n/a | eq(msg.keys(), ['First', 'Second', 'Third', 'First']) |
---|
592 | n/a | eq(msg.values(), ['One Hundred', 'Twenty', 'Three', 'Eleven']) |
---|
593 | n/a | self.assertRaises(KeyError, msg.replace_header, 'Fourth', 'Missing') |
---|
594 | n/a | |
---|
595 | n/a | def test_get_content_disposition(self): |
---|
596 | n/a | msg = Message() |
---|
597 | n/a | self.assertIsNone(msg.get_content_disposition()) |
---|
598 | n/a | msg.add_header('Content-Disposition', 'attachment', |
---|
599 | n/a | filename='random.avi') |
---|
600 | n/a | self.assertEqual(msg.get_content_disposition(), 'attachment') |
---|
601 | n/a | msg.replace_header('Content-Disposition', 'inline') |
---|
602 | n/a | self.assertEqual(msg.get_content_disposition(), 'inline') |
---|
603 | n/a | msg.replace_header('Content-Disposition', 'InlinE') |
---|
604 | n/a | self.assertEqual(msg.get_content_disposition(), 'inline') |
---|
605 | n/a | |
---|
606 | n/a | # test_defect_handling:test_invalid_chars_in_base64_payload |
---|
607 | n/a | def test_broken_base64_payload(self): |
---|
608 | n/a | x = 'AwDp0P7//y6LwKEAcPa/6Q=9' |
---|
609 | n/a | msg = Message() |
---|
610 | n/a | msg['content-type'] = 'audio/x-midi' |
---|
611 | n/a | msg['content-transfer-encoding'] = 'base64' |
---|
612 | n/a | msg.set_payload(x) |
---|
613 | n/a | self.assertEqual(msg.get_payload(decode=True), |
---|
614 | n/a | (b'\x03\x00\xe9\xd0\xfe\xff\xff.\x8b\xc0' |
---|
615 | n/a | b'\xa1\x00p\xf6\xbf\xe9\x0f')) |
---|
616 | n/a | self.assertIsInstance(msg.defects[0], |
---|
617 | n/a | errors.InvalidBase64CharactersDefect) |
---|
618 | n/a | |
---|
619 | n/a | def test_broken_unicode_payload(self): |
---|
620 | n/a | # This test improves coverage but is not a compliance test. |
---|
621 | n/a | # The behavior in this situation is currently undefined by the API. |
---|
622 | n/a | x = 'this is a br\xf6ken thing to do' |
---|
623 | n/a | msg = Message() |
---|
624 | n/a | msg['content-type'] = 'text/plain' |
---|
625 | n/a | msg['content-transfer-encoding'] = '8bit' |
---|
626 | n/a | msg.set_payload(x) |
---|
627 | n/a | self.assertEqual(msg.get_payload(decode=True), |
---|
628 | n/a | bytes(x, 'raw-unicode-escape')) |
---|
629 | n/a | |
---|
630 | n/a | def test_questionable_bytes_payload(self): |
---|
631 | n/a | # This test improves coverage but is not a compliance test, |
---|
632 | n/a | # since it involves poking inside the black box. |
---|
633 | n/a | x = 'this is a quéstionable thing to do'.encode('utf-8') |
---|
634 | n/a | msg = Message() |
---|
635 | n/a | msg['content-type'] = 'text/plain; charset="utf-8"' |
---|
636 | n/a | msg['content-transfer-encoding'] = '8bit' |
---|
637 | n/a | msg._payload = x |
---|
638 | n/a | self.assertEqual(msg.get_payload(decode=True), x) |
---|
639 | n/a | |
---|
640 | n/a | # Issue 1078919 |
---|
641 | n/a | def test_ascii_add_header(self): |
---|
642 | n/a | msg = Message() |
---|
643 | n/a | msg.add_header('Content-Disposition', 'attachment', |
---|
644 | n/a | filename='bud.gif') |
---|
645 | n/a | self.assertEqual('attachment; filename="bud.gif"', |
---|
646 | n/a | msg['Content-Disposition']) |
---|
647 | n/a | |
---|
648 | n/a | def test_noascii_add_header(self): |
---|
649 | n/a | msg = Message() |
---|
650 | n/a | msg.add_header('Content-Disposition', 'attachment', |
---|
651 | n/a | filename="FuÃballer.ppt") |
---|
652 | n/a | self.assertEqual( |
---|
653 | n/a | 'attachment; filename*=utf-8\'\'Fu%C3%9Fballer.ppt', |
---|
654 | n/a | msg['Content-Disposition']) |
---|
655 | n/a | |
---|
656 | n/a | def test_nonascii_add_header_via_triple(self): |
---|
657 | n/a | msg = Message() |
---|
658 | n/a | msg.add_header('Content-Disposition', 'attachment', |
---|
659 | n/a | filename=('iso-8859-1', '', 'FuÃballer.ppt')) |
---|
660 | n/a | self.assertEqual( |
---|
661 | n/a | 'attachment; filename*=iso-8859-1\'\'Fu%DFballer.ppt', |
---|
662 | n/a | msg['Content-Disposition']) |
---|
663 | n/a | |
---|
664 | n/a | def test_ascii_add_header_with_tspecial(self): |
---|
665 | n/a | msg = Message() |
---|
666 | n/a | msg.add_header('Content-Disposition', 'attachment', |
---|
667 | n/a | filename="windows [filename].ppt") |
---|
668 | n/a | self.assertEqual( |
---|
669 | n/a | 'attachment; filename="windows [filename].ppt"', |
---|
670 | n/a | msg['Content-Disposition']) |
---|
671 | n/a | |
---|
672 | n/a | def test_nonascii_add_header_with_tspecial(self): |
---|
673 | n/a | msg = Message() |
---|
674 | n/a | msg.add_header('Content-Disposition', 'attachment', |
---|
675 | n/a | filename="FuÃballer [filename].ppt") |
---|
676 | n/a | self.assertEqual( |
---|
677 | n/a | "attachment; filename*=utf-8''Fu%C3%9Fballer%20%5Bfilename%5D.ppt", |
---|
678 | n/a | msg['Content-Disposition']) |
---|
679 | n/a | |
---|
680 | n/a | def test_binary_quopri_payload(self): |
---|
681 | n/a | for charset in ('latin-1', 'ascii'): |
---|
682 | n/a | msg = Message() |
---|
683 | n/a | msg['content-type'] = 'text/plain; charset=%s' % charset |
---|
684 | n/a | msg['content-transfer-encoding'] = 'quoted-printable' |
---|
685 | n/a | msg.set_payload(b'foo=e6=96=87bar') |
---|
686 | n/a | self.assertEqual( |
---|
687 | n/a | msg.get_payload(decode=True), |
---|
688 | n/a | b'foo\xe6\x96\x87bar', |
---|
689 | n/a | 'get_payload returns wrong result with charset %s.' % charset) |
---|
690 | n/a | |
---|
691 | n/a | def test_binary_base64_payload(self): |
---|
692 | n/a | for charset in ('latin-1', 'ascii'): |
---|
693 | n/a | msg = Message() |
---|
694 | n/a | msg['content-type'] = 'text/plain; charset=%s' % charset |
---|
695 | n/a | msg['content-transfer-encoding'] = 'base64' |
---|
696 | n/a | msg.set_payload(b'Zm9v5paHYmFy') |
---|
697 | n/a | self.assertEqual( |
---|
698 | n/a | msg.get_payload(decode=True), |
---|
699 | n/a | b'foo\xe6\x96\x87bar', |
---|
700 | n/a | 'get_payload returns wrong result with charset %s.' % charset) |
---|
701 | n/a | |
---|
702 | n/a | def test_binary_uuencode_payload(self): |
---|
703 | n/a | for charset in ('latin-1', 'ascii'): |
---|
704 | n/a | for encoding in ('x-uuencode', 'uuencode', 'uue', 'x-uue'): |
---|
705 | n/a | msg = Message() |
---|
706 | n/a | msg['content-type'] = 'text/plain; charset=%s' % charset |
---|
707 | n/a | msg['content-transfer-encoding'] = encoding |
---|
708 | n/a | msg.set_payload(b"begin 666 -\n)9F]OYI:'8F%R\n \nend\n") |
---|
709 | n/a | self.assertEqual( |
---|
710 | n/a | msg.get_payload(decode=True), |
---|
711 | n/a | b'foo\xe6\x96\x87bar', |
---|
712 | n/a | str(('get_payload returns wrong result ', |
---|
713 | n/a | 'with charset {0} and encoding {1}.')).\ |
---|
714 | n/a | format(charset, encoding)) |
---|
715 | n/a | |
---|
716 | n/a | def test_add_header_with_name_only_param(self): |
---|
717 | n/a | msg = Message() |
---|
718 | n/a | msg.add_header('Content-Disposition', 'inline', foo_bar=None) |
---|
719 | n/a | self.assertEqual("inline; foo-bar", msg['Content-Disposition']) |
---|
720 | n/a | |
---|
721 | n/a | def test_add_header_with_no_value(self): |
---|
722 | n/a | msg = Message() |
---|
723 | n/a | msg.add_header('X-Status', None) |
---|
724 | n/a | self.assertEqual('', msg['X-Status']) |
---|
725 | n/a | |
---|
726 | n/a | # Issue 5871: reject an attempt to embed a header inside a header value |
---|
727 | n/a | # (header injection attack). |
---|
728 | n/a | def test_embedded_header_via_Header_rejected(self): |
---|
729 | n/a | msg = Message() |
---|
730 | n/a | msg['Dummy'] = Header('dummy\nX-Injected-Header: test') |
---|
731 | n/a | self.assertRaises(errors.HeaderParseError, msg.as_string) |
---|
732 | n/a | |
---|
733 | n/a | def test_embedded_header_via_string_rejected(self): |
---|
734 | n/a | msg = Message() |
---|
735 | n/a | msg['Dummy'] = 'dummy\nX-Injected-Header: test' |
---|
736 | n/a | self.assertRaises(errors.HeaderParseError, msg.as_string) |
---|
737 | n/a | |
---|
738 | n/a | def test_unicode_header_defaults_to_utf8_encoding(self): |
---|
739 | n/a | # Issue 14291 |
---|
740 | n/a | m = MIMEText('abc\n') |
---|
741 | n/a | m['Subject'] = 'Ã test' |
---|
742 | n/a | self.assertEqual(str(m),textwrap.dedent("""\ |
---|
743 | n/a | Content-Type: text/plain; charset="us-ascii" |
---|
744 | n/a | MIME-Version: 1.0 |
---|
745 | n/a | Content-Transfer-Encoding: 7bit |
---|
746 | n/a | Subject: =?utf-8?q?=C3=89_test?= |
---|
747 | n/a | |
---|
748 | n/a | abc |
---|
749 | n/a | """)) |
---|
750 | n/a | |
---|
751 | n/a | def test_unicode_body_defaults_to_utf8_encoding(self): |
---|
752 | n/a | # Issue 14291 |
---|
753 | n/a | m = MIMEText('Ã testabc\n') |
---|
754 | n/a | self.assertEqual(str(m),textwrap.dedent("""\ |
---|
755 | n/a | Content-Type: text/plain; charset="utf-8" |
---|
756 | n/a | MIME-Version: 1.0 |
---|
757 | n/a | Content-Transfer-Encoding: base64 |
---|
758 | n/a | |
---|
759 | n/a | w4kgdGVzdGFiYwo= |
---|
760 | n/a | """)) |
---|
761 | n/a | |
---|
762 | n/a | |
---|
763 | n/a | # Test the email.encoders module |
---|
764 | n/a | class TestEncoders(unittest.TestCase): |
---|
765 | n/a | |
---|
766 | n/a | def test_EncodersEncode_base64(self): |
---|
767 | n/a | with openfile('PyBanner048.gif', 'rb') as fp: |
---|
768 | n/a | bindata = fp.read() |
---|
769 | n/a | mimed = email.mime.image.MIMEImage(bindata) |
---|
770 | n/a | base64ed = mimed.get_payload() |
---|
771 | n/a | # the transfer-encoded body lines should all be <=76 characters |
---|
772 | n/a | lines = base64ed.split('\n') |
---|
773 | n/a | self.assertLessEqual(max([ len(x) for x in lines ]), 76) |
---|
774 | n/a | |
---|
775 | n/a | def test_encode_empty_payload(self): |
---|
776 | n/a | eq = self.assertEqual |
---|
777 | n/a | msg = Message() |
---|
778 | n/a | msg.set_charset('us-ascii') |
---|
779 | n/a | eq(msg['content-transfer-encoding'], '7bit') |
---|
780 | n/a | |
---|
781 | n/a | def test_default_cte(self): |
---|
782 | n/a | eq = self.assertEqual |
---|
783 | n/a | # 7bit data and the default us-ascii _charset |
---|
784 | n/a | msg = MIMEText('hello world') |
---|
785 | n/a | eq(msg['content-transfer-encoding'], '7bit') |
---|
786 | n/a | # Similar, but with 8bit data |
---|
787 | n/a | msg = MIMEText('hello \xf8 world') |
---|
788 | n/a | eq(msg['content-transfer-encoding'], 'base64') |
---|
789 | n/a | # And now with a different charset |
---|
790 | n/a | msg = MIMEText('hello \xf8 world', _charset='iso-8859-1') |
---|
791 | n/a | eq(msg['content-transfer-encoding'], 'quoted-printable') |
---|
792 | n/a | |
---|
793 | n/a | def test_encode7or8bit(self): |
---|
794 | n/a | # Make sure a charset whose input character set is 8bit but |
---|
795 | n/a | # whose output character set is 7bit gets a transfer-encoding |
---|
796 | n/a | # of 7bit. |
---|
797 | n/a | eq = self.assertEqual |
---|
798 | n/a | msg = MIMEText('æ\n', _charset='euc-jp') |
---|
799 | n/a | eq(msg['content-transfer-encoding'], '7bit') |
---|
800 | n/a | eq(msg.as_string(), textwrap.dedent("""\ |
---|
801 | n/a | MIME-Version: 1.0 |
---|
802 | n/a | Content-Type: text/plain; charset="iso-2022-jp" |
---|
803 | n/a | Content-Transfer-Encoding: 7bit |
---|
804 | n/a | |
---|
805 | n/a | \x1b$BJ8\x1b(B |
---|
806 | n/a | """)) |
---|
807 | n/a | |
---|
808 | n/a | def test_qp_encode_latin1(self): |
---|
809 | n/a | msg = MIMEText('\xe1\xf6\n', 'text', 'ISO-8859-1') |
---|
810 | n/a | self.assertEqual(str(msg), textwrap.dedent("""\ |
---|
811 | n/a | MIME-Version: 1.0 |
---|
812 | n/a | Content-Type: text/text; charset="iso-8859-1" |
---|
813 | n/a | Content-Transfer-Encoding: quoted-printable |
---|
814 | n/a | |
---|
815 | n/a | =E1=F6 |
---|
816 | n/a | """)) |
---|
817 | n/a | |
---|
818 | n/a | def test_qp_encode_non_latin1(self): |
---|
819 | n/a | # Issue 16948 |
---|
820 | n/a | msg = MIMEText('\u017c\n', 'text', 'ISO-8859-2') |
---|
821 | n/a | self.assertEqual(str(msg), textwrap.dedent("""\ |
---|
822 | n/a | MIME-Version: 1.0 |
---|
823 | n/a | Content-Type: text/text; charset="iso-8859-2" |
---|
824 | n/a | Content-Transfer-Encoding: quoted-printable |
---|
825 | n/a | |
---|
826 | n/a | =BF |
---|
827 | n/a | """)) |
---|
828 | n/a | |
---|
829 | n/a | |
---|
830 | n/a | # Test long header wrapping |
---|
831 | n/a | class TestLongHeaders(TestEmailBase): |
---|
832 | n/a | |
---|
833 | n/a | maxDiff = None |
---|
834 | n/a | |
---|
835 | n/a | def test_split_long_continuation(self): |
---|
836 | n/a | eq = self.ndiffAssertEqual |
---|
837 | n/a | msg = email.message_from_string("""\ |
---|
838 | n/a | Subject: bug demonstration |
---|
839 | n/a | \t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 |
---|
840 | n/a | \tmore text |
---|
841 | n/a | |
---|
842 | n/a | test |
---|
843 | n/a | """) |
---|
844 | n/a | sfp = StringIO() |
---|
845 | n/a | g = Generator(sfp) |
---|
846 | n/a | g.flatten(msg) |
---|
847 | n/a | eq(sfp.getvalue(), """\ |
---|
848 | n/a | Subject: bug demonstration |
---|
849 | n/a | \t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 |
---|
850 | n/a | \tmore text |
---|
851 | n/a | |
---|
852 | n/a | test |
---|
853 | n/a | """) |
---|
854 | n/a | |
---|
855 | n/a | def test_another_long_almost_unsplittable_header(self): |
---|
856 | n/a | eq = self.ndiffAssertEqual |
---|
857 | n/a | hstr = """\ |
---|
858 | n/a | bug demonstration |
---|
859 | n/a | \t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 |
---|
860 | n/a | \tmore text""" |
---|
861 | n/a | h = Header(hstr, continuation_ws='\t') |
---|
862 | n/a | eq(h.encode(), """\ |
---|
863 | n/a | bug demonstration |
---|
864 | n/a | \t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 |
---|
865 | n/a | \tmore text""") |
---|
866 | n/a | h = Header(hstr.replace('\t', ' ')) |
---|
867 | n/a | eq(h.encode(), """\ |
---|
868 | n/a | bug demonstration |
---|
869 | n/a | 12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 |
---|
870 | n/a | more text""") |
---|
871 | n/a | |
---|
872 | n/a | def test_long_nonstring(self): |
---|
873 | n/a | eq = self.ndiffAssertEqual |
---|
874 | n/a | g = Charset("iso-8859-1") |
---|
875 | n/a | cz = Charset("iso-8859-2") |
---|
876 | n/a | utf8 = Charset("utf-8") |
---|
877 | n/a | g_head = (b'Die Mieter treten hier ein werden mit einem Foerderband ' |
---|
878 | n/a | b'komfortabel den Korridor entlang, an s\xfcdl\xfcndischen ' |
---|
879 | n/a | b'Wandgem\xe4lden vorbei, gegen die rotierenden Klingen ' |
---|
880 | n/a | b'bef\xf6rdert. ') |
---|
881 | n/a | cz_head = (b'Finan\xe8ni metropole se hroutily pod tlakem jejich ' |
---|
882 | n/a | b'd\xf9vtipu.. ') |
---|
883 | n/a | utf8_head = ('\u6b63\u78ba\u306b\u8a00\u3046\u3068\u7ffb\u8a33\u306f' |
---|
884 | n/a | '\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u4e00' |
---|
885 | n/a | '\u90e8\u306f\u30c9\u30a4\u30c4\u8a9e\u3067\u3059\u304c' |
---|
886 | n/a | '\u3001\u3042\u3068\u306f\u3067\u305f\u3089\u3081\u3067' |
---|
887 | n/a | '\u3059\u3002\u5b9f\u969b\u306b\u306f\u300cWenn ist das ' |
---|
888 | n/a | 'Nunstuck git und Slotermeyer? Ja! Beiherhund das Oder ' |
---|
889 | n/a | 'die Flipperwaldt gersput.\u300d\u3068\u8a00\u3063\u3066' |
---|
890 | n/a | '\u3044\u307e\u3059\u3002') |
---|
891 | n/a | h = Header(g_head, g, header_name='Subject') |
---|
892 | n/a | h.append(cz_head, cz) |
---|
893 | n/a | h.append(utf8_head, utf8) |
---|
894 | n/a | msg = Message() |
---|
895 | n/a | msg['Subject'] = h |
---|
896 | n/a | sfp = StringIO() |
---|
897 | n/a | g = Generator(sfp) |
---|
898 | n/a | g.flatten(msg) |
---|
899 | n/a | eq(sfp.getvalue(), """\ |
---|
900 | n/a | Subject: =?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_einem_Foerderb?= |
---|
901 | n/a | =?iso-8859-1?q?and_komfortabel_den_Korridor_entlang=2C_an_s=FCdl=FCndischen?= |
---|
902 | n/a | =?iso-8859-1?q?_Wandgem=E4lden_vorbei=2C_gegen_die_rotierenden_Klingen_bef?= |
---|
903 | n/a | =?iso-8859-1?q?=F6rdert=2E_?= =?iso-8859-2?q?Finan=E8ni_metropole_se_hrouti?= |
---|
904 | n/a | =?iso-8859-2?q?ly_pod_tlakem_jejich_d=F9vtipu=2E=2E_?= =?utf-8?b?5q2j56K6?= |
---|
905 | n/a | =?utf-8?b?44Gr6KiA44GG44Go57+76Kiz44Gv44GV44KM44Gm44GE44G+44Gb44KT44CC5LiA?= |
---|
906 | n/a | =?utf-8?b?6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM44CB44GC44Go44Gv44Gn44Gf44KJ?= |
---|
907 | n/a | =?utf-8?b?44KB44Gn44GZ44CC5a6f6Zqb44Gr44Gv44CMV2VubiBpc3QgZGFzIE51bnN0dWNr?= |
---|
908 | n/a | =?utf-8?b?IGdpdCB1bmQgU2xvdGVybWV5ZXI/IEphISBCZWloZXJodW5kIGRhcyBPZGVyIGRp?= |
---|
909 | n/a | =?utf-8?b?ZSBGbGlwcGVyd2FsZHQgZ2Vyc3B1dC7jgI3jgajoqIDjgaPjgabjgYTjgb7jgZk=?= |
---|
910 | n/a | =?utf-8?b?44CC?= |
---|
911 | n/a | |
---|
912 | n/a | """) |
---|
913 | n/a | eq(h.encode(maxlinelen=76), """\ |
---|
914 | n/a | =?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_einem_Foerde?= |
---|
915 | n/a | =?iso-8859-1?q?rband_komfortabel_den_Korridor_entlang=2C_an_s=FCdl=FCndis?= |
---|
916 | n/a | =?iso-8859-1?q?chen_Wandgem=E4lden_vorbei=2C_gegen_die_rotierenden_Klinge?= |
---|
917 | n/a | =?iso-8859-1?q?n_bef=F6rdert=2E_?= =?iso-8859-2?q?Finan=E8ni_metropole_se?= |
---|
918 | n/a | =?iso-8859-2?q?_hroutily_pod_tlakem_jejich_d=F9vtipu=2E=2E_?= |
---|
919 | n/a | =?utf-8?b?5q2j56K644Gr6KiA44GG44Go57+76Kiz44Gv44GV44KM44Gm44GE44G+44Gb?= |
---|
920 | n/a | =?utf-8?b?44KT44CC5LiA6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM44CB44GC44Go?= |
---|
921 | n/a | =?utf-8?b?44Gv44Gn44Gf44KJ44KB44Gn44GZ44CC5a6f6Zqb44Gr44Gv44CMV2VubiBp?= |
---|
922 | n/a | =?utf-8?b?c3QgZGFzIE51bnN0dWNrIGdpdCB1bmQgU2xvdGVybWV5ZXI/IEphISBCZWlo?= |
---|
923 | n/a | =?utf-8?b?ZXJodW5kIGRhcyBPZGVyIGRpZSBGbGlwcGVyd2FsZHQgZ2Vyc3B1dC7jgI0=?= |
---|
924 | n/a | =?utf-8?b?44Go6KiA44Gj44Gm44GE44G+44GZ44CC?=""") |
---|
925 | n/a | |
---|
926 | n/a | def test_long_header_encode(self): |
---|
927 | n/a | eq = self.ndiffAssertEqual |
---|
928 | n/a | h = Header('wasnipoop; giraffes="very-long-necked-animals"; ' |
---|
929 | n/a | 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"', |
---|
930 | n/a | header_name='X-Foobar-Spoink-Defrobnit') |
---|
931 | n/a | eq(h.encode(), '''\ |
---|
932 | n/a | wasnipoop; giraffes="very-long-necked-animals"; |
---|
933 | n/a | spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''') |
---|
934 | n/a | |
---|
935 | n/a | def test_long_header_encode_with_tab_continuation_is_just_a_hint(self): |
---|
936 | n/a | eq = self.ndiffAssertEqual |
---|
937 | n/a | h = Header('wasnipoop; giraffes="very-long-necked-animals"; ' |
---|
938 | n/a | 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"', |
---|
939 | n/a | header_name='X-Foobar-Spoink-Defrobnit', |
---|
940 | n/a | continuation_ws='\t') |
---|
941 | n/a | eq(h.encode(), '''\ |
---|
942 | n/a | wasnipoop; giraffes="very-long-necked-animals"; |
---|
943 | n/a | spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''') |
---|
944 | n/a | |
---|
945 | n/a | def test_long_header_encode_with_tab_continuation(self): |
---|
946 | n/a | eq = self.ndiffAssertEqual |
---|
947 | n/a | h = Header('wasnipoop; giraffes="very-long-necked-animals";\t' |
---|
948 | n/a | 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"', |
---|
949 | n/a | header_name='X-Foobar-Spoink-Defrobnit', |
---|
950 | n/a | continuation_ws='\t') |
---|
951 | n/a | eq(h.encode(), '''\ |
---|
952 | n/a | wasnipoop; giraffes="very-long-necked-animals"; |
---|
953 | n/a | \tspooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''') |
---|
954 | n/a | |
---|
955 | n/a | def test_header_encode_with_different_output_charset(self): |
---|
956 | n/a | h = Header('æ', 'euc-jp') |
---|
957 | n/a | self.assertEqual(h.encode(), "=?iso-2022-jp?b?GyRCSjgbKEI=?=") |
---|
958 | n/a | |
---|
959 | n/a | def test_long_header_encode_with_different_output_charset(self): |
---|
960 | n/a | h = Header(b'test-ja \xa4\xd8\xc5\xea\xb9\xc6\xa4\xb5\xa4\xec\xa4' |
---|
961 | n/a | b'\xbf\xa5\xe1\xa1\xbc\xa5\xeb\xa4\xcf\xbb\xca\xb2\xf1\xbc\xd4' |
---|
962 | n/a | b'\xa4\xce\xbe\xb5\xc7\xa7\xa4\xf2\xc2\xd4\xa4\xc3\xa4\xc6\xa4' |
---|
963 | n/a | b'\xa4\xa4\xde\xa4\xb9'.decode('euc-jp'), 'euc-jp') |
---|
964 | n/a | res = """\ |
---|
965 | n/a | =?iso-2022-jp?b?dGVzdC1qYSAbJEIkWEVqOUYkNSRsJD8lYSE8JWskTztKMnE8VCROPjUbKEI=?= |
---|
966 | n/a | =?iso-2022-jp?b?GyRCRyckckJUJEMkRiQkJF4kORsoQg==?=""" |
---|
967 | n/a | self.assertEqual(h.encode(), res) |
---|
968 | n/a | |
---|
969 | n/a | def test_header_splitter(self): |
---|
970 | n/a | eq = self.ndiffAssertEqual |
---|
971 | n/a | msg = MIMEText('') |
---|
972 | n/a | # It'd be great if we could use add_header() here, but that doesn't |
---|
973 | n/a | # guarantee an order of the parameters. |
---|
974 | n/a | msg['X-Foobar-Spoink-Defrobnit'] = ( |
---|
975 | n/a | 'wasnipoop; giraffes="very-long-necked-animals"; ' |
---|
976 | n/a | 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"') |
---|
977 | n/a | sfp = StringIO() |
---|
978 | n/a | g = Generator(sfp) |
---|
979 | n/a | g.flatten(msg) |
---|
980 | n/a | eq(sfp.getvalue(), '''\ |
---|
981 | n/a | Content-Type: text/plain; charset="us-ascii" |
---|
982 | n/a | MIME-Version: 1.0 |
---|
983 | n/a | Content-Transfer-Encoding: 7bit |
---|
984 | n/a | X-Foobar-Spoink-Defrobnit: wasnipoop; giraffes="very-long-necked-animals"; |
---|
985 | n/a | spooge="yummy"; hippos="gargantuan"; marshmallows="gooey" |
---|
986 | n/a | |
---|
987 | n/a | ''') |
---|
988 | n/a | |
---|
989 | n/a | def test_no_semis_header_splitter(self): |
---|
990 | n/a | eq = self.ndiffAssertEqual |
---|
991 | n/a | msg = Message() |
---|
992 | n/a | msg['From'] = 'test@dom.ain' |
---|
993 | n/a | msg['References'] = SPACE.join('<%d@dom.ain>' % i for i in range(10)) |
---|
994 | n/a | msg.set_payload('Test') |
---|
995 | n/a | sfp = StringIO() |
---|
996 | n/a | g = Generator(sfp) |
---|
997 | n/a | g.flatten(msg) |
---|
998 | n/a | eq(sfp.getvalue(), """\ |
---|
999 | n/a | From: test@dom.ain |
---|
1000 | n/a | References: <0@dom.ain> <1@dom.ain> <2@dom.ain> <3@dom.ain> <4@dom.ain> |
---|
1001 | n/a | <5@dom.ain> <6@dom.ain> <7@dom.ain> <8@dom.ain> <9@dom.ain> |
---|
1002 | n/a | |
---|
1003 | n/a | Test""") |
---|
1004 | n/a | |
---|
1005 | n/a | def test_last_split_chunk_does_not_fit(self): |
---|
1006 | n/a | eq = self.ndiffAssertEqual |
---|
1007 | n/a | h = Header('Subject: the first part of this is short, but_the_second' |
---|
1008 | n/a | '_part_does_not_fit_within_maxlinelen_and_thus_should_be_on_a_line' |
---|
1009 | n/a | '_all_by_itself') |
---|
1010 | n/a | eq(h.encode(), """\ |
---|
1011 | n/a | Subject: the first part of this is short, |
---|
1012 | n/a | but_the_second_part_does_not_fit_within_maxlinelen_and_thus_should_be_on_a_line_all_by_itself""") |
---|
1013 | n/a | |
---|
1014 | n/a | def test_splittable_leading_char_followed_by_overlong_unsplitable(self): |
---|
1015 | n/a | eq = self.ndiffAssertEqual |
---|
1016 | n/a | h = Header(', but_the_second' |
---|
1017 | n/a | '_part_does_not_fit_within_maxlinelen_and_thus_should_be_on_a_line' |
---|
1018 | n/a | '_all_by_itself') |
---|
1019 | n/a | eq(h.encode(), """\ |
---|
1020 | n/a | , |
---|
1021 | n/a | but_the_second_part_does_not_fit_within_maxlinelen_and_thus_should_be_on_a_line_all_by_itself""") |
---|
1022 | n/a | |
---|
1023 | n/a | def test_multiple_splittable_leading_char_followed_by_overlong_unsplitable(self): |
---|
1024 | n/a | eq = self.ndiffAssertEqual |
---|
1025 | n/a | h = Header(', , but_the_second' |
---|
1026 | n/a | '_part_does_not_fit_within_maxlinelen_and_thus_should_be_on_a_line' |
---|
1027 | n/a | '_all_by_itself') |
---|
1028 | n/a | eq(h.encode(), """\ |
---|
1029 | n/a | , , |
---|
1030 | n/a | but_the_second_part_does_not_fit_within_maxlinelen_and_thus_should_be_on_a_line_all_by_itself""") |
---|
1031 | n/a | |
---|
1032 | n/a | def test_trailing_splitable_on_overlong_unsplitable(self): |
---|
1033 | n/a | eq = self.ndiffAssertEqual |
---|
1034 | n/a | h = Header('this_part_does_not_fit_within_maxlinelen_and_thus_should_' |
---|
1035 | n/a | 'be_on_a_line_all_by_itself;') |
---|
1036 | n/a | eq(h.encode(), "this_part_does_not_fit_within_maxlinelen_and_thus_should_" |
---|
1037 | n/a | "be_on_a_line_all_by_itself;") |
---|
1038 | n/a | |
---|
1039 | n/a | def test_trailing_splitable_on_overlong_unsplitable_with_leading_splitable(self): |
---|
1040 | n/a | eq = self.ndiffAssertEqual |
---|
1041 | n/a | h = Header('; ' |
---|
1042 | n/a | 'this_part_does_not_fit_within_maxlinelen_and_thus_should_' |
---|
1043 | n/a | 'be_on_a_line_all_by_itself; ') |
---|
1044 | n/a | eq(h.encode(), """\ |
---|
1045 | n/a | ; |
---|
1046 | n/a | this_part_does_not_fit_within_maxlinelen_and_thus_should_be_on_a_line_all_by_itself; """) |
---|
1047 | n/a | |
---|
1048 | n/a | def test_long_header_with_multiple_sequential_split_chars(self): |
---|
1049 | n/a | eq = self.ndiffAssertEqual |
---|
1050 | n/a | h = Header('This is a long line that has two whitespaces in a row. ' |
---|
1051 | n/a | 'This used to cause truncation of the header when folded') |
---|
1052 | n/a | eq(h.encode(), """\ |
---|
1053 | n/a | This is a long line that has two whitespaces in a row. This used to cause |
---|
1054 | n/a | truncation of the header when folded""") |
---|
1055 | n/a | |
---|
1056 | n/a | def test_splitter_split_on_punctuation_only_if_fws_with_header(self): |
---|
1057 | n/a | eq = self.ndiffAssertEqual |
---|
1058 | n/a | h = Header('thisverylongheaderhas;semicolons;and,commas,but' |
---|
1059 | n/a | 'they;arenotlegal;fold,points') |
---|
1060 | n/a | eq(h.encode(), "thisverylongheaderhas;semicolons;and,commas,butthey;" |
---|
1061 | n/a | "arenotlegal;fold,points") |
---|
1062 | n/a | |
---|
1063 | n/a | def test_leading_splittable_in_the_middle_just_before_overlong_last_part(self): |
---|
1064 | n/a | eq = self.ndiffAssertEqual |
---|
1065 | n/a | h = Header('this is a test where we need to have more than one line ' |
---|
1066 | n/a | 'before; our final line that is just too big to fit;; ' |
---|
1067 | n/a | 'this_part_does_not_fit_within_maxlinelen_and_thus_should_' |
---|
1068 | n/a | 'be_on_a_line_all_by_itself;') |
---|
1069 | n/a | eq(h.encode(), """\ |
---|
1070 | n/a | this is a test where we need to have more than one line before; |
---|
1071 | n/a | our final line that is just too big to fit;; |
---|
1072 | n/a | this_part_does_not_fit_within_maxlinelen_and_thus_should_be_on_a_line_all_by_itself;""") |
---|
1073 | n/a | |
---|
1074 | n/a | def test_overlong_last_part_followed_by_split_point(self): |
---|
1075 | n/a | eq = self.ndiffAssertEqual |
---|
1076 | n/a | h = Header('this_part_does_not_fit_within_maxlinelen_and_thus_should_' |
---|
1077 | n/a | 'be_on_a_line_all_by_itself ') |
---|
1078 | n/a | eq(h.encode(), "this_part_does_not_fit_within_maxlinelen_and_thus_" |
---|
1079 | n/a | "should_be_on_a_line_all_by_itself ") |
---|
1080 | n/a | |
---|
1081 | n/a | def test_multiline_with_overlong_parts_separated_by_two_split_points(self): |
---|
1082 | n/a | eq = self.ndiffAssertEqual |
---|
1083 | n/a | h = Header('this_is_a__test_where_we_need_to_have_more_than_one_line_' |
---|
1084 | n/a | 'before_our_final_line_; ; ' |
---|
1085 | n/a | 'this_part_does_not_fit_within_maxlinelen_and_thus_should_' |
---|
1086 | n/a | 'be_on_a_line_all_by_itself; ') |
---|
1087 | n/a | eq(h.encode(), """\ |
---|
1088 | n/a | this_is_a__test_where_we_need_to_have_more_than_one_line_before_our_final_line_; |
---|
1089 | n/a | ; |
---|
1090 | n/a | this_part_does_not_fit_within_maxlinelen_and_thus_should_be_on_a_line_all_by_itself; """) |
---|
1091 | n/a | |
---|
1092 | n/a | def test_multiline_with_overlong_last_part_followed_by_split_point(self): |
---|
1093 | n/a | eq = self.ndiffAssertEqual |
---|
1094 | n/a | h = Header('this is a test where we need to have more than one line ' |
---|
1095 | n/a | 'before our final line; ; ' |
---|
1096 | n/a | 'this_part_does_not_fit_within_maxlinelen_and_thus_should_' |
---|
1097 | n/a | 'be_on_a_line_all_by_itself; ') |
---|
1098 | n/a | eq(h.encode(), """\ |
---|
1099 | n/a | this is a test where we need to have more than one line before our final line; |
---|
1100 | n/a | ; |
---|
1101 | n/a | this_part_does_not_fit_within_maxlinelen_and_thus_should_be_on_a_line_all_by_itself; """) |
---|
1102 | n/a | |
---|
1103 | n/a | def test_long_header_with_whitespace_runs(self): |
---|
1104 | n/a | eq = self.ndiffAssertEqual |
---|
1105 | n/a | msg = Message() |
---|
1106 | n/a | msg['From'] = 'test@dom.ain' |
---|
1107 | n/a | msg['References'] = SPACE.join(['<foo@dom.ain> '] * 10) |
---|
1108 | n/a | msg.set_payload('Test') |
---|
1109 | n/a | sfp = StringIO() |
---|
1110 | n/a | g = Generator(sfp) |
---|
1111 | n/a | g.flatten(msg) |
---|
1112 | n/a | eq(sfp.getvalue(), """\ |
---|
1113 | n/a | From: test@dom.ain |
---|
1114 | n/a | References: <foo@dom.ain> <foo@dom.ain> <foo@dom.ain> <foo@dom.ain> |
---|
1115 | n/a | <foo@dom.ain> <foo@dom.ain> <foo@dom.ain> <foo@dom.ain> |
---|
1116 | n/a | <foo@dom.ain> <foo@dom.ain>\x20\x20 |
---|
1117 | n/a | |
---|
1118 | n/a | Test""") |
---|
1119 | n/a | |
---|
1120 | n/a | def test_long_run_with_semi_header_splitter(self): |
---|
1121 | n/a | eq = self.ndiffAssertEqual |
---|
1122 | n/a | msg = Message() |
---|
1123 | n/a | msg['From'] = 'test@dom.ain' |
---|
1124 | n/a | msg['References'] = SPACE.join(['<foo@dom.ain>'] * 10) + '; abc' |
---|
1125 | n/a | msg.set_payload('Test') |
---|
1126 | n/a | sfp = StringIO() |
---|
1127 | n/a | g = Generator(sfp) |
---|
1128 | n/a | g.flatten(msg) |
---|
1129 | n/a | eq(sfp.getvalue(), """\ |
---|
1130 | n/a | From: test@dom.ain |
---|
1131 | n/a | References: <foo@dom.ain> <foo@dom.ain> <foo@dom.ain> <foo@dom.ain> |
---|
1132 | n/a | <foo@dom.ain> <foo@dom.ain> <foo@dom.ain> <foo@dom.ain> <foo@dom.ain> |
---|
1133 | n/a | <foo@dom.ain>; abc |
---|
1134 | n/a | |
---|
1135 | n/a | Test""") |
---|
1136 | n/a | |
---|
1137 | n/a | def test_splitter_split_on_punctuation_only_if_fws(self): |
---|
1138 | n/a | eq = self.ndiffAssertEqual |
---|
1139 | n/a | msg = Message() |
---|
1140 | n/a | msg['From'] = 'test@dom.ain' |
---|
1141 | n/a | msg['References'] = ('thisverylongheaderhas;semicolons;and,commas,but' |
---|
1142 | n/a | 'they;arenotlegal;fold,points') |
---|
1143 | n/a | msg.set_payload('Test') |
---|
1144 | n/a | sfp = StringIO() |
---|
1145 | n/a | g = Generator(sfp) |
---|
1146 | n/a | g.flatten(msg) |
---|
1147 | n/a | # XXX the space after the header should not be there. |
---|
1148 | n/a | eq(sfp.getvalue(), """\ |
---|
1149 | n/a | From: test@dom.ain |
---|
1150 | n/a | References:\x20 |
---|
1151 | n/a | thisverylongheaderhas;semicolons;and,commas,butthey;arenotlegal;fold,points |
---|
1152 | n/a | |
---|
1153 | n/a | Test""") |
---|
1154 | n/a | |
---|
1155 | n/a | def test_no_split_long_header(self): |
---|
1156 | n/a | eq = self.ndiffAssertEqual |
---|
1157 | n/a | hstr = 'References: ' + 'x' * 80 |
---|
1158 | n/a | h = Header(hstr) |
---|
1159 | n/a | # These come on two lines because Headers are really field value |
---|
1160 | n/a | # classes and don't really know about their field names. |
---|
1161 | n/a | eq(h.encode(), """\ |
---|
1162 | n/a | References: |
---|
1163 | n/a | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx""") |
---|
1164 | n/a | h = Header('x' * 80) |
---|
1165 | n/a | eq(h.encode(), 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') |
---|
1166 | n/a | |
---|
1167 | n/a | def test_splitting_multiple_long_lines(self): |
---|
1168 | n/a | eq = self.ndiffAssertEqual |
---|
1169 | n/a | hstr = """\ |
---|
1170 | n/a | from babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for <mailman-admin@babylon.socal-raves.org>; Sat, 2 Feb 2002 17:00:06 -0800 (PST) |
---|
1171 | n/a | \tfrom babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for <mailman-admin@babylon.socal-raves.org>; Sat, 2 Feb 2002 17:00:06 -0800 (PST) |
---|
1172 | n/a | \tfrom babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for <mailman-admin@babylon.socal-raves.org>; Sat, 2 Feb 2002 17:00:06 -0800 (PST) |
---|
1173 | n/a | """ |
---|
1174 | n/a | h = Header(hstr, continuation_ws='\t') |
---|
1175 | n/a | eq(h.encode(), """\ |
---|
1176 | n/a | from babylon.socal-raves.org (localhost [127.0.0.1]); |
---|
1177 | n/a | by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; |
---|
1178 | n/a | for <mailman-admin@babylon.socal-raves.org>; |
---|
1179 | n/a | Sat, 2 Feb 2002 17:00:06 -0800 (PST) |
---|
1180 | n/a | \tfrom babylon.socal-raves.org (localhost [127.0.0.1]); |
---|
1181 | n/a | by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; |
---|
1182 | n/a | for <mailman-admin@babylon.socal-raves.org>; |
---|
1183 | n/a | Sat, 2 Feb 2002 17:00:06 -0800 (PST) |
---|
1184 | n/a | \tfrom babylon.socal-raves.org (localhost [127.0.0.1]); |
---|
1185 | n/a | by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; |
---|
1186 | n/a | for <mailman-admin@babylon.socal-raves.org>; |
---|
1187 | n/a | Sat, 2 Feb 2002 17:00:06 -0800 (PST)""") |
---|
1188 | n/a | |
---|
1189 | n/a | def test_splitting_first_line_only_is_long(self): |
---|
1190 | n/a | eq = self.ndiffAssertEqual |
---|
1191 | n/a | hstr = """\ |
---|
1192 | n/a | from modemcable093.139-201-24.que.mc.videotron.ca ([24.201.139.93] helo=cthulhu.gerg.ca) |
---|
1193 | n/a | \tby kronos.mems-exchange.org with esmtp (Exim 4.05) |
---|
1194 | n/a | \tid 17k4h5-00034i-00 |
---|
1195 | n/a | \tfor test@mems-exchange.org; Wed, 28 Aug 2002 11:25:20 -0400""" |
---|
1196 | n/a | h = Header(hstr, maxlinelen=78, header_name='Received', |
---|
1197 | n/a | continuation_ws='\t') |
---|
1198 | n/a | eq(h.encode(), """\ |
---|
1199 | n/a | from modemcable093.139-201-24.que.mc.videotron.ca ([24.201.139.93] |
---|
1200 | n/a | helo=cthulhu.gerg.ca) |
---|
1201 | n/a | \tby kronos.mems-exchange.org with esmtp (Exim 4.05) |
---|
1202 | n/a | \tid 17k4h5-00034i-00 |
---|
1203 | n/a | \tfor test@mems-exchange.org; Wed, 28 Aug 2002 11:25:20 -0400""") |
---|
1204 | n/a | |
---|
1205 | n/a | def test_long_8bit_header(self): |
---|
1206 | n/a | eq = self.ndiffAssertEqual |
---|
1207 | n/a | msg = Message() |
---|
1208 | n/a | h = Header('Britische Regierung gibt', 'iso-8859-1', |
---|
1209 | n/a | header_name='Subject') |
---|
1210 | n/a | h.append('gr\xfcnes Licht f\xfcr Offshore-Windkraftprojekte') |
---|
1211 | n/a | eq(h.encode(maxlinelen=76), """\ |
---|
1212 | n/a | =?iso-8859-1?q?Britische_Regierung_gibt_gr=FCnes_Licht_f=FCr_Offs?= |
---|
1213 | n/a | =?iso-8859-1?q?hore-Windkraftprojekte?=""") |
---|
1214 | n/a | msg['Subject'] = h |
---|
1215 | n/a | eq(msg.as_string(maxheaderlen=76), """\ |
---|
1216 | n/a | Subject: =?iso-8859-1?q?Britische_Regierung_gibt_gr=FCnes_Licht_f=FCr_Offs?= |
---|
1217 | n/a | =?iso-8859-1?q?hore-Windkraftprojekte?= |
---|
1218 | n/a | |
---|
1219 | n/a | """) |
---|
1220 | n/a | eq(msg.as_string(maxheaderlen=0), """\ |
---|
1221 | n/a | Subject: =?iso-8859-1?q?Britische_Regierung_gibt_gr=FCnes_Licht_f=FCr_Offshore-Windkraftprojekte?= |
---|
1222 | n/a | |
---|
1223 | n/a | """) |
---|
1224 | n/a | |
---|
1225 | n/a | def test_long_8bit_header_no_charset(self): |
---|
1226 | n/a | eq = self.ndiffAssertEqual |
---|
1227 | n/a | msg = Message() |
---|
1228 | n/a | header_string = ('Britische Regierung gibt gr\xfcnes Licht ' |
---|
1229 | n/a | 'f\xfcr Offshore-Windkraftprojekte ' |
---|
1230 | n/a | '<a-very-long-address@example.com>') |
---|
1231 | n/a | msg['Reply-To'] = header_string |
---|
1232 | n/a | eq(msg.as_string(maxheaderlen=78), """\ |
---|
1233 | n/a | Reply-To: =?utf-8?q?Britische_Regierung_gibt_gr=C3=BCnes_Licht_f=C3=BCr_Offs?= |
---|
1234 | n/a | =?utf-8?q?hore-Windkraftprojekte_=3Ca-very-long-address=40example=2Ecom=3E?= |
---|
1235 | n/a | |
---|
1236 | n/a | """) |
---|
1237 | n/a | msg = Message() |
---|
1238 | n/a | msg['Reply-To'] = Header(header_string, |
---|
1239 | n/a | header_name='Reply-To') |
---|
1240 | n/a | eq(msg.as_string(maxheaderlen=78), """\ |
---|
1241 | n/a | Reply-To: =?utf-8?q?Britische_Regierung_gibt_gr=C3=BCnes_Licht_f=C3=BCr_Offs?= |
---|
1242 | n/a | =?utf-8?q?hore-Windkraftprojekte_=3Ca-very-long-address=40example=2Ecom=3E?= |
---|
1243 | n/a | |
---|
1244 | n/a | """) |
---|
1245 | n/a | |
---|
1246 | n/a | def test_long_to_header(self): |
---|
1247 | n/a | eq = self.ndiffAssertEqual |
---|
1248 | n/a | to = ('"Someone Test #A" <someone@eecs.umich.edu>,' |
---|
1249 | n/a | '<someone@eecs.umich.edu>, ' |
---|
1250 | n/a | '"Someone Test #B" <someone@umich.edu>, ' |
---|
1251 | n/a | '"Someone Test #C" <someone@eecs.umich.edu>, ' |
---|
1252 | n/a | '"Someone Test #D" <someone@eecs.umich.edu>') |
---|
1253 | n/a | msg = Message() |
---|
1254 | n/a | msg['To'] = to |
---|
1255 | n/a | eq(msg.as_string(maxheaderlen=78), '''\ |
---|
1256 | n/a | To: "Someone Test #A" <someone@eecs.umich.edu>,<someone@eecs.umich.edu>, |
---|
1257 | n/a | "Someone Test #B" <someone@umich.edu>, |
---|
1258 | n/a | "Someone Test #C" <someone@eecs.umich.edu>, |
---|
1259 | n/a | "Someone Test #D" <someone@eecs.umich.edu> |
---|
1260 | n/a | |
---|
1261 | n/a | ''') |
---|
1262 | n/a | |
---|
1263 | n/a | def test_long_line_after_append(self): |
---|
1264 | n/a | eq = self.ndiffAssertEqual |
---|
1265 | n/a | s = 'This is an example of string which has almost the limit of header length.' |
---|
1266 | n/a | h = Header(s) |
---|
1267 | n/a | h.append('Add another line.') |
---|
1268 | n/a | eq(h.encode(maxlinelen=76), """\ |
---|
1269 | n/a | This is an example of string which has almost the limit of header length. |
---|
1270 | n/a | Add another line.""") |
---|
1271 | n/a | |
---|
1272 | n/a | def test_shorter_line_with_append(self): |
---|
1273 | n/a | eq = self.ndiffAssertEqual |
---|
1274 | n/a | s = 'This is a shorter line.' |
---|
1275 | n/a | h = Header(s) |
---|
1276 | n/a | h.append('Add another sentence. (Surprise?)') |
---|
1277 | n/a | eq(h.encode(), |
---|
1278 | n/a | 'This is a shorter line. Add another sentence. (Surprise?)') |
---|
1279 | n/a | |
---|
1280 | n/a | def test_long_field_name(self): |
---|
1281 | n/a | eq = self.ndiffAssertEqual |
---|
1282 | n/a | fn = 'X-Very-Very-Very-Long-Header-Name' |
---|
1283 | n/a | gs = ('Die Mieter treten hier ein werden mit einem Foerderband ' |
---|
1284 | n/a | 'komfortabel den Korridor entlang, an s\xfcdl\xfcndischen ' |
---|
1285 | n/a | 'Wandgem\xe4lden vorbei, gegen die rotierenden Klingen ' |
---|
1286 | n/a | 'bef\xf6rdert. ') |
---|
1287 | n/a | h = Header(gs, 'iso-8859-1', header_name=fn) |
---|
1288 | n/a | # BAW: this seems broken because the first line is too long |
---|
1289 | n/a | eq(h.encode(maxlinelen=76), """\ |
---|
1290 | n/a | =?iso-8859-1?q?Die_Mieter_treten_hier_e?= |
---|
1291 | n/a | =?iso-8859-1?q?in_werden_mit_einem_Foerderband_komfortabel_den_Korridor_e?= |
---|
1292 | n/a | =?iso-8859-1?q?ntlang=2C_an_s=FCdl=FCndischen_Wandgem=E4lden_vorbei=2C_ge?= |
---|
1293 | n/a | =?iso-8859-1?q?gen_die_rotierenden_Klingen_bef=F6rdert=2E_?=""") |
---|
1294 | n/a | |
---|
1295 | n/a | def test_long_received_header(self): |
---|
1296 | n/a | h = ('from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) ' |
---|
1297 | n/a | 'by hrothgar.la.mastaler.com (tmda-ofmipd) with ESMTP; ' |
---|
1298 | n/a | 'Wed, 05 Mar 2003 18:10:18 -0700') |
---|
1299 | n/a | msg = Message() |
---|
1300 | n/a | msg['Received-1'] = Header(h, continuation_ws='\t') |
---|
1301 | n/a | msg['Received-2'] = h |
---|
1302 | n/a | # This should be splitting on spaces not semicolons. |
---|
1303 | n/a | self.ndiffAssertEqual(msg.as_string(maxheaderlen=78), """\ |
---|
1304 | n/a | Received-1: from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) by |
---|
1305 | n/a | hrothgar.la.mastaler.com (tmda-ofmipd) with ESMTP; |
---|
1306 | n/a | Wed, 05 Mar 2003 18:10:18 -0700 |
---|
1307 | n/a | Received-2: from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) by |
---|
1308 | n/a | hrothgar.la.mastaler.com (tmda-ofmipd) with ESMTP; |
---|
1309 | n/a | Wed, 05 Mar 2003 18:10:18 -0700 |
---|
1310 | n/a | |
---|
1311 | n/a | """) |
---|
1312 | n/a | |
---|
1313 | n/a | def test_string_headerinst_eq(self): |
---|
1314 | n/a | h = ('<15975.17901.207240.414604@sgigritzmann1.mathematik.' |
---|
1315 | n/a | 'tu-muenchen.de> (David Bremner\'s message of ' |
---|
1316 | n/a | '"Thu, 6 Mar 2003 13:58:21 +0100")') |
---|
1317 | n/a | msg = Message() |
---|
1318 | n/a | msg['Received-1'] = Header(h, header_name='Received-1', |
---|
1319 | n/a | continuation_ws='\t') |
---|
1320 | n/a | msg['Received-2'] = h |
---|
1321 | n/a | # XXX The space after the ':' should not be there. |
---|
1322 | n/a | self.ndiffAssertEqual(msg.as_string(maxheaderlen=78), """\ |
---|
1323 | n/a | Received-1:\x20 |
---|
1324 | n/a | <15975.17901.207240.414604@sgigritzmann1.mathematik.tu-muenchen.de> (David |
---|
1325 | n/a | Bremner's message of \"Thu, 6 Mar 2003 13:58:21 +0100\") |
---|
1326 | n/a | Received-2:\x20 |
---|
1327 | n/a | <15975.17901.207240.414604@sgigritzmann1.mathematik.tu-muenchen.de> (David |
---|
1328 | n/a | Bremner's message of \"Thu, 6 Mar 2003 13:58:21 +0100\") |
---|
1329 | n/a | |
---|
1330 | n/a | """) |
---|
1331 | n/a | |
---|
1332 | n/a | def test_long_unbreakable_lines_with_continuation(self): |
---|
1333 | n/a | eq = self.ndiffAssertEqual |
---|
1334 | n/a | msg = Message() |
---|
1335 | n/a | t = """\ |
---|
1336 | n/a | iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9 |
---|
1337 | n/a | locQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp""" |
---|
1338 | n/a | msg['Face-1'] = t |
---|
1339 | n/a | msg['Face-2'] = Header(t, header_name='Face-2') |
---|
1340 | n/a | msg['Face-3'] = ' ' + t |
---|
1341 | n/a | # XXX This splitting is all wrong. It the first value line should be |
---|
1342 | n/a | # snug against the field name or the space after the header not there. |
---|
1343 | n/a | eq(msg.as_string(maxheaderlen=78), """\ |
---|
1344 | n/a | Face-1:\x20 |
---|
1345 | n/a | iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9 |
---|
1346 | n/a | locQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp |
---|
1347 | n/a | Face-2:\x20 |
---|
1348 | n/a | iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9 |
---|
1349 | n/a | locQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp |
---|
1350 | n/a | Face-3:\x20 |
---|
1351 | n/a | iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9 |
---|
1352 | n/a | locQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp |
---|
1353 | n/a | |
---|
1354 | n/a | """) |
---|
1355 | n/a | |
---|
1356 | n/a | def test_another_long_multiline_header(self): |
---|
1357 | n/a | eq = self.ndiffAssertEqual |
---|
1358 | n/a | m = ('Received: from siimage.com ' |
---|
1359 | n/a | '([172.25.1.3]) by zima.siliconimage.com with ' |
---|
1360 | n/a | 'Microsoft SMTPSVC(5.0.2195.4905); ' |
---|
1361 | n/a | 'Wed, 16 Oct 2002 07:41:11 -0700') |
---|
1362 | n/a | msg = email.message_from_string(m) |
---|
1363 | n/a | eq(msg.as_string(maxheaderlen=78), '''\ |
---|
1364 | n/a | Received: from siimage.com ([172.25.1.3]) by zima.siliconimage.com with |
---|
1365 | n/a | Microsoft SMTPSVC(5.0.2195.4905); Wed, 16 Oct 2002 07:41:11 -0700 |
---|
1366 | n/a | |
---|
1367 | n/a | ''') |
---|
1368 | n/a | |
---|
1369 | n/a | def test_long_lines_with_different_header(self): |
---|
1370 | n/a | eq = self.ndiffAssertEqual |
---|
1371 | n/a | h = ('List-Unsubscribe: ' |
---|
1372 | n/a | '<http://lists.sourceforge.net/lists/listinfo/spamassassin-talk>,' |
---|
1373 | n/a | ' <mailto:spamassassin-talk-request@lists.sourceforge.net' |
---|
1374 | n/a | '?subject=unsubscribe>') |
---|
1375 | n/a | msg = Message() |
---|
1376 | n/a | msg['List'] = h |
---|
1377 | n/a | msg['List'] = Header(h, header_name='List') |
---|
1378 | n/a | eq(msg.as_string(maxheaderlen=78), """\ |
---|
1379 | n/a | List: List-Unsubscribe: |
---|
1380 | n/a | <http://lists.sourceforge.net/lists/listinfo/spamassassin-talk>, |
---|
1381 | n/a | <mailto:spamassassin-talk-request@lists.sourceforge.net?subject=unsubscribe> |
---|
1382 | n/a | List: List-Unsubscribe: |
---|
1383 | n/a | <http://lists.sourceforge.net/lists/listinfo/spamassassin-talk>, |
---|
1384 | n/a | <mailto:spamassassin-talk-request@lists.sourceforge.net?subject=unsubscribe> |
---|
1385 | n/a | |
---|
1386 | n/a | """) |
---|
1387 | n/a | |
---|
1388 | n/a | def test_long_rfc2047_header_with_embedded_fws(self): |
---|
1389 | n/a | h = Header(textwrap.dedent("""\ |
---|
1390 | n/a | We're going to pretend this header is in a non-ascii character set |
---|
1391 | n/a | \tto see if line wrapping with encoded words and embedded |
---|
1392 | n/a | folding white space works"""), |
---|
1393 | n/a | charset='utf-8', |
---|
1394 | n/a | header_name='Test') |
---|
1395 | n/a | self.assertEqual(h.encode()+'\n', textwrap.dedent("""\ |
---|
1396 | n/a | =?utf-8?q?We=27re_going_to_pretend_this_header_is_in_a_non-ascii_chara?= |
---|
1397 | n/a | =?utf-8?q?cter_set?= |
---|
1398 | n/a | =?utf-8?q?_to_see_if_line_wrapping_with_encoded_words_and_embedded?= |
---|
1399 | n/a | =?utf-8?q?_folding_white_space_works?=""")+'\n') |
---|
1400 | n/a | |
---|
1401 | n/a | |
---|
1402 | n/a | |
---|
1403 | n/a | # Test mangling of "From " lines in the body of a message |
---|
1404 | n/a | class TestFromMangling(unittest.TestCase): |
---|
1405 | n/a | def setUp(self): |
---|
1406 | n/a | self.msg = Message() |
---|
1407 | n/a | self.msg['From'] = 'aaa@bbb.org' |
---|
1408 | n/a | self.msg.set_payload("""\ |
---|
1409 | n/a | From the desk of A.A.A.: |
---|
1410 | n/a | Blah blah blah |
---|
1411 | n/a | """) |
---|
1412 | n/a | |
---|
1413 | n/a | def test_mangled_from(self): |
---|
1414 | n/a | s = StringIO() |
---|
1415 | n/a | g = Generator(s, mangle_from_=True) |
---|
1416 | n/a | g.flatten(self.msg) |
---|
1417 | n/a | self.assertEqual(s.getvalue(), """\ |
---|
1418 | n/a | From: aaa@bbb.org |
---|
1419 | n/a | |
---|
1420 | n/a | >From the desk of A.A.A.: |
---|
1421 | n/a | Blah blah blah |
---|
1422 | n/a | """) |
---|
1423 | n/a | |
---|
1424 | n/a | def test_dont_mangle_from(self): |
---|
1425 | n/a | s = StringIO() |
---|
1426 | n/a | g = Generator(s, mangle_from_=False) |
---|
1427 | n/a | g.flatten(self.msg) |
---|
1428 | n/a | self.assertEqual(s.getvalue(), """\ |
---|
1429 | n/a | From: aaa@bbb.org |
---|
1430 | n/a | |
---|
1431 | n/a | From the desk of A.A.A.: |
---|
1432 | n/a | Blah blah blah |
---|
1433 | n/a | """) |
---|
1434 | n/a | |
---|
1435 | n/a | def test_mangle_from_in_preamble_and_epilog(self): |
---|
1436 | n/a | s = StringIO() |
---|
1437 | n/a | g = Generator(s, mangle_from_=True) |
---|
1438 | n/a | msg = email.message_from_string(textwrap.dedent("""\ |
---|
1439 | n/a | From: foo@bar.com |
---|
1440 | n/a | Mime-Version: 1.0 |
---|
1441 | n/a | Content-Type: multipart/mixed; boundary=XXX |
---|
1442 | n/a | |
---|
1443 | n/a | From somewhere unknown |
---|
1444 | n/a | |
---|
1445 | n/a | --XXX |
---|
1446 | n/a | Content-Type: text/plain |
---|
1447 | n/a | |
---|
1448 | n/a | foo |
---|
1449 | n/a | |
---|
1450 | n/a | --XXX-- |
---|
1451 | n/a | |
---|
1452 | n/a | From somewhere unknowable |
---|
1453 | n/a | """)) |
---|
1454 | n/a | g.flatten(msg) |
---|
1455 | n/a | self.assertEqual(len([1 for x in s.getvalue().split('\n') |
---|
1456 | n/a | if x.startswith('>From ')]), 2) |
---|
1457 | n/a | |
---|
1458 | n/a | def test_mangled_from_with_bad_bytes(self): |
---|
1459 | n/a | source = textwrap.dedent("""\ |
---|
1460 | n/a | Content-Type: text/plain; charset="utf-8" |
---|
1461 | n/a | MIME-Version: 1.0 |
---|
1462 | n/a | Content-Transfer-Encoding: 8bit |
---|
1463 | n/a | From: aaa@bbb.org |
---|
1464 | n/a | |
---|
1465 | n/a | """).encode('utf-8') |
---|
1466 | n/a | msg = email.message_from_bytes(source + b'From R\xc3\xb6lli\n') |
---|
1467 | n/a | b = BytesIO() |
---|
1468 | n/a | g = BytesGenerator(b, mangle_from_=True) |
---|
1469 | n/a | g.flatten(msg) |
---|
1470 | n/a | self.assertEqual(b.getvalue(), source + b'>From R\xc3\xb6lli\n') |
---|
1471 | n/a | |
---|
1472 | n/a | |
---|
1473 | n/a | # Test the basic MIMEAudio class |
---|
1474 | n/a | class TestMIMEAudio(unittest.TestCase): |
---|
1475 | n/a | def setUp(self): |
---|
1476 | n/a | with openfile('audiotest.au', 'rb') as fp: |
---|
1477 | n/a | self._audiodata = fp.read() |
---|
1478 | n/a | self._au = MIMEAudio(self._audiodata) |
---|
1479 | n/a | |
---|
1480 | n/a | def test_guess_minor_type(self): |
---|
1481 | n/a | self.assertEqual(self._au.get_content_type(), 'audio/basic') |
---|
1482 | n/a | |
---|
1483 | n/a | def test_encoding(self): |
---|
1484 | n/a | payload = self._au.get_payload() |
---|
1485 | n/a | self.assertEqual(base64.decodebytes(bytes(payload, 'ascii')), |
---|
1486 | n/a | self._audiodata) |
---|
1487 | n/a | |
---|
1488 | n/a | def test_checkSetMinor(self): |
---|
1489 | n/a | au = MIMEAudio(self._audiodata, 'fish') |
---|
1490 | n/a | self.assertEqual(au.get_content_type(), 'audio/fish') |
---|
1491 | n/a | |
---|
1492 | n/a | def test_add_header(self): |
---|
1493 | n/a | eq = self.assertEqual |
---|
1494 | n/a | self._au.add_header('Content-Disposition', 'attachment', |
---|
1495 | n/a | filename='audiotest.au') |
---|
1496 | n/a | eq(self._au['content-disposition'], |
---|
1497 | n/a | 'attachment; filename="audiotest.au"') |
---|
1498 | n/a | eq(self._au.get_params(header='content-disposition'), |
---|
1499 | n/a | [('attachment', ''), ('filename', 'audiotest.au')]) |
---|
1500 | n/a | eq(self._au.get_param('filename', header='content-disposition'), |
---|
1501 | n/a | 'audiotest.au') |
---|
1502 | n/a | missing = [] |
---|
1503 | n/a | eq(self._au.get_param('attachment', header='content-disposition'), '') |
---|
1504 | n/a | self.assertIs(self._au.get_param('foo', failobj=missing, |
---|
1505 | n/a | header='content-disposition'), missing) |
---|
1506 | n/a | # Try some missing stuff |
---|
1507 | n/a | self.assertIs(self._au.get_param('foobar', missing), missing) |
---|
1508 | n/a | self.assertIs(self._au.get_param('attachment', missing, |
---|
1509 | n/a | header='foobar'), missing) |
---|
1510 | n/a | |
---|
1511 | n/a | |
---|
1512 | n/a | |
---|
1513 | n/a | # Test the basic MIMEImage class |
---|
1514 | n/a | class TestMIMEImage(unittest.TestCase): |
---|
1515 | n/a | def setUp(self): |
---|
1516 | n/a | with openfile('PyBanner048.gif', 'rb') as fp: |
---|
1517 | n/a | self._imgdata = fp.read() |
---|
1518 | n/a | self._im = MIMEImage(self._imgdata) |
---|
1519 | n/a | |
---|
1520 | n/a | def test_guess_minor_type(self): |
---|
1521 | n/a | self.assertEqual(self._im.get_content_type(), 'image/gif') |
---|
1522 | n/a | |
---|
1523 | n/a | def test_encoding(self): |
---|
1524 | n/a | payload = self._im.get_payload() |
---|
1525 | n/a | self.assertEqual(base64.decodebytes(bytes(payload, 'ascii')), |
---|
1526 | n/a | self._imgdata) |
---|
1527 | n/a | |
---|
1528 | n/a | def test_checkSetMinor(self): |
---|
1529 | n/a | im = MIMEImage(self._imgdata, 'fish') |
---|
1530 | n/a | self.assertEqual(im.get_content_type(), 'image/fish') |
---|
1531 | n/a | |
---|
1532 | n/a | def test_add_header(self): |
---|
1533 | n/a | eq = self.assertEqual |
---|
1534 | n/a | self._im.add_header('Content-Disposition', 'attachment', |
---|
1535 | n/a | filename='dingusfish.gif') |
---|
1536 | n/a | eq(self._im['content-disposition'], |
---|
1537 | n/a | 'attachment; filename="dingusfish.gif"') |
---|
1538 | n/a | eq(self._im.get_params(header='content-disposition'), |
---|
1539 | n/a | [('attachment', ''), ('filename', 'dingusfish.gif')]) |
---|
1540 | n/a | eq(self._im.get_param('filename', header='content-disposition'), |
---|
1541 | n/a | 'dingusfish.gif') |
---|
1542 | n/a | missing = [] |
---|
1543 | n/a | eq(self._im.get_param('attachment', header='content-disposition'), '') |
---|
1544 | n/a | self.assertIs(self._im.get_param('foo', failobj=missing, |
---|
1545 | n/a | header='content-disposition'), missing) |
---|
1546 | n/a | # Try some missing stuff |
---|
1547 | n/a | self.assertIs(self._im.get_param('foobar', missing), missing) |
---|
1548 | n/a | self.assertIs(self._im.get_param('attachment', missing, |
---|
1549 | n/a | header='foobar'), missing) |
---|
1550 | n/a | |
---|
1551 | n/a | |
---|
1552 | n/a | |
---|
1553 | n/a | # Test the basic MIMEApplication class |
---|
1554 | n/a | class TestMIMEApplication(unittest.TestCase): |
---|
1555 | n/a | def test_headers(self): |
---|
1556 | n/a | eq = self.assertEqual |
---|
1557 | n/a | msg = MIMEApplication(b'\xfa\xfb\xfc\xfd\xfe\xff') |
---|
1558 | n/a | eq(msg.get_content_type(), 'application/octet-stream') |
---|
1559 | n/a | eq(msg['content-transfer-encoding'], 'base64') |
---|
1560 | n/a | |
---|
1561 | n/a | def test_body(self): |
---|
1562 | n/a | eq = self.assertEqual |
---|
1563 | n/a | bytesdata = b'\xfa\xfb\xfc\xfd\xfe\xff' |
---|
1564 | n/a | msg = MIMEApplication(bytesdata) |
---|
1565 | n/a | # whitespace in the cte encoded block is RFC-irrelevant. |
---|
1566 | n/a | eq(msg.get_payload().strip(), '+vv8/f7/') |
---|
1567 | n/a | eq(msg.get_payload(decode=True), bytesdata) |
---|
1568 | n/a | |
---|
1569 | n/a | def test_binary_body_with_encode_7or8bit(self): |
---|
1570 | n/a | # Issue 17171. |
---|
1571 | n/a | bytesdata = b'\xfa\xfb\xfc\xfd\xfe\xff' |
---|
1572 | n/a | msg = MIMEApplication(bytesdata, _encoder=encoders.encode_7or8bit) |
---|
1573 | n/a | # Treated as a string, this will be invalid code points. |
---|
1574 | n/a | self.assertEqual(msg.get_payload(), '\uFFFD' * len(bytesdata)) |
---|
1575 | n/a | self.assertEqual(msg.get_payload(decode=True), bytesdata) |
---|
1576 | n/a | self.assertEqual(msg['Content-Transfer-Encoding'], '8bit') |
---|
1577 | n/a | s = BytesIO() |
---|
1578 | n/a | g = BytesGenerator(s) |
---|
1579 | n/a | g.flatten(msg) |
---|
1580 | n/a | wireform = s.getvalue() |
---|
1581 | n/a | msg2 = email.message_from_bytes(wireform) |
---|
1582 | n/a | self.assertEqual(msg.get_payload(), '\uFFFD' * len(bytesdata)) |
---|
1583 | n/a | self.assertEqual(msg2.get_payload(decode=True), bytesdata) |
---|
1584 | n/a | self.assertEqual(msg2['Content-Transfer-Encoding'], '8bit') |
---|
1585 | n/a | |
---|
1586 | n/a | def test_binary_body_with_encode_noop(self): |
---|
1587 | n/a | # Issue 16564: This does not produce an RFC valid message, since to be |
---|
1588 | n/a | # valid it should have a CTE of binary. But the below works in |
---|
1589 | n/a | # Python2, and is documented as working this way. |
---|
1590 | n/a | bytesdata = b'\xfa\xfb\xfc\xfd\xfe\xff' |
---|
1591 | n/a | msg = MIMEApplication(bytesdata, _encoder=encoders.encode_noop) |
---|
1592 | n/a | # Treated as a string, this will be invalid code points. |
---|
1593 | n/a | self.assertEqual(msg.get_payload(), '\uFFFD' * len(bytesdata)) |
---|
1594 | n/a | self.assertEqual(msg.get_payload(decode=True), bytesdata) |
---|
1595 | n/a | s = BytesIO() |
---|
1596 | n/a | g = BytesGenerator(s) |
---|
1597 | n/a | g.flatten(msg) |
---|
1598 | n/a | wireform = s.getvalue() |
---|
1599 | n/a | msg2 = email.message_from_bytes(wireform) |
---|
1600 | n/a | self.assertEqual(msg.get_payload(), '\uFFFD' * len(bytesdata)) |
---|
1601 | n/a | self.assertEqual(msg2.get_payload(decode=True), bytesdata) |
---|
1602 | n/a | |
---|
1603 | n/a | def test_binary_body_with_unicode_linend_encode_noop(self): |
---|
1604 | n/a | # Issue 19003: This is a variation on #16564. |
---|
1605 | n/a | bytesdata = b'\x0b\xfa\xfb\xfc\xfd\xfe\xff' |
---|
1606 | n/a | msg = MIMEApplication(bytesdata, _encoder=encoders.encode_noop) |
---|
1607 | n/a | self.assertEqual(msg.get_payload(decode=True), bytesdata) |
---|
1608 | n/a | s = BytesIO() |
---|
1609 | n/a | g = BytesGenerator(s) |
---|
1610 | n/a | g.flatten(msg) |
---|
1611 | n/a | wireform = s.getvalue() |
---|
1612 | n/a | msg2 = email.message_from_bytes(wireform) |
---|
1613 | n/a | self.assertEqual(msg2.get_payload(decode=True), bytesdata) |
---|
1614 | n/a | |
---|
1615 | n/a | def test_binary_body_with_encode_quopri(self): |
---|
1616 | n/a | # Issue 14360. |
---|
1617 | n/a | bytesdata = b'\xfa\xfb\xfc\xfd\xfe\xff ' |
---|
1618 | n/a | msg = MIMEApplication(bytesdata, _encoder=encoders.encode_quopri) |
---|
1619 | n/a | self.assertEqual(msg.get_payload(), '=FA=FB=FC=FD=FE=FF=20') |
---|
1620 | n/a | self.assertEqual(msg.get_payload(decode=True), bytesdata) |
---|
1621 | n/a | self.assertEqual(msg['Content-Transfer-Encoding'], 'quoted-printable') |
---|
1622 | n/a | s = BytesIO() |
---|
1623 | n/a | g = BytesGenerator(s) |
---|
1624 | n/a | g.flatten(msg) |
---|
1625 | n/a | wireform = s.getvalue() |
---|
1626 | n/a | msg2 = email.message_from_bytes(wireform) |
---|
1627 | n/a | self.assertEqual(msg.get_payload(), '=FA=FB=FC=FD=FE=FF=20') |
---|
1628 | n/a | self.assertEqual(msg2.get_payload(decode=True), bytesdata) |
---|
1629 | n/a | self.assertEqual(msg2['Content-Transfer-Encoding'], 'quoted-printable') |
---|
1630 | n/a | |
---|
1631 | n/a | def test_binary_body_with_encode_base64(self): |
---|
1632 | n/a | bytesdata = b'\xfa\xfb\xfc\xfd\xfe\xff' |
---|
1633 | n/a | msg = MIMEApplication(bytesdata, _encoder=encoders.encode_base64) |
---|
1634 | n/a | self.assertEqual(msg.get_payload(), '+vv8/f7/\n') |
---|
1635 | n/a | self.assertEqual(msg.get_payload(decode=True), bytesdata) |
---|
1636 | n/a | s = BytesIO() |
---|
1637 | n/a | g = BytesGenerator(s) |
---|
1638 | n/a | g.flatten(msg) |
---|
1639 | n/a | wireform = s.getvalue() |
---|
1640 | n/a | msg2 = email.message_from_bytes(wireform) |
---|
1641 | n/a | self.assertEqual(msg.get_payload(), '+vv8/f7/\n') |
---|
1642 | n/a | self.assertEqual(msg2.get_payload(decode=True), bytesdata) |
---|
1643 | n/a | |
---|
1644 | n/a | |
---|
1645 | n/a | # Test the basic MIMEText class |
---|
1646 | n/a | class TestMIMEText(unittest.TestCase): |
---|
1647 | n/a | def setUp(self): |
---|
1648 | n/a | self._msg = MIMEText('hello there') |
---|
1649 | n/a | |
---|
1650 | n/a | def test_types(self): |
---|
1651 | n/a | eq = self.assertEqual |
---|
1652 | n/a | eq(self._msg.get_content_type(), 'text/plain') |
---|
1653 | n/a | eq(self._msg.get_param('charset'), 'us-ascii') |
---|
1654 | n/a | missing = [] |
---|
1655 | n/a | self.assertIs(self._msg.get_param('foobar', missing), missing) |
---|
1656 | n/a | self.assertIs(self._msg.get_param('charset', missing, header='foobar'), |
---|
1657 | n/a | missing) |
---|
1658 | n/a | |
---|
1659 | n/a | def test_payload(self): |
---|
1660 | n/a | self.assertEqual(self._msg.get_payload(), 'hello there') |
---|
1661 | n/a | self.assertFalse(self._msg.is_multipart()) |
---|
1662 | n/a | |
---|
1663 | n/a | def test_charset(self): |
---|
1664 | n/a | eq = self.assertEqual |
---|
1665 | n/a | msg = MIMEText('hello there', _charset='us-ascii') |
---|
1666 | n/a | eq(msg.get_charset().input_charset, 'us-ascii') |
---|
1667 | n/a | eq(msg['content-type'], 'text/plain; charset="us-ascii"') |
---|
1668 | n/a | # Also accept a Charset instance |
---|
1669 | n/a | charset = Charset('utf-8') |
---|
1670 | n/a | charset.body_encoding = None |
---|
1671 | n/a | msg = MIMEText('hello there', _charset=charset) |
---|
1672 | n/a | eq(msg.get_charset().input_charset, 'utf-8') |
---|
1673 | n/a | eq(msg['content-type'], 'text/plain; charset="utf-8"') |
---|
1674 | n/a | eq(msg.get_payload(), 'hello there') |
---|
1675 | n/a | |
---|
1676 | n/a | def test_7bit_input(self): |
---|
1677 | n/a | eq = self.assertEqual |
---|
1678 | n/a | msg = MIMEText('hello there', _charset='us-ascii') |
---|
1679 | n/a | eq(msg.get_charset().input_charset, 'us-ascii') |
---|
1680 | n/a | eq(msg['content-type'], 'text/plain; charset="us-ascii"') |
---|
1681 | n/a | |
---|
1682 | n/a | def test_7bit_input_no_charset(self): |
---|
1683 | n/a | eq = self.assertEqual |
---|
1684 | n/a | msg = MIMEText('hello there') |
---|
1685 | n/a | eq(msg.get_charset(), 'us-ascii') |
---|
1686 | n/a | eq(msg['content-type'], 'text/plain; charset="us-ascii"') |
---|
1687 | n/a | self.assertIn('hello there', msg.as_string()) |
---|
1688 | n/a | |
---|
1689 | n/a | def test_utf8_input(self): |
---|
1690 | n/a | teststr = '\u043a\u0438\u0440\u0438\u043b\u0438\u0446\u0430' |
---|
1691 | n/a | eq = self.assertEqual |
---|
1692 | n/a | msg = MIMEText(teststr, _charset='utf-8') |
---|
1693 | n/a | eq(msg.get_charset().output_charset, 'utf-8') |
---|
1694 | n/a | eq(msg['content-type'], 'text/plain; charset="utf-8"') |
---|
1695 | n/a | eq(msg.get_payload(decode=True), teststr.encode('utf-8')) |
---|
1696 | n/a | |
---|
1697 | n/a | @unittest.skip("can't fix because of backward compat in email5, " |
---|
1698 | n/a | "will fix in email6") |
---|
1699 | n/a | def test_utf8_input_no_charset(self): |
---|
1700 | n/a | teststr = '\u043a\u0438\u0440\u0438\u043b\u0438\u0446\u0430' |
---|
1701 | n/a | self.assertRaises(UnicodeEncodeError, MIMEText, teststr) |
---|
1702 | n/a | |
---|
1703 | n/a | |
---|
1704 | n/a | |
---|
1705 | n/a | # Test complicated multipart/* messages |
---|
1706 | n/a | class TestMultipart(TestEmailBase): |
---|
1707 | n/a | def setUp(self): |
---|
1708 | n/a | with openfile('PyBanner048.gif', 'rb') as fp: |
---|
1709 | n/a | data = fp.read() |
---|
1710 | n/a | container = MIMEBase('multipart', 'mixed', boundary='BOUNDARY') |
---|
1711 | n/a | image = MIMEImage(data, name='dingusfish.gif') |
---|
1712 | n/a | image.add_header('content-disposition', 'attachment', |
---|
1713 | n/a | filename='dingusfish.gif') |
---|
1714 | n/a | intro = MIMEText('''\ |
---|
1715 | n/a | Hi there, |
---|
1716 | n/a | |
---|
1717 | n/a | This is the dingus fish. |
---|
1718 | n/a | ''') |
---|
1719 | n/a | container.attach(intro) |
---|
1720 | n/a | container.attach(image) |
---|
1721 | n/a | container['From'] = 'Barry <barry@digicool.com>' |
---|
1722 | n/a | container['To'] = 'Dingus Lovers <cravindogs@cravindogs.com>' |
---|
1723 | n/a | container['Subject'] = 'Here is your dingus fish' |
---|
1724 | n/a | |
---|
1725 | n/a | now = 987809702.54848599 |
---|
1726 | n/a | timetuple = time.localtime(now) |
---|
1727 | n/a | if timetuple[-1] == 0: |
---|
1728 | n/a | tzsecs = time.timezone |
---|
1729 | n/a | else: |
---|
1730 | n/a | tzsecs = time.altzone |
---|
1731 | n/a | if tzsecs > 0: |
---|
1732 | n/a | sign = '-' |
---|
1733 | n/a | else: |
---|
1734 | n/a | sign = '+' |
---|
1735 | n/a | tzoffset = ' %s%04d' % (sign, tzsecs / 36) |
---|
1736 | n/a | container['Date'] = time.strftime( |
---|
1737 | n/a | '%a, %d %b %Y %H:%M:%S', |
---|
1738 | n/a | time.localtime(now)) + tzoffset |
---|
1739 | n/a | self._msg = container |
---|
1740 | n/a | self._im = image |
---|
1741 | n/a | self._txt = intro |
---|
1742 | n/a | |
---|
1743 | n/a | def test_hierarchy(self): |
---|
1744 | n/a | # convenience |
---|
1745 | n/a | eq = self.assertEqual |
---|
1746 | n/a | raises = self.assertRaises |
---|
1747 | n/a | # tests |
---|
1748 | n/a | m = self._msg |
---|
1749 | n/a | self.assertTrue(m.is_multipart()) |
---|
1750 | n/a | eq(m.get_content_type(), 'multipart/mixed') |
---|
1751 | n/a | eq(len(m.get_payload()), 2) |
---|
1752 | n/a | raises(IndexError, m.get_payload, 2) |
---|
1753 | n/a | m0 = m.get_payload(0) |
---|
1754 | n/a | m1 = m.get_payload(1) |
---|
1755 | n/a | self.assertIs(m0, self._txt) |
---|
1756 | n/a | self.assertIs(m1, self._im) |
---|
1757 | n/a | eq(m.get_payload(), [m0, m1]) |
---|
1758 | n/a | self.assertFalse(m0.is_multipart()) |
---|
1759 | n/a | self.assertFalse(m1.is_multipart()) |
---|
1760 | n/a | |
---|
1761 | n/a | def test_empty_multipart_idempotent(self): |
---|
1762 | n/a | text = """\ |
---|
1763 | n/a | Content-Type: multipart/mixed; boundary="BOUNDARY" |
---|
1764 | n/a | MIME-Version: 1.0 |
---|
1765 | n/a | Subject: A subject |
---|
1766 | n/a | To: aperson@dom.ain |
---|
1767 | n/a | From: bperson@dom.ain |
---|
1768 | n/a | |
---|
1769 | n/a | |
---|
1770 | n/a | --BOUNDARY |
---|
1771 | n/a | |
---|
1772 | n/a | |
---|
1773 | n/a | --BOUNDARY-- |
---|
1774 | n/a | """ |
---|
1775 | n/a | msg = Parser().parsestr(text) |
---|
1776 | n/a | self.ndiffAssertEqual(text, msg.as_string()) |
---|
1777 | n/a | |
---|
1778 | n/a | def test_no_parts_in_a_multipart_with_none_epilogue(self): |
---|
1779 | n/a | outer = MIMEBase('multipart', 'mixed') |
---|
1780 | n/a | outer['Subject'] = 'A subject' |
---|
1781 | n/a | outer['To'] = 'aperson@dom.ain' |
---|
1782 | n/a | outer['From'] = 'bperson@dom.ain' |
---|
1783 | n/a | outer.set_boundary('BOUNDARY') |
---|
1784 | n/a | self.ndiffAssertEqual(outer.as_string(), '''\ |
---|
1785 | n/a | Content-Type: multipart/mixed; boundary="BOUNDARY" |
---|
1786 | n/a | MIME-Version: 1.0 |
---|
1787 | n/a | Subject: A subject |
---|
1788 | n/a | To: aperson@dom.ain |
---|
1789 | n/a | From: bperson@dom.ain |
---|
1790 | n/a | |
---|
1791 | n/a | --BOUNDARY |
---|
1792 | n/a | |
---|
1793 | n/a | --BOUNDARY-- |
---|
1794 | n/a | ''') |
---|
1795 | n/a | |
---|
1796 | n/a | def test_no_parts_in_a_multipart_with_empty_epilogue(self): |
---|
1797 | n/a | outer = MIMEBase('multipart', 'mixed') |
---|
1798 | n/a | outer['Subject'] = 'A subject' |
---|
1799 | n/a | outer['To'] = 'aperson@dom.ain' |
---|
1800 | n/a | outer['From'] = 'bperson@dom.ain' |
---|
1801 | n/a | outer.preamble = '' |
---|
1802 | n/a | outer.epilogue = '' |
---|
1803 | n/a | outer.set_boundary('BOUNDARY') |
---|
1804 | n/a | self.ndiffAssertEqual(outer.as_string(), '''\ |
---|
1805 | n/a | Content-Type: multipart/mixed; boundary="BOUNDARY" |
---|
1806 | n/a | MIME-Version: 1.0 |
---|
1807 | n/a | Subject: A subject |
---|
1808 | n/a | To: aperson@dom.ain |
---|
1809 | n/a | From: bperson@dom.ain |
---|
1810 | n/a | |
---|
1811 | n/a | |
---|
1812 | n/a | --BOUNDARY |
---|
1813 | n/a | |
---|
1814 | n/a | --BOUNDARY-- |
---|
1815 | n/a | ''') |
---|
1816 | n/a | |
---|
1817 | n/a | def test_one_part_in_a_multipart(self): |
---|
1818 | n/a | eq = self.ndiffAssertEqual |
---|
1819 | n/a | outer = MIMEBase('multipart', 'mixed') |
---|
1820 | n/a | outer['Subject'] = 'A subject' |
---|
1821 | n/a | outer['To'] = 'aperson@dom.ain' |
---|
1822 | n/a | outer['From'] = 'bperson@dom.ain' |
---|
1823 | n/a | outer.set_boundary('BOUNDARY') |
---|
1824 | n/a | msg = MIMEText('hello world') |
---|
1825 | n/a | outer.attach(msg) |
---|
1826 | n/a | eq(outer.as_string(), '''\ |
---|
1827 | n/a | Content-Type: multipart/mixed; boundary="BOUNDARY" |
---|
1828 | n/a | MIME-Version: 1.0 |
---|
1829 | n/a | Subject: A subject |
---|
1830 | n/a | To: aperson@dom.ain |
---|
1831 | n/a | From: bperson@dom.ain |
---|
1832 | n/a | |
---|
1833 | n/a | --BOUNDARY |
---|
1834 | n/a | Content-Type: text/plain; charset="us-ascii" |
---|
1835 | n/a | MIME-Version: 1.0 |
---|
1836 | n/a | Content-Transfer-Encoding: 7bit |
---|
1837 | n/a | |
---|
1838 | n/a | hello world |
---|
1839 | n/a | --BOUNDARY-- |
---|
1840 | n/a | ''') |
---|
1841 | n/a | |
---|
1842 | n/a | def test_seq_parts_in_a_multipart_with_empty_preamble(self): |
---|
1843 | n/a | eq = self.ndiffAssertEqual |
---|
1844 | n/a | outer = MIMEBase('multipart', 'mixed') |
---|
1845 | n/a | outer['Subject'] = 'A subject' |
---|
1846 | n/a | outer['To'] = 'aperson@dom.ain' |
---|
1847 | n/a | outer['From'] = 'bperson@dom.ain' |
---|
1848 | n/a | outer.preamble = '' |
---|
1849 | n/a | msg = MIMEText('hello world') |
---|
1850 | n/a | outer.attach(msg) |
---|
1851 | n/a | outer.set_boundary('BOUNDARY') |
---|
1852 | n/a | eq(outer.as_string(), '''\ |
---|
1853 | n/a | Content-Type: multipart/mixed; boundary="BOUNDARY" |
---|
1854 | n/a | MIME-Version: 1.0 |
---|
1855 | n/a | Subject: A subject |
---|
1856 | n/a | To: aperson@dom.ain |
---|
1857 | n/a | From: bperson@dom.ain |
---|
1858 | n/a | |
---|
1859 | n/a | |
---|
1860 | n/a | --BOUNDARY |
---|
1861 | n/a | Content-Type: text/plain; charset="us-ascii" |
---|
1862 | n/a | MIME-Version: 1.0 |
---|
1863 | n/a | Content-Transfer-Encoding: 7bit |
---|
1864 | n/a | |
---|
1865 | n/a | hello world |
---|
1866 | n/a | --BOUNDARY-- |
---|
1867 | n/a | ''') |
---|
1868 | n/a | |
---|
1869 | n/a | |
---|
1870 | n/a | def test_seq_parts_in_a_multipart_with_none_preamble(self): |
---|
1871 | n/a | eq = self.ndiffAssertEqual |
---|
1872 | n/a | outer = MIMEBase('multipart', 'mixed') |
---|
1873 | n/a | outer['Subject'] = 'A subject' |
---|
1874 | n/a | outer['To'] = 'aperson@dom.ain' |
---|
1875 | n/a | outer['From'] = 'bperson@dom.ain' |
---|
1876 | n/a | outer.preamble = None |
---|
1877 | n/a | msg = MIMEText('hello world') |
---|
1878 | n/a | outer.attach(msg) |
---|
1879 | n/a | outer.set_boundary('BOUNDARY') |
---|
1880 | n/a | eq(outer.as_string(), '''\ |
---|
1881 | n/a | Content-Type: multipart/mixed; boundary="BOUNDARY" |
---|
1882 | n/a | MIME-Version: 1.0 |
---|
1883 | n/a | Subject: A subject |
---|
1884 | n/a | To: aperson@dom.ain |
---|
1885 | n/a | From: bperson@dom.ain |
---|
1886 | n/a | |
---|
1887 | n/a | --BOUNDARY |
---|
1888 | n/a | Content-Type: text/plain; charset="us-ascii" |
---|
1889 | n/a | MIME-Version: 1.0 |
---|
1890 | n/a | Content-Transfer-Encoding: 7bit |
---|
1891 | n/a | |
---|
1892 | n/a | hello world |
---|
1893 | n/a | --BOUNDARY-- |
---|
1894 | n/a | ''') |
---|
1895 | n/a | |
---|
1896 | n/a | |
---|
1897 | n/a | def test_seq_parts_in_a_multipart_with_none_epilogue(self): |
---|
1898 | n/a | eq = self.ndiffAssertEqual |
---|
1899 | n/a | outer = MIMEBase('multipart', 'mixed') |
---|
1900 | n/a | outer['Subject'] = 'A subject' |
---|
1901 | n/a | outer['To'] = 'aperson@dom.ain' |
---|
1902 | n/a | outer['From'] = 'bperson@dom.ain' |
---|
1903 | n/a | outer.epilogue = None |
---|
1904 | n/a | msg = MIMEText('hello world') |
---|
1905 | n/a | outer.attach(msg) |
---|
1906 | n/a | outer.set_boundary('BOUNDARY') |
---|
1907 | n/a | eq(outer.as_string(), '''\ |
---|
1908 | n/a | Content-Type: multipart/mixed; boundary="BOUNDARY" |
---|
1909 | n/a | MIME-Version: 1.0 |
---|
1910 | n/a | Subject: A subject |
---|
1911 | n/a | To: aperson@dom.ain |
---|
1912 | n/a | From: bperson@dom.ain |
---|
1913 | n/a | |
---|
1914 | n/a | --BOUNDARY |
---|
1915 | n/a | Content-Type: text/plain; charset="us-ascii" |
---|
1916 | n/a | MIME-Version: 1.0 |
---|
1917 | n/a | Content-Transfer-Encoding: 7bit |
---|
1918 | n/a | |
---|
1919 | n/a | hello world |
---|
1920 | n/a | --BOUNDARY-- |
---|
1921 | n/a | ''') |
---|
1922 | n/a | |
---|
1923 | n/a | |
---|
1924 | n/a | def test_seq_parts_in_a_multipart_with_empty_epilogue(self): |
---|
1925 | n/a | eq = self.ndiffAssertEqual |
---|
1926 | n/a | outer = MIMEBase('multipart', 'mixed') |
---|
1927 | n/a | outer['Subject'] = 'A subject' |
---|
1928 | n/a | outer['To'] = 'aperson@dom.ain' |
---|
1929 | n/a | outer['From'] = 'bperson@dom.ain' |
---|
1930 | n/a | outer.epilogue = '' |
---|
1931 | n/a | msg = MIMEText('hello world') |
---|
1932 | n/a | outer.attach(msg) |
---|
1933 | n/a | outer.set_boundary('BOUNDARY') |
---|
1934 | n/a | eq(outer.as_string(), '''\ |
---|
1935 | n/a | Content-Type: multipart/mixed; boundary="BOUNDARY" |
---|
1936 | n/a | MIME-Version: 1.0 |
---|
1937 | n/a | Subject: A subject |
---|
1938 | n/a | To: aperson@dom.ain |
---|
1939 | n/a | From: bperson@dom.ain |
---|
1940 | n/a | |
---|
1941 | n/a | --BOUNDARY |
---|
1942 | n/a | Content-Type: text/plain; charset="us-ascii" |
---|
1943 | n/a | MIME-Version: 1.0 |
---|
1944 | n/a | Content-Transfer-Encoding: 7bit |
---|
1945 | n/a | |
---|
1946 | n/a | hello world |
---|
1947 | n/a | --BOUNDARY-- |
---|
1948 | n/a | ''') |
---|
1949 | n/a | |
---|
1950 | n/a | |
---|
1951 | n/a | def test_seq_parts_in_a_multipart_with_nl_epilogue(self): |
---|
1952 | n/a | eq = self.ndiffAssertEqual |
---|
1953 | n/a | outer = MIMEBase('multipart', 'mixed') |
---|
1954 | n/a | outer['Subject'] = 'A subject' |
---|
1955 | n/a | outer['To'] = 'aperson@dom.ain' |
---|
1956 | n/a | outer['From'] = 'bperson@dom.ain' |
---|
1957 | n/a | outer.epilogue = '\n' |
---|
1958 | n/a | msg = MIMEText('hello world') |
---|
1959 | n/a | outer.attach(msg) |
---|
1960 | n/a | outer.set_boundary('BOUNDARY') |
---|
1961 | n/a | eq(outer.as_string(), '''\ |
---|
1962 | n/a | Content-Type: multipart/mixed; boundary="BOUNDARY" |
---|
1963 | n/a | MIME-Version: 1.0 |
---|
1964 | n/a | Subject: A subject |
---|
1965 | n/a | To: aperson@dom.ain |
---|
1966 | n/a | From: bperson@dom.ain |
---|
1967 | n/a | |
---|
1968 | n/a | --BOUNDARY |
---|
1969 | n/a | Content-Type: text/plain; charset="us-ascii" |
---|
1970 | n/a | MIME-Version: 1.0 |
---|
1971 | n/a | Content-Transfer-Encoding: 7bit |
---|
1972 | n/a | |
---|
1973 | n/a | hello world |
---|
1974 | n/a | --BOUNDARY-- |
---|
1975 | n/a | |
---|
1976 | n/a | ''') |
---|
1977 | n/a | |
---|
1978 | n/a | def test_message_external_body(self): |
---|
1979 | n/a | eq = self.assertEqual |
---|
1980 | n/a | msg = self._msgobj('msg_36.txt') |
---|
1981 | n/a | eq(len(msg.get_payload()), 2) |
---|
1982 | n/a | msg1 = msg.get_payload(1) |
---|
1983 | n/a | eq(msg1.get_content_type(), 'multipart/alternative') |
---|
1984 | n/a | eq(len(msg1.get_payload()), 2) |
---|
1985 | n/a | for subpart in msg1.get_payload(): |
---|
1986 | n/a | eq(subpart.get_content_type(), 'message/external-body') |
---|
1987 | n/a | eq(len(subpart.get_payload()), 1) |
---|
1988 | n/a | subsubpart = subpart.get_payload(0) |
---|
1989 | n/a | eq(subsubpart.get_content_type(), 'text/plain') |
---|
1990 | n/a | |
---|
1991 | n/a | def test_double_boundary(self): |
---|
1992 | n/a | # msg_37.txt is a multipart that contains two dash-boundary's in a |
---|
1993 | n/a | # row. Our interpretation of RFC 2046 calls for ignoring the second |
---|
1994 | n/a | # and subsequent boundaries. |
---|
1995 | n/a | msg = self._msgobj('msg_37.txt') |
---|
1996 | n/a | self.assertEqual(len(msg.get_payload()), 3) |
---|
1997 | n/a | |
---|
1998 | n/a | def test_nested_inner_contains_outer_boundary(self): |
---|
1999 | n/a | eq = self.ndiffAssertEqual |
---|
2000 | n/a | # msg_38.txt has an inner part that contains outer boundaries. My |
---|
2001 | n/a | # interpretation of RFC 2046 (based on sections 5.1 and 5.1.2) say |
---|
2002 | n/a | # these are illegal and should be interpreted as unterminated inner |
---|
2003 | n/a | # parts. |
---|
2004 | n/a | msg = self._msgobj('msg_38.txt') |
---|
2005 | n/a | sfp = StringIO() |
---|
2006 | n/a | iterators._structure(msg, sfp) |
---|
2007 | n/a | eq(sfp.getvalue(), """\ |
---|
2008 | n/a | multipart/mixed |
---|
2009 | n/a | multipart/mixed |
---|
2010 | n/a | multipart/alternative |
---|
2011 | n/a | text/plain |
---|
2012 | n/a | text/plain |
---|
2013 | n/a | text/plain |
---|
2014 | n/a | text/plain |
---|
2015 | n/a | """) |
---|
2016 | n/a | |
---|
2017 | n/a | def test_nested_with_same_boundary(self): |
---|
2018 | n/a | eq = self.ndiffAssertEqual |
---|
2019 | n/a | # msg 39.txt is similarly evil in that it's got inner parts that use |
---|
2020 | n/a | # the same boundary as outer parts. Again, I believe the way this is |
---|
2021 | n/a | # parsed is closest to the spirit of RFC 2046 |
---|
2022 | n/a | msg = self._msgobj('msg_39.txt') |
---|
2023 | n/a | sfp = StringIO() |
---|
2024 | n/a | iterators._structure(msg, sfp) |
---|
2025 | n/a | eq(sfp.getvalue(), """\ |
---|
2026 | n/a | multipart/mixed |
---|
2027 | n/a | multipart/mixed |
---|
2028 | n/a | multipart/alternative |
---|
2029 | n/a | application/octet-stream |
---|
2030 | n/a | application/octet-stream |
---|
2031 | n/a | text/plain |
---|
2032 | n/a | """) |
---|
2033 | n/a | |
---|
2034 | n/a | def test_boundary_in_non_multipart(self): |
---|
2035 | n/a | msg = self._msgobj('msg_40.txt') |
---|
2036 | n/a | self.assertEqual(msg.as_string(), '''\ |
---|
2037 | n/a | MIME-Version: 1.0 |
---|
2038 | n/a | Content-Type: text/html; boundary="--961284236552522269" |
---|
2039 | n/a | |
---|
2040 | n/a | ----961284236552522269 |
---|
2041 | n/a | Content-Type: text/html; |
---|
2042 | n/a | Content-Transfer-Encoding: 7Bit |
---|
2043 | n/a | |
---|
2044 | n/a | <html></html> |
---|
2045 | n/a | |
---|
2046 | n/a | ----961284236552522269-- |
---|
2047 | n/a | ''') |
---|
2048 | n/a | |
---|
2049 | n/a | def test_boundary_with_leading_space(self): |
---|
2050 | n/a | eq = self.assertEqual |
---|
2051 | n/a | msg = email.message_from_string('''\ |
---|
2052 | n/a | MIME-Version: 1.0 |
---|
2053 | n/a | Content-Type: multipart/mixed; boundary=" XXXX" |
---|
2054 | n/a | |
---|
2055 | n/a | -- XXXX |
---|
2056 | n/a | Content-Type: text/plain |
---|
2057 | n/a | |
---|
2058 | n/a | |
---|
2059 | n/a | -- XXXX |
---|
2060 | n/a | Content-Type: text/plain |
---|
2061 | n/a | |
---|
2062 | n/a | -- XXXX-- |
---|
2063 | n/a | ''') |
---|
2064 | n/a | self.assertTrue(msg.is_multipart()) |
---|
2065 | n/a | eq(msg.get_boundary(), ' XXXX') |
---|
2066 | n/a | eq(len(msg.get_payload()), 2) |
---|
2067 | n/a | |
---|
2068 | n/a | def test_boundary_without_trailing_newline(self): |
---|
2069 | n/a | m = Parser().parsestr("""\ |
---|
2070 | n/a | Content-Type: multipart/mixed; boundary="===============0012394164==" |
---|
2071 | n/a | MIME-Version: 1.0 |
---|
2072 | n/a | |
---|
2073 | n/a | --===============0012394164== |
---|
2074 | n/a | Content-Type: image/file1.jpg |
---|
2075 | n/a | MIME-Version: 1.0 |
---|
2076 | n/a | Content-Transfer-Encoding: base64 |
---|
2077 | n/a | |
---|
2078 | n/a | YXNkZg== |
---|
2079 | n/a | --===============0012394164==--""") |
---|
2080 | n/a | self.assertEqual(m.get_payload(0).get_payload(), 'YXNkZg==') |
---|
2081 | n/a | |
---|
2082 | n/a | def test_mimebase_default_policy(self): |
---|
2083 | n/a | m = MIMEBase('multipart', 'mixed') |
---|
2084 | n/a | self.assertIs(m.policy, email.policy.compat32) |
---|
2085 | n/a | |
---|
2086 | n/a | def test_mimebase_custom_policy(self): |
---|
2087 | n/a | m = MIMEBase('multipart', 'mixed', policy=email.policy.default) |
---|
2088 | n/a | self.assertIs(m.policy, email.policy.default) |
---|
2089 | n/a | |
---|
2090 | n/a | # Test some badly formatted messages |
---|
2091 | n/a | class TestNonConformant(TestEmailBase): |
---|
2092 | n/a | |
---|
2093 | n/a | def test_parse_missing_minor_type(self): |
---|
2094 | n/a | eq = self.assertEqual |
---|
2095 | n/a | msg = self._msgobj('msg_14.txt') |
---|
2096 | n/a | eq(msg.get_content_type(), 'text/plain') |
---|
2097 | n/a | eq(msg.get_content_maintype(), 'text') |
---|
2098 | n/a | eq(msg.get_content_subtype(), 'plain') |
---|
2099 | n/a | |
---|
2100 | n/a | # test_defect_handling |
---|
2101 | n/a | def test_same_boundary_inner_outer(self): |
---|
2102 | n/a | msg = self._msgobj('msg_15.txt') |
---|
2103 | n/a | # XXX We can probably eventually do better |
---|
2104 | n/a | inner = msg.get_payload(0) |
---|
2105 | n/a | self.assertTrue(hasattr(inner, 'defects')) |
---|
2106 | n/a | self.assertEqual(len(inner.defects), 1) |
---|
2107 | n/a | self.assertIsInstance(inner.defects[0], |
---|
2108 | n/a | errors.StartBoundaryNotFoundDefect) |
---|
2109 | n/a | |
---|
2110 | n/a | # test_defect_handling |
---|
2111 | n/a | def test_multipart_no_boundary(self): |
---|
2112 | n/a | msg = self._msgobj('msg_25.txt') |
---|
2113 | n/a | self.assertIsInstance(msg.get_payload(), str) |
---|
2114 | n/a | self.assertEqual(len(msg.defects), 2) |
---|
2115 | n/a | self.assertIsInstance(msg.defects[0], |
---|
2116 | n/a | errors.NoBoundaryInMultipartDefect) |
---|
2117 | n/a | self.assertIsInstance(msg.defects[1], |
---|
2118 | n/a | errors.MultipartInvariantViolationDefect) |
---|
2119 | n/a | |
---|
2120 | n/a | multipart_msg = textwrap.dedent("""\ |
---|
2121 | n/a | Date: Wed, 14 Nov 2007 12:56:23 GMT |
---|
2122 | n/a | From: foo@bar.invalid |
---|
2123 | n/a | To: foo@bar.invalid |
---|
2124 | n/a | Subject: Content-Transfer-Encoding: base64 and multipart |
---|
2125 | n/a | MIME-Version: 1.0 |
---|
2126 | n/a | Content-Type: multipart/mixed; |
---|
2127 | n/a | boundary="===============3344438784458119861=="{} |
---|
2128 | n/a | |
---|
2129 | n/a | --===============3344438784458119861== |
---|
2130 | n/a | Content-Type: text/plain |
---|
2131 | n/a | |
---|
2132 | n/a | Test message |
---|
2133 | n/a | |
---|
2134 | n/a | --===============3344438784458119861== |
---|
2135 | n/a | Content-Type: application/octet-stream |
---|
2136 | n/a | Content-Transfer-Encoding: base64 |
---|
2137 | n/a | |
---|
2138 | n/a | YWJj |
---|
2139 | n/a | |
---|
2140 | n/a | --===============3344438784458119861==-- |
---|
2141 | n/a | """) |
---|
2142 | n/a | |
---|
2143 | n/a | # test_defect_handling |
---|
2144 | n/a | def test_multipart_invalid_cte(self): |
---|
2145 | n/a | msg = self._str_msg( |
---|
2146 | n/a | self.multipart_msg.format("\nContent-Transfer-Encoding: base64")) |
---|
2147 | n/a | self.assertEqual(len(msg.defects), 1) |
---|
2148 | n/a | self.assertIsInstance(msg.defects[0], |
---|
2149 | n/a | errors.InvalidMultipartContentTransferEncodingDefect) |
---|
2150 | n/a | |
---|
2151 | n/a | # test_defect_handling |
---|
2152 | n/a | def test_multipart_no_cte_no_defect(self): |
---|
2153 | n/a | msg = self._str_msg(self.multipart_msg.format('')) |
---|
2154 | n/a | self.assertEqual(len(msg.defects), 0) |
---|
2155 | n/a | |
---|
2156 | n/a | # test_defect_handling |
---|
2157 | n/a | def test_multipart_valid_cte_no_defect(self): |
---|
2158 | n/a | for cte in ('7bit', '8bit', 'BINary'): |
---|
2159 | n/a | msg = self._str_msg( |
---|
2160 | n/a | self.multipart_msg.format( |
---|
2161 | n/a | "\nContent-Transfer-Encoding: {}".format(cte))) |
---|
2162 | n/a | self.assertEqual(len(msg.defects), 0) |
---|
2163 | n/a | |
---|
2164 | n/a | # test_headerregistry.TestContentTyopeHeader invalid_1 and invalid_2. |
---|
2165 | n/a | def test_invalid_content_type(self): |
---|
2166 | n/a | eq = self.assertEqual |
---|
2167 | n/a | neq = self.ndiffAssertEqual |
---|
2168 | n/a | msg = Message() |
---|
2169 | n/a | # RFC 2045, $5.2 says invalid yields text/plain |
---|
2170 | n/a | msg['Content-Type'] = 'text' |
---|
2171 | n/a | eq(msg.get_content_maintype(), 'text') |
---|
2172 | n/a | eq(msg.get_content_subtype(), 'plain') |
---|
2173 | n/a | eq(msg.get_content_type(), 'text/plain') |
---|
2174 | n/a | # Clear the old value and try something /really/ invalid |
---|
2175 | n/a | del msg['content-type'] |
---|
2176 | n/a | msg['Content-Type'] = 'foo' |
---|
2177 | n/a | eq(msg.get_content_maintype(), 'text') |
---|
2178 | n/a | eq(msg.get_content_subtype(), 'plain') |
---|
2179 | n/a | eq(msg.get_content_type(), 'text/plain') |
---|
2180 | n/a | # Still, make sure that the message is idempotently generated |
---|
2181 | n/a | s = StringIO() |
---|
2182 | n/a | g = Generator(s) |
---|
2183 | n/a | g.flatten(msg) |
---|
2184 | n/a | neq(s.getvalue(), 'Content-Type: foo\n\n') |
---|
2185 | n/a | |
---|
2186 | n/a | def test_no_start_boundary(self): |
---|
2187 | n/a | eq = self.ndiffAssertEqual |
---|
2188 | n/a | msg = self._msgobj('msg_31.txt') |
---|
2189 | n/a | eq(msg.get_payload(), """\ |
---|
2190 | n/a | --BOUNDARY |
---|
2191 | n/a | Content-Type: text/plain |
---|
2192 | n/a | |
---|
2193 | n/a | message 1 |
---|
2194 | n/a | |
---|
2195 | n/a | --BOUNDARY |
---|
2196 | n/a | Content-Type: text/plain |
---|
2197 | n/a | |
---|
2198 | n/a | message 2 |
---|
2199 | n/a | |
---|
2200 | n/a | --BOUNDARY-- |
---|
2201 | n/a | """) |
---|
2202 | n/a | |
---|
2203 | n/a | def test_no_separating_blank_line(self): |
---|
2204 | n/a | eq = self.ndiffAssertEqual |
---|
2205 | n/a | msg = self._msgobj('msg_35.txt') |
---|
2206 | n/a | eq(msg.as_string(), """\ |
---|
2207 | n/a | From: aperson@dom.ain |
---|
2208 | n/a | To: bperson@dom.ain |
---|
2209 | n/a | Subject: here's something interesting |
---|
2210 | n/a | |
---|
2211 | n/a | counter to RFC 2822, there's no separating newline here |
---|
2212 | n/a | """) |
---|
2213 | n/a | |
---|
2214 | n/a | # test_defect_handling |
---|
2215 | n/a | def test_lying_multipart(self): |
---|
2216 | n/a | msg = self._msgobj('msg_41.txt') |
---|
2217 | n/a | self.assertTrue(hasattr(msg, 'defects')) |
---|
2218 | n/a | self.assertEqual(len(msg.defects), 2) |
---|
2219 | n/a | self.assertIsInstance(msg.defects[0], |
---|
2220 | n/a | errors.NoBoundaryInMultipartDefect) |
---|
2221 | n/a | self.assertIsInstance(msg.defects[1], |
---|
2222 | n/a | errors.MultipartInvariantViolationDefect) |
---|
2223 | n/a | |
---|
2224 | n/a | # test_defect_handling |
---|
2225 | n/a | def test_missing_start_boundary(self): |
---|
2226 | n/a | outer = self._msgobj('msg_42.txt') |
---|
2227 | n/a | # The message structure is: |
---|
2228 | n/a | # |
---|
2229 | n/a | # multipart/mixed |
---|
2230 | n/a | # text/plain |
---|
2231 | n/a | # message/rfc822 |
---|
2232 | n/a | # multipart/mixed [*] |
---|
2233 | n/a | # |
---|
2234 | n/a | # [*] This message is missing its start boundary |
---|
2235 | n/a | bad = outer.get_payload(1).get_payload(0) |
---|
2236 | n/a | self.assertEqual(len(bad.defects), 1) |
---|
2237 | n/a | self.assertIsInstance(bad.defects[0], |
---|
2238 | n/a | errors.StartBoundaryNotFoundDefect) |
---|
2239 | n/a | |
---|
2240 | n/a | # test_defect_handling |
---|
2241 | n/a | def test_first_line_is_continuation_header(self): |
---|
2242 | n/a | eq = self.assertEqual |
---|
2243 | n/a | m = ' Line 1\nSubject: test\n\nbody' |
---|
2244 | n/a | msg = email.message_from_string(m) |
---|
2245 | n/a | eq(msg.keys(), ['Subject']) |
---|
2246 | n/a | eq(msg.get_payload(), 'body') |
---|
2247 | n/a | eq(len(msg.defects), 1) |
---|
2248 | n/a | self.assertDefectsEqual(msg.defects, |
---|
2249 | n/a | [errors.FirstHeaderLineIsContinuationDefect]) |
---|
2250 | n/a | eq(msg.defects[0].line, ' Line 1\n') |
---|
2251 | n/a | |
---|
2252 | n/a | # test_defect_handling |
---|
2253 | n/a | def test_missing_header_body_separator(self): |
---|
2254 | n/a | # Our heuristic if we see a line that doesn't look like a header (no |
---|
2255 | n/a | # leading whitespace but no ':') is to assume that the blank line that |
---|
2256 | n/a | # separates the header from the body is missing, and to stop parsing |
---|
2257 | n/a | # headers and start parsing the body. |
---|
2258 | n/a | msg = self._str_msg('Subject: test\nnot a header\nTo: abc\n\nb\n') |
---|
2259 | n/a | self.assertEqual(msg.keys(), ['Subject']) |
---|
2260 | n/a | self.assertEqual(msg.get_payload(), 'not a header\nTo: abc\n\nb\n') |
---|
2261 | n/a | self.assertDefectsEqual(msg.defects, |
---|
2262 | n/a | [errors.MissingHeaderBodySeparatorDefect]) |
---|
2263 | n/a | |
---|
2264 | n/a | |
---|
2265 | n/a | # Test RFC 2047 header encoding and decoding |
---|
2266 | n/a | class TestRFC2047(TestEmailBase): |
---|
2267 | n/a | def test_rfc2047_multiline(self): |
---|
2268 | n/a | eq = self.assertEqual |
---|
2269 | n/a | s = """Re: =?mac-iceland?q?r=8Aksm=9Arg=8Cs?= baz |
---|
2270 | n/a | foo bar =?mac-iceland?q?r=8Aksm=9Arg=8Cs?=""" |
---|
2271 | n/a | dh = decode_header(s) |
---|
2272 | n/a | eq(dh, [ |
---|
2273 | n/a | (b'Re: ', None), |
---|
2274 | n/a | (b'r\x8aksm\x9arg\x8cs', 'mac-iceland'), |
---|
2275 | n/a | (b' baz foo bar ', None), |
---|
2276 | n/a | (b'r\x8aksm\x9arg\x8cs', 'mac-iceland')]) |
---|
2277 | n/a | header = make_header(dh) |
---|
2278 | n/a | eq(str(header), |
---|
2279 | n/a | 'Re: r\xe4ksm\xf6rg\xe5s baz foo bar r\xe4ksm\xf6rg\xe5s') |
---|
2280 | n/a | self.ndiffAssertEqual(header.encode(maxlinelen=76), """\ |
---|
2281 | n/a | Re: =?mac-iceland?q?r=8Aksm=9Arg=8Cs?= baz foo bar =?mac-iceland?q?r=8Aksm?= |
---|
2282 | n/a | =?mac-iceland?q?=9Arg=8Cs?=""") |
---|
2283 | n/a | |
---|
2284 | n/a | def test_whitespace_keeper_unicode(self): |
---|
2285 | n/a | eq = self.assertEqual |
---|
2286 | n/a | s = '=?ISO-8859-1?Q?Andr=E9?= Pirard <pirard@dom.ain>' |
---|
2287 | n/a | dh = decode_header(s) |
---|
2288 | n/a | eq(dh, [(b'Andr\xe9', 'iso-8859-1'), |
---|
2289 | n/a | (b' Pirard <pirard@dom.ain>', None)]) |
---|
2290 | n/a | header = str(make_header(dh)) |
---|
2291 | n/a | eq(header, 'Andr\xe9 Pirard <pirard@dom.ain>') |
---|
2292 | n/a | |
---|
2293 | n/a | def test_whitespace_keeper_unicode_2(self): |
---|
2294 | n/a | eq = self.assertEqual |
---|
2295 | n/a | s = 'The =?iso-8859-1?b?cXVpY2sgYnJvd24gZm94?= jumped over the =?iso-8859-1?b?bGF6eSBkb2c=?=' |
---|
2296 | n/a | dh = decode_header(s) |
---|
2297 | n/a | eq(dh, [(b'The ', None), (b'quick brown fox', 'iso-8859-1'), |
---|
2298 | n/a | (b' jumped over the ', None), (b'lazy dog', 'iso-8859-1')]) |
---|
2299 | n/a | hu = str(make_header(dh)) |
---|
2300 | n/a | eq(hu, 'The quick brown fox jumped over the lazy dog') |
---|
2301 | n/a | |
---|
2302 | n/a | def test_rfc2047_missing_whitespace(self): |
---|
2303 | n/a | s = 'Sm=?ISO-8859-1?B?9g==?=rg=?ISO-8859-1?B?5Q==?=sbord' |
---|
2304 | n/a | dh = decode_header(s) |
---|
2305 | n/a | self.assertEqual(dh, [(b'Sm', None), (b'\xf6', 'iso-8859-1'), |
---|
2306 | n/a | (b'rg', None), (b'\xe5', 'iso-8859-1'), |
---|
2307 | n/a | (b'sbord', None)]) |
---|
2308 | n/a | |
---|
2309 | n/a | def test_rfc2047_with_whitespace(self): |
---|
2310 | n/a | s = 'Sm =?ISO-8859-1?B?9g==?= rg =?ISO-8859-1?B?5Q==?= sbord' |
---|
2311 | n/a | dh = decode_header(s) |
---|
2312 | n/a | self.assertEqual(dh, [(b'Sm ', None), (b'\xf6', 'iso-8859-1'), |
---|
2313 | n/a | (b' rg ', None), (b'\xe5', 'iso-8859-1'), |
---|
2314 | n/a | (b' sbord', None)]) |
---|
2315 | n/a | |
---|
2316 | n/a | def test_rfc2047_B_bad_padding(self): |
---|
2317 | n/a | s = '=?iso-8859-1?B?%s?=' |
---|
2318 | n/a | data = [ # only test complete bytes |
---|
2319 | n/a | ('dm==', b'v'), ('dm=', b'v'), ('dm', b'v'), |
---|
2320 | n/a | ('dmk=', b'vi'), ('dmk', b'vi') |
---|
2321 | n/a | ] |
---|
2322 | n/a | for q, a in data: |
---|
2323 | n/a | dh = decode_header(s % q) |
---|
2324 | n/a | self.assertEqual(dh, [(a, 'iso-8859-1')]) |
---|
2325 | n/a | |
---|
2326 | n/a | def test_rfc2047_Q_invalid_digits(self): |
---|
2327 | n/a | # issue 10004. |
---|
2328 | n/a | s = '=?iso-8859-1?Q?andr=e9=zz?=' |
---|
2329 | n/a | self.assertEqual(decode_header(s), |
---|
2330 | n/a | [(b'andr\xe9=zz', 'iso-8859-1')]) |
---|
2331 | n/a | |
---|
2332 | n/a | def test_rfc2047_rfc2047_1(self): |
---|
2333 | n/a | # 1st testcase at end of rfc2047 |
---|
2334 | n/a | s = '(=?ISO-8859-1?Q?a?=)' |
---|
2335 | n/a | self.assertEqual(decode_header(s), |
---|
2336 | n/a | [(b'(', None), (b'a', 'iso-8859-1'), (b')', None)]) |
---|
2337 | n/a | |
---|
2338 | n/a | def test_rfc2047_rfc2047_2(self): |
---|
2339 | n/a | # 2nd testcase at end of rfc2047 |
---|
2340 | n/a | s = '(=?ISO-8859-1?Q?a?= b)' |
---|
2341 | n/a | self.assertEqual(decode_header(s), |
---|
2342 | n/a | [(b'(', None), (b'a', 'iso-8859-1'), (b' b)', None)]) |
---|
2343 | n/a | |
---|
2344 | n/a | def test_rfc2047_rfc2047_3(self): |
---|
2345 | n/a | # 3rd testcase at end of rfc2047 |
---|
2346 | n/a | s = '(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)' |
---|
2347 | n/a | self.assertEqual(decode_header(s), |
---|
2348 | n/a | [(b'(', None), (b'ab', 'iso-8859-1'), (b')', None)]) |
---|
2349 | n/a | |
---|
2350 | n/a | def test_rfc2047_rfc2047_4(self): |
---|
2351 | n/a | # 4th testcase at end of rfc2047 |
---|
2352 | n/a | s = '(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)' |
---|
2353 | n/a | self.assertEqual(decode_header(s), |
---|
2354 | n/a | [(b'(', None), (b'ab', 'iso-8859-1'), (b')', None)]) |
---|
2355 | n/a | |
---|
2356 | n/a | def test_rfc2047_rfc2047_5a(self): |
---|
2357 | n/a | # 5th testcase at end of rfc2047 newline is \r\n |
---|
2358 | n/a | s = '(=?ISO-8859-1?Q?a?=\r\n =?ISO-8859-1?Q?b?=)' |
---|
2359 | n/a | self.assertEqual(decode_header(s), |
---|
2360 | n/a | [(b'(', None), (b'ab', 'iso-8859-1'), (b')', None)]) |
---|
2361 | n/a | |
---|
2362 | n/a | def test_rfc2047_rfc2047_5b(self): |
---|
2363 | n/a | # 5th testcase at end of rfc2047 newline is \n |
---|
2364 | n/a | s = '(=?ISO-8859-1?Q?a?=\n =?ISO-8859-1?Q?b?=)' |
---|
2365 | n/a | self.assertEqual(decode_header(s), |
---|
2366 | n/a | [(b'(', None), (b'ab', 'iso-8859-1'), (b')', None)]) |
---|
2367 | n/a | |
---|
2368 | n/a | def test_rfc2047_rfc2047_6(self): |
---|
2369 | n/a | # 6th testcase at end of rfc2047 |
---|
2370 | n/a | s = '(=?ISO-8859-1?Q?a_b?=)' |
---|
2371 | n/a | self.assertEqual(decode_header(s), |
---|
2372 | n/a | [(b'(', None), (b'a b', 'iso-8859-1'), (b')', None)]) |
---|
2373 | n/a | |
---|
2374 | n/a | def test_rfc2047_rfc2047_7(self): |
---|
2375 | n/a | # 7th testcase at end of rfc2047 |
---|
2376 | n/a | s = '(=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=)' |
---|
2377 | n/a | self.assertEqual(decode_header(s), |
---|
2378 | n/a | [(b'(', None), (b'a', 'iso-8859-1'), (b' b', 'iso-8859-2'), |
---|
2379 | n/a | (b')', None)]) |
---|
2380 | n/a | self.assertEqual(make_header(decode_header(s)).encode(), s.lower()) |
---|
2381 | n/a | self.assertEqual(str(make_header(decode_header(s))), '(a b)') |
---|
2382 | n/a | |
---|
2383 | n/a | def test_multiline_header(self): |
---|
2384 | n/a | s = '=?windows-1252?q?=22M=FCller_T=22?=\r\n <T.Mueller@xxx.com>' |
---|
2385 | n/a | self.assertEqual(decode_header(s), |
---|
2386 | n/a | [(b'"M\xfcller T"', 'windows-1252'), |
---|
2387 | n/a | (b'<T.Mueller@xxx.com>', None)]) |
---|
2388 | n/a | self.assertEqual(make_header(decode_header(s)).encode(), |
---|
2389 | n/a | ''.join(s.splitlines())) |
---|
2390 | n/a | self.assertEqual(str(make_header(decode_header(s))), |
---|
2391 | n/a | '"Müller T" <T.Mueller@xxx.com>') |
---|
2392 | n/a | |
---|
2393 | n/a | |
---|
2394 | n/a | # Test the MIMEMessage class |
---|
2395 | n/a | class TestMIMEMessage(TestEmailBase): |
---|
2396 | n/a | def setUp(self): |
---|
2397 | n/a | with openfile('msg_11.txt') as fp: |
---|
2398 | n/a | self._text = fp.read() |
---|
2399 | n/a | |
---|
2400 | n/a | def test_type_error(self): |
---|
2401 | n/a | self.assertRaises(TypeError, MIMEMessage, 'a plain string') |
---|
2402 | n/a | |
---|
2403 | n/a | def test_valid_argument(self): |
---|
2404 | n/a | eq = self.assertEqual |
---|
2405 | n/a | subject = 'A sub-message' |
---|
2406 | n/a | m = Message() |
---|
2407 | n/a | m['Subject'] = subject |
---|
2408 | n/a | r = MIMEMessage(m) |
---|
2409 | n/a | eq(r.get_content_type(), 'message/rfc822') |
---|
2410 | n/a | payload = r.get_payload() |
---|
2411 | n/a | self.assertIsInstance(payload, list) |
---|
2412 | n/a | eq(len(payload), 1) |
---|
2413 | n/a | subpart = payload[0] |
---|
2414 | n/a | self.assertIs(subpart, m) |
---|
2415 | n/a | eq(subpart['subject'], subject) |
---|
2416 | n/a | |
---|
2417 | n/a | def test_bad_multipart(self): |
---|
2418 | n/a | msg1 = Message() |
---|
2419 | n/a | msg1['Subject'] = 'subpart 1' |
---|
2420 | n/a | msg2 = Message() |
---|
2421 | n/a | msg2['Subject'] = 'subpart 2' |
---|
2422 | n/a | r = MIMEMessage(msg1) |
---|
2423 | n/a | self.assertRaises(errors.MultipartConversionError, r.attach, msg2) |
---|
2424 | n/a | |
---|
2425 | n/a | def test_generate(self): |
---|
2426 | n/a | # First craft the message to be encapsulated |
---|
2427 | n/a | m = Message() |
---|
2428 | n/a | m['Subject'] = 'An enclosed message' |
---|
2429 | n/a | m.set_payload('Here is the body of the message.\n') |
---|
2430 | n/a | r = MIMEMessage(m) |
---|
2431 | n/a | r['Subject'] = 'The enclosing message' |
---|
2432 | n/a | s = StringIO() |
---|
2433 | n/a | g = Generator(s) |
---|
2434 | n/a | g.flatten(r) |
---|
2435 | n/a | self.assertEqual(s.getvalue(), """\ |
---|
2436 | n/a | Content-Type: message/rfc822 |
---|
2437 | n/a | MIME-Version: 1.0 |
---|
2438 | n/a | Subject: The enclosing message |
---|
2439 | n/a | |
---|
2440 | n/a | Subject: An enclosed message |
---|
2441 | n/a | |
---|
2442 | n/a | Here is the body of the message. |
---|
2443 | n/a | """) |
---|
2444 | n/a | |
---|
2445 | n/a | def test_parse_message_rfc822(self): |
---|
2446 | n/a | eq = self.assertEqual |
---|
2447 | n/a | msg = self._msgobj('msg_11.txt') |
---|
2448 | n/a | eq(msg.get_content_type(), 'message/rfc822') |
---|
2449 | n/a | payload = msg.get_payload() |
---|
2450 | n/a | self.assertIsInstance(payload, list) |
---|
2451 | n/a | eq(len(payload), 1) |
---|
2452 | n/a | submsg = payload[0] |
---|
2453 | n/a | self.assertIsInstance(submsg, Message) |
---|
2454 | n/a | eq(submsg['subject'], 'An enclosed message') |
---|
2455 | n/a | eq(submsg.get_payload(), 'Here is the body of the message.\n') |
---|
2456 | n/a | |
---|
2457 | n/a | def test_dsn(self): |
---|
2458 | n/a | eq = self.assertEqual |
---|
2459 | n/a | # msg 16 is a Delivery Status Notification, see RFC 1894 |
---|
2460 | n/a | msg = self._msgobj('msg_16.txt') |
---|
2461 | n/a | eq(msg.get_content_type(), 'multipart/report') |
---|
2462 | n/a | self.assertTrue(msg.is_multipart()) |
---|
2463 | n/a | eq(len(msg.get_payload()), 3) |
---|
2464 | n/a | # Subpart 1 is a text/plain, human readable section |
---|
2465 | n/a | subpart = msg.get_payload(0) |
---|
2466 | n/a | eq(subpart.get_content_type(), 'text/plain') |
---|
2467 | n/a | eq(subpart.get_payload(), """\ |
---|
2468 | n/a | This report relates to a message you sent with the following header fields: |
---|
2469 | n/a | |
---|
2470 | n/a | Message-id: <002001c144a6$8752e060$56104586@oxy.edu> |
---|
2471 | n/a | Date: Sun, 23 Sep 2001 20:10:55 -0700 |
---|
2472 | n/a | From: "Ian T. Henry" <henryi@oxy.edu> |
---|
2473 | n/a | To: SoCal Raves <scr@socal-raves.org> |
---|
2474 | n/a | Subject: [scr] yeah for Ians!! |
---|
2475 | n/a | |
---|
2476 | n/a | Your message cannot be delivered to the following recipients: |
---|
2477 | n/a | |
---|
2478 | n/a | Recipient address: jangel1@cougar.noc.ucla.edu |
---|
2479 | n/a | Reason: recipient reached disk quota |
---|
2480 | n/a | |
---|
2481 | n/a | """) |
---|
2482 | n/a | # Subpart 2 contains the machine parsable DSN information. It |
---|
2483 | n/a | # consists of two blocks of headers, represented by two nested Message |
---|
2484 | n/a | # objects. |
---|
2485 | n/a | subpart = msg.get_payload(1) |
---|
2486 | n/a | eq(subpart.get_content_type(), 'message/delivery-status') |
---|
2487 | n/a | eq(len(subpart.get_payload()), 2) |
---|
2488 | n/a | # message/delivery-status should treat each block as a bunch of |
---|
2489 | n/a | # headers, i.e. a bunch of Message objects. |
---|
2490 | n/a | dsn1 = subpart.get_payload(0) |
---|
2491 | n/a | self.assertIsInstance(dsn1, Message) |
---|
2492 | n/a | eq(dsn1['original-envelope-id'], '0GK500B4HD0888@cougar.noc.ucla.edu') |
---|
2493 | n/a | eq(dsn1.get_param('dns', header='reporting-mta'), '') |
---|
2494 | n/a | # Try a missing one <wink> |
---|
2495 | n/a | eq(dsn1.get_param('nsd', header='reporting-mta'), None) |
---|
2496 | n/a | dsn2 = subpart.get_payload(1) |
---|
2497 | n/a | self.assertIsInstance(dsn2, Message) |
---|
2498 | n/a | eq(dsn2['action'], 'failed') |
---|
2499 | n/a | eq(dsn2.get_params(header='original-recipient'), |
---|
2500 | n/a | [('rfc822', ''), ('jangel1@cougar.noc.ucla.edu', '')]) |
---|
2501 | n/a | eq(dsn2.get_param('rfc822', header='final-recipient'), '') |
---|
2502 | n/a | # Subpart 3 is the original message |
---|
2503 | n/a | subpart = msg.get_payload(2) |
---|
2504 | n/a | eq(subpart.get_content_type(), 'message/rfc822') |
---|
2505 | n/a | payload = subpart.get_payload() |
---|
2506 | n/a | self.assertIsInstance(payload, list) |
---|
2507 | n/a | eq(len(payload), 1) |
---|
2508 | n/a | subsubpart = payload[0] |
---|
2509 | n/a | self.assertIsInstance(subsubpart, Message) |
---|
2510 | n/a | eq(subsubpart.get_content_type(), 'text/plain') |
---|
2511 | n/a | eq(subsubpart['message-id'], |
---|
2512 | n/a | '<002001c144a6$8752e060$56104586@oxy.edu>') |
---|
2513 | n/a | |
---|
2514 | n/a | def test_epilogue(self): |
---|
2515 | n/a | eq = self.ndiffAssertEqual |
---|
2516 | n/a | with openfile('msg_21.txt') as fp: |
---|
2517 | n/a | text = fp.read() |
---|
2518 | n/a | msg = Message() |
---|
2519 | n/a | msg['From'] = 'aperson@dom.ain' |
---|
2520 | n/a | msg['To'] = 'bperson@dom.ain' |
---|
2521 | n/a | msg['Subject'] = 'Test' |
---|
2522 | n/a | msg.preamble = 'MIME message' |
---|
2523 | n/a | msg.epilogue = 'End of MIME message\n' |
---|
2524 | n/a | msg1 = MIMEText('One') |
---|
2525 | n/a | msg2 = MIMEText('Two') |
---|
2526 | n/a | msg.add_header('Content-Type', 'multipart/mixed', boundary='BOUNDARY') |
---|
2527 | n/a | msg.attach(msg1) |
---|
2528 | n/a | msg.attach(msg2) |
---|
2529 | n/a | sfp = StringIO() |
---|
2530 | n/a | g = Generator(sfp) |
---|
2531 | n/a | g.flatten(msg) |
---|
2532 | n/a | eq(sfp.getvalue(), text) |
---|
2533 | n/a | |
---|
2534 | n/a | def test_no_nl_preamble(self): |
---|
2535 | n/a | eq = self.ndiffAssertEqual |
---|
2536 | n/a | msg = Message() |
---|
2537 | n/a | msg['From'] = 'aperson@dom.ain' |
---|
2538 | n/a | msg['To'] = 'bperson@dom.ain' |
---|
2539 | n/a | msg['Subject'] = 'Test' |
---|
2540 | n/a | msg.preamble = 'MIME message' |
---|
2541 | n/a | msg.epilogue = '' |
---|
2542 | n/a | msg1 = MIMEText('One') |
---|
2543 | n/a | msg2 = MIMEText('Two') |
---|
2544 | n/a | msg.add_header('Content-Type', 'multipart/mixed', boundary='BOUNDARY') |
---|
2545 | n/a | msg.attach(msg1) |
---|
2546 | n/a | msg.attach(msg2) |
---|
2547 | n/a | eq(msg.as_string(), """\ |
---|
2548 | n/a | From: aperson@dom.ain |
---|
2549 | n/a | To: bperson@dom.ain |
---|
2550 | n/a | Subject: Test |
---|
2551 | n/a | Content-Type: multipart/mixed; boundary="BOUNDARY" |
---|
2552 | n/a | |
---|
2553 | n/a | MIME message |
---|
2554 | n/a | --BOUNDARY |
---|
2555 | n/a | Content-Type: text/plain; charset="us-ascii" |
---|
2556 | n/a | MIME-Version: 1.0 |
---|
2557 | n/a | Content-Transfer-Encoding: 7bit |
---|
2558 | n/a | |
---|
2559 | n/a | One |
---|
2560 | n/a | --BOUNDARY |
---|
2561 | n/a | Content-Type: text/plain; charset="us-ascii" |
---|
2562 | n/a | MIME-Version: 1.0 |
---|
2563 | n/a | Content-Transfer-Encoding: 7bit |
---|
2564 | n/a | |
---|
2565 | n/a | Two |
---|
2566 | n/a | --BOUNDARY-- |
---|
2567 | n/a | """) |
---|
2568 | n/a | |
---|
2569 | n/a | def test_default_type(self): |
---|
2570 | n/a | eq = self.assertEqual |
---|
2571 | n/a | with openfile('msg_30.txt') as fp: |
---|
2572 | n/a | msg = email.message_from_file(fp) |
---|
2573 | n/a | container1 = msg.get_payload(0) |
---|
2574 | n/a | eq(container1.get_default_type(), 'message/rfc822') |
---|
2575 | n/a | eq(container1.get_content_type(), 'message/rfc822') |
---|
2576 | n/a | container2 = msg.get_payload(1) |
---|
2577 | n/a | eq(container2.get_default_type(), 'message/rfc822') |
---|
2578 | n/a | eq(container2.get_content_type(), 'message/rfc822') |
---|
2579 | n/a | container1a = container1.get_payload(0) |
---|
2580 | n/a | eq(container1a.get_default_type(), 'text/plain') |
---|
2581 | n/a | eq(container1a.get_content_type(), 'text/plain') |
---|
2582 | n/a | container2a = container2.get_payload(0) |
---|
2583 | n/a | eq(container2a.get_default_type(), 'text/plain') |
---|
2584 | n/a | eq(container2a.get_content_type(), 'text/plain') |
---|
2585 | n/a | |
---|
2586 | n/a | def test_default_type_with_explicit_container_type(self): |
---|
2587 | n/a | eq = self.assertEqual |
---|
2588 | n/a | with openfile('msg_28.txt') as fp: |
---|
2589 | n/a | msg = email.message_from_file(fp) |
---|
2590 | n/a | container1 = msg.get_payload(0) |
---|
2591 | n/a | eq(container1.get_default_type(), 'message/rfc822') |
---|
2592 | n/a | eq(container1.get_content_type(), 'message/rfc822') |
---|
2593 | n/a | container2 = msg.get_payload(1) |
---|
2594 | n/a | eq(container2.get_default_type(), 'message/rfc822') |
---|
2595 | n/a | eq(container2.get_content_type(), 'message/rfc822') |
---|
2596 | n/a | container1a = container1.get_payload(0) |
---|
2597 | n/a | eq(container1a.get_default_type(), 'text/plain') |
---|
2598 | n/a | eq(container1a.get_content_type(), 'text/plain') |
---|
2599 | n/a | container2a = container2.get_payload(0) |
---|
2600 | n/a | eq(container2a.get_default_type(), 'text/plain') |
---|
2601 | n/a | eq(container2a.get_content_type(), 'text/plain') |
---|
2602 | n/a | |
---|
2603 | n/a | def test_default_type_non_parsed(self): |
---|
2604 | n/a | eq = self.assertEqual |
---|
2605 | n/a | neq = self.ndiffAssertEqual |
---|
2606 | n/a | # Set up container |
---|
2607 | n/a | container = MIMEMultipart('digest', 'BOUNDARY') |
---|
2608 | n/a | container.epilogue = '' |
---|
2609 | n/a | # Set up subparts |
---|
2610 | n/a | subpart1a = MIMEText('message 1\n') |
---|
2611 | n/a | subpart2a = MIMEText('message 2\n') |
---|
2612 | n/a | subpart1 = MIMEMessage(subpart1a) |
---|
2613 | n/a | subpart2 = MIMEMessage(subpart2a) |
---|
2614 | n/a | container.attach(subpart1) |
---|
2615 | n/a | container.attach(subpart2) |
---|
2616 | n/a | eq(subpart1.get_content_type(), 'message/rfc822') |
---|
2617 | n/a | eq(subpart1.get_default_type(), 'message/rfc822') |
---|
2618 | n/a | eq(subpart2.get_content_type(), 'message/rfc822') |
---|
2619 | n/a | eq(subpart2.get_default_type(), 'message/rfc822') |
---|
2620 | n/a | neq(container.as_string(0), '''\ |
---|
2621 | n/a | Content-Type: multipart/digest; boundary="BOUNDARY" |
---|
2622 | n/a | MIME-Version: 1.0 |
---|
2623 | n/a | |
---|
2624 | n/a | --BOUNDARY |
---|
2625 | n/a | Content-Type: message/rfc822 |
---|
2626 | n/a | MIME-Version: 1.0 |
---|
2627 | n/a | |
---|
2628 | n/a | Content-Type: text/plain; charset="us-ascii" |
---|
2629 | n/a | MIME-Version: 1.0 |
---|
2630 | n/a | Content-Transfer-Encoding: 7bit |
---|
2631 | n/a | |
---|
2632 | n/a | message 1 |
---|
2633 | n/a | |
---|
2634 | n/a | --BOUNDARY |
---|
2635 | n/a | Content-Type: message/rfc822 |
---|
2636 | n/a | MIME-Version: 1.0 |
---|
2637 | n/a | |
---|
2638 | n/a | Content-Type: text/plain; charset="us-ascii" |
---|
2639 | n/a | MIME-Version: 1.0 |
---|
2640 | n/a | Content-Transfer-Encoding: 7bit |
---|
2641 | n/a | |
---|
2642 | n/a | message 2 |
---|
2643 | n/a | |
---|
2644 | n/a | --BOUNDARY-- |
---|
2645 | n/a | ''') |
---|
2646 | n/a | del subpart1['content-type'] |
---|
2647 | n/a | del subpart1['mime-version'] |
---|
2648 | n/a | del subpart2['content-type'] |
---|
2649 | n/a | del subpart2['mime-version'] |
---|
2650 | n/a | eq(subpart1.get_content_type(), 'message/rfc822') |
---|
2651 | n/a | eq(subpart1.get_default_type(), 'message/rfc822') |
---|
2652 | n/a | eq(subpart2.get_content_type(), 'message/rfc822') |
---|
2653 | n/a | eq(subpart2.get_default_type(), 'message/rfc822') |
---|
2654 | n/a | neq(container.as_string(0), '''\ |
---|
2655 | n/a | Content-Type: multipart/digest; boundary="BOUNDARY" |
---|
2656 | n/a | MIME-Version: 1.0 |
---|
2657 | n/a | |
---|
2658 | n/a | --BOUNDARY |
---|
2659 | n/a | |
---|
2660 | n/a | Content-Type: text/plain; charset="us-ascii" |
---|
2661 | n/a | MIME-Version: 1.0 |
---|
2662 | n/a | Content-Transfer-Encoding: 7bit |
---|
2663 | n/a | |
---|
2664 | n/a | message 1 |
---|
2665 | n/a | |
---|
2666 | n/a | --BOUNDARY |
---|
2667 | n/a | |
---|
2668 | n/a | Content-Type: text/plain; charset="us-ascii" |
---|
2669 | n/a | MIME-Version: 1.0 |
---|
2670 | n/a | Content-Transfer-Encoding: 7bit |
---|
2671 | n/a | |
---|
2672 | n/a | message 2 |
---|
2673 | n/a | |
---|
2674 | n/a | --BOUNDARY-- |
---|
2675 | n/a | ''') |
---|
2676 | n/a | |
---|
2677 | n/a | def test_mime_attachments_in_constructor(self): |
---|
2678 | n/a | eq = self.assertEqual |
---|
2679 | n/a | text1 = MIMEText('') |
---|
2680 | n/a | text2 = MIMEText('') |
---|
2681 | n/a | msg = MIMEMultipart(_subparts=(text1, text2)) |
---|
2682 | n/a | eq(len(msg.get_payload()), 2) |
---|
2683 | n/a | eq(msg.get_payload(0), text1) |
---|
2684 | n/a | eq(msg.get_payload(1), text2) |
---|
2685 | n/a | |
---|
2686 | n/a | def test_default_multipart_constructor(self): |
---|
2687 | n/a | msg = MIMEMultipart() |
---|
2688 | n/a | self.assertTrue(msg.is_multipart()) |
---|
2689 | n/a | |
---|
2690 | n/a | def test_multipart_default_policy(self): |
---|
2691 | n/a | msg = MIMEMultipart() |
---|
2692 | n/a | msg['To'] = 'a@b.com' |
---|
2693 | n/a | msg['To'] = 'c@d.com' |
---|
2694 | n/a | self.assertEqual(msg.get_all('to'), ['a@b.com', 'c@d.com']) |
---|
2695 | n/a | |
---|
2696 | n/a | def test_multipart_custom_policy(self): |
---|
2697 | n/a | msg = MIMEMultipart(policy=email.policy.default) |
---|
2698 | n/a | msg['To'] = 'a@b.com' |
---|
2699 | n/a | with self.assertRaises(ValueError) as cm: |
---|
2700 | n/a | msg['To'] = 'c@d.com' |
---|
2701 | n/a | self.assertEqual(str(cm.exception), |
---|
2702 | n/a | 'There may be at most 1 To headers in a message') |
---|
2703 | n/a | |
---|
2704 | n/a | # A general test of parser->model->generator idempotency. IOW, read a message |
---|
2705 | n/a | # in, parse it into a message object tree, then without touching the tree, |
---|
2706 | n/a | # regenerate the plain text. The original text and the transformed text |
---|
2707 | n/a | # should be identical. Note: that we ignore the Unix-From since that may |
---|
2708 | n/a | # contain a changed date. |
---|
2709 | n/a | class TestIdempotent(TestEmailBase): |
---|
2710 | n/a | |
---|
2711 | n/a | linesep = '\n' |
---|
2712 | n/a | |
---|
2713 | n/a | def _msgobj(self, filename): |
---|
2714 | n/a | with openfile(filename) as fp: |
---|
2715 | n/a | data = fp.read() |
---|
2716 | n/a | msg = email.message_from_string(data) |
---|
2717 | n/a | return msg, data |
---|
2718 | n/a | |
---|
2719 | n/a | def _idempotent(self, msg, text, unixfrom=False): |
---|
2720 | n/a | eq = self.ndiffAssertEqual |
---|
2721 | n/a | s = StringIO() |
---|
2722 | n/a | g = Generator(s, maxheaderlen=0) |
---|
2723 | n/a | g.flatten(msg, unixfrom=unixfrom) |
---|
2724 | n/a | eq(text, s.getvalue()) |
---|
2725 | n/a | |
---|
2726 | n/a | def test_parse_text_message(self): |
---|
2727 | n/a | eq = self.assertEqual |
---|
2728 | n/a | msg, text = self._msgobj('msg_01.txt') |
---|
2729 | n/a | eq(msg.get_content_type(), 'text/plain') |
---|
2730 | n/a | eq(msg.get_content_maintype(), 'text') |
---|
2731 | n/a | eq(msg.get_content_subtype(), 'plain') |
---|
2732 | n/a | eq(msg.get_params()[1], ('charset', 'us-ascii')) |
---|
2733 | n/a | eq(msg.get_param('charset'), 'us-ascii') |
---|
2734 | n/a | eq(msg.preamble, None) |
---|
2735 | n/a | eq(msg.epilogue, None) |
---|
2736 | n/a | self._idempotent(msg, text) |
---|
2737 | n/a | |
---|
2738 | n/a | def test_parse_untyped_message(self): |
---|
2739 | n/a | eq = self.assertEqual |
---|
2740 | n/a | msg, text = self._msgobj('msg_03.txt') |
---|
2741 | n/a | eq(msg.get_content_type(), 'text/plain') |
---|
2742 | n/a | eq(msg.get_params(), None) |
---|
2743 | n/a | eq(msg.get_param('charset'), None) |
---|
2744 | n/a | self._idempotent(msg, text) |
---|
2745 | n/a | |
---|
2746 | n/a | def test_simple_multipart(self): |
---|
2747 | n/a | msg, text = self._msgobj('msg_04.txt') |
---|
2748 | n/a | self._idempotent(msg, text) |
---|
2749 | n/a | |
---|
2750 | n/a | def test_MIME_digest(self): |
---|
2751 | n/a | msg, text = self._msgobj('msg_02.txt') |
---|
2752 | n/a | self._idempotent(msg, text) |
---|
2753 | n/a | |
---|
2754 | n/a | def test_long_header(self): |
---|
2755 | n/a | msg, text = self._msgobj('msg_27.txt') |
---|
2756 | n/a | self._idempotent(msg, text) |
---|
2757 | n/a | |
---|
2758 | n/a | def test_MIME_digest_with_part_headers(self): |
---|
2759 | n/a | msg, text = self._msgobj('msg_28.txt') |
---|
2760 | n/a | self._idempotent(msg, text) |
---|
2761 | n/a | |
---|
2762 | n/a | def test_mixed_with_image(self): |
---|
2763 | n/a | msg, text = self._msgobj('msg_06.txt') |
---|
2764 | n/a | self._idempotent(msg, text) |
---|
2765 | n/a | |
---|
2766 | n/a | def test_multipart_report(self): |
---|
2767 | n/a | msg, text = self._msgobj('msg_05.txt') |
---|
2768 | n/a | self._idempotent(msg, text) |
---|
2769 | n/a | |
---|
2770 | n/a | def test_dsn(self): |
---|
2771 | n/a | msg, text = self._msgobj('msg_16.txt') |
---|
2772 | n/a | self._idempotent(msg, text) |
---|
2773 | n/a | |
---|
2774 | n/a | def test_preamble_epilogue(self): |
---|
2775 | n/a | msg, text = self._msgobj('msg_21.txt') |
---|
2776 | n/a | self._idempotent(msg, text) |
---|
2777 | n/a | |
---|
2778 | n/a | def test_multipart_one_part(self): |
---|
2779 | n/a | msg, text = self._msgobj('msg_23.txt') |
---|
2780 | n/a | self._idempotent(msg, text) |
---|
2781 | n/a | |
---|
2782 | n/a | def test_multipart_no_parts(self): |
---|
2783 | n/a | msg, text = self._msgobj('msg_24.txt') |
---|
2784 | n/a | self._idempotent(msg, text) |
---|
2785 | n/a | |
---|
2786 | n/a | def test_no_start_boundary(self): |
---|
2787 | n/a | msg, text = self._msgobj('msg_31.txt') |
---|
2788 | n/a | self._idempotent(msg, text) |
---|
2789 | n/a | |
---|
2790 | n/a | def test_rfc2231_charset(self): |
---|
2791 | n/a | msg, text = self._msgobj('msg_32.txt') |
---|
2792 | n/a | self._idempotent(msg, text) |
---|
2793 | n/a | |
---|
2794 | n/a | def test_more_rfc2231_parameters(self): |
---|
2795 | n/a | msg, text = self._msgobj('msg_33.txt') |
---|
2796 | n/a | self._idempotent(msg, text) |
---|
2797 | n/a | |
---|
2798 | n/a | def test_text_plain_in_a_multipart_digest(self): |
---|
2799 | n/a | msg, text = self._msgobj('msg_34.txt') |
---|
2800 | n/a | self._idempotent(msg, text) |
---|
2801 | n/a | |
---|
2802 | n/a | def test_nested_multipart_mixeds(self): |
---|
2803 | n/a | msg, text = self._msgobj('msg_12a.txt') |
---|
2804 | n/a | self._idempotent(msg, text) |
---|
2805 | n/a | |
---|
2806 | n/a | def test_message_external_body_idempotent(self): |
---|
2807 | n/a | msg, text = self._msgobj('msg_36.txt') |
---|
2808 | n/a | self._idempotent(msg, text) |
---|
2809 | n/a | |
---|
2810 | n/a | def test_message_delivery_status(self): |
---|
2811 | n/a | msg, text = self._msgobj('msg_43.txt') |
---|
2812 | n/a | self._idempotent(msg, text, unixfrom=True) |
---|
2813 | n/a | |
---|
2814 | n/a | def test_message_signed_idempotent(self): |
---|
2815 | n/a | msg, text = self._msgobj('msg_45.txt') |
---|
2816 | n/a | self._idempotent(msg, text) |
---|
2817 | n/a | |
---|
2818 | n/a | def test_content_type(self): |
---|
2819 | n/a | eq = self.assertEqual |
---|
2820 | n/a | # Get a message object and reset the seek pointer for other tests |
---|
2821 | n/a | msg, text = self._msgobj('msg_05.txt') |
---|
2822 | n/a | eq(msg.get_content_type(), 'multipart/report') |
---|
2823 | n/a | # Test the Content-Type: parameters |
---|
2824 | n/a | params = {} |
---|
2825 | n/a | for pk, pv in msg.get_params(): |
---|
2826 | n/a | params[pk] = pv |
---|
2827 | n/a | eq(params['report-type'], 'delivery-status') |
---|
2828 | n/a | eq(params['boundary'], 'D1690A7AC1.996856090/mail.example.com') |
---|
2829 | n/a | eq(msg.preamble, 'This is a MIME-encapsulated message.' + self.linesep) |
---|
2830 | n/a | eq(msg.epilogue, self.linesep) |
---|
2831 | n/a | eq(len(msg.get_payload()), 3) |
---|
2832 | n/a | # Make sure the subparts are what we expect |
---|
2833 | n/a | msg1 = msg.get_payload(0) |
---|
2834 | n/a | eq(msg1.get_content_type(), 'text/plain') |
---|
2835 | n/a | eq(msg1.get_payload(), 'Yadda yadda yadda' + self.linesep) |
---|
2836 | n/a | msg2 = msg.get_payload(1) |
---|
2837 | n/a | eq(msg2.get_content_type(), 'text/plain') |
---|
2838 | n/a | eq(msg2.get_payload(), 'Yadda yadda yadda' + self.linesep) |
---|
2839 | n/a | msg3 = msg.get_payload(2) |
---|
2840 | n/a | eq(msg3.get_content_type(), 'message/rfc822') |
---|
2841 | n/a | self.assertIsInstance(msg3, Message) |
---|
2842 | n/a | payload = msg3.get_payload() |
---|
2843 | n/a | self.assertIsInstance(payload, list) |
---|
2844 | n/a | eq(len(payload), 1) |
---|
2845 | n/a | msg4 = payload[0] |
---|
2846 | n/a | self.assertIsInstance(msg4, Message) |
---|
2847 | n/a | eq(msg4.get_payload(), 'Yadda yadda yadda' + self.linesep) |
---|
2848 | n/a | |
---|
2849 | n/a | def test_parser(self): |
---|
2850 | n/a | eq = self.assertEqual |
---|
2851 | n/a | msg, text = self._msgobj('msg_06.txt') |
---|
2852 | n/a | # Check some of the outer headers |
---|
2853 | n/a | eq(msg.get_content_type(), 'message/rfc822') |
---|
2854 | n/a | # Make sure the payload is a list of exactly one sub-Message, and that |
---|
2855 | n/a | # that submessage has a type of text/plain |
---|
2856 | n/a | payload = msg.get_payload() |
---|
2857 | n/a | self.assertIsInstance(payload, list) |
---|
2858 | n/a | eq(len(payload), 1) |
---|
2859 | n/a | msg1 = payload[0] |
---|
2860 | n/a | self.assertIsInstance(msg1, Message) |
---|
2861 | n/a | eq(msg1.get_content_type(), 'text/plain') |
---|
2862 | n/a | self.assertIsInstance(msg1.get_payload(), str) |
---|
2863 | n/a | eq(msg1.get_payload(), self.linesep) |
---|
2864 | n/a | |
---|
2865 | n/a | |
---|
2866 | n/a | |
---|
2867 | n/a | # Test various other bits of the package's functionality |
---|
2868 | n/a | class TestMiscellaneous(TestEmailBase): |
---|
2869 | n/a | def test_message_from_string(self): |
---|
2870 | n/a | with openfile('msg_01.txt') as fp: |
---|
2871 | n/a | text = fp.read() |
---|
2872 | n/a | msg = email.message_from_string(text) |
---|
2873 | n/a | s = StringIO() |
---|
2874 | n/a | # Don't wrap/continue long headers since we're trying to test |
---|
2875 | n/a | # idempotency. |
---|
2876 | n/a | g = Generator(s, maxheaderlen=0) |
---|
2877 | n/a | g.flatten(msg) |
---|
2878 | n/a | self.assertEqual(text, s.getvalue()) |
---|
2879 | n/a | |
---|
2880 | n/a | def test_message_from_file(self): |
---|
2881 | n/a | with openfile('msg_01.txt') as fp: |
---|
2882 | n/a | text = fp.read() |
---|
2883 | n/a | fp.seek(0) |
---|
2884 | n/a | msg = email.message_from_file(fp) |
---|
2885 | n/a | s = StringIO() |
---|
2886 | n/a | # Don't wrap/continue long headers since we're trying to test |
---|
2887 | n/a | # idempotency. |
---|
2888 | n/a | g = Generator(s, maxheaderlen=0) |
---|
2889 | n/a | g.flatten(msg) |
---|
2890 | n/a | self.assertEqual(text, s.getvalue()) |
---|
2891 | n/a | |
---|
2892 | n/a | def test_message_from_string_with_class(self): |
---|
2893 | n/a | with openfile('msg_01.txt') as fp: |
---|
2894 | n/a | text = fp.read() |
---|
2895 | n/a | |
---|
2896 | n/a | # Create a subclass |
---|
2897 | n/a | class MyMessage(Message): |
---|
2898 | n/a | pass |
---|
2899 | n/a | |
---|
2900 | n/a | msg = email.message_from_string(text, MyMessage) |
---|
2901 | n/a | self.assertIsInstance(msg, MyMessage) |
---|
2902 | n/a | # Try something more complicated |
---|
2903 | n/a | with openfile('msg_02.txt') as fp: |
---|
2904 | n/a | text = fp.read() |
---|
2905 | n/a | msg = email.message_from_string(text, MyMessage) |
---|
2906 | n/a | for subpart in msg.walk(): |
---|
2907 | n/a | self.assertIsInstance(subpart, MyMessage) |
---|
2908 | n/a | |
---|
2909 | n/a | def test_message_from_file_with_class(self): |
---|
2910 | n/a | # Create a subclass |
---|
2911 | n/a | class MyMessage(Message): |
---|
2912 | n/a | pass |
---|
2913 | n/a | |
---|
2914 | n/a | with openfile('msg_01.txt') as fp: |
---|
2915 | n/a | msg = email.message_from_file(fp, MyMessage) |
---|
2916 | n/a | self.assertIsInstance(msg, MyMessage) |
---|
2917 | n/a | # Try something more complicated |
---|
2918 | n/a | with openfile('msg_02.txt') as fp: |
---|
2919 | n/a | msg = email.message_from_file(fp, MyMessage) |
---|
2920 | n/a | for subpart in msg.walk(): |
---|
2921 | n/a | self.assertIsInstance(subpart, MyMessage) |
---|
2922 | n/a | |
---|
2923 | n/a | def test_custom_message_does_not_require_arguments(self): |
---|
2924 | n/a | class MyMessage(Message): |
---|
2925 | n/a | def __init__(self): |
---|
2926 | n/a | super().__init__() |
---|
2927 | n/a | msg = self._str_msg("Subject: test\n\ntest", MyMessage) |
---|
2928 | n/a | self.assertIsInstance(msg, MyMessage) |
---|
2929 | n/a | |
---|
2930 | n/a | def test__all__(self): |
---|
2931 | n/a | module = __import__('email') |
---|
2932 | n/a | self.assertEqual(sorted(module.__all__), [ |
---|
2933 | n/a | 'base64mime', 'charset', 'encoders', 'errors', 'feedparser', |
---|
2934 | n/a | 'generator', 'header', 'iterators', 'message', |
---|
2935 | n/a | 'message_from_binary_file', 'message_from_bytes', |
---|
2936 | n/a | 'message_from_file', 'message_from_string', 'mime', 'parser', |
---|
2937 | n/a | 'quoprimime', 'utils', |
---|
2938 | n/a | ]) |
---|
2939 | n/a | |
---|
2940 | n/a | def test_formatdate(self): |
---|
2941 | n/a | now = time.time() |
---|
2942 | n/a | self.assertEqual(utils.parsedate(utils.formatdate(now))[:6], |
---|
2943 | n/a | time.gmtime(now)[:6]) |
---|
2944 | n/a | |
---|
2945 | n/a | def test_formatdate_localtime(self): |
---|
2946 | n/a | now = time.time() |
---|
2947 | n/a | self.assertEqual( |
---|
2948 | n/a | utils.parsedate(utils.formatdate(now, localtime=True))[:6], |
---|
2949 | n/a | time.localtime(now)[:6]) |
---|
2950 | n/a | |
---|
2951 | n/a | def test_formatdate_usegmt(self): |
---|
2952 | n/a | now = time.time() |
---|
2953 | n/a | self.assertEqual( |
---|
2954 | n/a | utils.formatdate(now, localtime=False), |
---|
2955 | n/a | time.strftime('%a, %d %b %Y %H:%M:%S -0000', time.gmtime(now))) |
---|
2956 | n/a | self.assertEqual( |
---|
2957 | n/a | utils.formatdate(now, localtime=False, usegmt=True), |
---|
2958 | n/a | time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(now))) |
---|
2959 | n/a | |
---|
2960 | n/a | # parsedate and parsedate_tz will become deprecated interfaces someday |
---|
2961 | n/a | def test_parsedate_returns_None_for_invalid_strings(self): |
---|
2962 | n/a | self.assertIsNone(utils.parsedate('')) |
---|
2963 | n/a | self.assertIsNone(utils.parsedate_tz('')) |
---|
2964 | n/a | self.assertIsNone(utils.parsedate('0')) |
---|
2965 | n/a | self.assertIsNone(utils.parsedate_tz('0')) |
---|
2966 | n/a | self.assertIsNone(utils.parsedate('A Complete Waste of Time')) |
---|
2967 | n/a | self.assertIsNone(utils.parsedate_tz('A Complete Waste of Time')) |
---|
2968 | n/a | # Not a part of the spec but, but this has historically worked: |
---|
2969 | n/a | self.assertIsNone(utils.parsedate(None)) |
---|
2970 | n/a | self.assertIsNone(utils.parsedate_tz(None)) |
---|
2971 | n/a | |
---|
2972 | n/a | def test_parsedate_compact(self): |
---|
2973 | n/a | # The FWS after the comma is optional |
---|
2974 | n/a | self.assertEqual(utils.parsedate('Wed,3 Apr 2002 14:58:26 +0800'), |
---|
2975 | n/a | utils.parsedate('Wed, 3 Apr 2002 14:58:26 +0800')) |
---|
2976 | n/a | |
---|
2977 | n/a | def test_parsedate_no_dayofweek(self): |
---|
2978 | n/a | eq = self.assertEqual |
---|
2979 | n/a | eq(utils.parsedate_tz('25 Feb 2003 13:47:26 -0800'), |
---|
2980 | n/a | (2003, 2, 25, 13, 47, 26, 0, 1, -1, -28800)) |
---|
2981 | n/a | |
---|
2982 | n/a | def test_parsedate_compact_no_dayofweek(self): |
---|
2983 | n/a | eq = self.assertEqual |
---|
2984 | n/a | eq(utils.parsedate_tz('5 Feb 2003 13:47:26 -0800'), |
---|
2985 | n/a | (2003, 2, 5, 13, 47, 26, 0, 1, -1, -28800)) |
---|
2986 | n/a | |
---|
2987 | n/a | def test_parsedate_no_space_before_positive_offset(self): |
---|
2988 | n/a | self.assertEqual(utils.parsedate_tz('Wed, 3 Apr 2002 14:58:26+0800'), |
---|
2989 | n/a | (2002, 4, 3, 14, 58, 26, 0, 1, -1, 28800)) |
---|
2990 | n/a | |
---|
2991 | n/a | def test_parsedate_no_space_before_negative_offset(self): |
---|
2992 | n/a | # Issue 1155362: we already handled '+' for this case. |
---|
2993 | n/a | self.assertEqual(utils.parsedate_tz('Wed, 3 Apr 2002 14:58:26-0800'), |
---|
2994 | n/a | (2002, 4, 3, 14, 58, 26, 0, 1, -1, -28800)) |
---|
2995 | n/a | |
---|
2996 | n/a | |
---|
2997 | n/a | def test_parsedate_accepts_time_with_dots(self): |
---|
2998 | n/a | eq = self.assertEqual |
---|
2999 | n/a | eq(utils.parsedate_tz('5 Feb 2003 13.47.26 -0800'), |
---|
3000 | n/a | (2003, 2, 5, 13, 47, 26, 0, 1, -1, -28800)) |
---|
3001 | n/a | eq(utils.parsedate_tz('5 Feb 2003 13.47 -0800'), |
---|
3002 | n/a | (2003, 2, 5, 13, 47, 0, 0, 1, -1, -28800)) |
---|
3003 | n/a | |
---|
3004 | n/a | def test_parsedate_acceptable_to_time_functions(self): |
---|
3005 | n/a | eq = self.assertEqual |
---|
3006 | n/a | timetup = utils.parsedate('5 Feb 2003 13:47:26 -0800') |
---|
3007 | n/a | t = int(time.mktime(timetup)) |
---|
3008 | n/a | eq(time.localtime(t)[:6], timetup[:6]) |
---|
3009 | n/a | eq(int(time.strftime('%Y', timetup)), 2003) |
---|
3010 | n/a | timetup = utils.parsedate_tz('5 Feb 2003 13:47:26 -0800') |
---|
3011 | n/a | t = int(time.mktime(timetup[:9])) |
---|
3012 | n/a | eq(time.localtime(t)[:6], timetup[:6]) |
---|
3013 | n/a | eq(int(time.strftime('%Y', timetup[:9])), 2003) |
---|
3014 | n/a | |
---|
3015 | n/a | def test_mktime_tz(self): |
---|
3016 | n/a | self.assertEqual(utils.mktime_tz((1970, 1, 1, 0, 0, 0, |
---|
3017 | n/a | -1, -1, -1, 0)), 0) |
---|
3018 | n/a | self.assertEqual(utils.mktime_tz((1970, 1, 1, 0, 0, 0, |
---|
3019 | n/a | -1, -1, -1, 1234)), -1234) |
---|
3020 | n/a | |
---|
3021 | n/a | def test_parsedate_y2k(self): |
---|
3022 | n/a | """Test for parsing a date with a two-digit year. |
---|
3023 | n/a | |
---|
3024 | n/a | Parsing a date with a two-digit year should return the correct |
---|
3025 | n/a | four-digit year. RFC822 allows two-digit years, but RFC2822 (which |
---|
3026 | n/a | obsoletes RFC822) requires four-digit years. |
---|
3027 | n/a | |
---|
3028 | n/a | """ |
---|
3029 | n/a | self.assertEqual(utils.parsedate_tz('25 Feb 03 13:47:26 -0800'), |
---|
3030 | n/a | utils.parsedate_tz('25 Feb 2003 13:47:26 -0800')) |
---|
3031 | n/a | self.assertEqual(utils.parsedate_tz('25 Feb 71 13:47:26 -0800'), |
---|
3032 | n/a | utils.parsedate_tz('25 Feb 1971 13:47:26 -0800')) |
---|
3033 | n/a | |
---|
3034 | n/a | def test_parseaddr_empty(self): |
---|
3035 | n/a | self.assertEqual(utils.parseaddr('<>'), ('', '')) |
---|
3036 | n/a | self.assertEqual(utils.formataddr(utils.parseaddr('<>')), '') |
---|
3037 | n/a | |
---|
3038 | n/a | def test_noquote_dump(self): |
---|
3039 | n/a | self.assertEqual( |
---|
3040 | n/a | utils.formataddr(('A Silly Person', 'person@dom.ain')), |
---|
3041 | n/a | 'A Silly Person <person@dom.ain>') |
---|
3042 | n/a | |
---|
3043 | n/a | def test_escape_dump(self): |
---|
3044 | n/a | self.assertEqual( |
---|
3045 | n/a | utils.formataddr(('A (Very) Silly Person', 'person@dom.ain')), |
---|
3046 | n/a | r'"A (Very) Silly Person" <person@dom.ain>') |
---|
3047 | n/a | self.assertEqual( |
---|
3048 | n/a | utils.parseaddr(r'"A \(Very\) Silly Person" <person@dom.ain>'), |
---|
3049 | n/a | ('A (Very) Silly Person', 'person@dom.ain')) |
---|
3050 | n/a | a = r'A \(Special\) Person' |
---|
3051 | n/a | b = 'person@dom.ain' |
---|
3052 | n/a | self.assertEqual(utils.parseaddr(utils.formataddr((a, b))), (a, b)) |
---|
3053 | n/a | |
---|
3054 | n/a | def test_escape_backslashes(self): |
---|
3055 | n/a | self.assertEqual( |
---|
3056 | n/a | utils.formataddr((r'Arthur \Backslash\ Foobar', 'person@dom.ain')), |
---|
3057 | n/a | r'"Arthur \\Backslash\\ Foobar" <person@dom.ain>') |
---|
3058 | n/a | a = r'Arthur \Backslash\ Foobar' |
---|
3059 | n/a | b = 'person@dom.ain' |
---|
3060 | n/a | self.assertEqual(utils.parseaddr(utils.formataddr((a, b))), (a, b)) |
---|
3061 | n/a | |
---|
3062 | n/a | def test_quotes_unicode_names(self): |
---|
3063 | n/a | # issue 1690608. email.utils.formataddr() should be rfc2047 aware. |
---|
3064 | n/a | name = "H\u00e4ns W\u00fcrst" |
---|
3065 | n/a | addr = 'person@dom.ain' |
---|
3066 | n/a | utf8_base64 = "=?utf-8?b?SMOkbnMgV8O8cnN0?= <person@dom.ain>" |
---|
3067 | n/a | latin1_quopri = "=?iso-8859-1?q?H=E4ns_W=FCrst?= <person@dom.ain>" |
---|
3068 | n/a | self.assertEqual(utils.formataddr((name, addr)), utf8_base64) |
---|
3069 | n/a | self.assertEqual(utils.formataddr((name, addr), 'iso-8859-1'), |
---|
3070 | n/a | latin1_quopri) |
---|
3071 | n/a | |
---|
3072 | n/a | def test_accepts_any_charset_like_object(self): |
---|
3073 | n/a | # issue 1690608. email.utils.formataddr() should be rfc2047 aware. |
---|
3074 | n/a | name = "H\u00e4ns W\u00fcrst" |
---|
3075 | n/a | addr = 'person@dom.ain' |
---|
3076 | n/a | utf8_base64 = "=?utf-8?b?SMOkbnMgV8O8cnN0?= <person@dom.ain>" |
---|
3077 | n/a | foobar = "FOOBAR" |
---|
3078 | n/a | class CharsetMock: |
---|
3079 | n/a | def header_encode(self, string): |
---|
3080 | n/a | return foobar |
---|
3081 | n/a | mock = CharsetMock() |
---|
3082 | n/a | mock_expected = "%s <%s>" % (foobar, addr) |
---|
3083 | n/a | self.assertEqual(utils.formataddr((name, addr), mock), mock_expected) |
---|
3084 | n/a | self.assertEqual(utils.formataddr((name, addr), Charset('utf-8')), |
---|
3085 | n/a | utf8_base64) |
---|
3086 | n/a | |
---|
3087 | n/a | def test_invalid_charset_like_object_raises_error(self): |
---|
3088 | n/a | # issue 1690608. email.utils.formataddr() should be rfc2047 aware. |
---|
3089 | n/a | name = "H\u00e4ns W\u00fcrst" |
---|
3090 | n/a | addr = 'person@dom.ain' |
---|
3091 | n/a | # An object without a header_encode method: |
---|
3092 | n/a | bad_charset = object() |
---|
3093 | n/a | self.assertRaises(AttributeError, utils.formataddr, (name, addr), |
---|
3094 | n/a | bad_charset) |
---|
3095 | n/a | |
---|
3096 | n/a | def test_unicode_address_raises_error(self): |
---|
3097 | n/a | # issue 1690608. email.utils.formataddr() should be rfc2047 aware. |
---|
3098 | n/a | addr = 'pers\u00f6n@dom.in' |
---|
3099 | n/a | self.assertRaises(UnicodeError, utils.formataddr, (None, addr)) |
---|
3100 | n/a | self.assertRaises(UnicodeError, utils.formataddr, ("Name", addr)) |
---|
3101 | n/a | |
---|
3102 | n/a | def test_name_with_dot(self): |
---|
3103 | n/a | x = 'John X. Doe <jxd@example.com>' |
---|
3104 | n/a | y = '"John X. Doe" <jxd@example.com>' |
---|
3105 | n/a | a, b = ('John X. Doe', 'jxd@example.com') |
---|
3106 | n/a | self.assertEqual(utils.parseaddr(x), (a, b)) |
---|
3107 | n/a | self.assertEqual(utils.parseaddr(y), (a, b)) |
---|
3108 | n/a | # formataddr() quotes the name if there's a dot in it |
---|
3109 | n/a | self.assertEqual(utils.formataddr((a, b)), y) |
---|
3110 | n/a | |
---|
3111 | n/a | def test_parseaddr_preserves_quoted_pairs_in_addresses(self): |
---|
3112 | n/a | # issue 10005. Note that in the third test the second pair of |
---|
3113 | n/a | # backslashes is not actually a quoted pair because it is not inside a |
---|
3114 | n/a | # comment or quoted string: the address being parsed has a quoted |
---|
3115 | n/a | # string containing a quoted backslash, followed by 'example' and two |
---|
3116 | n/a | # backslashes, followed by another quoted string containing a space and |
---|
3117 | n/a | # the word 'example'. parseaddr copies those two backslashes |
---|
3118 | n/a | # literally. Per rfc5322 this is not technically correct since a \ may |
---|
3119 | n/a | # not appear in an address outside of a quoted string. It is probably |
---|
3120 | n/a | # a sensible Postel interpretation, though. |
---|
3121 | n/a | eq = self.assertEqual |
---|
3122 | n/a | eq(utils.parseaddr('""example" example"@example.com'), |
---|
3123 | n/a | ('', '""example" example"@example.com')) |
---|
3124 | n/a | eq(utils.parseaddr('"\\"example\\" example"@example.com'), |
---|
3125 | n/a | ('', '"\\"example\\" example"@example.com')) |
---|
3126 | n/a | eq(utils.parseaddr('"\\\\"example\\\\" example"@example.com'), |
---|
3127 | n/a | ('', '"\\\\"example\\\\" example"@example.com')) |
---|
3128 | n/a | |
---|
3129 | n/a | def test_parseaddr_preserves_spaces_in_local_part(self): |
---|
3130 | n/a | # issue 9286. A normal RFC5322 local part should not contain any |
---|
3131 | n/a | # folding white space, but legacy local parts can (they are a sequence |
---|
3132 | n/a | # of atoms, not dotatoms). On the other hand we strip whitespace from |
---|
3133 | n/a | # before the @ and around dots, on the assumption that the whitespace |
---|
3134 | n/a | # around the punctuation is a mistake in what would otherwise be |
---|
3135 | n/a | # an RFC5322 local part. Leading whitespace is, usual, stripped as well. |
---|
3136 | n/a | self.assertEqual(('', "merwok wok@xample.com"), |
---|
3137 | n/a | utils.parseaddr("merwok wok@xample.com")) |
---|
3138 | n/a | self.assertEqual(('', "merwok wok@xample.com"), |
---|
3139 | n/a | utils.parseaddr("merwok wok@xample.com")) |
---|
3140 | n/a | self.assertEqual(('', "merwok wok@xample.com"), |
---|
3141 | n/a | utils.parseaddr(" merwok wok @xample.com")) |
---|
3142 | n/a | self.assertEqual(('', 'merwok"wok" wok@xample.com'), |
---|
3143 | n/a | utils.parseaddr('merwok"wok" wok@xample.com')) |
---|
3144 | n/a | self.assertEqual(('', 'merwok.wok.wok@xample.com'), |
---|
3145 | n/a | utils.parseaddr('merwok. wok . wok@xample.com')) |
---|
3146 | n/a | |
---|
3147 | n/a | def test_formataddr_does_not_quote_parens_in_quoted_string(self): |
---|
3148 | n/a | addr = ("'foo@example.com' (foo@example.com)", |
---|
3149 | n/a | 'foo@example.com') |
---|
3150 | n/a | addrstr = ('"\'foo@example.com\' ' |
---|
3151 | n/a | '(foo@example.com)" <foo@example.com>') |
---|
3152 | n/a | self.assertEqual(utils.parseaddr(addrstr), addr) |
---|
3153 | n/a | self.assertEqual(utils.formataddr(addr), addrstr) |
---|
3154 | n/a | |
---|
3155 | n/a | |
---|
3156 | n/a | def test_multiline_from_comment(self): |
---|
3157 | n/a | x = """\ |
---|
3158 | n/a | Foo |
---|
3159 | n/a | \tBar <foo@example.com>""" |
---|
3160 | n/a | self.assertEqual(utils.parseaddr(x), ('Foo Bar', 'foo@example.com')) |
---|
3161 | n/a | |
---|
3162 | n/a | def test_quote_dump(self): |
---|
3163 | n/a | self.assertEqual( |
---|
3164 | n/a | utils.formataddr(('A Silly; Person', 'person@dom.ain')), |
---|
3165 | n/a | r'"A Silly; Person" <person@dom.ain>') |
---|
3166 | n/a | |
---|
3167 | n/a | def test_charset_richcomparisons(self): |
---|
3168 | n/a | eq = self.assertEqual |
---|
3169 | n/a | ne = self.assertNotEqual |
---|
3170 | n/a | cset1 = Charset() |
---|
3171 | n/a | cset2 = Charset() |
---|
3172 | n/a | eq(cset1, 'us-ascii') |
---|
3173 | n/a | eq(cset1, 'US-ASCII') |
---|
3174 | n/a | eq(cset1, 'Us-AsCiI') |
---|
3175 | n/a | eq('us-ascii', cset1) |
---|
3176 | n/a | eq('US-ASCII', cset1) |
---|
3177 | n/a | eq('Us-AsCiI', cset1) |
---|
3178 | n/a | ne(cset1, 'usascii') |
---|
3179 | n/a | ne(cset1, 'USASCII') |
---|
3180 | n/a | ne(cset1, 'UsAsCiI') |
---|
3181 | n/a | ne('usascii', cset1) |
---|
3182 | n/a | ne('USASCII', cset1) |
---|
3183 | n/a | ne('UsAsCiI', cset1) |
---|
3184 | n/a | eq(cset1, cset2) |
---|
3185 | n/a | eq(cset2, cset1) |
---|
3186 | n/a | |
---|
3187 | n/a | def test_getaddresses(self): |
---|
3188 | n/a | eq = self.assertEqual |
---|
3189 | n/a | eq(utils.getaddresses(['aperson@dom.ain (Al Person)', |
---|
3190 | n/a | 'Bud Person <bperson@dom.ain>']), |
---|
3191 | n/a | [('Al Person', 'aperson@dom.ain'), |
---|
3192 | n/a | ('Bud Person', 'bperson@dom.ain')]) |
---|
3193 | n/a | |
---|
3194 | n/a | def test_getaddresses_nasty(self): |
---|
3195 | n/a | eq = self.assertEqual |
---|
3196 | n/a | eq(utils.getaddresses(['foo: ;']), [('', '')]) |
---|
3197 | n/a | eq(utils.getaddresses( |
---|
3198 | n/a | ['[]*-- =~$']), |
---|
3199 | n/a | [('', ''), ('', ''), ('', '*--')]) |
---|
3200 | n/a | eq(utils.getaddresses( |
---|
3201 | n/a | ['foo: ;', '"Jason R. Mastaler" <jason@dom.ain>']), |
---|
3202 | n/a | [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) |
---|
3203 | n/a | |
---|
3204 | n/a | def test_getaddresses_embedded_comment(self): |
---|
3205 | n/a | """Test proper handling of a nested comment""" |
---|
3206 | n/a | eq = self.assertEqual |
---|
3207 | n/a | addrs = utils.getaddresses(['User ((nested comment)) <foo@bar.com>']) |
---|
3208 | n/a | eq(addrs[0][1], 'foo@bar.com') |
---|
3209 | n/a | |
---|
3210 | n/a | def test_make_msgid_collisions(self): |
---|
3211 | n/a | # Test make_msgid uniqueness, even with multiple threads |
---|
3212 | n/a | class MsgidsThread(Thread): |
---|
3213 | n/a | def run(self): |
---|
3214 | n/a | # generate msgids for 3 seconds |
---|
3215 | n/a | self.msgids = [] |
---|
3216 | n/a | append = self.msgids.append |
---|
3217 | n/a | make_msgid = utils.make_msgid |
---|
3218 | n/a | clock = time.monotonic |
---|
3219 | n/a | tfin = clock() + 3.0 |
---|
3220 | n/a | while clock() < tfin: |
---|
3221 | n/a | append(make_msgid(domain='testdomain-string')) |
---|
3222 | n/a | |
---|
3223 | n/a | threads = [MsgidsThread() for i in range(5)] |
---|
3224 | n/a | with start_threads(threads): |
---|
3225 | n/a | pass |
---|
3226 | n/a | all_ids = sum([t.msgids for t in threads], []) |
---|
3227 | n/a | self.assertEqual(len(set(all_ids)), len(all_ids)) |
---|
3228 | n/a | |
---|
3229 | n/a | def test_utils_quote_unquote(self): |
---|
3230 | n/a | eq = self.assertEqual |
---|
3231 | n/a | msg = Message() |
---|
3232 | n/a | msg.add_header('content-disposition', 'attachment', |
---|
3233 | n/a | filename='foo\\wacky"name') |
---|
3234 | n/a | eq(msg.get_filename(), 'foo\\wacky"name') |
---|
3235 | n/a | |
---|
3236 | n/a | def test_get_body_encoding_with_bogus_charset(self): |
---|
3237 | n/a | charset = Charset('not a charset') |
---|
3238 | n/a | self.assertEqual(charset.get_body_encoding(), 'base64') |
---|
3239 | n/a | |
---|
3240 | n/a | def test_get_body_encoding_with_uppercase_charset(self): |
---|
3241 | n/a | eq = self.assertEqual |
---|
3242 | n/a | msg = Message() |
---|
3243 | n/a | msg['Content-Type'] = 'text/plain; charset=UTF-8' |
---|
3244 | n/a | eq(msg['content-type'], 'text/plain; charset=UTF-8') |
---|
3245 | n/a | charsets = msg.get_charsets() |
---|
3246 | n/a | eq(len(charsets), 1) |
---|
3247 | n/a | eq(charsets[0], 'utf-8') |
---|
3248 | n/a | charset = Charset(charsets[0]) |
---|
3249 | n/a | eq(charset.get_body_encoding(), 'base64') |
---|
3250 | n/a | msg.set_payload(b'hello world', charset=charset) |
---|
3251 | n/a | eq(msg.get_payload(), 'aGVsbG8gd29ybGQ=\n') |
---|
3252 | n/a | eq(msg.get_payload(decode=True), b'hello world') |
---|
3253 | n/a | eq(msg['content-transfer-encoding'], 'base64') |
---|
3254 | n/a | # Try another one |
---|
3255 | n/a | msg = Message() |
---|
3256 | n/a | msg['Content-Type'] = 'text/plain; charset="US-ASCII"' |
---|
3257 | n/a | charsets = msg.get_charsets() |
---|
3258 | n/a | eq(len(charsets), 1) |
---|
3259 | n/a | eq(charsets[0], 'us-ascii') |
---|
3260 | n/a | charset = Charset(charsets[0]) |
---|
3261 | n/a | eq(charset.get_body_encoding(), encoders.encode_7or8bit) |
---|
3262 | n/a | msg.set_payload('hello world', charset=charset) |
---|
3263 | n/a | eq(msg.get_payload(), 'hello world') |
---|
3264 | n/a | eq(msg['content-transfer-encoding'], '7bit') |
---|
3265 | n/a | |
---|
3266 | n/a | def test_charsets_case_insensitive(self): |
---|
3267 | n/a | lc = Charset('us-ascii') |
---|
3268 | n/a | uc = Charset('US-ASCII') |
---|
3269 | n/a | self.assertEqual(lc.get_body_encoding(), uc.get_body_encoding()) |
---|
3270 | n/a | |
---|
3271 | n/a | def test_partial_falls_inside_message_delivery_status(self): |
---|
3272 | n/a | eq = self.ndiffAssertEqual |
---|
3273 | n/a | # The Parser interface provides chunks of data to FeedParser in 8192 |
---|
3274 | n/a | # byte gulps. SF bug #1076485 found one of those chunks inside |
---|
3275 | n/a | # message/delivery-status header block, which triggered an |
---|
3276 | n/a | # unreadline() of NeedMoreData. |
---|
3277 | n/a | msg = self._msgobj('msg_43.txt') |
---|
3278 | n/a | sfp = StringIO() |
---|
3279 | n/a | iterators._structure(msg, sfp) |
---|
3280 | n/a | eq(sfp.getvalue(), """\ |
---|
3281 | n/a | multipart/report |
---|
3282 | n/a | text/plain |
---|
3283 | n/a | message/delivery-status |
---|
3284 | n/a | text/plain |
---|
3285 | n/a | text/plain |
---|
3286 | n/a | text/plain |
---|
3287 | n/a | text/plain |
---|
3288 | n/a | text/plain |
---|
3289 | n/a | text/plain |
---|
3290 | n/a | text/plain |
---|
3291 | n/a | text/plain |
---|
3292 | n/a | text/plain |
---|
3293 | n/a | text/plain |
---|
3294 | n/a | text/plain |
---|
3295 | n/a | text/plain |
---|
3296 | n/a | text/plain |
---|
3297 | n/a | text/plain |
---|
3298 | n/a | text/plain |
---|
3299 | n/a | text/plain |
---|
3300 | n/a | text/plain |
---|
3301 | n/a | text/plain |
---|
3302 | n/a | text/plain |
---|
3303 | n/a | text/plain |
---|
3304 | n/a | text/plain |
---|
3305 | n/a | text/plain |
---|
3306 | n/a | text/plain |
---|
3307 | n/a | text/plain |
---|
3308 | n/a | text/plain |
---|
3309 | n/a | text/plain |
---|
3310 | n/a | text/rfc822-headers |
---|
3311 | n/a | """) |
---|
3312 | n/a | |
---|
3313 | n/a | def test_make_msgid_domain(self): |
---|
3314 | n/a | self.assertEqual( |
---|
3315 | n/a | email.utils.make_msgid(domain='testdomain-string')[-19:], |
---|
3316 | n/a | '@testdomain-string>') |
---|
3317 | n/a | |
---|
3318 | n/a | def test_make_msgid_idstring(self): |
---|
3319 | n/a | self.assertEqual( |
---|
3320 | n/a | email.utils.make_msgid(idstring='test-idstring', |
---|
3321 | n/a | domain='testdomain-string')[-33:], |
---|
3322 | n/a | '.test-idstring@testdomain-string>') |
---|
3323 | n/a | |
---|
3324 | n/a | def test_make_msgid_default_domain(self): |
---|
3325 | n/a | self.assertTrue( |
---|
3326 | n/a | email.utils.make_msgid().endswith( |
---|
3327 | n/a | '@' + getfqdn() + '>')) |
---|
3328 | n/a | |
---|
3329 | n/a | def test_Generator_linend(self): |
---|
3330 | n/a | # Issue 14645. |
---|
3331 | n/a | with openfile('msg_26.txt', newline='\n') as f: |
---|
3332 | n/a | msgtxt = f.read() |
---|
3333 | n/a | msgtxt_nl = msgtxt.replace('\r\n', '\n') |
---|
3334 | n/a | msg = email.message_from_string(msgtxt) |
---|
3335 | n/a | s = StringIO() |
---|
3336 | n/a | g = email.generator.Generator(s) |
---|
3337 | n/a | g.flatten(msg) |
---|
3338 | n/a | self.assertEqual(s.getvalue(), msgtxt_nl) |
---|
3339 | n/a | |
---|
3340 | n/a | def test_BytesGenerator_linend(self): |
---|
3341 | n/a | # Issue 14645. |
---|
3342 | n/a | with openfile('msg_26.txt', newline='\n') as f: |
---|
3343 | n/a | msgtxt = f.read() |
---|
3344 | n/a | msgtxt_nl = msgtxt.replace('\r\n', '\n') |
---|
3345 | n/a | msg = email.message_from_string(msgtxt_nl) |
---|
3346 | n/a | s = BytesIO() |
---|
3347 | n/a | g = email.generator.BytesGenerator(s) |
---|
3348 | n/a | g.flatten(msg, linesep='\r\n') |
---|
3349 | n/a | self.assertEqual(s.getvalue().decode('ascii'), msgtxt) |
---|
3350 | n/a | |
---|
3351 | n/a | def test_BytesGenerator_linend_with_non_ascii(self): |
---|
3352 | n/a | # Issue 14645. |
---|
3353 | n/a | with openfile('msg_26.txt', 'rb') as f: |
---|
3354 | n/a | msgtxt = f.read() |
---|
3355 | n/a | msgtxt = msgtxt.replace(b'with attachment', b'fo\xf6') |
---|
3356 | n/a | msgtxt_nl = msgtxt.replace(b'\r\n', b'\n') |
---|
3357 | n/a | msg = email.message_from_bytes(msgtxt_nl) |
---|
3358 | n/a | s = BytesIO() |
---|
3359 | n/a | g = email.generator.BytesGenerator(s) |
---|
3360 | n/a | g.flatten(msg, linesep='\r\n') |
---|
3361 | n/a | self.assertEqual(s.getvalue(), msgtxt) |
---|
3362 | n/a | |
---|
3363 | n/a | def test_mime_classes_policy_argument(self): |
---|
3364 | n/a | with openfile('audiotest.au', 'rb') as fp: |
---|
3365 | n/a | audiodata = fp.read() |
---|
3366 | n/a | with openfile('PyBanner048.gif', 'rb') as fp: |
---|
3367 | n/a | bindata = fp.read() |
---|
3368 | n/a | classes = [ |
---|
3369 | n/a | (MIMEApplication, ('',)), |
---|
3370 | n/a | (MIMEAudio, (audiodata,)), |
---|
3371 | n/a | (MIMEImage, (bindata,)), |
---|
3372 | n/a | (MIMEMessage, (Message(),)), |
---|
3373 | n/a | (MIMENonMultipart, ('multipart', 'mixed')), |
---|
3374 | n/a | (MIMEText, ('',)), |
---|
3375 | n/a | ] |
---|
3376 | n/a | for cls, constructor in classes: |
---|
3377 | n/a | with self.subTest(cls=cls.__name__, policy='compat32'): |
---|
3378 | n/a | m = cls(*constructor) |
---|
3379 | n/a | self.assertIs(m.policy, email.policy.compat32) |
---|
3380 | n/a | with self.subTest(cls=cls.__name__, policy='default'): |
---|
3381 | n/a | m = cls(*constructor, policy=email.policy.default) |
---|
3382 | n/a | self.assertIs(m.policy, email.policy.default) |
---|
3383 | n/a | |
---|
3384 | n/a | |
---|
3385 | n/a | # Test the iterator/generators |
---|
3386 | n/a | class TestIterators(TestEmailBase): |
---|
3387 | n/a | def test_body_line_iterator(self): |
---|
3388 | n/a | eq = self.assertEqual |
---|
3389 | n/a | neq = self.ndiffAssertEqual |
---|
3390 | n/a | # First a simple non-multipart message |
---|
3391 | n/a | msg = self._msgobj('msg_01.txt') |
---|
3392 | n/a | it = iterators.body_line_iterator(msg) |
---|
3393 | n/a | lines = list(it) |
---|
3394 | n/a | eq(len(lines), 6) |
---|
3395 | n/a | neq(EMPTYSTRING.join(lines), msg.get_payload()) |
---|
3396 | n/a | # Now a more complicated multipart |
---|
3397 | n/a | msg = self._msgobj('msg_02.txt') |
---|
3398 | n/a | it = iterators.body_line_iterator(msg) |
---|
3399 | n/a | lines = list(it) |
---|
3400 | n/a | eq(len(lines), 43) |
---|
3401 | n/a | with openfile('msg_19.txt') as fp: |
---|
3402 | n/a | neq(EMPTYSTRING.join(lines), fp.read()) |
---|
3403 | n/a | |
---|
3404 | n/a | def test_typed_subpart_iterator(self): |
---|
3405 | n/a | eq = self.assertEqual |
---|
3406 | n/a | msg = self._msgobj('msg_04.txt') |
---|
3407 | n/a | it = iterators.typed_subpart_iterator(msg, 'text') |
---|
3408 | n/a | lines = [] |
---|
3409 | n/a | subparts = 0 |
---|
3410 | n/a | for subpart in it: |
---|
3411 | n/a | subparts += 1 |
---|
3412 | n/a | lines.append(subpart.get_payload()) |
---|
3413 | n/a | eq(subparts, 2) |
---|
3414 | n/a | eq(EMPTYSTRING.join(lines), """\ |
---|
3415 | n/a | a simple kind of mirror |
---|
3416 | n/a | to reflect upon our own |
---|
3417 | n/a | a simple kind of mirror |
---|
3418 | n/a | to reflect upon our own |
---|
3419 | n/a | """) |
---|
3420 | n/a | |
---|
3421 | n/a | def test_typed_subpart_iterator_default_type(self): |
---|
3422 | n/a | eq = self.assertEqual |
---|
3423 | n/a | msg = self._msgobj('msg_03.txt') |
---|
3424 | n/a | it = iterators.typed_subpart_iterator(msg, 'text', 'plain') |
---|
3425 | n/a | lines = [] |
---|
3426 | n/a | subparts = 0 |
---|
3427 | n/a | for subpart in it: |
---|
3428 | n/a | subparts += 1 |
---|
3429 | n/a | lines.append(subpart.get_payload()) |
---|
3430 | n/a | eq(subparts, 1) |
---|
3431 | n/a | eq(EMPTYSTRING.join(lines), """\ |
---|
3432 | n/a | |
---|
3433 | n/a | Hi, |
---|
3434 | n/a | |
---|
3435 | n/a | Do you like this message? |
---|
3436 | n/a | |
---|
3437 | n/a | -Me |
---|
3438 | n/a | """) |
---|
3439 | n/a | |
---|
3440 | n/a | def test_pushCR_LF(self): |
---|
3441 | n/a | '''FeedParser BufferedSubFile.push() assumed it received complete |
---|
3442 | n/a | line endings. A CR ending one push() followed by a LF starting |
---|
3443 | n/a | the next push() added an empty line. |
---|
3444 | n/a | ''' |
---|
3445 | n/a | imt = [ |
---|
3446 | n/a | ("a\r \n", 2), |
---|
3447 | n/a | ("b", 0), |
---|
3448 | n/a | ("c\n", 1), |
---|
3449 | n/a | ("", 0), |
---|
3450 | n/a | ("d\r\n", 1), |
---|
3451 | n/a | ("e\r", 0), |
---|
3452 | n/a | ("\nf", 1), |
---|
3453 | n/a | ("\r\n", 1), |
---|
3454 | n/a | ] |
---|
3455 | n/a | from email.feedparser import BufferedSubFile, NeedMoreData |
---|
3456 | n/a | bsf = BufferedSubFile() |
---|
3457 | n/a | om = [] |
---|
3458 | n/a | nt = 0 |
---|
3459 | n/a | for il, n in imt: |
---|
3460 | n/a | bsf.push(il) |
---|
3461 | n/a | nt += n |
---|
3462 | n/a | n1 = 0 |
---|
3463 | n/a | for ol in iter(bsf.readline, NeedMoreData): |
---|
3464 | n/a | om.append(ol) |
---|
3465 | n/a | n1 += 1 |
---|
3466 | n/a | self.assertEqual(n, n1) |
---|
3467 | n/a | self.assertEqual(len(om), nt) |
---|
3468 | n/a | self.assertEqual(''.join([il for il, n in imt]), ''.join(om)) |
---|
3469 | n/a | |
---|
3470 | n/a | def test_push_random(self): |
---|
3471 | n/a | from email.feedparser import BufferedSubFile, NeedMoreData |
---|
3472 | n/a | |
---|
3473 | n/a | n = 10000 |
---|
3474 | n/a | chunksize = 5 |
---|
3475 | n/a | chars = 'abcd \t\r\n' |
---|
3476 | n/a | |
---|
3477 | n/a | s = ''.join(choice(chars) for i in range(n)) + '\n' |
---|
3478 | n/a | target = s.splitlines(True) |
---|
3479 | n/a | |
---|
3480 | n/a | bsf = BufferedSubFile() |
---|
3481 | n/a | lines = [] |
---|
3482 | n/a | for i in range(0, len(s), chunksize): |
---|
3483 | n/a | chunk = s[i:i+chunksize] |
---|
3484 | n/a | bsf.push(chunk) |
---|
3485 | n/a | lines.extend(iter(bsf.readline, NeedMoreData)) |
---|
3486 | n/a | self.assertEqual(lines, target) |
---|
3487 | n/a | |
---|
3488 | n/a | |
---|
3489 | n/a | class TestFeedParsers(TestEmailBase): |
---|
3490 | n/a | |
---|
3491 | n/a | def parse(self, chunks): |
---|
3492 | n/a | feedparser = FeedParser() |
---|
3493 | n/a | for chunk in chunks: |
---|
3494 | n/a | feedparser.feed(chunk) |
---|
3495 | n/a | return feedparser.close() |
---|
3496 | n/a | |
---|
3497 | n/a | def test_empty_header_name_handled(self): |
---|
3498 | n/a | # Issue 19996 |
---|
3499 | n/a | msg = self.parse("First: val\n: bad\nSecond: val") |
---|
3500 | n/a | self.assertEqual(msg['First'], 'val') |
---|
3501 | n/a | self.assertEqual(msg['Second'], 'val') |
---|
3502 | n/a | |
---|
3503 | n/a | def test_newlines(self): |
---|
3504 | n/a | m = self.parse(['a:\nb:\rc:\r\nd:\n']) |
---|
3505 | n/a | self.assertEqual(m.keys(), ['a', 'b', 'c', 'd']) |
---|
3506 | n/a | m = self.parse(['a:\nb:\rc:\r\nd:']) |
---|
3507 | n/a | self.assertEqual(m.keys(), ['a', 'b', 'c', 'd']) |
---|
3508 | n/a | m = self.parse(['a:\rb', 'c:\n']) |
---|
3509 | n/a | self.assertEqual(m.keys(), ['a', 'bc']) |
---|
3510 | n/a | m = self.parse(['a:\r', 'b:\n']) |
---|
3511 | n/a | self.assertEqual(m.keys(), ['a', 'b']) |
---|
3512 | n/a | m = self.parse(['a:\r', '\nb:\n']) |
---|
3513 | n/a | self.assertEqual(m.keys(), ['a', 'b']) |
---|
3514 | n/a | |
---|
3515 | n/a | # Only CR and LF should break header fields |
---|
3516 | n/a | m = self.parse(['a:\x85b:\u2028c:\n']) |
---|
3517 | n/a | self.assertEqual(m.items(), [('a', '\x85b:\u2028c:')]) |
---|
3518 | n/a | m = self.parse(['a:\r', 'b:\x85', 'c:\n']) |
---|
3519 | n/a | self.assertEqual(m.items(), [('a', ''), ('b', '\x85c:')]) |
---|
3520 | n/a | |
---|
3521 | n/a | def test_long_lines(self): |
---|
3522 | n/a | # Expected peak memory use on 32-bit platform: 6*N*M bytes. |
---|
3523 | n/a | M, N = 1000, 20000 |
---|
3524 | n/a | m = self.parse(['a:b\n\n'] + ['x'*M] * N) |
---|
3525 | n/a | self.assertEqual(m.items(), [('a', 'b')]) |
---|
3526 | n/a | self.assertEqual(m.get_payload(), 'x'*M*N) |
---|
3527 | n/a | m = self.parse(['a:b\r\r'] + ['x'*M] * N) |
---|
3528 | n/a | self.assertEqual(m.items(), [('a', 'b')]) |
---|
3529 | n/a | self.assertEqual(m.get_payload(), 'x'*M*N) |
---|
3530 | n/a | m = self.parse(['a:b\r\r'] + ['x'*M+'\x85'] * N) |
---|
3531 | n/a | self.assertEqual(m.items(), [('a', 'b')]) |
---|
3532 | n/a | self.assertEqual(m.get_payload(), ('x'*M+'\x85')*N) |
---|
3533 | n/a | m = self.parse(['a:\r', 'b: '] + ['x'*M] * N) |
---|
3534 | n/a | self.assertEqual(m.items(), [('a', ''), ('b', 'x'*M*N)]) |
---|
3535 | n/a | |
---|
3536 | n/a | |
---|
3537 | n/a | class TestParsers(TestEmailBase): |
---|
3538 | n/a | |
---|
3539 | n/a | def test_header_parser(self): |
---|
3540 | n/a | eq = self.assertEqual |
---|
3541 | n/a | # Parse only the headers of a complex multipart MIME document |
---|
3542 | n/a | with openfile('msg_02.txt') as fp: |
---|
3543 | n/a | msg = HeaderParser().parse(fp) |
---|
3544 | n/a | eq(msg['from'], 'ppp-request@zzz.org') |
---|
3545 | n/a | eq(msg['to'], 'ppp@zzz.org') |
---|
3546 | n/a | eq(msg.get_content_type(), 'multipart/mixed') |
---|
3547 | n/a | self.assertFalse(msg.is_multipart()) |
---|
3548 | n/a | self.assertIsInstance(msg.get_payload(), str) |
---|
3549 | n/a | |
---|
3550 | n/a | def test_bytes_header_parser(self): |
---|
3551 | n/a | eq = self.assertEqual |
---|
3552 | n/a | # Parse only the headers of a complex multipart MIME document |
---|
3553 | n/a | with openfile('msg_02.txt', 'rb') as fp: |
---|
3554 | n/a | msg = email.parser.BytesHeaderParser().parse(fp) |
---|
3555 | n/a | eq(msg['from'], 'ppp-request@zzz.org') |
---|
3556 | n/a | eq(msg['to'], 'ppp@zzz.org') |
---|
3557 | n/a | eq(msg.get_content_type(), 'multipart/mixed') |
---|
3558 | n/a | self.assertFalse(msg.is_multipart()) |
---|
3559 | n/a | self.assertIsInstance(msg.get_payload(), str) |
---|
3560 | n/a | self.assertIsInstance(msg.get_payload(decode=True), bytes) |
---|
3561 | n/a | |
---|
3562 | n/a | def test_bytes_parser_does_not_close_file(self): |
---|
3563 | n/a | with openfile('msg_02.txt', 'rb') as fp: |
---|
3564 | n/a | email.parser.BytesParser().parse(fp) |
---|
3565 | n/a | self.assertFalse(fp.closed) |
---|
3566 | n/a | |
---|
3567 | n/a | def test_bytes_parser_on_exception_does_not_close_file(self): |
---|
3568 | n/a | with openfile('msg_15.txt', 'rb') as fp: |
---|
3569 | n/a | bytesParser = email.parser.BytesParser |
---|
3570 | n/a | self.assertRaises(email.errors.StartBoundaryNotFoundDefect, |
---|
3571 | n/a | bytesParser(policy=email.policy.strict).parse, |
---|
3572 | n/a | fp) |
---|
3573 | n/a | self.assertFalse(fp.closed) |
---|
3574 | n/a | |
---|
3575 | n/a | def test_parser_does_not_close_file(self): |
---|
3576 | n/a | with openfile('msg_02.txt', 'r') as fp: |
---|
3577 | n/a | email.parser.Parser().parse(fp) |
---|
3578 | n/a | self.assertFalse(fp.closed) |
---|
3579 | n/a | |
---|
3580 | n/a | def test_parser_on_exception_does_not_close_file(self): |
---|
3581 | n/a | with openfile('msg_15.txt', 'r') as fp: |
---|
3582 | n/a | parser = email.parser.Parser |
---|
3583 | n/a | self.assertRaises(email.errors.StartBoundaryNotFoundDefect, |
---|
3584 | n/a | parser(policy=email.policy.strict).parse, fp) |
---|
3585 | n/a | self.assertFalse(fp.closed) |
---|
3586 | n/a | |
---|
3587 | n/a | def test_whitespace_continuation(self): |
---|
3588 | n/a | eq = self.assertEqual |
---|
3589 | n/a | # This message contains a line after the Subject: header that has only |
---|
3590 | n/a | # whitespace, but it is not empty! |
---|
3591 | n/a | msg = email.message_from_string("""\ |
---|
3592 | n/a | From: aperson@dom.ain |
---|
3593 | n/a | To: bperson@dom.ain |
---|
3594 | n/a | Subject: the next line has a space on it |
---|
3595 | n/a | \x20 |
---|
3596 | n/a | Date: Mon, 8 Apr 2002 15:09:19 -0400 |
---|
3597 | n/a | Message-ID: spam |
---|
3598 | n/a | |
---|
3599 | n/a | Here's the message body |
---|
3600 | n/a | """) |
---|
3601 | n/a | eq(msg['subject'], 'the next line has a space on it\n ') |
---|
3602 | n/a | eq(msg['message-id'], 'spam') |
---|
3603 | n/a | eq(msg.get_payload(), "Here's the message body\n") |
---|
3604 | n/a | |
---|
3605 | n/a | def test_whitespace_continuation_last_header(self): |
---|
3606 | n/a | eq = self.assertEqual |
---|
3607 | n/a | # Like the previous test, but the subject line is the last |
---|
3608 | n/a | # header. |
---|
3609 | n/a | msg = email.message_from_string("""\ |
---|
3610 | n/a | From: aperson@dom.ain |
---|
3611 | n/a | To: bperson@dom.ain |
---|
3612 | n/a | Date: Mon, 8 Apr 2002 15:09:19 -0400 |
---|
3613 | n/a | Message-ID: spam |
---|
3614 | n/a | Subject: the next line has a space on it |
---|
3615 | n/a | \x20 |
---|
3616 | n/a | |
---|
3617 | n/a | Here's the message body |
---|
3618 | n/a | """) |
---|
3619 | n/a | eq(msg['subject'], 'the next line has a space on it\n ') |
---|
3620 | n/a | eq(msg['message-id'], 'spam') |
---|
3621 | n/a | eq(msg.get_payload(), "Here's the message body\n") |
---|
3622 | n/a | |
---|
3623 | n/a | def test_crlf_separation(self): |
---|
3624 | n/a | eq = self.assertEqual |
---|
3625 | n/a | with openfile('msg_26.txt', newline='\n') as fp: |
---|
3626 | n/a | msg = Parser().parse(fp) |
---|
3627 | n/a | eq(len(msg.get_payload()), 2) |
---|
3628 | n/a | part1 = msg.get_payload(0) |
---|
3629 | n/a | eq(part1.get_content_type(), 'text/plain') |
---|
3630 | n/a | eq(part1.get_payload(), 'Simple email with attachment.\r\n\r\n') |
---|
3631 | n/a | part2 = msg.get_payload(1) |
---|
3632 | n/a | eq(part2.get_content_type(), 'application/riscos') |
---|
3633 | n/a | |
---|
3634 | n/a | def test_crlf_flatten(self): |
---|
3635 | n/a | # Using newline='\n' preserves the crlfs in this input file. |
---|
3636 | n/a | with openfile('msg_26.txt', newline='\n') as fp: |
---|
3637 | n/a | text = fp.read() |
---|
3638 | n/a | msg = email.message_from_string(text) |
---|
3639 | n/a | s = StringIO() |
---|
3640 | n/a | g = Generator(s) |
---|
3641 | n/a | g.flatten(msg, linesep='\r\n') |
---|
3642 | n/a | self.assertEqual(s.getvalue(), text) |
---|
3643 | n/a | |
---|
3644 | n/a | maxDiff = None |
---|
3645 | n/a | |
---|
3646 | n/a | def test_multipart_digest_with_extra_mime_headers(self): |
---|
3647 | n/a | eq = self.assertEqual |
---|
3648 | n/a | neq = self.ndiffAssertEqual |
---|
3649 | n/a | with openfile('msg_28.txt') as fp: |
---|
3650 | n/a | msg = email.message_from_file(fp) |
---|
3651 | n/a | # Structure is: |
---|
3652 | n/a | # multipart/digest |
---|
3653 | n/a | # message/rfc822 |
---|
3654 | n/a | # text/plain |
---|
3655 | n/a | # message/rfc822 |
---|
3656 | n/a | # text/plain |
---|
3657 | n/a | eq(msg.is_multipart(), 1) |
---|
3658 | n/a | eq(len(msg.get_payload()), 2) |
---|
3659 | n/a | part1 = msg.get_payload(0) |
---|
3660 | n/a | eq(part1.get_content_type(), 'message/rfc822') |
---|
3661 | n/a | eq(part1.is_multipart(), 1) |
---|
3662 | n/a | eq(len(part1.get_payload()), 1) |
---|
3663 | n/a | part1a = part1.get_payload(0) |
---|
3664 | n/a | eq(part1a.is_multipart(), 0) |
---|
3665 | n/a | eq(part1a.get_content_type(), 'text/plain') |
---|
3666 | n/a | neq(part1a.get_payload(), 'message 1\n') |
---|
3667 | n/a | # next message/rfc822 |
---|
3668 | n/a | part2 = msg.get_payload(1) |
---|
3669 | n/a | eq(part2.get_content_type(), 'message/rfc822') |
---|
3670 | n/a | eq(part2.is_multipart(), 1) |
---|
3671 | n/a | eq(len(part2.get_payload()), 1) |
---|
3672 | n/a | part2a = part2.get_payload(0) |
---|
3673 | n/a | eq(part2a.is_multipart(), 0) |
---|
3674 | n/a | eq(part2a.get_content_type(), 'text/plain') |
---|
3675 | n/a | neq(part2a.get_payload(), 'message 2\n') |
---|
3676 | n/a | |
---|
3677 | n/a | def test_three_lines(self): |
---|
3678 | n/a | # A bug report by Andrew McNamara |
---|
3679 | n/a | lines = ['From: Andrew Person <aperson@dom.ain', |
---|
3680 | n/a | 'Subject: Test', |
---|
3681 | n/a | 'Date: Tue, 20 Aug 2002 16:43:45 +1000'] |
---|
3682 | n/a | msg = email.message_from_string(NL.join(lines)) |
---|
3683 | n/a | self.assertEqual(msg['date'], 'Tue, 20 Aug 2002 16:43:45 +1000') |
---|
3684 | n/a | |
---|
3685 | n/a | def test_strip_line_feed_and_carriage_return_in_headers(self): |
---|
3686 | n/a | eq = self.assertEqual |
---|
3687 | n/a | # For [ 1002475 ] email message parser doesn't handle \r\n correctly |
---|
3688 | n/a | value1 = 'text' |
---|
3689 | n/a | value2 = 'more text' |
---|
3690 | n/a | m = 'Header: %s\r\nNext-Header: %s\r\n\r\nBody\r\n\r\n' % ( |
---|
3691 | n/a | value1, value2) |
---|
3692 | n/a | msg = email.message_from_string(m) |
---|
3693 | n/a | eq(msg.get('Header'), value1) |
---|
3694 | n/a | eq(msg.get('Next-Header'), value2) |
---|
3695 | n/a | |
---|
3696 | n/a | def test_rfc2822_header_syntax(self): |
---|
3697 | n/a | eq = self.assertEqual |
---|
3698 | n/a | m = '>From: foo\nFrom: bar\n!"#QUX;~: zoo\n\nbody' |
---|
3699 | n/a | msg = email.message_from_string(m) |
---|
3700 | n/a | eq(len(msg), 3) |
---|
3701 | n/a | eq(sorted(field for field in msg), ['!"#QUX;~', '>From', 'From']) |
---|
3702 | n/a | eq(msg.get_payload(), 'body') |
---|
3703 | n/a | |
---|
3704 | n/a | def test_rfc2822_space_not_allowed_in_header(self): |
---|
3705 | n/a | eq = self.assertEqual |
---|
3706 | n/a | m = '>From foo@example.com 11:25:53\nFrom: bar\n!"#QUX;~: zoo\n\nbody' |
---|
3707 | n/a | msg = email.message_from_string(m) |
---|
3708 | n/a | eq(len(msg.keys()), 0) |
---|
3709 | n/a | |
---|
3710 | n/a | def test_rfc2822_one_character_header(self): |
---|
3711 | n/a | eq = self.assertEqual |
---|
3712 | n/a | m = 'A: first header\nB: second header\nCC: third header\n\nbody' |
---|
3713 | n/a | msg = email.message_from_string(m) |
---|
3714 | n/a | headers = msg.keys() |
---|
3715 | n/a | headers.sort() |
---|
3716 | n/a | eq(headers, ['A', 'B', 'CC']) |
---|
3717 | n/a | eq(msg.get_payload(), 'body') |
---|
3718 | n/a | |
---|
3719 | n/a | def test_CRLFLF_at_end_of_part(self): |
---|
3720 | n/a | # issue 5610: feedparser should not eat two chars from body part ending |
---|
3721 | n/a | # with "\r\n\n". |
---|
3722 | n/a | m = ( |
---|
3723 | n/a | "From: foo@bar.com\n" |
---|
3724 | n/a | "To: baz\n" |
---|
3725 | n/a | "Mime-Version: 1.0\n" |
---|
3726 | n/a | "Content-Type: multipart/mixed; boundary=BOUNDARY\n" |
---|
3727 | n/a | "\n" |
---|
3728 | n/a | "--BOUNDARY\n" |
---|
3729 | n/a | "Content-Type: text/plain\n" |
---|
3730 | n/a | "\n" |
---|
3731 | n/a | "body ending with CRLF newline\r\n" |
---|
3732 | n/a | "\n" |
---|
3733 | n/a | "--BOUNDARY--\n" |
---|
3734 | n/a | ) |
---|
3735 | n/a | msg = email.message_from_string(m) |
---|
3736 | n/a | self.assertTrue(msg.get_payload(0).get_payload().endswith('\r\n')) |
---|
3737 | n/a | |
---|
3738 | n/a | |
---|
3739 | n/a | class Test8BitBytesHandling(TestEmailBase): |
---|
3740 | n/a | # In Python3 all input is string, but that doesn't work if the actual input |
---|
3741 | n/a | # uses an 8bit transfer encoding. To hack around that, in email 5.1 we |
---|
3742 | n/a | # decode byte streams using the surrogateescape error handler, and |
---|
3743 | n/a | # reconvert to binary at appropriate places if we detect surrogates. This |
---|
3744 | n/a | # doesn't allow us to transform headers with 8bit bytes (they get munged), |
---|
3745 | n/a | # but it does allow us to parse and preserve them, and to decode body |
---|
3746 | n/a | # parts that use an 8bit CTE. |
---|
3747 | n/a | |
---|
3748 | n/a | bodytest_msg = textwrap.dedent("""\ |
---|
3749 | n/a | From: foo@bar.com |
---|
3750 | n/a | To: baz |
---|
3751 | n/a | Mime-Version: 1.0 |
---|
3752 | n/a | Content-Type: text/plain; charset={charset} |
---|
3753 | n/a | Content-Transfer-Encoding: {cte} |
---|
3754 | n/a | |
---|
3755 | n/a | {bodyline} |
---|
3756 | n/a | """) |
---|
3757 | n/a | |
---|
3758 | n/a | def test_known_8bit_CTE(self): |
---|
3759 | n/a | m = self.bodytest_msg.format(charset='utf-8', |
---|
3760 | n/a | cte='8bit', |
---|
3761 | n/a | bodyline='pöstal').encode('utf-8') |
---|
3762 | n/a | msg = email.message_from_bytes(m) |
---|
3763 | n/a | self.assertEqual(msg.get_payload(), "pöstal\n") |
---|
3764 | n/a | self.assertEqual(msg.get_payload(decode=True), |
---|
3765 | n/a | "pöstal\n".encode('utf-8')) |
---|
3766 | n/a | |
---|
3767 | n/a | def test_unknown_8bit_CTE(self): |
---|
3768 | n/a | m = self.bodytest_msg.format(charset='notavalidcharset', |
---|
3769 | n/a | cte='8bit', |
---|
3770 | n/a | bodyline='pöstal').encode('utf-8') |
---|
3771 | n/a | msg = email.message_from_bytes(m) |
---|
3772 | n/a | self.assertEqual(msg.get_payload(), "p\uFFFD\uFFFDstal\n") |
---|
3773 | n/a | self.assertEqual(msg.get_payload(decode=True), |
---|
3774 | n/a | "pöstal\n".encode('utf-8')) |
---|
3775 | n/a | |
---|
3776 | n/a | def test_8bit_in_quopri_body(self): |
---|
3777 | n/a | # This is non-RFC compliant data...without 'decode' the library code |
---|
3778 | n/a | # decodes the body using the charset from the headers, and because the |
---|
3779 | n/a | # source byte really is utf-8 this works. This is likely to fail |
---|
3780 | n/a | # against real dirty data (ie: produce mojibake), but the data is |
---|
3781 | n/a | # invalid anyway so it is as good a guess as any. But this means that |
---|
3782 | n/a | # this test just confirms the current behavior; that behavior is not |
---|
3783 | n/a | # necessarily the best possible behavior. With 'decode' it is |
---|
3784 | n/a | # returning the raw bytes, so that test should be of correct behavior, |
---|
3785 | n/a | # or at least produce the same result that email4 did. |
---|
3786 | n/a | m = self.bodytest_msg.format(charset='utf-8', |
---|
3787 | n/a | cte='quoted-printable', |
---|
3788 | n/a | bodyline='p=C3=B6stál').encode('utf-8') |
---|
3789 | n/a | msg = email.message_from_bytes(m) |
---|
3790 | n/a | self.assertEqual(msg.get_payload(), 'p=C3=B6stál\n') |
---|
3791 | n/a | self.assertEqual(msg.get_payload(decode=True), |
---|
3792 | n/a | 'pöstál\n'.encode('utf-8')) |
---|
3793 | n/a | |
---|
3794 | n/a | def test_invalid_8bit_in_non_8bit_cte_uses_replace(self): |
---|
3795 | n/a | # This is similar to the previous test, but proves that if the 8bit |
---|
3796 | n/a | # byte is undecodeable in the specified charset, it gets replaced |
---|
3797 | n/a | # by the unicode 'unknown' character. Again, this may or may not |
---|
3798 | n/a | # be the ideal behavior. Note that if decode=False none of the |
---|
3799 | n/a | # decoders will get involved, so this is the only test we need |
---|
3800 | n/a | # for this behavior. |
---|
3801 | n/a | m = self.bodytest_msg.format(charset='ascii', |
---|
3802 | n/a | cte='quoted-printable', |
---|
3803 | n/a | bodyline='p=C3=B6stál').encode('utf-8') |
---|
3804 | n/a | msg = email.message_from_bytes(m) |
---|
3805 | n/a | self.assertEqual(msg.get_payload(), 'p=C3=B6st\uFFFD\uFFFDl\n') |
---|
3806 | n/a | self.assertEqual(msg.get_payload(decode=True), |
---|
3807 | n/a | 'pöstál\n'.encode('utf-8')) |
---|
3808 | n/a | |
---|
3809 | n/a | # test_defect_handling:test_invalid_chars_in_base64_payload |
---|
3810 | n/a | def test_8bit_in_base64_body(self): |
---|
3811 | n/a | # If we get 8bit bytes in a base64 body, we can just ignore them |
---|
3812 | n/a | # as being outside the base64 alphabet and decode anyway. But |
---|
3813 | n/a | # we register a defect. |
---|
3814 | n/a | m = self.bodytest_msg.format(charset='utf-8', |
---|
3815 | n/a | cte='base64', |
---|
3816 | n/a | bodyline='cMO2c3RhbAá=').encode('utf-8') |
---|
3817 | n/a | msg = email.message_from_bytes(m) |
---|
3818 | n/a | self.assertEqual(msg.get_payload(decode=True), |
---|
3819 | n/a | 'pöstal'.encode('utf-8')) |
---|
3820 | n/a | self.assertIsInstance(msg.defects[0], |
---|
3821 | n/a | errors.InvalidBase64CharactersDefect) |
---|
3822 | n/a | |
---|
3823 | n/a | def test_8bit_in_uuencode_body(self): |
---|
3824 | n/a | # Sticking an 8bit byte in a uuencode block makes it undecodable by |
---|
3825 | n/a | # normal means, so the block is returned undecoded, but as bytes. |
---|
3826 | n/a | m = self.bodytest_msg.format(charset='utf-8', |
---|
3827 | n/a | cte='uuencode', |
---|
3828 | n/a | bodyline='<,.V<W1A; á ').encode('utf-8') |
---|
3829 | n/a | msg = email.message_from_bytes(m) |
---|
3830 | n/a | self.assertEqual(msg.get_payload(decode=True), |
---|
3831 | n/a | '<,.V<W1A; á \n'.encode('utf-8')) |
---|
3832 | n/a | |
---|
3833 | n/a | |
---|
3834 | n/a | headertest_headers = ( |
---|
3835 | n/a | ('From: foo@bar.com', ('From', 'foo@bar.com')), |
---|
3836 | n/a | ('To: báz', ('To', '=?unknown-8bit?q?b=C3=A1z?=')), |
---|
3837 | n/a | ('Subject: Maintenant je vous présente mon collègue, le pouf célèbre\n' |
---|
3838 | n/a | '\tJean de Baddie', |
---|
3839 | n/a | ('Subject', '=?unknown-8bit?q?Maintenant_je_vous_pr=C3=A9sente_mon_' |
---|
3840 | n/a | 'coll=C3=A8gue=2C_le_pouf_c=C3=A9l=C3=A8bre?=\n' |
---|
3841 | n/a | ' =?unknown-8bit?q?_Jean_de_Baddie?=')), |
---|
3842 | n/a | ('From: göst', ('From', '=?unknown-8bit?b?Z8O2c3Q=?=')), |
---|
3843 | n/a | ) |
---|
3844 | n/a | headertest_msg = ('\n'.join([src for (src, _) in headertest_headers]) + |
---|
3845 | n/a | '\nYes, they are flying.\n').encode('utf-8') |
---|
3846 | n/a | |
---|
3847 | n/a | def test_get_8bit_header(self): |
---|
3848 | n/a | msg = email.message_from_bytes(self.headertest_msg) |
---|
3849 | n/a | self.assertEqual(str(msg.get('to')), 'b\uFFFD\uFFFDz') |
---|
3850 | n/a | self.assertEqual(str(msg['to']), 'b\uFFFD\uFFFDz') |
---|
3851 | n/a | |
---|
3852 | n/a | def test_print_8bit_headers(self): |
---|
3853 | n/a | msg = email.message_from_bytes(self.headertest_msg) |
---|
3854 | n/a | self.assertEqual(str(msg), |
---|
3855 | n/a | textwrap.dedent("""\ |
---|
3856 | n/a | From: {} |
---|
3857 | n/a | To: {} |
---|
3858 | n/a | Subject: {} |
---|
3859 | n/a | From: {} |
---|
3860 | n/a | |
---|
3861 | n/a | Yes, they are flying. |
---|
3862 | n/a | """).format(*[expected[1] for (_, expected) in |
---|
3863 | n/a | self.headertest_headers])) |
---|
3864 | n/a | |
---|
3865 | n/a | def test_values_with_8bit_headers(self): |
---|
3866 | n/a | msg = email.message_from_bytes(self.headertest_msg) |
---|
3867 | n/a | self.assertListEqual([str(x) for x in msg.values()], |
---|
3868 | n/a | ['foo@bar.com', |
---|
3869 | n/a | 'b\uFFFD\uFFFDz', |
---|
3870 | n/a | 'Maintenant je vous pr\uFFFD\uFFFDsente mon ' |
---|
3871 | n/a | 'coll\uFFFD\uFFFDgue, le pouf ' |
---|
3872 | n/a | 'c\uFFFD\uFFFDl\uFFFD\uFFFDbre\n' |
---|
3873 | n/a | '\tJean de Baddie', |
---|
3874 | n/a | "g\uFFFD\uFFFDst"]) |
---|
3875 | n/a | |
---|
3876 | n/a | def test_items_with_8bit_headers(self): |
---|
3877 | n/a | msg = email.message_from_bytes(self.headertest_msg) |
---|
3878 | n/a | self.assertListEqual([(str(x), str(y)) for (x, y) in msg.items()], |
---|
3879 | n/a | [('From', 'foo@bar.com'), |
---|
3880 | n/a | ('To', 'b\uFFFD\uFFFDz'), |
---|
3881 | n/a | ('Subject', 'Maintenant je vous ' |
---|
3882 | n/a | 'pr\uFFFD\uFFFDsente ' |
---|
3883 | n/a | 'mon coll\uFFFD\uFFFDgue, le pouf ' |
---|
3884 | n/a | 'c\uFFFD\uFFFDl\uFFFD\uFFFDbre\n' |
---|
3885 | n/a | '\tJean de Baddie'), |
---|
3886 | n/a | ('From', 'g\uFFFD\uFFFDst')]) |
---|
3887 | n/a | |
---|
3888 | n/a | def test_get_all_with_8bit_headers(self): |
---|
3889 | n/a | msg = email.message_from_bytes(self.headertest_msg) |
---|
3890 | n/a | self.assertListEqual([str(x) for x in msg.get_all('from')], |
---|
3891 | n/a | ['foo@bar.com', |
---|
3892 | n/a | 'g\uFFFD\uFFFDst']) |
---|
3893 | n/a | |
---|
3894 | n/a | def test_get_content_type_with_8bit(self): |
---|
3895 | n/a | msg = email.message_from_bytes(textwrap.dedent("""\ |
---|
3896 | n/a | Content-Type: text/pl\xA7in; charset=utf-8 |
---|
3897 | n/a | """).encode('latin-1')) |
---|
3898 | n/a | self.assertEqual(msg.get_content_type(), "text/pl\uFFFDin") |
---|
3899 | n/a | self.assertEqual(msg.get_content_maintype(), "text") |
---|
3900 | n/a | self.assertEqual(msg.get_content_subtype(), "pl\uFFFDin") |
---|
3901 | n/a | |
---|
3902 | n/a | # test_headerregistry.TestContentTypeHeader.non_ascii_in_params |
---|
3903 | n/a | def test_get_params_with_8bit(self): |
---|
3904 | n/a | msg = email.message_from_bytes( |
---|
3905 | n/a | 'X-Header: foo=\xa7ne; b\xa7r=two; baz=three\n'.encode('latin-1')) |
---|
3906 | n/a | self.assertEqual(msg.get_params(header='x-header'), |
---|
3907 | n/a | [('foo', '\uFFFDne'), ('b\uFFFDr', 'two'), ('baz', 'three')]) |
---|
3908 | n/a | self.assertEqual(msg.get_param('Foo', header='x-header'), '\uFFFdne') |
---|
3909 | n/a | # XXX: someday you might be able to get 'b\xa7r', for now you can't. |
---|
3910 | n/a | self.assertEqual(msg.get_param('b\xa7r', header='x-header'), None) |
---|
3911 | n/a | |
---|
3912 | n/a | # test_headerregistry.TestContentTypeHeader.non_ascii_in_rfc2231_value |
---|
3913 | n/a | def test_get_rfc2231_params_with_8bit(self): |
---|
3914 | n/a | msg = email.message_from_bytes(textwrap.dedent("""\ |
---|
3915 | n/a | Content-Type: text/plain; charset=us-ascii; |
---|
3916 | n/a | title*=us-ascii'en'This%20is%20not%20f\xa7n""" |
---|
3917 | n/a | ).encode('latin-1')) |
---|
3918 | n/a | self.assertEqual(msg.get_param('title'), |
---|
3919 | n/a | ('us-ascii', 'en', 'This is not f\uFFFDn')) |
---|
3920 | n/a | |
---|
3921 | n/a | def test_set_rfc2231_params_with_8bit(self): |
---|
3922 | n/a | msg = email.message_from_bytes(textwrap.dedent("""\ |
---|
3923 | n/a | Content-Type: text/plain; charset=us-ascii; |
---|
3924 | n/a | title*=us-ascii'en'This%20is%20not%20f\xa7n""" |
---|
3925 | n/a | ).encode('latin-1')) |
---|
3926 | n/a | msg.set_param('title', 'test') |
---|
3927 | n/a | self.assertEqual(msg.get_param('title'), 'test') |
---|
3928 | n/a | |
---|
3929 | n/a | def test_del_rfc2231_params_with_8bit(self): |
---|
3930 | n/a | msg = email.message_from_bytes(textwrap.dedent("""\ |
---|
3931 | n/a | Content-Type: text/plain; charset=us-ascii; |
---|
3932 | n/a | title*=us-ascii'en'This%20is%20not%20f\xa7n""" |
---|
3933 | n/a | ).encode('latin-1')) |
---|
3934 | n/a | msg.del_param('title') |
---|
3935 | n/a | self.assertEqual(msg.get_param('title'), None) |
---|
3936 | n/a | self.assertEqual(msg.get_content_maintype(), 'text') |
---|
3937 | n/a | |
---|
3938 | n/a | def test_get_payload_with_8bit_cte_header(self): |
---|
3939 | n/a | msg = email.message_from_bytes(textwrap.dedent("""\ |
---|
3940 | n/a | Content-Transfer-Encoding: b\xa7se64 |
---|
3941 | n/a | Content-Type: text/plain; charset=latin-1 |
---|
3942 | n/a | |
---|
3943 | n/a | payload |
---|
3944 | n/a | """).encode('latin-1')) |
---|
3945 | n/a | self.assertEqual(msg.get_payload(), 'payload\n') |
---|
3946 | n/a | self.assertEqual(msg.get_payload(decode=True), b'payload\n') |
---|
3947 | n/a | |
---|
3948 | n/a | non_latin_bin_msg = textwrap.dedent("""\ |
---|
3949 | n/a | From: foo@bar.com |
---|
3950 | n/a | To: báz |
---|
3951 | n/a | Subject: Maintenant je vous présente mon collègue, le pouf célèbre |
---|
3952 | n/a | \tJean de Baddie |
---|
3953 | n/a | Mime-Version: 1.0 |
---|
3954 | n/a | Content-Type: text/plain; charset="utf-8" |
---|
3955 | n/a | Content-Transfer-Encoding: 8bit |
---|
3956 | n/a | |
---|
3957 | n/a | Ðа, они леÑÑÑ. |
---|
3958 | n/a | """).encode('utf-8') |
---|
3959 | n/a | |
---|
3960 | n/a | def test_bytes_generator(self): |
---|
3961 | n/a | msg = email.message_from_bytes(self.non_latin_bin_msg) |
---|
3962 | n/a | out = BytesIO() |
---|
3963 | n/a | email.generator.BytesGenerator(out).flatten(msg) |
---|
3964 | n/a | self.assertEqual(out.getvalue(), self.non_latin_bin_msg) |
---|
3965 | n/a | |
---|
3966 | n/a | def test_bytes_generator_handles_None_body(self): |
---|
3967 | n/a | #Issue 11019 |
---|
3968 | n/a | msg = email.message.Message() |
---|
3969 | n/a | out = BytesIO() |
---|
3970 | n/a | email.generator.BytesGenerator(out).flatten(msg) |
---|
3971 | n/a | self.assertEqual(out.getvalue(), b"\n") |
---|
3972 | n/a | |
---|
3973 | n/a | non_latin_bin_msg_as7bit_wrapped = textwrap.dedent("""\ |
---|
3974 | n/a | From: foo@bar.com |
---|
3975 | n/a | To: =?unknown-8bit?q?b=C3=A1z?= |
---|
3976 | n/a | Subject: =?unknown-8bit?q?Maintenant_je_vous_pr=C3=A9sente_mon_coll=C3=A8gue?= |
---|
3977 | n/a | =?unknown-8bit?q?=2C_le_pouf_c=C3=A9l=C3=A8bre?= |
---|
3978 | n/a | =?unknown-8bit?q?_Jean_de_Baddie?= |
---|
3979 | n/a | Mime-Version: 1.0 |
---|
3980 | n/a | Content-Type: text/plain; charset="utf-8" |
---|
3981 | n/a | Content-Transfer-Encoding: base64 |
---|
3982 | n/a | |
---|
3983 | n/a | 0JTQsCwg0L7QvdC4INC70LXRgtGP0YIuCg== |
---|
3984 | n/a | """) |
---|
3985 | n/a | |
---|
3986 | n/a | def test_generator_handles_8bit(self): |
---|
3987 | n/a | msg = email.message_from_bytes(self.non_latin_bin_msg) |
---|
3988 | n/a | out = StringIO() |
---|
3989 | n/a | email.generator.Generator(out).flatten(msg) |
---|
3990 | n/a | self.assertEqual(out.getvalue(), self.non_latin_bin_msg_as7bit_wrapped) |
---|
3991 | n/a | |
---|
3992 | n/a | def test_str_generator_should_not_mutate_msg_when_handling_8bit(self): |
---|
3993 | n/a | msg = email.message_from_bytes(self.non_latin_bin_msg) |
---|
3994 | n/a | out = BytesIO() |
---|
3995 | n/a | BytesGenerator(out).flatten(msg) |
---|
3996 | n/a | orig_value = out.getvalue() |
---|
3997 | n/a | Generator(StringIO()).flatten(msg) # Should not mutate msg! |
---|
3998 | n/a | out = BytesIO() |
---|
3999 | n/a | BytesGenerator(out).flatten(msg) |
---|
4000 | n/a | self.assertEqual(out.getvalue(), orig_value) |
---|
4001 | n/a | |
---|
4002 | n/a | def test_bytes_generator_with_unix_from(self): |
---|
4003 | n/a | # The unixfrom contains a current date, so we can't check it |
---|
4004 | n/a | # literally. Just make sure the first word is 'From' and the |
---|
4005 | n/a | # rest of the message matches the input. |
---|
4006 | n/a | msg = email.message_from_bytes(self.non_latin_bin_msg) |
---|
4007 | n/a | out = BytesIO() |
---|
4008 | n/a | email.generator.BytesGenerator(out).flatten(msg, unixfrom=True) |
---|
4009 | n/a | lines = out.getvalue().split(b'\n') |
---|
4010 | n/a | self.assertEqual(lines[0].split()[0], b'From') |
---|
4011 | n/a | self.assertEqual(b'\n'.join(lines[1:]), self.non_latin_bin_msg) |
---|
4012 | n/a | |
---|
4013 | n/a | non_latin_bin_msg_as7bit = non_latin_bin_msg_as7bit_wrapped.split('\n') |
---|
4014 | n/a | non_latin_bin_msg_as7bit[2:4] = [ |
---|
4015 | n/a | 'Subject: =?unknown-8bit?q?Maintenant_je_vous_pr=C3=A9sente_mon_' |
---|
4016 | n/a | 'coll=C3=A8gue=2C_le_pouf_c=C3=A9l=C3=A8bre?='] |
---|
4017 | n/a | non_latin_bin_msg_as7bit = '\n'.join(non_latin_bin_msg_as7bit) |
---|
4018 | n/a | |
---|
4019 | n/a | def test_message_from_binary_file(self): |
---|
4020 | n/a | fn = 'test.msg' |
---|
4021 | n/a | self.addCleanup(unlink, fn) |
---|
4022 | n/a | with open(fn, 'wb') as testfile: |
---|
4023 | n/a | testfile.write(self.non_latin_bin_msg) |
---|
4024 | n/a | with open(fn, 'rb') as testfile: |
---|
4025 | n/a | m = email.parser.BytesParser().parse(testfile) |
---|
4026 | n/a | self.assertEqual(str(m), self.non_latin_bin_msg_as7bit) |
---|
4027 | n/a | |
---|
4028 | n/a | latin_bin_msg = textwrap.dedent("""\ |
---|
4029 | n/a | From: foo@bar.com |
---|
4030 | n/a | To: Dinsdale |
---|
4031 | n/a | Subject: Nudge nudge, wink, wink |
---|
4032 | n/a | Mime-Version: 1.0 |
---|
4033 | n/a | Content-Type: text/plain; charset="latin-1" |
---|
4034 | n/a | Content-Transfer-Encoding: 8bit |
---|
4035 | n/a | |
---|
4036 | n/a | oh là là , know what I mean, know what I mean? |
---|
4037 | n/a | """).encode('latin-1') |
---|
4038 | n/a | |
---|
4039 | n/a | latin_bin_msg_as7bit = textwrap.dedent("""\ |
---|
4040 | n/a | From: foo@bar.com |
---|
4041 | n/a | To: Dinsdale |
---|
4042 | n/a | Subject: Nudge nudge, wink, wink |
---|
4043 | n/a | Mime-Version: 1.0 |
---|
4044 | n/a | Content-Type: text/plain; charset="iso-8859-1" |
---|
4045 | n/a | Content-Transfer-Encoding: quoted-printable |
---|
4046 | n/a | |
---|
4047 | n/a | oh l=E0 l=E0, know what I mean, know what I mean? |
---|
4048 | n/a | """) |
---|
4049 | n/a | |
---|
4050 | n/a | def test_string_generator_reencodes_to_quopri_when_appropriate(self): |
---|
4051 | n/a | m = email.message_from_bytes(self.latin_bin_msg) |
---|
4052 | n/a | self.assertEqual(str(m), self.latin_bin_msg_as7bit) |
---|
4053 | n/a | |
---|
4054 | n/a | def test_decoded_generator_emits_unicode_body(self): |
---|
4055 | n/a | m = email.message_from_bytes(self.latin_bin_msg) |
---|
4056 | n/a | out = StringIO() |
---|
4057 | n/a | email.generator.DecodedGenerator(out).flatten(m) |
---|
4058 | n/a | #DecodedHeader output contains an extra blank line compared |
---|
4059 | n/a | #to the input message. RDM: not sure if this is a bug or not, |
---|
4060 | n/a | #but it is not specific to the 8bit->7bit conversion. |
---|
4061 | n/a | self.assertEqual(out.getvalue(), |
---|
4062 | n/a | self.latin_bin_msg.decode('latin-1')+'\n') |
---|
4063 | n/a | |
---|
4064 | n/a | def test_bytes_feedparser(self): |
---|
4065 | n/a | bfp = email.feedparser.BytesFeedParser() |
---|
4066 | n/a | for i in range(0, len(self.latin_bin_msg), 10): |
---|
4067 | n/a | bfp.feed(self.latin_bin_msg[i:i+10]) |
---|
4068 | n/a | m = bfp.close() |
---|
4069 | n/a | self.assertEqual(str(m), self.latin_bin_msg_as7bit) |
---|
4070 | n/a | |
---|
4071 | n/a | def test_crlf_flatten(self): |
---|
4072 | n/a | with openfile('msg_26.txt', 'rb') as fp: |
---|
4073 | n/a | text = fp.read() |
---|
4074 | n/a | msg = email.message_from_bytes(text) |
---|
4075 | n/a | s = BytesIO() |
---|
4076 | n/a | g = email.generator.BytesGenerator(s) |
---|
4077 | n/a | g.flatten(msg, linesep='\r\n') |
---|
4078 | n/a | self.assertEqual(s.getvalue(), text) |
---|
4079 | n/a | |
---|
4080 | n/a | def test_8bit_multipart(self): |
---|
4081 | n/a | # Issue 11605 |
---|
4082 | n/a | source = textwrap.dedent("""\ |
---|
4083 | n/a | Date: Fri, 18 Mar 2011 17:15:43 +0100 |
---|
4084 | n/a | To: foo@example.com |
---|
4085 | n/a | From: foodwatch-Newsletter <bar@example.com> |
---|
4086 | n/a | Subject: Aktuelles zu Japan, Klonfleisch und Smiley-System |
---|
4087 | n/a | Message-ID: <76a486bee62b0d200f33dc2ca08220ad@localhost.localdomain> |
---|
4088 | n/a | MIME-Version: 1.0 |
---|
4089 | n/a | Content-Type: multipart/alternative; |
---|
4090 | n/a | boundary="b1_76a486bee62b0d200f33dc2ca08220ad" |
---|
4091 | n/a | |
---|
4092 | n/a | --b1_76a486bee62b0d200f33dc2ca08220ad |
---|
4093 | n/a | Content-Type: text/plain; charset="utf-8" |
---|
4094 | n/a | Content-Transfer-Encoding: 8bit |
---|
4095 | n/a | |
---|
4096 | n/a | Guten Tag, , |
---|
4097 | n/a | |
---|
4098 | n/a | mit groÃer Betroffenheit verfolgen auch wir im foodwatch-Team die |
---|
4099 | n/a | Nachrichten aus Japan. |
---|
4100 | n/a | |
---|
4101 | n/a | |
---|
4102 | n/a | --b1_76a486bee62b0d200f33dc2ca08220ad |
---|
4103 | n/a | Content-Type: text/html; charset="utf-8" |
---|
4104 | n/a | Content-Transfer-Encoding: 8bit |
---|
4105 | n/a | |
---|
4106 | n/a | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" |
---|
4107 | n/a | "http://www.w3.org/TR/html4/loose.dtd"> |
---|
4108 | n/a | <html lang="de"> |
---|
4109 | n/a | <head> |
---|
4110 | n/a | <title>foodwatch - Newsletter</title> |
---|
4111 | n/a | </head> |
---|
4112 | n/a | <body> |
---|
4113 | n/a | <p>mit großer Betroffenheit verfolgen auch wir im foodwatch-Team |
---|
4114 | n/a | die Nachrichten aus Japan.</p> |
---|
4115 | n/a | </body> |
---|
4116 | n/a | </html> |
---|
4117 | n/a | --b1_76a486bee62b0d200f33dc2ca08220ad-- |
---|
4118 | n/a | |
---|
4119 | n/a | """).encode('utf-8') |
---|
4120 | n/a | msg = email.message_from_bytes(source) |
---|
4121 | n/a | s = BytesIO() |
---|
4122 | n/a | g = email.generator.BytesGenerator(s) |
---|
4123 | n/a | g.flatten(msg) |
---|
4124 | n/a | self.assertEqual(s.getvalue(), source) |
---|
4125 | n/a | |
---|
4126 | n/a | def test_bytes_generator_b_encoding_linesep(self): |
---|
4127 | n/a | # Issue 14062: b encoding was tacking on an extra \n. |
---|
4128 | n/a | m = Message() |
---|
4129 | n/a | # This has enough non-ascii that it should always end up b encoded. |
---|
4130 | n/a | m['Subject'] = Header('žluÅ¥ouÄký kůÅ') |
---|
4131 | n/a | s = BytesIO() |
---|
4132 | n/a | g = email.generator.BytesGenerator(s) |
---|
4133 | n/a | g.flatten(m, linesep='\r\n') |
---|
4134 | n/a | self.assertEqual( |
---|
4135 | n/a | s.getvalue(), |
---|
4136 | n/a | b'Subject: =?utf-8?b?xb5sdcWlb3XEjWvDvSBrxa/FiA==?=\r\n\r\n') |
---|
4137 | n/a | |
---|
4138 | n/a | def test_generator_b_encoding_linesep(self): |
---|
4139 | n/a | # Since this broke in ByteGenerator, test Generator for completeness. |
---|
4140 | n/a | m = Message() |
---|
4141 | n/a | # This has enough non-ascii that it should always end up b encoded. |
---|
4142 | n/a | m['Subject'] = Header('žluÅ¥ouÄký kůÅ') |
---|
4143 | n/a | s = StringIO() |
---|
4144 | n/a | g = email.generator.Generator(s) |
---|
4145 | n/a | g.flatten(m, linesep='\r\n') |
---|
4146 | n/a | self.assertEqual( |
---|
4147 | n/a | s.getvalue(), |
---|
4148 | n/a | 'Subject: =?utf-8?b?xb5sdcWlb3XEjWvDvSBrxa/FiA==?=\r\n\r\n') |
---|
4149 | n/a | |
---|
4150 | n/a | maxDiff = None |
---|
4151 | n/a | |
---|
4152 | n/a | |
---|
4153 | n/a | class BaseTestBytesGeneratorIdempotent: |
---|
4154 | n/a | |
---|
4155 | n/a | maxDiff = None |
---|
4156 | n/a | |
---|
4157 | n/a | def _msgobj(self, filename): |
---|
4158 | n/a | with openfile(filename, 'rb') as fp: |
---|
4159 | n/a | data = fp.read() |
---|
4160 | n/a | data = self.normalize_linesep_regex.sub(self.blinesep, data) |
---|
4161 | n/a | msg = email.message_from_bytes(data) |
---|
4162 | n/a | return msg, data |
---|
4163 | n/a | |
---|
4164 | n/a | def _idempotent(self, msg, data, unixfrom=False): |
---|
4165 | n/a | b = BytesIO() |
---|
4166 | n/a | g = email.generator.BytesGenerator(b, maxheaderlen=0) |
---|
4167 | n/a | g.flatten(msg, unixfrom=unixfrom, linesep=self.linesep) |
---|
4168 | n/a | self.assertEqual(data, b.getvalue()) |
---|
4169 | n/a | |
---|
4170 | n/a | |
---|
4171 | n/a | class TestBytesGeneratorIdempotentNL(BaseTestBytesGeneratorIdempotent, |
---|
4172 | n/a | TestIdempotent): |
---|
4173 | n/a | linesep = '\n' |
---|
4174 | n/a | blinesep = b'\n' |
---|
4175 | n/a | normalize_linesep_regex = re.compile(br'\r\n') |
---|
4176 | n/a | |
---|
4177 | n/a | |
---|
4178 | n/a | class TestBytesGeneratorIdempotentCRLF(BaseTestBytesGeneratorIdempotent, |
---|
4179 | n/a | TestIdempotent): |
---|
4180 | n/a | linesep = '\r\n' |
---|
4181 | n/a | blinesep = b'\r\n' |
---|
4182 | n/a | normalize_linesep_regex = re.compile(br'(?<!\r)\n') |
---|
4183 | n/a | |
---|
4184 | n/a | |
---|
4185 | n/a | class TestBase64(unittest.TestCase): |
---|
4186 | n/a | def test_len(self): |
---|
4187 | n/a | eq = self.assertEqual |
---|
4188 | n/a | eq(base64mime.header_length('hello'), |
---|
4189 | n/a | len(base64mime.body_encode(b'hello', eol=''))) |
---|
4190 | n/a | for size in range(15): |
---|
4191 | n/a | if size == 0 : bsize = 0 |
---|
4192 | n/a | elif size <= 3 : bsize = 4 |
---|
4193 | n/a | elif size <= 6 : bsize = 8 |
---|
4194 | n/a | elif size <= 9 : bsize = 12 |
---|
4195 | n/a | elif size <= 12: bsize = 16 |
---|
4196 | n/a | else : bsize = 20 |
---|
4197 | n/a | eq(base64mime.header_length('x' * size), bsize) |
---|
4198 | n/a | |
---|
4199 | n/a | def test_decode(self): |
---|
4200 | n/a | eq = self.assertEqual |
---|
4201 | n/a | eq(base64mime.decode(''), b'') |
---|
4202 | n/a | eq(base64mime.decode('aGVsbG8='), b'hello') |
---|
4203 | n/a | |
---|
4204 | n/a | def test_encode(self): |
---|
4205 | n/a | eq = self.assertEqual |
---|
4206 | n/a | eq(base64mime.body_encode(b''), b'') |
---|
4207 | n/a | eq(base64mime.body_encode(b'hello'), 'aGVsbG8=\n') |
---|
4208 | n/a | # Test the binary flag |
---|
4209 | n/a | eq(base64mime.body_encode(b'hello\n'), 'aGVsbG8K\n') |
---|
4210 | n/a | # Test the maxlinelen arg |
---|
4211 | n/a | eq(base64mime.body_encode(b'xxxx ' * 20, maxlinelen=40), """\ |
---|
4212 | n/a | eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg |
---|
4213 | n/a | eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg |
---|
4214 | n/a | eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg |
---|
4215 | n/a | eHh4eCB4eHh4IA== |
---|
4216 | n/a | """) |
---|
4217 | n/a | # Test the eol argument |
---|
4218 | n/a | eq(base64mime.body_encode(b'xxxx ' * 20, maxlinelen=40, eol='\r\n'), |
---|
4219 | n/a | """\ |
---|
4220 | n/a | eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg\r |
---|
4221 | n/a | eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg\r |
---|
4222 | n/a | eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg\r |
---|
4223 | n/a | eHh4eCB4eHh4IA==\r |
---|
4224 | n/a | """) |
---|
4225 | n/a | |
---|
4226 | n/a | def test_header_encode(self): |
---|
4227 | n/a | eq = self.assertEqual |
---|
4228 | n/a | he = base64mime.header_encode |
---|
4229 | n/a | eq(he('hello'), '=?iso-8859-1?b?aGVsbG8=?=') |
---|
4230 | n/a | eq(he('hello\r\nworld'), '=?iso-8859-1?b?aGVsbG8NCndvcmxk?=') |
---|
4231 | n/a | eq(he('hello\nworld'), '=?iso-8859-1?b?aGVsbG8Kd29ybGQ=?=') |
---|
4232 | n/a | # Test the charset option |
---|
4233 | n/a | eq(he('hello', charset='iso-8859-2'), '=?iso-8859-2?b?aGVsbG8=?=') |
---|
4234 | n/a | eq(he('hello\nworld'), '=?iso-8859-1?b?aGVsbG8Kd29ybGQ=?=') |
---|
4235 | n/a | |
---|
4236 | n/a | |
---|
4237 | n/a | |
---|
4238 | n/a | class TestQuopri(unittest.TestCase): |
---|
4239 | n/a | def setUp(self): |
---|
4240 | n/a | # Set of characters (as byte integers) that don't need to be encoded |
---|
4241 | n/a | # in headers. |
---|
4242 | n/a | self.hlit = list(chain( |
---|
4243 | n/a | range(ord('a'), ord('z') + 1), |
---|
4244 | n/a | range(ord('A'), ord('Z') + 1), |
---|
4245 | n/a | range(ord('0'), ord('9') + 1), |
---|
4246 | n/a | (c for c in b'!*+-/'))) |
---|
4247 | n/a | # Set of characters (as byte integers) that do need to be encoded in |
---|
4248 | n/a | # headers. |
---|
4249 | n/a | self.hnon = [c for c in range(256) if c not in self.hlit] |
---|
4250 | n/a | assert len(self.hlit) + len(self.hnon) == 256 |
---|
4251 | n/a | # Set of characters (as byte integers) that don't need to be encoded |
---|
4252 | n/a | # in bodies. |
---|
4253 | n/a | self.blit = list(range(ord(' '), ord('~') + 1)) |
---|
4254 | n/a | self.blit.append(ord('\t')) |
---|
4255 | n/a | self.blit.remove(ord('=')) |
---|
4256 | n/a | # Set of characters (as byte integers) that do need to be encoded in |
---|
4257 | n/a | # bodies. |
---|
4258 | n/a | self.bnon = [c for c in range(256) if c not in self.blit] |
---|
4259 | n/a | assert len(self.blit) + len(self.bnon) == 256 |
---|
4260 | n/a | |
---|
4261 | n/a | def test_quopri_header_check(self): |
---|
4262 | n/a | for c in self.hlit: |
---|
4263 | n/a | self.assertFalse(quoprimime.header_check(c), |
---|
4264 | n/a | 'Should not be header quopri encoded: %s' % chr(c)) |
---|
4265 | n/a | for c in self.hnon: |
---|
4266 | n/a | self.assertTrue(quoprimime.header_check(c), |
---|
4267 | n/a | 'Should be header quopri encoded: %s' % chr(c)) |
---|
4268 | n/a | |
---|
4269 | n/a | def test_quopri_body_check(self): |
---|
4270 | n/a | for c in self.blit: |
---|
4271 | n/a | self.assertFalse(quoprimime.body_check(c), |
---|
4272 | n/a | 'Should not be body quopri encoded: %s' % chr(c)) |
---|
4273 | n/a | for c in self.bnon: |
---|
4274 | n/a | self.assertTrue(quoprimime.body_check(c), |
---|
4275 | n/a | 'Should be body quopri encoded: %s' % chr(c)) |
---|
4276 | n/a | |
---|
4277 | n/a | def test_header_quopri_len(self): |
---|
4278 | n/a | eq = self.assertEqual |
---|
4279 | n/a | eq(quoprimime.header_length(b'hello'), 5) |
---|
4280 | n/a | # RFC 2047 chrome is not included in header_length(). |
---|
4281 | n/a | eq(len(quoprimime.header_encode(b'hello', charset='xxx')), |
---|
4282 | n/a | quoprimime.header_length(b'hello') + |
---|
4283 | n/a | # =?xxx?q?...?= means 10 extra characters |
---|
4284 | n/a | 10) |
---|
4285 | n/a | eq(quoprimime.header_length(b'h@e@l@l@o@'), 20) |
---|
4286 | n/a | # RFC 2047 chrome is not included in header_length(). |
---|
4287 | n/a | eq(len(quoprimime.header_encode(b'h@e@l@l@o@', charset='xxx')), |
---|
4288 | n/a | quoprimime.header_length(b'h@e@l@l@o@') + |
---|
4289 | n/a | # =?xxx?q?...?= means 10 extra characters |
---|
4290 | n/a | 10) |
---|
4291 | n/a | for c in self.hlit: |
---|
4292 | n/a | eq(quoprimime.header_length(bytes([c])), 1, |
---|
4293 | n/a | 'expected length 1 for %r' % chr(c)) |
---|
4294 | n/a | for c in self.hnon: |
---|
4295 | n/a | # Space is special; it's encoded to _ |
---|
4296 | n/a | if c == ord(' '): |
---|
4297 | n/a | continue |
---|
4298 | n/a | eq(quoprimime.header_length(bytes([c])), 3, |
---|
4299 | n/a | 'expected length 3 for %r' % chr(c)) |
---|
4300 | n/a | eq(quoprimime.header_length(b' '), 1) |
---|
4301 | n/a | |
---|
4302 | n/a | def test_body_quopri_len(self): |
---|
4303 | n/a | eq = self.assertEqual |
---|
4304 | n/a | for c in self.blit: |
---|
4305 | n/a | eq(quoprimime.body_length(bytes([c])), 1) |
---|
4306 | n/a | for c in self.bnon: |
---|
4307 | n/a | eq(quoprimime.body_length(bytes([c])), 3) |
---|
4308 | n/a | |
---|
4309 | n/a | def test_quote_unquote_idempotent(self): |
---|
4310 | n/a | for x in range(256): |
---|
4311 | n/a | c = chr(x) |
---|
4312 | n/a | self.assertEqual(quoprimime.unquote(quoprimime.quote(c)), c) |
---|
4313 | n/a | |
---|
4314 | n/a | def _test_header_encode(self, header, expected_encoded_header, charset=None): |
---|
4315 | n/a | if charset is None: |
---|
4316 | n/a | encoded_header = quoprimime.header_encode(header) |
---|
4317 | n/a | else: |
---|
4318 | n/a | encoded_header = quoprimime.header_encode(header, charset) |
---|
4319 | n/a | self.assertEqual(encoded_header, expected_encoded_header) |
---|
4320 | n/a | |
---|
4321 | n/a | def test_header_encode_null(self): |
---|
4322 | n/a | self._test_header_encode(b'', '') |
---|
4323 | n/a | |
---|
4324 | n/a | def test_header_encode_one_word(self): |
---|
4325 | n/a | self._test_header_encode(b'hello', '=?iso-8859-1?q?hello?=') |
---|
4326 | n/a | |
---|
4327 | n/a | def test_header_encode_two_lines(self): |
---|
4328 | n/a | self._test_header_encode(b'hello\nworld', |
---|
4329 | n/a | '=?iso-8859-1?q?hello=0Aworld?=') |
---|
4330 | n/a | |
---|
4331 | n/a | def test_header_encode_non_ascii(self): |
---|
4332 | n/a | self._test_header_encode(b'hello\xc7there', |
---|
4333 | n/a | '=?iso-8859-1?q?hello=C7there?=') |
---|
4334 | n/a | |
---|
4335 | n/a | def test_header_encode_alt_charset(self): |
---|
4336 | n/a | self._test_header_encode(b'hello', '=?iso-8859-2?q?hello?=', |
---|
4337 | n/a | charset='iso-8859-2') |
---|
4338 | n/a | |
---|
4339 | n/a | def _test_header_decode(self, encoded_header, expected_decoded_header): |
---|
4340 | n/a | decoded_header = quoprimime.header_decode(encoded_header) |
---|
4341 | n/a | self.assertEqual(decoded_header, expected_decoded_header) |
---|
4342 | n/a | |
---|
4343 | n/a | def test_header_decode_null(self): |
---|
4344 | n/a | self._test_header_decode('', '') |
---|
4345 | n/a | |
---|
4346 | n/a | def test_header_decode_one_word(self): |
---|
4347 | n/a | self._test_header_decode('hello', 'hello') |
---|
4348 | n/a | |
---|
4349 | n/a | def test_header_decode_two_lines(self): |
---|
4350 | n/a | self._test_header_decode('hello=0Aworld', 'hello\nworld') |
---|
4351 | n/a | |
---|
4352 | n/a | def test_header_decode_non_ascii(self): |
---|
4353 | n/a | self._test_header_decode('hello=C7there', 'hello\xc7there') |
---|
4354 | n/a | |
---|
4355 | n/a | def test_header_decode_re_bug_18380(self): |
---|
4356 | n/a | # Issue 18380: Call re.sub with a positional argument for flags in the wrong position |
---|
4357 | n/a | self.assertEqual(quoprimime.header_decode('=30' * 257), '0' * 257) |
---|
4358 | n/a | |
---|
4359 | n/a | def _test_decode(self, encoded, expected_decoded, eol=None): |
---|
4360 | n/a | if eol is None: |
---|
4361 | n/a | decoded = quoprimime.decode(encoded) |
---|
4362 | n/a | else: |
---|
4363 | n/a | decoded = quoprimime.decode(encoded, eol=eol) |
---|
4364 | n/a | self.assertEqual(decoded, expected_decoded) |
---|
4365 | n/a | |
---|
4366 | n/a | def test_decode_null_word(self): |
---|
4367 | n/a | self._test_decode('', '') |
---|
4368 | n/a | |
---|
4369 | n/a | def test_decode_null_line_null_word(self): |
---|
4370 | n/a | self._test_decode('\r\n', '\n') |
---|
4371 | n/a | |
---|
4372 | n/a | def test_decode_one_word(self): |
---|
4373 | n/a | self._test_decode('hello', 'hello') |
---|
4374 | n/a | |
---|
4375 | n/a | def test_decode_one_word_eol(self): |
---|
4376 | n/a | self._test_decode('hello', 'hello', eol='X') |
---|
4377 | n/a | |
---|
4378 | n/a | def test_decode_one_line(self): |
---|
4379 | n/a | self._test_decode('hello\r\n', 'hello\n') |
---|
4380 | n/a | |
---|
4381 | n/a | def test_decode_one_line_lf(self): |
---|
4382 | n/a | self._test_decode('hello\n', 'hello\n') |
---|
4383 | n/a | |
---|
4384 | n/a | def test_decode_one_line_cr(self): |
---|
4385 | n/a | self._test_decode('hello\r', 'hello\n') |
---|
4386 | n/a | |
---|
4387 | n/a | def test_decode_one_line_nl(self): |
---|
4388 | n/a | self._test_decode('hello\n', 'helloX', eol='X') |
---|
4389 | n/a | |
---|
4390 | n/a | def test_decode_one_line_crnl(self): |
---|
4391 | n/a | self._test_decode('hello\r\n', 'helloX', eol='X') |
---|
4392 | n/a | |
---|
4393 | n/a | def test_decode_one_line_one_word(self): |
---|
4394 | n/a | self._test_decode('hello\r\nworld', 'hello\nworld') |
---|
4395 | n/a | |
---|
4396 | n/a | def test_decode_one_line_one_word_eol(self): |
---|
4397 | n/a | self._test_decode('hello\r\nworld', 'helloXworld', eol='X') |
---|
4398 | n/a | |
---|
4399 | n/a | def test_decode_two_lines(self): |
---|
4400 | n/a | self._test_decode('hello\r\nworld\r\n', 'hello\nworld\n') |
---|
4401 | n/a | |
---|
4402 | n/a | def test_decode_two_lines_eol(self): |
---|
4403 | n/a | self._test_decode('hello\r\nworld\r\n', 'helloXworldX', eol='X') |
---|
4404 | n/a | |
---|
4405 | n/a | def test_decode_one_long_line(self): |
---|
4406 | n/a | self._test_decode('Spam' * 250, 'Spam' * 250) |
---|
4407 | n/a | |
---|
4408 | n/a | def test_decode_one_space(self): |
---|
4409 | n/a | self._test_decode(' ', '') |
---|
4410 | n/a | |
---|
4411 | n/a | def test_decode_multiple_spaces(self): |
---|
4412 | n/a | self._test_decode(' ' * 5, '') |
---|
4413 | n/a | |
---|
4414 | n/a | def test_decode_one_line_trailing_spaces(self): |
---|
4415 | n/a | self._test_decode('hello \r\n', 'hello\n') |
---|
4416 | n/a | |
---|
4417 | n/a | def test_decode_two_lines_trailing_spaces(self): |
---|
4418 | n/a | self._test_decode('hello \r\nworld \r\n', 'hello\nworld\n') |
---|
4419 | n/a | |
---|
4420 | n/a | def test_decode_quoted_word(self): |
---|
4421 | n/a | self._test_decode('=22quoted=20words=22', '"quoted words"') |
---|
4422 | n/a | |
---|
4423 | n/a | def test_decode_uppercase_quoting(self): |
---|
4424 | n/a | self._test_decode('ab=CD=EF', 'ab\xcd\xef') |
---|
4425 | n/a | |
---|
4426 | n/a | def test_decode_lowercase_quoting(self): |
---|
4427 | n/a | self._test_decode('ab=cd=ef', 'ab\xcd\xef') |
---|
4428 | n/a | |
---|
4429 | n/a | def test_decode_soft_line_break(self): |
---|
4430 | n/a | self._test_decode('soft line=\r\nbreak', 'soft linebreak') |
---|
4431 | n/a | |
---|
4432 | n/a | def test_decode_false_quoting(self): |
---|
4433 | n/a | self._test_decode('A=1,B=A ==> A+B==2', 'A=1,B=A ==> A+B==2') |
---|
4434 | n/a | |
---|
4435 | n/a | def _test_encode(self, body, expected_encoded_body, maxlinelen=None, eol=None): |
---|
4436 | n/a | kwargs = {} |
---|
4437 | n/a | if maxlinelen is None: |
---|
4438 | n/a | # Use body_encode's default. |
---|
4439 | n/a | maxlinelen = 76 |
---|
4440 | n/a | else: |
---|
4441 | n/a | kwargs['maxlinelen'] = maxlinelen |
---|
4442 | n/a | if eol is None: |
---|
4443 | n/a | # Use body_encode's default. |
---|
4444 | n/a | eol = '\n' |
---|
4445 | n/a | else: |
---|
4446 | n/a | kwargs['eol'] = eol |
---|
4447 | n/a | encoded_body = quoprimime.body_encode(body, **kwargs) |
---|
4448 | n/a | self.assertEqual(encoded_body, expected_encoded_body) |
---|
4449 | n/a | if eol == '\n' or eol == '\r\n': |
---|
4450 | n/a | # We know how to split the result back into lines, so maxlinelen |
---|
4451 | n/a | # can be checked. |
---|
4452 | n/a | for line in encoded_body.splitlines(): |
---|
4453 | n/a | self.assertLessEqual(len(line), maxlinelen) |
---|
4454 | n/a | |
---|
4455 | n/a | def test_encode_null(self): |
---|
4456 | n/a | self._test_encode('', '') |
---|
4457 | n/a | |
---|
4458 | n/a | def test_encode_null_lines(self): |
---|
4459 | n/a | self._test_encode('\n\n', '\n\n') |
---|
4460 | n/a | |
---|
4461 | n/a | def test_encode_one_line(self): |
---|
4462 | n/a | self._test_encode('hello\n', 'hello\n') |
---|
4463 | n/a | |
---|
4464 | n/a | def test_encode_one_line_crlf(self): |
---|
4465 | n/a | self._test_encode('hello\r\n', 'hello\n') |
---|
4466 | n/a | |
---|
4467 | n/a | def test_encode_one_line_eol(self): |
---|
4468 | n/a | self._test_encode('hello\n', 'hello\r\n', eol='\r\n') |
---|
4469 | n/a | |
---|
4470 | n/a | def test_encode_one_line_eol_after_non_ascii(self): |
---|
4471 | n/a | # issue 20206; see changeset 0cf700464177 for why the encode/decode. |
---|
4472 | n/a | self._test_encode('hello\u03c5\n'.encode('utf-8').decode('latin1'), |
---|
4473 | n/a | 'hello=CF=85\r\n', eol='\r\n') |
---|
4474 | n/a | |
---|
4475 | n/a | def test_encode_one_space(self): |
---|
4476 | n/a | self._test_encode(' ', '=20') |
---|
4477 | n/a | |
---|
4478 | n/a | def test_encode_one_line_one_space(self): |
---|
4479 | n/a | self._test_encode(' \n', '=20\n') |
---|
4480 | n/a | |
---|
4481 | n/a | # XXX: body_encode() expect strings, but uses ord(char) from these strings |
---|
4482 | n/a | # to index into a 256-entry list. For code points above 255, this will fail. |
---|
4483 | n/a | # Should there be a check for 8-bit only ord() values in body, or at least |
---|
4484 | n/a | # a comment about the expected input? |
---|
4485 | n/a | |
---|
4486 | n/a | def test_encode_two_lines_one_space(self): |
---|
4487 | n/a | self._test_encode(' \n \n', '=20\n=20\n') |
---|
4488 | n/a | |
---|
4489 | n/a | def test_encode_one_word_trailing_spaces(self): |
---|
4490 | n/a | self._test_encode('hello ', 'hello =20') |
---|
4491 | n/a | |
---|
4492 | n/a | def test_encode_one_line_trailing_spaces(self): |
---|
4493 | n/a | self._test_encode('hello \n', 'hello =20\n') |
---|
4494 | n/a | |
---|
4495 | n/a | def test_encode_one_word_trailing_tab(self): |
---|
4496 | n/a | self._test_encode('hello \t', 'hello =09') |
---|
4497 | n/a | |
---|
4498 | n/a | def test_encode_one_line_trailing_tab(self): |
---|
4499 | n/a | self._test_encode('hello \t\n', 'hello =09\n') |
---|
4500 | n/a | |
---|
4501 | n/a | def test_encode_trailing_space_before_maxlinelen(self): |
---|
4502 | n/a | self._test_encode('abcd \n1234', 'abcd =\n\n1234', maxlinelen=6) |
---|
4503 | n/a | |
---|
4504 | n/a | def test_encode_trailing_space_at_maxlinelen(self): |
---|
4505 | n/a | self._test_encode('abcd \n1234', 'abcd=\n=20\n1234', maxlinelen=5) |
---|
4506 | n/a | |
---|
4507 | n/a | def test_encode_trailing_space_beyond_maxlinelen(self): |
---|
4508 | n/a | self._test_encode('abcd \n1234', 'abc=\nd=20\n1234', maxlinelen=4) |
---|
4509 | n/a | |
---|
4510 | n/a | def test_encode_whitespace_lines(self): |
---|
4511 | n/a | self._test_encode(' \n' * 5, '=20\n' * 5) |
---|
4512 | n/a | |
---|
4513 | n/a | def test_encode_quoted_equals(self): |
---|
4514 | n/a | self._test_encode('a = b', 'a =3D b') |
---|
4515 | n/a | |
---|
4516 | n/a | def test_encode_one_long_string(self): |
---|
4517 | n/a | self._test_encode('x' * 100, 'x' * 75 + '=\n' + 'x' * 25) |
---|
4518 | n/a | |
---|
4519 | n/a | def test_encode_one_long_line(self): |
---|
4520 | n/a | self._test_encode('x' * 100 + '\n', 'x' * 75 + '=\n' + 'x' * 25 + '\n') |
---|
4521 | n/a | |
---|
4522 | n/a | def test_encode_one_very_long_line(self): |
---|
4523 | n/a | self._test_encode('x' * 200 + '\n', |
---|
4524 | n/a | 2 * ('x' * 75 + '=\n') + 'x' * 50 + '\n') |
---|
4525 | n/a | |
---|
4526 | n/a | def test_encode_shortest_maxlinelen(self): |
---|
4527 | n/a | self._test_encode('=' * 5, '=3D=\n' * 4 + '=3D', maxlinelen=4) |
---|
4528 | n/a | |
---|
4529 | n/a | def test_encode_maxlinelen_too_small(self): |
---|
4530 | n/a | self.assertRaises(ValueError, self._test_encode, '', '', maxlinelen=3) |
---|
4531 | n/a | |
---|
4532 | n/a | def test_encode(self): |
---|
4533 | n/a | eq = self.assertEqual |
---|
4534 | n/a | eq(quoprimime.body_encode(''), '') |
---|
4535 | n/a | eq(quoprimime.body_encode('hello'), 'hello') |
---|
4536 | n/a | # Test the binary flag |
---|
4537 | n/a | eq(quoprimime.body_encode('hello\r\nworld'), 'hello\nworld') |
---|
4538 | n/a | # Test the maxlinelen arg |
---|
4539 | n/a | eq(quoprimime.body_encode('xxxx ' * 20, maxlinelen=40), """\ |
---|
4540 | n/a | xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx= |
---|
4541 | n/a | xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxx= |
---|
4542 | n/a | x xxxx xxxx xxxx xxxx=20""") |
---|
4543 | n/a | # Test the eol argument |
---|
4544 | n/a | eq(quoprimime.body_encode('xxxx ' * 20, maxlinelen=40, eol='\r\n'), |
---|
4545 | n/a | """\ |
---|
4546 | n/a | xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx=\r |
---|
4547 | n/a | xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxx=\r |
---|
4548 | n/a | x xxxx xxxx xxxx xxxx=20""") |
---|
4549 | n/a | eq(quoprimime.body_encode("""\ |
---|
4550 | n/a | one line |
---|
4551 | n/a | |
---|
4552 | n/a | two line"""), """\ |
---|
4553 | n/a | one line |
---|
4554 | n/a | |
---|
4555 | n/a | two line""") |
---|
4556 | n/a | |
---|
4557 | n/a | |
---|
4558 | n/a | |
---|
4559 | n/a | # Test the Charset class |
---|
4560 | n/a | class TestCharset(unittest.TestCase): |
---|
4561 | n/a | def tearDown(self): |
---|
4562 | n/a | from email import charset as CharsetModule |
---|
4563 | n/a | try: |
---|
4564 | n/a | del CharsetModule.CHARSETS['fake'] |
---|
4565 | n/a | except KeyError: |
---|
4566 | n/a | pass |
---|
4567 | n/a | |
---|
4568 | n/a | def test_codec_encodeable(self): |
---|
4569 | n/a | eq = self.assertEqual |
---|
4570 | n/a | # Make sure us-ascii = no Unicode conversion |
---|
4571 | n/a | c = Charset('us-ascii') |
---|
4572 | n/a | eq(c.header_encode('Hello World!'), 'Hello World!') |
---|
4573 | n/a | # Test 8-bit idempotency with us-ascii |
---|
4574 | n/a | s = '\xa4\xa2\xa4\xa4\xa4\xa6\xa4\xa8\xa4\xaa' |
---|
4575 | n/a | self.assertRaises(UnicodeError, c.header_encode, s) |
---|
4576 | n/a | c = Charset('utf-8') |
---|
4577 | n/a | eq(c.header_encode(s), '=?utf-8?b?wqTCosKkwqTCpMKmwqTCqMKkwqo=?=') |
---|
4578 | n/a | |
---|
4579 | n/a | def test_body_encode(self): |
---|
4580 | n/a | eq = self.assertEqual |
---|
4581 | n/a | # Try a charset with QP body encoding |
---|
4582 | n/a | c = Charset('iso-8859-1') |
---|
4583 | n/a | eq('hello w=F6rld', c.body_encode('hello w\xf6rld')) |
---|
4584 | n/a | # Try a charset with Base64 body encoding |
---|
4585 | n/a | c = Charset('utf-8') |
---|
4586 | n/a | eq('aGVsbG8gd29ybGQ=\n', c.body_encode(b'hello world')) |
---|
4587 | n/a | # Try a charset with None body encoding |
---|
4588 | n/a | c = Charset('us-ascii') |
---|
4589 | n/a | eq('hello world', c.body_encode('hello world')) |
---|
4590 | n/a | # Try the convert argument, where input codec != output codec |
---|
4591 | n/a | c = Charset('euc-jp') |
---|
4592 | n/a | # With apologies to Tokio Kikuchi ;) |
---|
4593 | n/a | # XXX FIXME |
---|
4594 | n/a | ## try: |
---|
4595 | n/a | ## eq('\x1b$B5FCO;~IW\x1b(B', |
---|
4596 | n/a | ## c.body_encode('\xb5\xc6\xc3\xcf\xbb\xfe\xc9\xd7')) |
---|
4597 | n/a | ## eq('\xb5\xc6\xc3\xcf\xbb\xfe\xc9\xd7', |
---|
4598 | n/a | ## c.body_encode('\xb5\xc6\xc3\xcf\xbb\xfe\xc9\xd7', False)) |
---|
4599 | n/a | ## except LookupError: |
---|
4600 | n/a | ## # We probably don't have the Japanese codecs installed |
---|
4601 | n/a | ## pass |
---|
4602 | n/a | # Testing SF bug #625509, which we have to fake, since there are no |
---|
4603 | n/a | # built-in encodings where the header encoding is QP but the body |
---|
4604 | n/a | # encoding is not. |
---|
4605 | n/a | from email import charset as CharsetModule |
---|
4606 | n/a | CharsetModule.add_charset('fake', CharsetModule.QP, None, 'utf-8') |
---|
4607 | n/a | c = Charset('fake') |
---|
4608 | n/a | eq('hello world', c.body_encode('hello world')) |
---|
4609 | n/a | |
---|
4610 | n/a | def test_unicode_charset_name(self): |
---|
4611 | n/a | charset = Charset('us-ascii') |
---|
4612 | n/a | self.assertEqual(str(charset), 'us-ascii') |
---|
4613 | n/a | self.assertRaises(errors.CharsetError, Charset, 'asc\xffii') |
---|
4614 | n/a | |
---|
4615 | n/a | |
---|
4616 | n/a | |
---|
4617 | n/a | # Test multilingual MIME headers. |
---|
4618 | n/a | class TestHeader(TestEmailBase): |
---|
4619 | n/a | def test_simple(self): |
---|
4620 | n/a | eq = self.ndiffAssertEqual |
---|
4621 | n/a | h = Header('Hello World!') |
---|
4622 | n/a | eq(h.encode(), 'Hello World!') |
---|
4623 | n/a | h.append(' Goodbye World!') |
---|
4624 | n/a | eq(h.encode(), 'Hello World! Goodbye World!') |
---|
4625 | n/a | |
---|
4626 | n/a | def test_simple_surprise(self): |
---|
4627 | n/a | eq = self.ndiffAssertEqual |
---|
4628 | n/a | h = Header('Hello World!') |
---|
4629 | n/a | eq(h.encode(), 'Hello World!') |
---|
4630 | n/a | h.append('Goodbye World!') |
---|
4631 | n/a | eq(h.encode(), 'Hello World! Goodbye World!') |
---|
4632 | n/a | |
---|
4633 | n/a | def test_header_needs_no_decoding(self): |
---|
4634 | n/a | h = 'no decoding needed' |
---|
4635 | n/a | self.assertEqual(decode_header(h), [(h, None)]) |
---|
4636 | n/a | |
---|
4637 | n/a | def test_long(self): |
---|
4638 | n/a | h = Header("I am the very model of a modern Major-General; I've information vegetable, animal, and mineral; I know the kings of England, and I quote the fights historical from Marathon to Waterloo, in order categorical; I'm very well acquainted, too, with matters mathematical; I understand equations, both the simple and quadratical; about binomial theorem I'm teeming with a lot o' news, with many cheerful facts about the square of the hypotenuse.", |
---|
4639 | n/a | maxlinelen=76) |
---|
4640 | n/a | for l in h.encode(splitchars=' ').split('\n '): |
---|
4641 | n/a | self.assertLessEqual(len(l), 76) |
---|
4642 | n/a | |
---|
4643 | n/a | def test_multilingual(self): |
---|
4644 | n/a | eq = self.ndiffAssertEqual |
---|
4645 | n/a | g = Charset("iso-8859-1") |
---|
4646 | n/a | cz = Charset("iso-8859-2") |
---|
4647 | n/a | utf8 = Charset("utf-8") |
---|
4648 | n/a | g_head = (b'Die Mieter treten hier ein werden mit einem ' |
---|
4649 | n/a | b'Foerderband komfortabel den Korridor entlang, ' |
---|
4650 | n/a | b'an s\xfcdl\xfcndischen Wandgem\xe4lden vorbei, ' |
---|
4651 | n/a | b'gegen die rotierenden Klingen bef\xf6rdert. ') |
---|
4652 | n/a | cz_head = (b'Finan\xe8ni metropole se hroutily pod tlakem jejich ' |
---|
4653 | n/a | b'd\xf9vtipu.. ') |
---|
4654 | n/a | utf8_head = ('\u6b63\u78ba\u306b\u8a00\u3046\u3068\u7ffb\u8a33\u306f' |
---|
4655 | n/a | '\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u4e00' |
---|
4656 | n/a | '\u90e8\u306f\u30c9\u30a4\u30c4\u8a9e\u3067\u3059\u304c' |
---|
4657 | n/a | '\u3001\u3042\u3068\u306f\u3067\u305f\u3089\u3081\u3067' |
---|
4658 | n/a | '\u3059\u3002\u5b9f\u969b\u306b\u306f\u300cWenn ist das ' |
---|
4659 | n/a | 'Nunstuck git und Slotermeyer? Ja! Beiherhund das Oder ' |
---|
4660 | n/a | 'die Flipperwaldt gersput.\u300d\u3068\u8a00\u3063\u3066' |
---|
4661 | n/a | '\u3044\u307e\u3059\u3002') |
---|
4662 | n/a | h = Header(g_head, g) |
---|
4663 | n/a | h.append(cz_head, cz) |
---|
4664 | n/a | h.append(utf8_head, utf8) |
---|
4665 | n/a | enc = h.encode(maxlinelen=76) |
---|
4666 | n/a | eq(enc, """\ |
---|
4667 | n/a | =?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_einem_Foerderband_kom?= |
---|
4668 | n/a | =?iso-8859-1?q?fortabel_den_Korridor_entlang=2C_an_s=FCdl=FCndischen_Wand?= |
---|
4669 | n/a | =?iso-8859-1?q?gem=E4lden_vorbei=2C_gegen_die_rotierenden_Klingen_bef=F6r?= |
---|
4670 | n/a | =?iso-8859-1?q?dert=2E_?= =?iso-8859-2?q?Finan=E8ni_metropole_se_hroutily?= |
---|
4671 | n/a | =?iso-8859-2?q?_pod_tlakem_jejich_d=F9vtipu=2E=2E_?= =?utf-8?b?5q2j56K6?= |
---|
4672 | n/a | =?utf-8?b?44Gr6KiA44GG44Go57+76Kiz44Gv44GV44KM44Gm44GE44G+44Gb44KT44CC?= |
---|
4673 | n/a | =?utf-8?b?5LiA6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM44CB44GC44Go44Gv44Gn?= |
---|
4674 | n/a | =?utf-8?b?44Gf44KJ44KB44Gn44GZ44CC5a6f6Zqb44Gr44Gv44CMV2VubiBpc3QgZGFz?= |
---|
4675 | n/a | =?utf-8?b?IE51bnN0dWNrIGdpdCB1bmQgU2xvdGVybWV5ZXI/IEphISBCZWloZXJodW5k?= |
---|
4676 | n/a | =?utf-8?b?IGRhcyBPZGVyIGRpZSBGbGlwcGVyd2FsZHQgZ2Vyc3B1dC7jgI3jgajoqIA=?= |
---|
4677 | n/a | =?utf-8?b?44Gj44Gm44GE44G+44GZ44CC?=""") |
---|
4678 | n/a | decoded = decode_header(enc) |
---|
4679 | n/a | eq(len(decoded), 3) |
---|
4680 | n/a | eq(decoded[0], (g_head, 'iso-8859-1')) |
---|
4681 | n/a | eq(decoded[1], (cz_head, 'iso-8859-2')) |
---|
4682 | n/a | eq(decoded[2], (utf8_head.encode('utf-8'), 'utf-8')) |
---|
4683 | n/a | ustr = str(h) |
---|
4684 | n/a | eq(ustr, |
---|
4685 | n/a | (b'Die Mieter treten hier ein werden mit einem Foerderband ' |
---|
4686 | n/a | b'komfortabel den Korridor entlang, an s\xc3\xbcdl\xc3\xbcndischen ' |
---|
4687 | n/a | b'Wandgem\xc3\xa4lden vorbei, gegen die rotierenden Klingen ' |
---|
4688 | n/a | b'bef\xc3\xb6rdert. Finan\xc4\x8dni metropole se hroutily pod ' |
---|
4689 | n/a | b'tlakem jejich d\xc5\xafvtipu.. \xe6\xad\xa3\xe7\xa2\xba\xe3\x81' |
---|
4690 | n/a | b'\xab\xe8\xa8\x80\xe3\x81\x86\xe3\x81\xa8\xe7\xbf\xbb\xe8\xa8\xb3' |
---|
4691 | n/a | b'\xe3\x81\xaf\xe3\x81\x95\xe3\x82\x8c\xe3\x81\xa6\xe3\x81\x84\xe3' |
---|
4692 | n/a | b'\x81\xbe\xe3\x81\x9b\xe3\x82\x93\xe3\x80\x82\xe4\xb8\x80\xe9\x83' |
---|
4693 | n/a | b'\xa8\xe3\x81\xaf\xe3\x83\x89\xe3\x82\xa4\xe3\x83\x84\xe8\xaa\x9e' |
---|
4694 | n/a | b'\xe3\x81\xa7\xe3\x81\x99\xe3\x81\x8c\xe3\x80\x81\xe3\x81\x82\xe3' |
---|
4695 | n/a | b'\x81\xa8\xe3\x81\xaf\xe3\x81\xa7\xe3\x81\x9f\xe3\x82\x89\xe3\x82' |
---|
4696 | n/a | b'\x81\xe3\x81\xa7\xe3\x81\x99\xe3\x80\x82\xe5\xae\x9f\xe9\x9a\x9b' |
---|
4697 | n/a | b'\xe3\x81\xab\xe3\x81\xaf\xe3\x80\x8cWenn ist das Nunstuck git ' |
---|
4698 | n/a | b'und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt ' |
---|
4699 | n/a | b'gersput.\xe3\x80\x8d\xe3\x81\xa8\xe8\xa8\x80\xe3\x81\xa3\xe3\x81' |
---|
4700 | n/a | b'\xa6\xe3\x81\x84\xe3\x81\xbe\xe3\x81\x99\xe3\x80\x82' |
---|
4701 | n/a | ).decode('utf-8')) |
---|
4702 | n/a | # Test make_header() |
---|
4703 | n/a | newh = make_header(decode_header(enc)) |
---|
4704 | n/a | eq(newh, h) |
---|
4705 | n/a | |
---|
4706 | n/a | def test_empty_header_encode(self): |
---|
4707 | n/a | h = Header() |
---|
4708 | n/a | self.assertEqual(h.encode(), '') |
---|
4709 | n/a | |
---|
4710 | n/a | def test_header_ctor_default_args(self): |
---|
4711 | n/a | eq = self.ndiffAssertEqual |
---|
4712 | n/a | h = Header() |
---|
4713 | n/a | eq(h, '') |
---|
4714 | n/a | h.append('foo', Charset('iso-8859-1')) |
---|
4715 | n/a | eq(h, 'foo') |
---|
4716 | n/a | |
---|
4717 | n/a | def test_explicit_maxlinelen(self): |
---|
4718 | n/a | eq = self.ndiffAssertEqual |
---|
4719 | n/a | hstr = ('A very long line that must get split to something other ' |
---|
4720 | n/a | 'than at the 76th character boundary to test the non-default ' |
---|
4721 | n/a | 'behavior') |
---|
4722 | n/a | h = Header(hstr) |
---|
4723 | n/a | eq(h.encode(), '''\ |
---|
4724 | n/a | A very long line that must get split to something other than at the 76th |
---|
4725 | n/a | character boundary to test the non-default behavior''') |
---|
4726 | n/a | eq(str(h), hstr) |
---|
4727 | n/a | h = Header(hstr, header_name='Subject') |
---|
4728 | n/a | eq(h.encode(), '''\ |
---|
4729 | n/a | A very long line that must get split to something other than at the |
---|
4730 | n/a | 76th character boundary to test the non-default behavior''') |
---|
4731 | n/a | eq(str(h), hstr) |
---|
4732 | n/a | h = Header(hstr, maxlinelen=1024, header_name='Subject') |
---|
4733 | n/a | eq(h.encode(), hstr) |
---|
4734 | n/a | eq(str(h), hstr) |
---|
4735 | n/a | |
---|
4736 | n/a | def test_quopri_splittable(self): |
---|
4737 | n/a | eq = self.ndiffAssertEqual |
---|
4738 | n/a | h = Header(charset='iso-8859-1', maxlinelen=20) |
---|
4739 | n/a | x = 'xxxx ' * 20 |
---|
4740 | n/a | h.append(x) |
---|
4741 | n/a | s = h.encode() |
---|
4742 | n/a | eq(s, """\ |
---|
4743 | n/a | =?iso-8859-1?q?xxx?= |
---|
4744 | n/a | =?iso-8859-1?q?x_?= |
---|
4745 | n/a | =?iso-8859-1?q?xx?= |
---|
4746 | n/a | =?iso-8859-1?q?xx?= |
---|
4747 | n/a | =?iso-8859-1?q?_x?= |
---|
4748 | n/a | =?iso-8859-1?q?xx?= |
---|
4749 | n/a | =?iso-8859-1?q?x_?= |
---|
4750 | n/a | =?iso-8859-1?q?xx?= |
---|
4751 | n/a | =?iso-8859-1?q?xx?= |
---|
4752 | n/a | =?iso-8859-1?q?_x?= |
---|
4753 | n/a | =?iso-8859-1?q?xx?= |
---|
4754 | n/a | =?iso-8859-1?q?x_?= |
---|
4755 | n/a | =?iso-8859-1?q?xx?= |
---|
4756 | n/a | =?iso-8859-1?q?xx?= |
---|
4757 | n/a | =?iso-8859-1?q?_x?= |
---|
4758 | n/a | =?iso-8859-1?q?xx?= |
---|
4759 | n/a | =?iso-8859-1?q?x_?= |
---|
4760 | n/a | =?iso-8859-1?q?xx?= |
---|
4761 | n/a | =?iso-8859-1?q?xx?= |
---|
4762 | n/a | =?iso-8859-1?q?_x?= |
---|
4763 | n/a | =?iso-8859-1?q?xx?= |
---|
4764 | n/a | =?iso-8859-1?q?x_?= |
---|
4765 | n/a | =?iso-8859-1?q?xx?= |
---|
4766 | n/a | =?iso-8859-1?q?xx?= |
---|
4767 | n/a | =?iso-8859-1?q?_x?= |
---|
4768 | n/a | =?iso-8859-1?q?xx?= |
---|
4769 | n/a | =?iso-8859-1?q?x_?= |
---|
4770 | n/a | =?iso-8859-1?q?xx?= |
---|
4771 | n/a | =?iso-8859-1?q?xx?= |
---|
4772 | n/a | =?iso-8859-1?q?_x?= |
---|
4773 | n/a | =?iso-8859-1?q?xx?= |
---|
4774 | n/a | =?iso-8859-1?q?x_?= |
---|
4775 | n/a | =?iso-8859-1?q?xx?= |
---|
4776 | n/a | =?iso-8859-1?q?xx?= |
---|
4777 | n/a | =?iso-8859-1?q?_x?= |
---|
4778 | n/a | =?iso-8859-1?q?xx?= |
---|
4779 | n/a | =?iso-8859-1?q?x_?= |
---|
4780 | n/a | =?iso-8859-1?q?xx?= |
---|
4781 | n/a | =?iso-8859-1?q?xx?= |
---|
4782 | n/a | =?iso-8859-1?q?_x?= |
---|
4783 | n/a | =?iso-8859-1?q?xx?= |
---|
4784 | n/a | =?iso-8859-1?q?x_?= |
---|
4785 | n/a | =?iso-8859-1?q?xx?= |
---|
4786 | n/a | =?iso-8859-1?q?xx?= |
---|
4787 | n/a | =?iso-8859-1?q?_x?= |
---|
4788 | n/a | =?iso-8859-1?q?xx?= |
---|
4789 | n/a | =?iso-8859-1?q?x_?= |
---|
4790 | n/a | =?iso-8859-1?q?xx?= |
---|
4791 | n/a | =?iso-8859-1?q?xx?= |
---|
4792 | n/a | =?iso-8859-1?q?_?=""") |
---|
4793 | n/a | eq(x, str(make_header(decode_header(s)))) |
---|
4794 | n/a | h = Header(charset='iso-8859-1', maxlinelen=40) |
---|
4795 | n/a | h.append('xxxx ' * 20) |
---|
4796 | n/a | s = h.encode() |
---|
4797 | n/a | eq(s, """\ |
---|
4798 | n/a | =?iso-8859-1?q?xxxx_xxxx_xxxx_xxxx_xxx?= |
---|
4799 | n/a | =?iso-8859-1?q?x_xxxx_xxxx_xxxx_xxxx_?= |
---|
4800 | n/a | =?iso-8859-1?q?xxxx_xxxx_xxxx_xxxx_xx?= |
---|
4801 | n/a | =?iso-8859-1?q?xx_xxxx_xxxx_xxxx_xxxx?= |
---|
4802 | n/a | =?iso-8859-1?q?_xxxx_xxxx_?=""") |
---|
4803 | n/a | eq(x, str(make_header(decode_header(s)))) |
---|
4804 | n/a | |
---|
4805 | n/a | def test_base64_splittable(self): |
---|
4806 | n/a | eq = self.ndiffAssertEqual |
---|
4807 | n/a | h = Header(charset='koi8-r', maxlinelen=20) |
---|
4808 | n/a | x = 'xxxx ' * 20 |
---|
4809 | n/a | h.append(x) |
---|
4810 | n/a | s = h.encode() |
---|
4811 | n/a | eq(s, """\ |
---|
4812 | n/a | =?koi8-r?b?eHh4?= |
---|
4813 | n/a | =?koi8-r?b?eCB4?= |
---|
4814 | n/a | =?koi8-r?b?eHh4?= |
---|
4815 | n/a | =?koi8-r?b?IHh4?= |
---|
4816 | n/a | =?koi8-r?b?eHgg?= |
---|
4817 | n/a | =?koi8-r?b?eHh4?= |
---|
4818 | n/a | =?koi8-r?b?eCB4?= |
---|
4819 | n/a | =?koi8-r?b?eHh4?= |
---|
4820 | n/a | =?koi8-r?b?IHh4?= |
---|
4821 | n/a | =?koi8-r?b?eHgg?= |
---|
4822 | n/a | =?koi8-r?b?eHh4?= |
---|
4823 | n/a | =?koi8-r?b?eCB4?= |
---|
4824 | n/a | =?koi8-r?b?eHh4?= |
---|
4825 | n/a | =?koi8-r?b?IHh4?= |
---|
4826 | n/a | =?koi8-r?b?eHgg?= |
---|
4827 | n/a | =?koi8-r?b?eHh4?= |
---|
4828 | n/a | =?koi8-r?b?eCB4?= |
---|
4829 | n/a | =?koi8-r?b?eHh4?= |
---|
4830 | n/a | =?koi8-r?b?IHh4?= |
---|
4831 | n/a | =?koi8-r?b?eHgg?= |
---|
4832 | n/a | =?koi8-r?b?eHh4?= |
---|
4833 | n/a | =?koi8-r?b?eCB4?= |
---|
4834 | n/a | =?koi8-r?b?eHh4?= |
---|
4835 | n/a | =?koi8-r?b?IHh4?= |
---|
4836 | n/a | =?koi8-r?b?eHgg?= |
---|
4837 | n/a | =?koi8-r?b?eHh4?= |
---|
4838 | n/a | =?koi8-r?b?eCB4?= |
---|
4839 | n/a | =?koi8-r?b?eHh4?= |
---|
4840 | n/a | =?koi8-r?b?IHh4?= |
---|
4841 | n/a | =?koi8-r?b?eHgg?= |
---|
4842 | n/a | =?koi8-r?b?eHh4?= |
---|
4843 | n/a | =?koi8-r?b?eCB4?= |
---|
4844 | n/a | =?koi8-r?b?eHh4?= |
---|
4845 | n/a | =?koi8-r?b?IA==?=""") |
---|
4846 | n/a | eq(x, str(make_header(decode_header(s)))) |
---|
4847 | n/a | h = Header(charset='koi8-r', maxlinelen=40) |
---|
4848 | n/a | h.append(x) |
---|
4849 | n/a | s = h.encode() |
---|
4850 | n/a | eq(s, """\ |
---|
4851 | n/a | =?koi8-r?b?eHh4eCB4eHh4IHh4eHggeHh4?= |
---|
4852 | n/a | =?koi8-r?b?eCB4eHh4IHh4eHggeHh4eCB4?= |
---|
4853 | n/a | =?koi8-r?b?eHh4IHh4eHggeHh4eCB4eHh4?= |
---|
4854 | n/a | =?koi8-r?b?IHh4eHggeHh4eCB4eHh4IHh4?= |
---|
4855 | n/a | =?koi8-r?b?eHggeHh4eCB4eHh4IHh4eHgg?= |
---|
4856 | n/a | =?koi8-r?b?eHh4eCB4eHh4IA==?=""") |
---|
4857 | n/a | eq(x, str(make_header(decode_header(s)))) |
---|
4858 | n/a | |
---|
4859 | n/a | def test_us_ascii_header(self): |
---|
4860 | n/a | eq = self.assertEqual |
---|
4861 | n/a | s = 'hello' |
---|
4862 | n/a | x = decode_header(s) |
---|
4863 | n/a | eq(x, [('hello', None)]) |
---|
4864 | n/a | h = make_header(x) |
---|
4865 | n/a | eq(s, h.encode()) |
---|
4866 | n/a | |
---|
4867 | n/a | def test_string_charset(self): |
---|
4868 | n/a | eq = self.assertEqual |
---|
4869 | n/a | h = Header() |
---|
4870 | n/a | h.append('hello', 'iso-8859-1') |
---|
4871 | n/a | eq(h, 'hello') |
---|
4872 | n/a | |
---|
4873 | n/a | ## def test_unicode_error(self): |
---|
4874 | n/a | ## raises = self.assertRaises |
---|
4875 | n/a | ## raises(UnicodeError, Header, u'[P\xf6stal]', 'us-ascii') |
---|
4876 | n/a | ## raises(UnicodeError, Header, '[P\xf6stal]', 'us-ascii') |
---|
4877 | n/a | ## h = Header() |
---|
4878 | n/a | ## raises(UnicodeError, h.append, u'[P\xf6stal]', 'us-ascii') |
---|
4879 | n/a | ## raises(UnicodeError, h.append, '[P\xf6stal]', 'us-ascii') |
---|
4880 | n/a | ## raises(UnicodeError, Header, u'\u83ca\u5730\u6642\u592b', 'iso-8859-1') |
---|
4881 | n/a | |
---|
4882 | n/a | def test_utf8_shortest(self): |
---|
4883 | n/a | eq = self.assertEqual |
---|
4884 | n/a | h = Header('p\xf6stal', 'utf-8') |
---|
4885 | n/a | eq(h.encode(), '=?utf-8?q?p=C3=B6stal?=') |
---|
4886 | n/a | h = Header('\u83ca\u5730\u6642\u592b', 'utf-8') |
---|
4887 | n/a | eq(h.encode(), '=?utf-8?b?6I+K5Zyw5pmC5aSr?=') |
---|
4888 | n/a | |
---|
4889 | n/a | def test_bad_8bit_header(self): |
---|
4890 | n/a | raises = self.assertRaises |
---|
4891 | n/a | eq = self.assertEqual |
---|
4892 | n/a | x = b'Ynwp4dUEbay Auction Semiar- No Charge \x96 Earn Big' |
---|
4893 | n/a | raises(UnicodeError, Header, x) |
---|
4894 | n/a | h = Header() |
---|
4895 | n/a | raises(UnicodeError, h.append, x) |
---|
4896 | n/a | e = x.decode('utf-8', 'replace') |
---|
4897 | n/a | eq(str(Header(x, errors='replace')), e) |
---|
4898 | n/a | h.append(x, errors='replace') |
---|
4899 | n/a | eq(str(h), e) |
---|
4900 | n/a | |
---|
4901 | n/a | def test_escaped_8bit_header(self): |
---|
4902 | n/a | x = b'Ynwp4dUEbay Auction Semiar- No Charge \x96 Earn Big' |
---|
4903 | n/a | e = x.decode('ascii', 'surrogateescape') |
---|
4904 | n/a | h = Header(e, charset=email.charset.UNKNOWN8BIT) |
---|
4905 | n/a | self.assertEqual(str(h), |
---|
4906 | n/a | 'Ynwp4dUEbay Auction Semiar- No Charge \uFFFD Earn Big') |
---|
4907 | n/a | self.assertEqual(email.header.decode_header(h), [(x, 'unknown-8bit')]) |
---|
4908 | n/a | |
---|
4909 | n/a | def test_header_handles_binary_unknown8bit(self): |
---|
4910 | n/a | x = b'Ynwp4dUEbay Auction Semiar- No Charge \x96 Earn Big' |
---|
4911 | n/a | h = Header(x, charset=email.charset.UNKNOWN8BIT) |
---|
4912 | n/a | self.assertEqual(str(h), |
---|
4913 | n/a | 'Ynwp4dUEbay Auction Semiar- No Charge \uFFFD Earn Big') |
---|
4914 | n/a | self.assertEqual(email.header.decode_header(h), [(x, 'unknown-8bit')]) |
---|
4915 | n/a | |
---|
4916 | n/a | def test_make_header_handles_binary_unknown8bit(self): |
---|
4917 | n/a | x = b'Ynwp4dUEbay Auction Semiar- No Charge \x96 Earn Big' |
---|
4918 | n/a | h = Header(x, charset=email.charset.UNKNOWN8BIT) |
---|
4919 | n/a | h2 = email.header.make_header(email.header.decode_header(h)) |
---|
4920 | n/a | self.assertEqual(str(h2), |
---|
4921 | n/a | 'Ynwp4dUEbay Auction Semiar- No Charge \uFFFD Earn Big') |
---|
4922 | n/a | self.assertEqual(email.header.decode_header(h2), [(x, 'unknown-8bit')]) |
---|
4923 | n/a | |
---|
4924 | n/a | def test_modify_returned_list_does_not_change_header(self): |
---|
4925 | n/a | h = Header('test') |
---|
4926 | n/a | chunks = email.header.decode_header(h) |
---|
4927 | n/a | chunks.append(('ascii', 'test2')) |
---|
4928 | n/a | self.assertEqual(str(h), 'test') |
---|
4929 | n/a | |
---|
4930 | n/a | def test_encoded_adjacent_nonencoded(self): |
---|
4931 | n/a | eq = self.assertEqual |
---|
4932 | n/a | h = Header() |
---|
4933 | n/a | h.append('hello', 'iso-8859-1') |
---|
4934 | n/a | h.append('world') |
---|
4935 | n/a | s = h.encode() |
---|
4936 | n/a | eq(s, '=?iso-8859-1?q?hello?= world') |
---|
4937 | n/a | h = make_header(decode_header(s)) |
---|
4938 | n/a | eq(h.encode(), s) |
---|
4939 | n/a | |
---|
4940 | n/a | def test_whitespace_keeper(self): |
---|
4941 | n/a | eq = self.assertEqual |
---|
4942 | n/a | s = 'Subject: =?koi8-r?b?8NLP18XSy8EgzsEgxsnOwczYztk=?= =?koi8-r?q?=CA?= zz.' |
---|
4943 | n/a | parts = decode_header(s) |
---|
4944 | n/a | eq(parts, [(b'Subject: ', None), (b'\xf0\xd2\xcf\xd7\xc5\xd2\xcb\xc1 \xce\xc1 \xc6\xc9\xce\xc1\xcc\xd8\xce\xd9\xca', 'koi8-r'), (b' zz.', None)]) |
---|
4945 | n/a | hdr = make_header(parts) |
---|
4946 | n/a | eq(hdr.encode(), |
---|
4947 | n/a | 'Subject: =?koi8-r?b?8NLP18XSy8EgzsEgxsnOwczYztnK?= zz.') |
---|
4948 | n/a | |
---|
4949 | n/a | def test_broken_base64_header(self): |
---|
4950 | n/a | raises = self.assertRaises |
---|
4951 | n/a | s = 'Subject: =?EUC-KR?B?CSixpLDtKSC/7Liuvsax4iC6uLmwMcijIKHaILzSwd/H0SC8+LCjwLsgv7W/+Mj3I ?=' |
---|
4952 | n/a | raises(errors.HeaderParseError, decode_header, s) |
---|
4953 | n/a | |
---|
4954 | n/a | def test_shift_jis_charset(self): |
---|
4955 | n/a | h = Header('æ', charset='shift_jis') |
---|
4956 | n/a | self.assertEqual(h.encode(), '=?iso-2022-jp?b?GyRCSjgbKEI=?=') |
---|
4957 | n/a | |
---|
4958 | n/a | def test_flatten_header_with_no_value(self): |
---|
4959 | n/a | # Issue 11401 (regression from email 4.x) Note that the space after |
---|
4960 | n/a | # the header doesn't reflect the input, but this is also the way |
---|
4961 | n/a | # email 4.x behaved. At some point it would be nice to fix that. |
---|
4962 | n/a | msg = email.message_from_string("EmptyHeader:") |
---|
4963 | n/a | self.assertEqual(str(msg), "EmptyHeader: \n\n") |
---|
4964 | n/a | |
---|
4965 | n/a | def test_encode_preserves_leading_ws_on_value(self): |
---|
4966 | n/a | msg = Message() |
---|
4967 | n/a | msg['SomeHeader'] = ' value with leading ws' |
---|
4968 | n/a | self.assertEqual(str(msg), "SomeHeader: value with leading ws\n\n") |
---|
4969 | n/a | |
---|
4970 | n/a | |
---|
4971 | n/a | |
---|
4972 | n/a | # Test RFC 2231 header parameters (en/de)coding |
---|
4973 | n/a | class TestRFC2231(TestEmailBase): |
---|
4974 | n/a | |
---|
4975 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_encoded_with_double_quotes |
---|
4976 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_single_quote_inside_double_quotes |
---|
4977 | n/a | def test_get_param(self): |
---|
4978 | n/a | eq = self.assertEqual |
---|
4979 | n/a | msg = self._msgobj('msg_29.txt') |
---|
4980 | n/a | eq(msg.get_param('title'), |
---|
4981 | n/a | ('us-ascii', 'en', 'This is even more ***fun*** isn\'t it!')) |
---|
4982 | n/a | eq(msg.get_param('title', unquote=False), |
---|
4983 | n/a | ('us-ascii', 'en', '"This is even more ***fun*** isn\'t it!"')) |
---|
4984 | n/a | |
---|
4985 | n/a | def test_set_param(self): |
---|
4986 | n/a | eq = self.ndiffAssertEqual |
---|
4987 | n/a | msg = Message() |
---|
4988 | n/a | msg.set_param('title', 'This is even more ***fun*** isn\'t it!', |
---|
4989 | n/a | charset='us-ascii') |
---|
4990 | n/a | eq(msg.get_param('title'), |
---|
4991 | n/a | ('us-ascii', '', 'This is even more ***fun*** isn\'t it!')) |
---|
4992 | n/a | msg.set_param('title', 'This is even more ***fun*** isn\'t it!', |
---|
4993 | n/a | charset='us-ascii', language='en') |
---|
4994 | n/a | eq(msg.get_param('title'), |
---|
4995 | n/a | ('us-ascii', 'en', 'This is even more ***fun*** isn\'t it!')) |
---|
4996 | n/a | msg = self._msgobj('msg_01.txt') |
---|
4997 | n/a | msg.set_param('title', 'This is even more ***fun*** isn\'t it!', |
---|
4998 | n/a | charset='us-ascii', language='en') |
---|
4999 | n/a | eq(msg.as_string(maxheaderlen=78), """\ |
---|
5000 | n/a | Return-Path: <bbb@zzz.org> |
---|
5001 | n/a | Delivered-To: bbb@zzz.org |
---|
5002 | n/a | Received: by mail.zzz.org (Postfix, from userid 889) |
---|
5003 | n/a | \tid 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) |
---|
5004 | n/a | MIME-Version: 1.0 |
---|
5005 | n/a | Content-Transfer-Encoding: 7bit |
---|
5006 | n/a | Message-ID: <15090.61304.110929.45684@aaa.zzz.org> |
---|
5007 | n/a | From: bbb@ddd.com (John X. Doe) |
---|
5008 | n/a | To: bbb@zzz.org |
---|
5009 | n/a | Subject: This is a test message |
---|
5010 | n/a | Date: Fri, 4 May 2001 14:05:44 -0400 |
---|
5011 | n/a | Content-Type: text/plain; charset=us-ascii; |
---|
5012 | n/a | title*=us-ascii'en'This%20is%20even%20more%20%2A%2A%2Afun%2A%2A%2A%20isn%27t%20it%21 |
---|
5013 | n/a | |
---|
5014 | n/a | |
---|
5015 | n/a | Hi, |
---|
5016 | n/a | |
---|
5017 | n/a | Do you like this message? |
---|
5018 | n/a | |
---|
5019 | n/a | -Me |
---|
5020 | n/a | """) |
---|
5021 | n/a | |
---|
5022 | n/a | def test_set_param_requote(self): |
---|
5023 | n/a | msg = Message() |
---|
5024 | n/a | msg.set_param('title', 'foo') |
---|
5025 | n/a | self.assertEqual(msg['content-type'], 'text/plain; title="foo"') |
---|
5026 | n/a | msg.set_param('title', 'bar', requote=False) |
---|
5027 | n/a | self.assertEqual(msg['content-type'], 'text/plain; title=bar') |
---|
5028 | n/a | # tspecial is still quoted. |
---|
5029 | n/a | msg.set_param('title', "(bar)bell", requote=False) |
---|
5030 | n/a | self.assertEqual(msg['content-type'], 'text/plain; title="(bar)bell"') |
---|
5031 | n/a | |
---|
5032 | n/a | def test_del_param(self): |
---|
5033 | n/a | eq = self.ndiffAssertEqual |
---|
5034 | n/a | msg = self._msgobj('msg_01.txt') |
---|
5035 | n/a | msg.set_param('foo', 'bar', charset='us-ascii', language='en') |
---|
5036 | n/a | msg.set_param('title', 'This is even more ***fun*** isn\'t it!', |
---|
5037 | n/a | charset='us-ascii', language='en') |
---|
5038 | n/a | msg.del_param('foo', header='Content-Type') |
---|
5039 | n/a | eq(msg.as_string(maxheaderlen=78), """\ |
---|
5040 | n/a | Return-Path: <bbb@zzz.org> |
---|
5041 | n/a | Delivered-To: bbb@zzz.org |
---|
5042 | n/a | Received: by mail.zzz.org (Postfix, from userid 889) |
---|
5043 | n/a | \tid 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) |
---|
5044 | n/a | MIME-Version: 1.0 |
---|
5045 | n/a | Content-Transfer-Encoding: 7bit |
---|
5046 | n/a | Message-ID: <15090.61304.110929.45684@aaa.zzz.org> |
---|
5047 | n/a | From: bbb@ddd.com (John X. Doe) |
---|
5048 | n/a | To: bbb@zzz.org |
---|
5049 | n/a | Subject: This is a test message |
---|
5050 | n/a | Date: Fri, 4 May 2001 14:05:44 -0400 |
---|
5051 | n/a | Content-Type: text/plain; charset="us-ascii"; |
---|
5052 | n/a | title*=us-ascii'en'This%20is%20even%20more%20%2A%2A%2Afun%2A%2A%2A%20isn%27t%20it%21 |
---|
5053 | n/a | |
---|
5054 | n/a | |
---|
5055 | n/a | Hi, |
---|
5056 | n/a | |
---|
5057 | n/a | Do you like this message? |
---|
5058 | n/a | |
---|
5059 | n/a | -Me |
---|
5060 | n/a | """) |
---|
5061 | n/a | |
---|
5062 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_encoded_charset |
---|
5063 | n/a | # I changed the charset name, though, because the one in the file isn't |
---|
5064 | n/a | # a legal charset name. Should add a test for an illegal charset. |
---|
5065 | n/a | def test_rfc2231_get_content_charset(self): |
---|
5066 | n/a | eq = self.assertEqual |
---|
5067 | n/a | msg = self._msgobj('msg_32.txt') |
---|
5068 | n/a | eq(msg.get_content_charset(), 'us-ascii') |
---|
5069 | n/a | |
---|
5070 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_encoded_no_double_quotes |
---|
5071 | n/a | def test_rfc2231_parse_rfc_quoting(self): |
---|
5072 | n/a | m = textwrap.dedent('''\ |
---|
5073 | n/a | Content-Disposition: inline; |
---|
5074 | n/a | \tfilename*0*=''This%20is%20even%20more%20; |
---|
5075 | n/a | \tfilename*1*=%2A%2A%2Afun%2A%2A%2A%20; |
---|
5076 | n/a | \tfilename*2="is it not.pdf" |
---|
5077 | n/a | |
---|
5078 | n/a | ''') |
---|
5079 | n/a | msg = email.message_from_string(m) |
---|
5080 | n/a | self.assertEqual(msg.get_filename(), |
---|
5081 | n/a | 'This is even more ***fun*** is it not.pdf') |
---|
5082 | n/a | self.assertEqual(m, msg.as_string()) |
---|
5083 | n/a | |
---|
5084 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_encoded_with_double_quotes |
---|
5085 | n/a | def test_rfc2231_parse_extra_quoting(self): |
---|
5086 | n/a | m = textwrap.dedent('''\ |
---|
5087 | n/a | Content-Disposition: inline; |
---|
5088 | n/a | \tfilename*0*="''This%20is%20even%20more%20"; |
---|
5089 | n/a | \tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; |
---|
5090 | n/a | \tfilename*2="is it not.pdf" |
---|
5091 | n/a | |
---|
5092 | n/a | ''') |
---|
5093 | n/a | msg = email.message_from_string(m) |
---|
5094 | n/a | self.assertEqual(msg.get_filename(), |
---|
5095 | n/a | 'This is even more ***fun*** is it not.pdf') |
---|
5096 | n/a | self.assertEqual(m, msg.as_string()) |
---|
5097 | n/a | |
---|
5098 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_no_language_or_charset |
---|
5099 | n/a | # but new test uses *0* because otherwise lang/charset is not valid. |
---|
5100 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_segmented_normal_values |
---|
5101 | n/a | def test_rfc2231_no_language_or_charset(self): |
---|
5102 | n/a | m = '''\ |
---|
5103 | n/a | Content-Transfer-Encoding: 8bit |
---|
5104 | n/a | Content-Disposition: inline; filename="file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOCAL_20SETTINGS_TEMP_nsmail.htm" |
---|
5105 | n/a | Content-Type: text/html; NAME*0=file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOCAL_20SETTINGS_TEM; NAME*1=P_nsmail.htm |
---|
5106 | n/a | |
---|
5107 | n/a | ''' |
---|
5108 | n/a | msg = email.message_from_string(m) |
---|
5109 | n/a | param = msg.get_param('NAME') |
---|
5110 | n/a | self.assertNotIsInstance(param, tuple) |
---|
5111 | n/a | self.assertEqual( |
---|
5112 | n/a | param, |
---|
5113 | n/a | 'file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOCAL_20SETTINGS_TEMP_nsmail.htm') |
---|
5114 | n/a | |
---|
5115 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_encoded_no_charset |
---|
5116 | n/a | def test_rfc2231_no_language_or_charset_in_filename(self): |
---|
5117 | n/a | m = '''\ |
---|
5118 | n/a | Content-Disposition: inline; |
---|
5119 | n/a | \tfilename*0*="''This%20is%20even%20more%20"; |
---|
5120 | n/a | \tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; |
---|
5121 | n/a | \tfilename*2="is it not.pdf" |
---|
5122 | n/a | |
---|
5123 | n/a | ''' |
---|
5124 | n/a | msg = email.message_from_string(m) |
---|
5125 | n/a | self.assertEqual(msg.get_filename(), |
---|
5126 | n/a | 'This is even more ***fun*** is it not.pdf') |
---|
5127 | n/a | |
---|
5128 | n/a | # Duplicate of previous test? |
---|
5129 | n/a | def test_rfc2231_no_language_or_charset_in_filename_encoded(self): |
---|
5130 | n/a | m = '''\ |
---|
5131 | n/a | Content-Disposition: inline; |
---|
5132 | n/a | \tfilename*0*="''This%20is%20even%20more%20"; |
---|
5133 | n/a | \tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; |
---|
5134 | n/a | \tfilename*2="is it not.pdf" |
---|
5135 | n/a | |
---|
5136 | n/a | ''' |
---|
5137 | n/a | msg = email.message_from_string(m) |
---|
5138 | n/a | self.assertEqual(msg.get_filename(), |
---|
5139 | n/a | 'This is even more ***fun*** is it not.pdf') |
---|
5140 | n/a | |
---|
5141 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_partly_encoded, |
---|
5142 | n/a | # but the test below is wrong (the first part should be decoded). |
---|
5143 | n/a | def test_rfc2231_partly_encoded(self): |
---|
5144 | n/a | m = '''\ |
---|
5145 | n/a | Content-Disposition: inline; |
---|
5146 | n/a | \tfilename*0="''This%20is%20even%20more%20"; |
---|
5147 | n/a | \tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; |
---|
5148 | n/a | \tfilename*2="is it not.pdf" |
---|
5149 | n/a | |
---|
5150 | n/a | ''' |
---|
5151 | n/a | msg = email.message_from_string(m) |
---|
5152 | n/a | self.assertEqual( |
---|
5153 | n/a | msg.get_filename(), |
---|
5154 | n/a | 'This%20is%20even%20more%20***fun*** is it not.pdf') |
---|
5155 | n/a | |
---|
5156 | n/a | def test_rfc2231_partly_nonencoded(self): |
---|
5157 | n/a | m = '''\ |
---|
5158 | n/a | Content-Disposition: inline; |
---|
5159 | n/a | \tfilename*0="This%20is%20even%20more%20"; |
---|
5160 | n/a | \tfilename*1="%2A%2A%2Afun%2A%2A%2A%20"; |
---|
5161 | n/a | \tfilename*2="is it not.pdf" |
---|
5162 | n/a | |
---|
5163 | n/a | ''' |
---|
5164 | n/a | msg = email.message_from_string(m) |
---|
5165 | n/a | self.assertEqual( |
---|
5166 | n/a | msg.get_filename(), |
---|
5167 | n/a | 'This%20is%20even%20more%20%2A%2A%2Afun%2A%2A%2A%20is it not.pdf') |
---|
5168 | n/a | |
---|
5169 | n/a | def test_rfc2231_no_language_or_charset_in_boundary(self): |
---|
5170 | n/a | m = '''\ |
---|
5171 | n/a | Content-Type: multipart/alternative; |
---|
5172 | n/a | \tboundary*0*="''This%20is%20even%20more%20"; |
---|
5173 | n/a | \tboundary*1*="%2A%2A%2Afun%2A%2A%2A%20"; |
---|
5174 | n/a | \tboundary*2="is it not.pdf" |
---|
5175 | n/a | |
---|
5176 | n/a | ''' |
---|
5177 | n/a | msg = email.message_from_string(m) |
---|
5178 | n/a | self.assertEqual(msg.get_boundary(), |
---|
5179 | n/a | 'This is even more ***fun*** is it not.pdf') |
---|
5180 | n/a | |
---|
5181 | n/a | def test_rfc2231_no_language_or_charset_in_charset(self): |
---|
5182 | n/a | # This is a nonsensical charset value, but tests the code anyway |
---|
5183 | n/a | m = '''\ |
---|
5184 | n/a | Content-Type: text/plain; |
---|
5185 | n/a | \tcharset*0*="This%20is%20even%20more%20"; |
---|
5186 | n/a | \tcharset*1*="%2A%2A%2Afun%2A%2A%2A%20"; |
---|
5187 | n/a | \tcharset*2="is it not.pdf" |
---|
5188 | n/a | |
---|
5189 | n/a | ''' |
---|
5190 | n/a | msg = email.message_from_string(m) |
---|
5191 | n/a | self.assertEqual(msg.get_content_charset(), |
---|
5192 | n/a | 'this is even more ***fun*** is it not.pdf') |
---|
5193 | n/a | |
---|
5194 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_unknown_charset_treated_as_ascii |
---|
5195 | n/a | def test_rfc2231_bad_encoding_in_filename(self): |
---|
5196 | n/a | m = '''\ |
---|
5197 | n/a | Content-Disposition: inline; |
---|
5198 | n/a | \tfilename*0*="bogus'xx'This%20is%20even%20more%20"; |
---|
5199 | n/a | \tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; |
---|
5200 | n/a | \tfilename*2="is it not.pdf" |
---|
5201 | n/a | |
---|
5202 | n/a | ''' |
---|
5203 | n/a | msg = email.message_from_string(m) |
---|
5204 | n/a | self.assertEqual(msg.get_filename(), |
---|
5205 | n/a | 'This is even more ***fun*** is it not.pdf') |
---|
5206 | n/a | |
---|
5207 | n/a | def test_rfc2231_bad_encoding_in_charset(self): |
---|
5208 | n/a | m = """\ |
---|
5209 | n/a | Content-Type: text/plain; charset*=bogus''utf-8%E2%80%9D |
---|
5210 | n/a | |
---|
5211 | n/a | """ |
---|
5212 | n/a | msg = email.message_from_string(m) |
---|
5213 | n/a | # This should return None because non-ascii characters in the charset |
---|
5214 | n/a | # are not allowed. |
---|
5215 | n/a | self.assertEqual(msg.get_content_charset(), None) |
---|
5216 | n/a | |
---|
5217 | n/a | def test_rfc2231_bad_character_in_charset(self): |
---|
5218 | n/a | m = """\ |
---|
5219 | n/a | Content-Type: text/plain; charset*=ascii''utf-8%E2%80%9D |
---|
5220 | n/a | |
---|
5221 | n/a | """ |
---|
5222 | n/a | msg = email.message_from_string(m) |
---|
5223 | n/a | # This should return None because non-ascii characters in the charset |
---|
5224 | n/a | # are not allowed. |
---|
5225 | n/a | self.assertEqual(msg.get_content_charset(), None) |
---|
5226 | n/a | |
---|
5227 | n/a | def test_rfc2231_bad_character_in_filename(self): |
---|
5228 | n/a | m = '''\ |
---|
5229 | n/a | Content-Disposition: inline; |
---|
5230 | n/a | \tfilename*0*="ascii'xx'This%20is%20even%20more%20"; |
---|
5231 | n/a | \tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; |
---|
5232 | n/a | \tfilename*2*="is it not.pdf%E2" |
---|
5233 | n/a | |
---|
5234 | n/a | ''' |
---|
5235 | n/a | msg = email.message_from_string(m) |
---|
5236 | n/a | self.assertEqual(msg.get_filename(), |
---|
5237 | n/a | 'This is even more ***fun*** is it not.pdf\ufffd') |
---|
5238 | n/a | |
---|
5239 | n/a | def test_rfc2231_unknown_encoding(self): |
---|
5240 | n/a | m = """\ |
---|
5241 | n/a | Content-Transfer-Encoding: 8bit |
---|
5242 | n/a | Content-Disposition: inline; filename*=X-UNKNOWN''myfile.txt |
---|
5243 | n/a | |
---|
5244 | n/a | """ |
---|
5245 | n/a | msg = email.message_from_string(m) |
---|
5246 | n/a | self.assertEqual(msg.get_filename(), 'myfile.txt') |
---|
5247 | n/a | |
---|
5248 | n/a | def test_rfc2231_single_tick_in_filename_extended(self): |
---|
5249 | n/a | eq = self.assertEqual |
---|
5250 | n/a | m = """\ |
---|
5251 | n/a | Content-Type: application/x-foo; |
---|
5252 | n/a | \tname*0*=\"Frank's\"; name*1*=\" Document\" |
---|
5253 | n/a | |
---|
5254 | n/a | """ |
---|
5255 | n/a | msg = email.message_from_string(m) |
---|
5256 | n/a | charset, language, s = msg.get_param('name') |
---|
5257 | n/a | eq(charset, None) |
---|
5258 | n/a | eq(language, None) |
---|
5259 | n/a | eq(s, "Frank's Document") |
---|
5260 | n/a | |
---|
5261 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_single_quote_inside_double_quotes |
---|
5262 | n/a | def test_rfc2231_single_tick_in_filename(self): |
---|
5263 | n/a | m = """\ |
---|
5264 | n/a | Content-Type: application/x-foo; name*0=\"Frank's\"; name*1=\" Document\" |
---|
5265 | n/a | |
---|
5266 | n/a | """ |
---|
5267 | n/a | msg = email.message_from_string(m) |
---|
5268 | n/a | param = msg.get_param('name') |
---|
5269 | n/a | self.assertNotIsInstance(param, tuple) |
---|
5270 | n/a | self.assertEqual(param, "Frank's Document") |
---|
5271 | n/a | |
---|
5272 | n/a | def test_rfc2231_missing_tick(self): |
---|
5273 | n/a | m = '''\ |
---|
5274 | n/a | Content-Disposition: inline; |
---|
5275 | n/a | \tfilename*0*="'This%20is%20broken"; |
---|
5276 | n/a | ''' |
---|
5277 | n/a | msg = email.message_from_string(m) |
---|
5278 | n/a | self.assertEqual( |
---|
5279 | n/a | msg.get_filename(), |
---|
5280 | n/a | "'This is broken") |
---|
5281 | n/a | |
---|
5282 | n/a | def test_rfc2231_missing_tick_with_encoded_non_ascii(self): |
---|
5283 | n/a | m = '''\ |
---|
5284 | n/a | Content-Disposition: inline; |
---|
5285 | n/a | \tfilename*0*="'This%20is%E2broken"; |
---|
5286 | n/a | ''' |
---|
5287 | n/a | msg = email.message_from_string(m) |
---|
5288 | n/a | self.assertEqual( |
---|
5289 | n/a | msg.get_filename(), |
---|
5290 | n/a | "'This is\ufffdbroken") |
---|
5291 | n/a | |
---|
5292 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_single_quote_in_value_with_charset_and_lang |
---|
5293 | n/a | def test_rfc2231_tick_attack_extended(self): |
---|
5294 | n/a | eq = self.assertEqual |
---|
5295 | n/a | m = """\ |
---|
5296 | n/a | Content-Type: application/x-foo; |
---|
5297 | n/a | \tname*0*=\"us-ascii'en-us'Frank's\"; name*1*=\" Document\" |
---|
5298 | n/a | |
---|
5299 | n/a | """ |
---|
5300 | n/a | msg = email.message_from_string(m) |
---|
5301 | n/a | charset, language, s = msg.get_param('name') |
---|
5302 | n/a | eq(charset, 'us-ascii') |
---|
5303 | n/a | eq(language, 'en-us') |
---|
5304 | n/a | eq(s, "Frank's Document") |
---|
5305 | n/a | |
---|
5306 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_single_quote_in_non_encoded_value |
---|
5307 | n/a | def test_rfc2231_tick_attack(self): |
---|
5308 | n/a | m = """\ |
---|
5309 | n/a | Content-Type: application/x-foo; |
---|
5310 | n/a | \tname*0=\"us-ascii'en-us'Frank's\"; name*1=\" Document\" |
---|
5311 | n/a | |
---|
5312 | n/a | """ |
---|
5313 | n/a | msg = email.message_from_string(m) |
---|
5314 | n/a | param = msg.get_param('name') |
---|
5315 | n/a | self.assertNotIsInstance(param, tuple) |
---|
5316 | n/a | self.assertEqual(param, "us-ascii'en-us'Frank's Document") |
---|
5317 | n/a | |
---|
5318 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_single_quotes_inside_quotes |
---|
5319 | n/a | def test_rfc2231_no_extended_values(self): |
---|
5320 | n/a | eq = self.assertEqual |
---|
5321 | n/a | m = """\ |
---|
5322 | n/a | Content-Type: application/x-foo; name=\"Frank's Document\" |
---|
5323 | n/a | |
---|
5324 | n/a | """ |
---|
5325 | n/a | msg = email.message_from_string(m) |
---|
5326 | n/a | eq(msg.get_param('name'), "Frank's Document") |
---|
5327 | n/a | |
---|
5328 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_encoded_then_unencoded_segments |
---|
5329 | n/a | def test_rfc2231_encoded_then_unencoded_segments(self): |
---|
5330 | n/a | eq = self.assertEqual |
---|
5331 | n/a | m = """\ |
---|
5332 | n/a | Content-Type: application/x-foo; |
---|
5333 | n/a | \tname*0*=\"us-ascii'en-us'My\"; |
---|
5334 | n/a | \tname*1=\" Document\"; |
---|
5335 | n/a | \tname*2*=\" For You\" |
---|
5336 | n/a | |
---|
5337 | n/a | """ |
---|
5338 | n/a | msg = email.message_from_string(m) |
---|
5339 | n/a | charset, language, s = msg.get_param('name') |
---|
5340 | n/a | eq(charset, 'us-ascii') |
---|
5341 | n/a | eq(language, 'en-us') |
---|
5342 | n/a | eq(s, 'My Document For You') |
---|
5343 | n/a | |
---|
5344 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_unencoded_then_encoded_segments |
---|
5345 | n/a | # test_headerregistry.TestContentTypeHeader.rfc2231_quoted_unencoded_then_encoded_segments |
---|
5346 | n/a | def test_rfc2231_unencoded_then_encoded_segments(self): |
---|
5347 | n/a | eq = self.assertEqual |
---|
5348 | n/a | m = """\ |
---|
5349 | n/a | Content-Type: application/x-foo; |
---|
5350 | n/a | \tname*0=\"us-ascii'en-us'My\"; |
---|
5351 | n/a | \tname*1*=\" Document\"; |
---|
5352 | n/a | \tname*2*=\" For You\" |
---|
5353 | n/a | |
---|
5354 | n/a | """ |
---|
5355 | n/a | msg = email.message_from_string(m) |
---|
5356 | n/a | charset, language, s = msg.get_param('name') |
---|
5357 | n/a | eq(charset, 'us-ascii') |
---|
5358 | n/a | eq(language, 'en-us') |
---|
5359 | n/a | eq(s, 'My Document For You') |
---|
5360 | n/a | |
---|
5361 | n/a | |
---|
5362 | n/a | |
---|
5363 | n/a | # Tests to ensure that signed parts of an email are completely preserved, as |
---|
5364 | n/a | # required by RFC1847 section 2.1. Note that these are incomplete, because the |
---|
5365 | n/a | # email package does not currently always preserve the body. See issue 1670765. |
---|
5366 | n/a | class TestSigned(TestEmailBase): |
---|
5367 | n/a | |
---|
5368 | n/a | def _msg_and_obj(self, filename): |
---|
5369 | n/a | with openfile(filename) as fp: |
---|
5370 | n/a | original = fp.read() |
---|
5371 | n/a | msg = email.message_from_string(original) |
---|
5372 | n/a | return original, msg |
---|
5373 | n/a | |
---|
5374 | n/a | def _signed_parts_eq(self, original, result): |
---|
5375 | n/a | # Extract the first mime part of each message |
---|
5376 | n/a | import re |
---|
5377 | n/a | repart = re.compile(r'^--([^\n]+)\n(.*?)\n--\1$', re.S | re.M) |
---|
5378 | n/a | inpart = repart.search(original).group(2) |
---|
5379 | n/a | outpart = repart.search(result).group(2) |
---|
5380 | n/a | self.assertEqual(outpart, inpart) |
---|
5381 | n/a | |
---|
5382 | n/a | def test_long_headers_as_string(self): |
---|
5383 | n/a | original, msg = self._msg_and_obj('msg_45.txt') |
---|
5384 | n/a | result = msg.as_string() |
---|
5385 | n/a | self._signed_parts_eq(original, result) |
---|
5386 | n/a | |
---|
5387 | n/a | def test_long_headers_as_string_maxheaderlen(self): |
---|
5388 | n/a | original, msg = self._msg_and_obj('msg_45.txt') |
---|
5389 | n/a | result = msg.as_string(maxheaderlen=60) |
---|
5390 | n/a | self._signed_parts_eq(original, result) |
---|
5391 | n/a | |
---|
5392 | n/a | def test_long_headers_flatten(self): |
---|
5393 | n/a | original, msg = self._msg_and_obj('msg_45.txt') |
---|
5394 | n/a | fp = StringIO() |
---|
5395 | n/a | Generator(fp).flatten(msg) |
---|
5396 | n/a | result = fp.getvalue() |
---|
5397 | n/a | self._signed_parts_eq(original, result) |
---|
5398 | n/a | |
---|
5399 | n/a | |
---|
5400 | n/a | |
---|
5401 | n/a | if __name__ == '__main__': |
---|
5402 | n/a | unittest.main() |
---|