blob: 14051c2de9b3be2cd4203d98e1d118e4f77942b6 [file] [log] [blame]
Barry Warsaw763af412002-01-27 06:48:47 +00001# Copyright (C) 2001,2002 Python Software Foundation
Barry Warsaw41075852001-09-23 03:18:13 +00002# email package unit tests
3
Barry Warsaw409a4c02002-04-10 21:01:31 +00004import sys
Barry Warsaw41075852001-09-23 03:18:13 +00005import os
6import time
7import unittest
8import base64
Barry Warsawb6a92132002-06-28 23:49:33 +00009import difflib
Barry Warsaw41075852001-09-23 03:18:13 +000010from cStringIO import StringIO
Barry Warsaw2c685062002-06-02 19:09:27 +000011from types import StringType, ListType
Barry Warsaw409a4c02002-04-10 21:01:31 +000012import warnings
Barry Warsaw41075852001-09-23 03:18:13 +000013
14import email
15
Barry Warsaw409a4c02002-04-10 21:01:31 +000016from email.Charset import Charset
17from email.Header import Header, decode_header
Barry Warsawbf7a59d2001-10-11 15:44:50 +000018from email.Parser import Parser, HeaderParser
Barry Warsaw41075852001-09-23 03:18:13 +000019from email.Generator import Generator, DecodedGenerator
20from email.Message import Message
Barry Warsawfee435a2001-10-09 19:23:57 +000021from email.MIMEAudio import MIMEAudio
Barry Warsaw65279d02001-09-26 05:47:08 +000022from email.MIMEText import MIMEText
23from email.MIMEImage import MIMEImage
Barry Warsaw41075852001-09-23 03:18:13 +000024from email.MIMEBase import MIMEBase
Barry Warsaw65279d02001-09-26 05:47:08 +000025from email.MIMEMessage import MIMEMessage
Barry Warsaw41075852001-09-23 03:18:13 +000026from email import Utils
27from email import Errors
28from email import Encoders
29from email import Iterators
Barry Warsaw409a4c02002-04-10 21:01:31 +000030from email import base64MIME
31from email import quopriMIME
Barry Warsaw41075852001-09-23 03:18:13 +000032
Barry Warsawc9ad32c2002-04-15 22:14:06 +000033import test_support
Guido van Rossum78f0dd32001-12-07 21:07:08 +000034from test_support import findfile, __file__ as test_support_file
Barry Warsaw41075852001-09-23 03:18:13 +000035
Barry Warsawc9ad32c2002-04-15 22:14:06 +000036
Barry Warsaw41075852001-09-23 03:18:13 +000037NL = '\n'
38EMPTYSTRING = ''
Barry Warsaw07227d12001-10-17 20:52:26 +000039SPACE = ' '
Barry Warsaw41075852001-09-23 03:18:13 +000040
Barry Warsaw409a4c02002-04-10 21:01:31 +000041# We don't care about DeprecationWarnings
42warnings.filterwarnings('ignore', '', DeprecationWarning, __name__)
43
Barry Warsaw41075852001-09-23 03:18:13 +000044
Barry Warsaw08a534d2001-10-04 17:58:50 +000045
Barry Warsaw41075852001-09-23 03:18:13 +000046def openfile(filename):
Guido van Rossum78f0dd32001-12-07 21:07:08 +000047 path = os.path.join(os.path.dirname(test_support_file), 'data', filename)
Barry Warsaw41075852001-09-23 03:18:13 +000048 return open(path)
49
50
Barry Warsaw08a534d2001-10-04 17:58:50 +000051
Barry Warsaw41075852001-09-23 03:18:13 +000052# Base test class
53class TestEmailBase(unittest.TestCase):
Barry Warsawb6a92132002-06-28 23:49:33 +000054 def ndiffAssertEqual(self, first, second):
55 """Like failUnlessEqual except use ndiff to produce readable output."""
56 if first <> second:
57 diff = difflib.ndiff(first.splitlines(), second.splitlines())
58 fp = StringIO()
59 print >> fp, NL, NL.join(diff)
60 raise self.failureException, fp.getvalue()
61
Barry Warsaw41075852001-09-23 03:18:13 +000062 def _msgobj(self, filename):
Barry Warsaw409a4c02002-04-10 21:01:31 +000063 fp = openfile(findfile(filename))
Barry Warsaw41075852001-09-23 03:18:13 +000064 try:
Barry Warsaw65279d02001-09-26 05:47:08 +000065 msg = email.message_from_file(fp)
Barry Warsaw41075852001-09-23 03:18:13 +000066 finally:
67 fp.close()
Barry Warsaw65279d02001-09-26 05:47:08 +000068 return msg
Barry Warsaw41075852001-09-23 03:18:13 +000069
70
Barry Warsaw08a534d2001-10-04 17:58:50 +000071
Barry Warsaw41075852001-09-23 03:18:13 +000072# Test various aspects of the Message class's API
73class TestMessageAPI(TestEmailBase):
Barry Warsaw2f6a0b02001-10-09 15:49:35 +000074 def test_get_all(self):
75 eq = self.assertEqual
76 msg = self._msgobj('msg_20.txt')
77 eq(msg.get_all('cc'), ['ccc@zzz.org', 'ddd@zzz.org', 'eee@zzz.org'])
78 eq(msg.get_all('xx', 'n/a'), 'n/a')
79
Barry Warsaw409a4c02002-04-10 21:01:31 +000080 def test_getset_charset(self):
81 eq = self.assertEqual
82 msg = Message()
83 eq(msg.get_charset(), None)
84 charset = Charset('iso-8859-1')
85 msg.set_charset(charset)
86 eq(msg['mime-version'], '1.0')
87 eq(msg.get_type(), 'text/plain')
88 eq(msg['content-type'], 'text/plain; charset="iso-8859-1"')
89 eq(msg.get_param('charset'), 'iso-8859-1')
90 eq(msg['content-transfer-encoding'], 'quoted-printable')
91 eq(msg.get_charset().input_charset, 'iso-8859-1')
92 # Remove the charset
93 msg.set_charset(None)
94 eq(msg.get_charset(), None)
95 eq(msg['content-type'], 'text/plain')
96 # Try adding a charset when there's already MIME headers present
97 msg = Message()
98 msg['MIME-Version'] = '2.0'
99 msg['Content-Type'] = 'text/x-weird'
100 msg['Content-Transfer-Encoding'] = 'quinted-puntable'
101 msg.set_charset(charset)
102 eq(msg['mime-version'], '2.0')
103 eq(msg['content-type'], 'text/x-weird; charset="iso-8859-1"')
104 eq(msg['content-transfer-encoding'], 'quinted-puntable')
105
106 def test_set_charset_from_string(self):
107 eq = self.assertEqual
108 msg = Message()
109 msg.set_charset('us-ascii')
110 eq(msg.get_charset().input_charset, 'us-ascii')
111 eq(msg['content-type'], 'text/plain; charset="us-ascii"')
112
113 def test_set_payload_with_charset(self):
114 msg = Message()
115 charset = Charset('iso-8859-1')
116 msg.set_payload('This is a string payload', charset)
117 self.assertEqual(msg.get_charset().input_charset, 'iso-8859-1')
118
Barry Warsaw41075852001-09-23 03:18:13 +0000119 def test_get_charsets(self):
120 eq = self.assertEqual
Tim Peters527e64f2001-10-04 05:36:56 +0000121
Barry Warsaw65279d02001-09-26 05:47:08 +0000122 msg = self._msgobj('msg_08.txt')
123 charsets = msg.get_charsets()
124 eq(charsets, [None, 'us-ascii', 'iso-8859-1', 'iso-8859-2', 'koi8-r'])
Barry Warsaw41075852001-09-23 03:18:13 +0000125
Barry Warsaw65279d02001-09-26 05:47:08 +0000126 msg = self._msgobj('msg_09.txt')
127 charsets = msg.get_charsets('dingbat')
128 eq(charsets, ['dingbat', 'us-ascii', 'iso-8859-1', 'dingbat',
129 'koi8-r'])
Barry Warsaw41075852001-09-23 03:18:13 +0000130
Barry Warsaw65279d02001-09-26 05:47:08 +0000131 msg = self._msgobj('msg_12.txt')
132 charsets = msg.get_charsets()
133 eq(charsets, [None, 'us-ascii', 'iso-8859-1', None, 'iso-8859-2',
134 'iso-8859-3', 'us-ascii', 'koi8-r'])
Barry Warsaw41075852001-09-23 03:18:13 +0000135
136 def test_get_filename(self):
137 eq = self.assertEqual
138
Barry Warsaw65279d02001-09-26 05:47:08 +0000139 msg = self._msgobj('msg_04.txt')
140 filenames = [p.get_filename() for p in msg.get_payload()]
Barry Warsaw41075852001-09-23 03:18:13 +0000141 eq(filenames, ['msg.txt', 'msg.txt'])
142
Barry Warsaw65279d02001-09-26 05:47:08 +0000143 msg = self._msgobj('msg_07.txt')
144 subpart = msg.get_payload(1)
Barry Warsaw41075852001-09-23 03:18:13 +0000145 eq(subpart.get_filename(), 'dingusfish.gif')
146
147 def test_get_boundary(self):
148 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000149 msg = self._msgobj('msg_07.txt')
Barry Warsaw41075852001-09-23 03:18:13 +0000150 # No quotes!
Barry Warsaw65279d02001-09-26 05:47:08 +0000151 eq(msg.get_boundary(), 'BOUNDARY')
Barry Warsaw41075852001-09-23 03:18:13 +0000152
153 def test_set_boundary(self):
154 eq = self.assertEqual
155 # This one has no existing boundary parameter, but the Content-Type:
156 # header appears fifth.
Barry Warsaw65279d02001-09-26 05:47:08 +0000157 msg = self._msgobj('msg_01.txt')
158 msg.set_boundary('BOUNDARY')
159 header, value = msg.items()[4]
Barry Warsaw41075852001-09-23 03:18:13 +0000160 eq(header.lower(), 'content-type')
Barry Warsaw9546e792002-06-29 05:58:45 +0000161 eq(value, 'text/plain; charset="us-ascii"; boundary="BOUNDARY"')
Barry Warsaw41075852001-09-23 03:18:13 +0000162 # This one has a Content-Type: header, with a boundary, stuck in the
163 # middle of its headers. Make sure the order is preserved; it should
164 # be fifth.
Barry Warsaw65279d02001-09-26 05:47:08 +0000165 msg = self._msgobj('msg_04.txt')
166 msg.set_boundary('BOUNDARY')
167 header, value = msg.items()[4]
Barry Warsaw41075852001-09-23 03:18:13 +0000168 eq(header.lower(), 'content-type')
169 eq(value, 'multipart/mixed; boundary="BOUNDARY"')
170 # And this one has no Content-Type: header at all.
Barry Warsaw65279d02001-09-26 05:47:08 +0000171 msg = self._msgobj('msg_03.txt')
Barry Warsaw41075852001-09-23 03:18:13 +0000172 self.assertRaises(Errors.HeaderParseError,
Barry Warsaw65279d02001-09-26 05:47:08 +0000173 msg.set_boundary, 'BOUNDARY')
Barry Warsaw41075852001-09-23 03:18:13 +0000174
175 def test_get_decoded_payload(self):
176 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000177 msg = self._msgobj('msg_10.txt')
Barry Warsaw41075852001-09-23 03:18:13 +0000178 # The outer message is a multipart
Barry Warsaw65279d02001-09-26 05:47:08 +0000179 eq(msg.get_payload(decode=1), None)
Barry Warsaw41075852001-09-23 03:18:13 +0000180 # Subpart 1 is 7bit encoded
Barry Warsaw65279d02001-09-26 05:47:08 +0000181 eq(msg.get_payload(0).get_payload(decode=1),
Barry Warsaw41075852001-09-23 03:18:13 +0000182 'This is a 7bit encoded message.\n')
183 # Subpart 2 is quopri
Barry Warsaw65279d02001-09-26 05:47:08 +0000184 eq(msg.get_payload(1).get_payload(decode=1),
Barry Warsaw41075852001-09-23 03:18:13 +0000185 '\xa1This is a Quoted Printable encoded message!\n')
186 # Subpart 3 is base64
Barry Warsaw65279d02001-09-26 05:47:08 +0000187 eq(msg.get_payload(2).get_payload(decode=1),
Barry Warsaw41075852001-09-23 03:18:13 +0000188 'This is a Base64 encoded message.')
189 # Subpart 4 has no Content-Transfer-Encoding: header.
Barry Warsaw65279d02001-09-26 05:47:08 +0000190 eq(msg.get_payload(3).get_payload(decode=1),
Barry Warsaw41075852001-09-23 03:18:13 +0000191 'This has no Content-Transfer-Encoding: header.\n')
192
193 def test_decoded_generator(self):
194 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000195 msg = self._msgobj('msg_07.txt')
196 fp = openfile('msg_17.txt')
197 try:
198 text = fp.read()
199 finally:
200 fp.close()
Barry Warsaw41075852001-09-23 03:18:13 +0000201 s = StringIO()
202 g = DecodedGenerator(s)
Barry Warsaw2c685062002-06-02 19:09:27 +0000203 g.flatten(msg)
Barry Warsaw65279d02001-09-26 05:47:08 +0000204 eq(s.getvalue(), text)
Barry Warsaw41075852001-09-23 03:18:13 +0000205
206 def test__contains__(self):
207 msg = Message()
208 msg['From'] = 'Me'
209 msg['to'] = 'You'
210 # Check for case insensitivity
211 self.failUnless('from' in msg)
212 self.failUnless('From' in msg)
213 self.failUnless('FROM' in msg)
214 self.failUnless('to' in msg)
215 self.failUnless('To' in msg)
216 self.failUnless('TO' in msg)
217
218 def test_as_string(self):
219 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000220 msg = self._msgobj('msg_01.txt')
Barry Warsaw41075852001-09-23 03:18:13 +0000221 fp = openfile('msg_01.txt')
222 try:
223 text = fp.read()
224 finally:
225 fp.close()
Barry Warsaw65279d02001-09-26 05:47:08 +0000226 eq(text, msg.as_string())
227 fullrepr = str(msg)
Barry Warsaw41075852001-09-23 03:18:13 +0000228 lines = fullrepr.split('\n')
229 self.failUnless(lines[0].startswith('From '))
230 eq(text, NL.join(lines[1:]))
231
232 def test_bad_param(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000233 msg = email.message_from_string("Content-Type: blarg; baz; boo\n")
Barry Warsaw41075852001-09-23 03:18:13 +0000234 self.assertEqual(msg.get_param('baz'), '')
235
236 def test_missing_filename(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000237 msg = email.message_from_string("From: foo\n")
Barry Warsaw41075852001-09-23 03:18:13 +0000238 self.assertEqual(msg.get_filename(), None)
239
240 def test_bogus_filename(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000241 msg = email.message_from_string(
242 "Content-Disposition: blarg; filename\n")
Barry Warsaw41075852001-09-23 03:18:13 +0000243 self.assertEqual(msg.get_filename(), '')
Tim Peters527e64f2001-10-04 05:36:56 +0000244
Barry Warsaw41075852001-09-23 03:18:13 +0000245 def test_missing_boundary(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000246 msg = email.message_from_string("From: foo\n")
Barry Warsaw41075852001-09-23 03:18:13 +0000247 self.assertEqual(msg.get_boundary(), None)
248
Barry Warsaw65279d02001-09-26 05:47:08 +0000249 def test_get_params(self):
250 eq = self.assertEqual
251 msg = email.message_from_string(
252 'X-Header: foo=one; bar=two; baz=three\n')
253 eq(msg.get_params(header='x-header'),
254 [('foo', 'one'), ('bar', 'two'), ('baz', 'three')])
255 msg = email.message_from_string(
256 'X-Header: foo; bar=one; baz=two\n')
257 eq(msg.get_params(header='x-header'),
258 [('foo', ''), ('bar', 'one'), ('baz', 'two')])
259 eq(msg.get_params(), None)
260 msg = email.message_from_string(
261 'X-Header: foo; bar="one"; baz=two\n')
262 eq(msg.get_params(header='x-header'),
263 [('foo', ''), ('bar', 'one'), ('baz', 'two')])
264
Barry Warsaw409a4c02002-04-10 21:01:31 +0000265 def test_get_param_liberal(self):
266 msg = Message()
267 msg['Content-Type'] = 'Content-Type: Multipart/mixed; boundary = "CPIMSSMTPC06p5f3tG"'
268 self.assertEqual(msg.get_param('boundary'), 'CPIMSSMTPC06p5f3tG')
269
Barry Warsaw65279d02001-09-26 05:47:08 +0000270 def test_get_param(self):
271 eq = self.assertEqual
272 msg = email.message_from_string(
273 "X-Header: foo=one; bar=two; baz=three\n")
274 eq(msg.get_param('bar', header='x-header'), 'two')
275 eq(msg.get_param('quuz', header='x-header'), None)
276 eq(msg.get_param('quuz'), None)
277 msg = email.message_from_string(
278 'X-Header: foo; bar="one"; baz=two\n')
279 eq(msg.get_param('foo', header='x-header'), '')
280 eq(msg.get_param('bar', header='x-header'), 'one')
281 eq(msg.get_param('baz', header='x-header'), 'two')
Barry Warsaw409a4c02002-04-10 21:01:31 +0000282 # XXX: We are not RFC-2045 compliant! We cannot parse:
283 # msg["Content-Type"] = 'text/plain; weird="hey; dolly? [you] @ <\\"home\\">?"'
284 # msg.get_param("weird")
285 # yet.
Barry Warsaw65279d02001-09-26 05:47:08 +0000286
Barry Warsaw2539cf52001-10-25 22:43:46 +0000287 def test_get_param_funky_continuation_lines(self):
288 msg = self._msgobj('msg_22.txt')
289 self.assertEqual(msg.get_payload(1).get_param('name'), 'wibble.JPG')
290
Barry Warsaw65279d02001-09-26 05:47:08 +0000291 def test_has_key(self):
292 msg = email.message_from_string('Header: exists')
293 self.failUnless(msg.has_key('header'))
294 self.failUnless(msg.has_key('Header'))
295 self.failUnless(msg.has_key('HEADER'))
296 self.failIf(msg.has_key('headeri'))
297
Barry Warsaw409a4c02002-04-10 21:01:31 +0000298 def test_set_param(self):
299 eq = self.assertEqual
300 msg = Message()
301 msg.set_param('charset', 'iso-2022-jp')
302 eq(msg.get_param('charset'), 'iso-2022-jp')
303 msg.set_param('importance', 'high value')
304 eq(msg.get_param('importance'), 'high value')
305 eq(msg.get_param('importance', unquote=0), '"high value"')
306 eq(msg.get_params(), [('text/plain', ''),
307 ('charset', 'iso-2022-jp'),
308 ('importance', 'high value')])
309 eq(msg.get_params(unquote=0), [('text/plain', ''),
310 ('charset', '"iso-2022-jp"'),
311 ('importance', '"high value"')])
312 msg.set_param('charset', 'iso-9999-xx', header='X-Jimmy')
313 eq(msg.get_param('charset', header='X-Jimmy'), 'iso-9999-xx')
Barry Warsaw41075852001-09-23 03:18:13 +0000314
Barry Warsaw409a4c02002-04-10 21:01:31 +0000315 def test_del_param(self):
316 eq = self.assertEqual
317 msg = self._msgobj('msg_05.txt')
318 eq(msg.get_params(),
319 [('multipart/report', ''), ('report-type', 'delivery-status'),
320 ('boundary', 'D1690A7AC1.996856090/mail.example.com')])
321 old_val = msg.get_param("report-type")
322 msg.del_param("report-type")
323 eq(msg.get_params(),
324 [('multipart/report', ''),
Barry Warsaw16f90552002-04-16 05:06:42 +0000325 ('boundary', 'D1690A7AC1.996856090/mail.example.com')])
Barry Warsaw409a4c02002-04-10 21:01:31 +0000326 msg.set_param("report-type", old_val)
327 eq(msg.get_params(),
328 [('multipart/report', ''),
329 ('boundary', 'D1690A7AC1.996856090/mail.example.com'),
330 ('report-type', old_val)])
331
332 def test_set_type(self):
333 eq = self.assertEqual
334 msg = Message()
335 self.assertRaises(ValueError, msg.set_type, 'text')
336 msg.set_type('text/plain')
337 eq(msg['content-type'], 'text/plain')
338 msg.set_param('charset', 'us-ascii')
339 eq(msg['content-type'], 'text/plain; charset="us-ascii"')
340 msg.set_type('text/html')
341 eq(msg['content-type'], 'text/html; charset="us-ascii"')
342
Barry Warsaw16f90552002-04-16 05:06:42 +0000343
Barry Warsaw08a534d2001-10-04 17:58:50 +0000344
Barry Warsaw41075852001-09-23 03:18:13 +0000345# Test the email.Encoders module
346class TestEncoders(unittest.TestCase):
347 def test_encode_noop(self):
348 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000349 msg = MIMEText('hello world', _encoder=Encoders.encode_noop)
Barry Warsaw41075852001-09-23 03:18:13 +0000350 eq(msg.get_payload(), 'hello world\n')
Barry Warsaw41075852001-09-23 03:18:13 +0000351
352 def test_encode_7bit(self):
353 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000354 msg = MIMEText('hello world', _encoder=Encoders.encode_7or8bit)
Barry Warsaw41075852001-09-23 03:18:13 +0000355 eq(msg.get_payload(), 'hello world\n')
356 eq(msg['content-transfer-encoding'], '7bit')
Barry Warsaw65279d02001-09-26 05:47:08 +0000357 msg = MIMEText('hello \x7f world', _encoder=Encoders.encode_7or8bit)
Barry Warsaw41075852001-09-23 03:18:13 +0000358 eq(msg.get_payload(), 'hello \x7f world\n')
359 eq(msg['content-transfer-encoding'], '7bit')
360
361 def test_encode_8bit(self):
362 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000363 msg = MIMEText('hello \x80 world', _encoder=Encoders.encode_7or8bit)
Barry Warsaw41075852001-09-23 03:18:13 +0000364 eq(msg.get_payload(), 'hello \x80 world\n')
365 eq(msg['content-transfer-encoding'], '8bit')
366
Barry Warsaw409a4c02002-04-10 21:01:31 +0000367 def test_encode_empty_payload(self):
368 eq = self.assertEqual
369 msg = Message()
370 msg.set_charset('us-ascii')
371 eq(msg['content-transfer-encoding'], '7bit')
372
Barry Warsaw41075852001-09-23 03:18:13 +0000373 def test_encode_base64(self):
374 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000375 msg = MIMEText('hello world', _encoder=Encoders.encode_base64)
Barry Warsaw41075852001-09-23 03:18:13 +0000376 eq(msg.get_payload(), 'aGVsbG8gd29ybGQK\n')
377 eq(msg['content-transfer-encoding'], 'base64')
378
379 def test_encode_quoted_printable(self):
380 eq = self.assertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000381 msg = MIMEText('hello world', _encoder=Encoders.encode_quopri)
Barry Warsaw41075852001-09-23 03:18:13 +0000382 eq(msg.get_payload(), 'hello=20world\n')
383 eq(msg['content-transfer-encoding'], 'quoted-printable')
384
Barry Warsaw409a4c02002-04-10 21:01:31 +0000385 def test_default_cte(self):
386 eq = self.assertEqual
387 msg = MIMEText('hello world')
388 eq(msg['content-transfer-encoding'], '7bit')
389
390 def test_default_cte(self):
391 eq = self.assertEqual
392 # With no explicit _charset its us-ascii, and all are 7-bit
393 msg = MIMEText('hello world')
394 eq(msg['content-transfer-encoding'], '7bit')
395 # Similar, but with 8-bit data
396 msg = MIMEText('hello \xf8 world')
397 eq(msg['content-transfer-encoding'], '8bit')
398 # And now with a different charset
399 msg = MIMEText('hello \xf8 world', _charset='iso-8859-1')
400 eq(msg['content-transfer-encoding'], 'quoted-printable')
401
Barry Warsaw41075852001-09-23 03:18:13 +0000402
Barry Warsaw08a534d2001-10-04 17:58:50 +0000403
404# Test long header wrapping
Barry Warsawb6a92132002-06-28 23:49:33 +0000405class TestLongHeaders(TestEmailBase):
406 def test_split_long_continuation(self):
407 eq = self.ndiffAssertEqual
408 msg = email.message_from_string("""\
409Subject: bug demonstration
410\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
411\tmore text
412
413test
414""")
415 sfp = StringIO()
416 g = Generator(sfp)
417 g.flatten(msg)
418 eq(sfp.getvalue(), """\
419Subject: bug demonstration
420\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
421\tmore text
422
423test
424""")
425
426 def test_another_long_almost_unsplittable_header(self):
427 eq = self.ndiffAssertEqual
428 hstr = """\
429bug demonstration
430\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
431\tmore text"""
432 h = Header(hstr, continuation_ws='\t')
433 eq(h.encode(), """\
434bug demonstration
435\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
436\tmore text""")
437 h = Header(hstr)
438 eq(h.encode(), """\
439bug demonstration
440 12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
441 more text""")
442
443 def test_long_nonstring(self):
444 eq = self.ndiffAssertEqual
445 g = Charset("iso-8859-1")
446 cz = Charset("iso-8859-2")
447 utf8 = Charset("utf-8")
448 g_head = "Die Mieter treten hier ein werden mit einem Foerderband komfortabel den Korridor entlang, an s\xfcdl\xfcndischen Wandgem\xe4lden vorbei, gegen die rotierenden Klingen bef\xf6rdert. "
449 cz_head = "Finan\xe8ni metropole se hroutily pod tlakem jejich d\xf9vtipu.. "
450 utf8_head = u"\u6b63\u78ba\u306b\u8a00\u3046\u3068\u7ffb\u8a33\u306f\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u4e00\u90e8\u306f\u30c9\u30a4\u30c4\u8a9e\u3067\u3059\u304c\u3001\u3042\u3068\u306f\u3067\u305f\u3089\u3081\u3067\u3059\u3002\u5b9f\u969b\u306b\u306f\u300cWenn ist das Nunstuck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput.\u300d\u3068\u8a00\u3063\u3066\u3044\u307e\u3059\u3002".encode("utf-8")
451 h = Header(g_head, g)
452 h.append(cz_head, cz)
453 h.append(utf8_head, utf8)
454 msg = Message()
455 msg['Subject'] = h
456 sfp = StringIO()
457 g = Generator(sfp)
458 g.flatten(msg)
459 eq(sfp.getvalue(), '''\
460Subject: =?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_eine?=
461 =?iso-8859-1?q?m_Foerderband_komfortabel_den_Korridor_ent?=
462 =?iso-8859-1?q?lang=2C_an_s=FCdl=FCndischen_Wandgem=E4lden_vorbei?=
463 =?iso-8859-1?q?=2C_gegen_die_rotierenden_Klingen_bef=F6rdert=2E_?=
464 =?iso-8859-2?q?Finan=E8ni_metropole_se_hroutil?=
465 =?iso-8859-2?q?y_pod_tlakem_jejich_d=F9vtipu=2E=2E_?=
466 =?utf-8?b?5q2j56K644Gr6KiA44GG44Go57+76Kiz44Gv?=
467 =?utf-8?b?44GV44KM44Gm44GE44G+44Gb44KT44CC5LiA?=
468 =?utf-8?b?6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM?=
469 =?utf-8?b?44CB44GC44Go44Gv44Gn44Gf44KJ44KB44Gn?=
470 =?utf-8?b?44GZ44CC5a6f6Zqb44Gr44Gv44CMV2VubiBpc3QgZGE=?=
471 =?utf-8?b?cyBOdW5zdHVjayBnaXQgdW5k?=
472 =?utf-8?b?IFNsb3Rlcm1leWVyPyBKYSEgQmVpaGVyaHVuZCBkYXMgT2Rl?=
473 =?utf-8?b?ciBkaWUgRmxpcHBlcndhbGR0?=
474 =?utf-8?b?IGdlcnNwdXQu44CN44Go6KiA44Gj44Gm44GE44G+44GZ44CC?=
475
476''')
477 eq(h.encode(), '''\
478=?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_eine?=
479 =?iso-8859-1?q?m_Foerderband_komfortabel_den_Korridor_ent?=
480 =?iso-8859-1?q?lang=2C_an_s=FCdl=FCndischen_Wandgem=E4lden_vorbei?=
481 =?iso-8859-1?q?=2C_gegen_die_rotierenden_Klingen_bef=F6rdert=2E_?=
482 =?iso-8859-2?q?Finan=E8ni_metropole_se_hroutil?=
483 =?iso-8859-2?q?y_pod_tlakem_jejich_d=F9vtipu=2E=2E_?=
484 =?utf-8?b?5q2j56K644Gr6KiA44GG44Go57+76Kiz44Gv?=
485 =?utf-8?b?44GV44KM44Gm44GE44G+44Gb44KT44CC5LiA?=
486 =?utf-8?b?6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM?=
487 =?utf-8?b?44CB44GC44Go44Gv44Gn44Gf44KJ44KB44Gn?=
488 =?utf-8?b?44GZ44CC5a6f6Zqb44Gr44Gv44CMV2VubiBpc3QgZGE=?=
489 =?utf-8?b?cyBOdW5zdHVjayBnaXQgdW5k?=
490 =?utf-8?b?IFNsb3Rlcm1leWVyPyBKYSEgQmVpaGVyaHVuZCBkYXMgT2Rl?=
491 =?utf-8?b?ciBkaWUgRmxpcHBlcndhbGR0?=
492 =?utf-8?b?IGdlcnNwdXQu44CN44Go6KiA44Gj44Gm44GE44G+44GZ44CC?=''')
493
494 def test_long_header_encode(self):
495 eq = self.ndiffAssertEqual
496 h = Header('wasnipoop; giraffes="very-long-necked-animals"; '
497 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"',
498 header_name='X-Foobar-Spoink-Defrobnit')
499 eq(h.encode(), '''\
500wasnipoop; giraffes="very-long-necked-animals";
501 spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''')
502
503 def test_long_header_encode_with_tab_continuation(self):
504 eq = self.ndiffAssertEqual
505 h = Header('wasnipoop; giraffes="very-long-necked-animals"; '
506 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"',
507 header_name='X-Foobar-Spoink-Defrobnit',
508 continuation_ws='\t')
509 eq(h.encode(), '''\
510wasnipoop; giraffes="very-long-necked-animals";
511\tspooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''')
512
Barry Warsaw41075852001-09-23 03:18:13 +0000513 def test_header_splitter(self):
Barry Warsawb6a92132002-06-28 23:49:33 +0000514 eq = self.ndiffAssertEqual
Barry Warsaw65279d02001-09-26 05:47:08 +0000515 msg = MIMEText('')
Barry Warsaw41075852001-09-23 03:18:13 +0000516 # It'd be great if we could use add_header() here, but that doesn't
517 # guarantee an order of the parameters.
518 msg['X-Foobar-Spoink-Defrobnit'] = (
519 'wasnipoop; giraffes="very-long-necked-animals"; '
520 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"')
521 sfp = StringIO()
522 g = Generator(sfp)
Barry Warsaw2c685062002-06-02 19:09:27 +0000523 g.flatten(msg)
Barry Warsawb6a92132002-06-28 23:49:33 +0000524 eq(sfp.getvalue(), '''\
Barry Warsaw409a4c02002-04-10 21:01:31 +0000525Content-Type: text/plain; charset="us-ascii"
526MIME-Version: 1.0
527Content-Transfer-Encoding: 7bit
528X-Foobar-Spoink-Defrobnit: wasnipoop; giraffes="very-long-necked-animals";
Barry Warsaw16f90552002-04-16 05:06:42 +0000529\tspooge="yummy"; hippos="gargantuan"; marshmallows="gooey"
Barry Warsaw409a4c02002-04-10 21:01:31 +0000530
531''')
Barry Warsaw41075852001-09-23 03:18:13 +0000532
Barry Warsaw07227d12001-10-17 20:52:26 +0000533 def test_no_semis_header_splitter(self):
Barry Warsawb6a92132002-06-28 23:49:33 +0000534 eq = self.ndiffAssertEqual
Barry Warsaw07227d12001-10-17 20:52:26 +0000535 msg = Message()
536 msg['From'] = 'test@dom.ain'
Barry Warsawb6a92132002-06-28 23:49:33 +0000537 msg['References'] = SPACE.join(['<%d@dom.ain>' % i for i in range(10)])
Barry Warsaw07227d12001-10-17 20:52:26 +0000538 msg.set_payload('Test')
539 sfp = StringIO()
540 g = Generator(sfp)
Barry Warsaw2c685062002-06-02 19:09:27 +0000541 g.flatten(msg)
Barry Warsawb6a92132002-06-28 23:49:33 +0000542 eq(sfp.getvalue(), """\
Barry Warsaw07227d12001-10-17 20:52:26 +0000543From: test@dom.ain
544References: <0@dom.ain> <1@dom.ain> <2@dom.ain> <3@dom.ain> <4@dom.ain>
Tim Peterse0c446b2001-10-18 21:57:37 +0000545\t<5@dom.ain> <6@dom.ain> <7@dom.ain> <8@dom.ain> <9@dom.ain>
Barry Warsaw07227d12001-10-17 20:52:26 +0000546
547Test""")
548
549 def test_no_split_long_header(self):
Barry Warsawb6a92132002-06-28 23:49:33 +0000550 eq = self.ndiffAssertEqual
551 hstr = 'References: ' + 'x' * 80
552 h = Header(hstr, continuation_ws='\t')
553 eq(h.encode(), """\
554References: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx""")
Barry Warsaw07227d12001-10-17 20:52:26 +0000555
Barry Warsaw409a4c02002-04-10 21:01:31 +0000556 def test_splitting_multiple_long_lines(self):
Barry Warsawb6a92132002-06-28 23:49:33 +0000557 eq = self.ndiffAssertEqual
558 hstr = """\
Barry Warsaw409a4c02002-04-10 21:01:31 +0000559from 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)
Barry Warsaw16f90552002-04-16 05:06:42 +0000560\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)
561\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)
Barry Warsaw409a4c02002-04-10 21:01:31 +0000562"""
Barry Warsawb6a92132002-06-28 23:49:33 +0000563 h = Header(hstr, continuation_ws='\t')
564 eq(h.encode(), """\
565from babylon.socal-raves.org (localhost [127.0.0.1]);
Barry Warsaw16f90552002-04-16 05:06:42 +0000566\tby babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81;
567\tfor <mailman-admin@babylon.socal-raves.org>;
568\tSat, 2 Feb 2002 17:00:06 -0800 (PST)
569\tfrom babylon.socal-raves.org (localhost [127.0.0.1]);
570\tby babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81;
571\tfor <mailman-admin@babylon.socal-raves.org>;
572\tSat, 2 Feb 2002 17:00:06 -0800 (PST)
573\tfrom babylon.socal-raves.org (localhost [127.0.0.1]);
574\tby babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81;
575\tfor <mailman-admin@babylon.socal-raves.org>;
Barry Warsawb6a92132002-06-28 23:49:33 +0000576\tSat, 2 Feb 2002 17:00:06 -0800 (PST)""")
Barry Warsaw409a4c02002-04-10 21:01:31 +0000577
Barry Warsaw41075852001-09-23 03:18:13 +0000578
Barry Warsaw08a534d2001-10-04 17:58:50 +0000579
580# Test mangling of "From " lines in the body of a message
Barry Warsaw41075852001-09-23 03:18:13 +0000581class TestFromMangling(unittest.TestCase):
582 def setUp(self):
583 self.msg = Message()
584 self.msg['From'] = 'aaa@bbb.org'
Barry Warsaw2c685062002-06-02 19:09:27 +0000585 self.msg.set_payload("""\
Barry Warsaw41075852001-09-23 03:18:13 +0000586From the desk of A.A.A.:
587Blah blah blah
588""")
589
590 def test_mangled_from(self):
591 s = StringIO()
592 g = Generator(s, mangle_from_=1)
Barry Warsaw2c685062002-06-02 19:09:27 +0000593 g.flatten(self.msg)
Barry Warsaw41075852001-09-23 03:18:13 +0000594 self.assertEqual(s.getvalue(), """\
595From: aaa@bbb.org
596
597>From the desk of A.A.A.:
598Blah blah blah
599""")
600
601 def test_dont_mangle_from(self):
602 s = StringIO()
603 g = Generator(s, mangle_from_=0)
Barry Warsaw2c685062002-06-02 19:09:27 +0000604 g.flatten(self.msg)
Barry Warsaw41075852001-09-23 03:18:13 +0000605 self.assertEqual(s.getvalue(), """\
606From: aaa@bbb.org
607
608From the desk of A.A.A.:
609Blah blah blah
610""")
611
612
Barry Warsaw08a534d2001-10-04 17:58:50 +0000613
Barry Warsawfee435a2001-10-09 19:23:57 +0000614# Test the basic MIMEAudio class
615class TestMIMEAudio(unittest.TestCase):
616 def setUp(self):
617 # In Python, audiotest.au lives in Lib/test not Lib/test/data
618 fp = open(findfile('audiotest.au'))
619 try:
620 self._audiodata = fp.read()
621 finally:
622 fp.close()
623 self._au = MIMEAudio(self._audiodata)
624
625 def test_guess_minor_type(self):
626 self.assertEqual(self._au.get_type(), 'audio/basic')
627
628 def test_encoding(self):
629 payload = self._au.get_payload()
630 self.assertEqual(base64.decodestring(payload), self._audiodata)
631
632 def checkSetMinor(self):
633 au = MIMEAudio(self._audiodata, 'fish')
634 self.assertEqual(im.get_type(), 'audio/fish')
635
636 def test_custom_encoder(self):
637 eq = self.assertEqual
638 def encoder(msg):
639 orig = msg.get_payload()
640 msg.set_payload(0)
641 msg['Content-Transfer-Encoding'] = 'broken64'
642 au = MIMEAudio(self._audiodata, _encoder=encoder)
643 eq(au.get_payload(), 0)
644 eq(au['content-transfer-encoding'], 'broken64')
645
646 def test_add_header(self):
647 eq = self.assertEqual
648 unless = self.failUnless
649 self._au.add_header('Content-Disposition', 'attachment',
650 filename='audiotest.au')
651 eq(self._au['content-disposition'],
652 'attachment; filename="audiotest.au"')
653 eq(self._au.get_params(header='content-disposition'),
654 [('attachment', ''), ('filename', 'audiotest.au')])
655 eq(self._au.get_param('filename', header='content-disposition'),
656 'audiotest.au')
657 missing = []
658 eq(self._au.get_param('attachment', header='content-disposition'), '')
659 unless(self._au.get_param('foo', failobj=missing,
660 header='content-disposition') is missing)
661 # Try some missing stuff
662 unless(self._au.get_param('foobar', missing) is missing)
663 unless(self._au.get_param('attachment', missing,
664 header='foobar') is missing)
665
666
667
Barry Warsaw65279d02001-09-26 05:47:08 +0000668# Test the basic MIMEImage class
669class TestMIMEImage(unittest.TestCase):
Barry Warsaw41075852001-09-23 03:18:13 +0000670 def setUp(self):
671 fp = openfile('PyBanner048.gif')
672 try:
673 self._imgdata = fp.read()
674 finally:
675 fp.close()
Barry Warsaw65279d02001-09-26 05:47:08 +0000676 self._im = MIMEImage(self._imgdata)
Barry Warsaw41075852001-09-23 03:18:13 +0000677
678 def test_guess_minor_type(self):
679 self.assertEqual(self._im.get_type(), 'image/gif')
680
681 def test_encoding(self):
682 payload = self._im.get_payload()
683 self.assertEqual(base64.decodestring(payload), self._imgdata)
684
685 def checkSetMinor(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000686 im = MIMEImage(self._imgdata, 'fish')
Barry Warsaw41075852001-09-23 03:18:13 +0000687 self.assertEqual(im.get_type(), 'image/fish')
688
689 def test_custom_encoder(self):
690 eq = self.assertEqual
691 def encoder(msg):
692 orig = msg.get_payload()
693 msg.set_payload(0)
694 msg['Content-Transfer-Encoding'] = 'broken64'
Barry Warsaw65279d02001-09-26 05:47:08 +0000695 im = MIMEImage(self._imgdata, _encoder=encoder)
Barry Warsaw41075852001-09-23 03:18:13 +0000696 eq(im.get_payload(), 0)
697 eq(im['content-transfer-encoding'], 'broken64')
698
699 def test_add_header(self):
700 eq = self.assertEqual
701 unless = self.failUnless
702 self._im.add_header('Content-Disposition', 'attachment',
703 filename='dingusfish.gif')
704 eq(self._im['content-disposition'],
705 'attachment; filename="dingusfish.gif"')
706 eq(self._im.get_params(header='content-disposition'),
Barry Warsaw65279d02001-09-26 05:47:08 +0000707 [('attachment', ''), ('filename', 'dingusfish.gif')])
Barry Warsaw41075852001-09-23 03:18:13 +0000708 eq(self._im.get_param('filename', header='content-disposition'),
709 'dingusfish.gif')
710 missing = []
Barry Warsaw65279d02001-09-26 05:47:08 +0000711 eq(self._im.get_param('attachment', header='content-disposition'), '')
712 unless(self._im.get_param('foo', failobj=missing,
Barry Warsaw41075852001-09-23 03:18:13 +0000713 header='content-disposition') is missing)
714 # Try some missing stuff
715 unless(self._im.get_param('foobar', missing) is missing)
716 unless(self._im.get_param('attachment', missing,
717 header='foobar') is missing)
718
719
Barry Warsaw08a534d2001-10-04 17:58:50 +0000720
Barry Warsaw65279d02001-09-26 05:47:08 +0000721# Test the basic MIMEText class
722class TestMIMEText(unittest.TestCase):
Barry Warsaw41075852001-09-23 03:18:13 +0000723 def setUp(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000724 self._msg = MIMEText('hello there')
Barry Warsaw41075852001-09-23 03:18:13 +0000725
726 def test_types(self):
727 eq = self.assertEqual
728 unless = self.failUnless
729 eq(self._msg.get_type(), 'text/plain')
730 eq(self._msg.get_param('charset'), 'us-ascii')
731 missing = []
732 unless(self._msg.get_param('foobar', missing) is missing)
733 unless(self._msg.get_param('charset', missing, header='foobar')
734 is missing)
735
736 def test_payload(self):
737 self.assertEqual(self._msg.get_payload(), 'hello there\n')
738 self.failUnless(not self._msg.is_multipart())
739
Barry Warsaw409a4c02002-04-10 21:01:31 +0000740 def test_charset(self):
741 eq = self.assertEqual
742 msg = MIMEText('hello there', _charset='us-ascii')
743 eq(msg.get_charset().input_charset, 'us-ascii')
744 eq(msg['content-type'], 'text/plain; charset="us-ascii"')
745
Barry Warsaw41075852001-09-23 03:18:13 +0000746
Barry Warsaw08a534d2001-10-04 17:58:50 +0000747
748# Test a more complicated multipart/mixed type message
Barry Warsaw41075852001-09-23 03:18:13 +0000749class TestMultipartMixed(unittest.TestCase):
750 def setUp(self):
751 fp = openfile('PyBanner048.gif')
752 try:
753 data = fp.read()
754 finally:
755 fp.close()
756
757 container = MIMEBase('multipart', 'mixed', boundary='BOUNDARY')
Barry Warsaw65279d02001-09-26 05:47:08 +0000758 image = MIMEImage(data, name='dingusfish.gif')
Barry Warsaw41075852001-09-23 03:18:13 +0000759 image.add_header('content-disposition', 'attachment',
760 filename='dingusfish.gif')
Barry Warsaw65279d02001-09-26 05:47:08 +0000761 intro = MIMEText('''\
Barry Warsaw41075852001-09-23 03:18:13 +0000762Hi there,
763
764This is the dingus fish.
765''')
Barry Warsaw2c685062002-06-02 19:09:27 +0000766 container.attach(intro)
767 container.attach(image)
Barry Warsaw41075852001-09-23 03:18:13 +0000768 container['From'] = 'Barry <barry@digicool.com>'
769 container['To'] = 'Dingus Lovers <cravindogs@cravindogs.com>'
770 container['Subject'] = 'Here is your dingus fish'
Tim Peters527e64f2001-10-04 05:36:56 +0000771
Barry Warsaw41075852001-09-23 03:18:13 +0000772 now = 987809702.54848599
773 timetuple = time.localtime(now)
774 if timetuple[-1] == 0:
775 tzsecs = time.timezone
776 else:
777 tzsecs = time.altzone
778 if tzsecs > 0:
779 sign = '-'
780 else:
781 sign = '+'
782 tzoffset = ' %s%04d' % (sign, tzsecs / 36)
783 container['Date'] = time.strftime(
784 '%a, %d %b %Y %H:%M:%S',
785 time.localtime(now)) + tzoffset
786 self._msg = container
787 self._im = image
788 self._txt = intro
789
790 def test_hierarchy(self):
791 # convenience
792 eq = self.assertEqual
793 unless = self.failUnless
794 raises = self.assertRaises
795 # tests
796 m = self._msg
797 unless(m.is_multipart())
798 eq(m.get_type(), 'multipart/mixed')
799 eq(len(m.get_payload()), 2)
800 raises(IndexError, m.get_payload, 2)
801 m0 = m.get_payload(0)
802 m1 = m.get_payload(1)
803 unless(m0 is self._txt)
804 unless(m1 is self._im)
805 eq(m.get_payload(), [m0, m1])
806 unless(not m0.is_multipart())
807 unless(not m1.is_multipart())
808
Barry Warsaw409a4c02002-04-10 21:01:31 +0000809 def test_no_parts_in_a_multipart(self):
810 outer = MIMEBase('multipart', 'mixed')
811 outer['Subject'] = 'A subject'
812 outer['To'] = 'aperson@dom.ain'
813 outer['From'] = 'bperson@dom.ain'
814 outer.preamble = ''
815 outer.epilogue = ''
816 outer.set_boundary('BOUNDARY')
817 msg = MIMEText('hello world')
818 self.assertEqual(outer.as_string(), '''\
819Content-Type: multipart/mixed; boundary="BOUNDARY"
820MIME-Version: 1.0
821Subject: A subject
822To: aperson@dom.ain
823From: bperson@dom.ain
824
825--BOUNDARY
826
827
828--BOUNDARY--
Barry Warsaw16f90552002-04-16 05:06:42 +0000829''')
Barry Warsaw409a4c02002-04-10 21:01:31 +0000830
831 def test_one_part_in_a_multipart(self):
832 outer = MIMEBase('multipart', 'mixed')
833 outer['Subject'] = 'A subject'
834 outer['To'] = 'aperson@dom.ain'
835 outer['From'] = 'bperson@dom.ain'
836 outer.preamble = ''
837 outer.epilogue = ''
838 outer.set_boundary('BOUNDARY')
839 msg = MIMEText('hello world')
840 outer.attach(msg)
841 self.assertEqual(outer.as_string(), '''\
842Content-Type: multipart/mixed; boundary="BOUNDARY"
843MIME-Version: 1.0
844Subject: A subject
845To: aperson@dom.ain
846From: bperson@dom.ain
847
848--BOUNDARY
849Content-Type: text/plain; charset="us-ascii"
850MIME-Version: 1.0
851Content-Transfer-Encoding: 7bit
852
853hello world
854
855--BOUNDARY--
Barry Warsaw16f90552002-04-16 05:06:42 +0000856''')
Barry Warsaw409a4c02002-04-10 21:01:31 +0000857
858 def test_seq_parts_in_a_multipart(self):
859 outer = MIMEBase('multipart', 'mixed')
860 outer['Subject'] = 'A subject'
861 outer['To'] = 'aperson@dom.ain'
862 outer['From'] = 'bperson@dom.ain'
863 outer.preamble = ''
864 outer.epilogue = ''
865 msg = MIMEText('hello world')
866 outer.attach(msg)
867 outer.set_boundary('BOUNDARY')
868 self.assertEqual(outer.as_string(), '''\
869Content-Type: multipart/mixed; boundary="BOUNDARY"
870MIME-Version: 1.0
871Subject: A subject
872To: aperson@dom.ain
873From: bperson@dom.ain
874
875--BOUNDARY
876Content-Type: text/plain; charset="us-ascii"
877MIME-Version: 1.0
878Content-Transfer-Encoding: 7bit
879
880hello world
881
882--BOUNDARY--
Barry Warsaw16f90552002-04-16 05:06:42 +0000883''')
Barry Warsaw409a4c02002-04-10 21:01:31 +0000884
Barry Warsaw41075852001-09-23 03:18:13 +0000885
Barry Warsaw08a534d2001-10-04 17:58:50 +0000886
887# Test some badly formatted messages
Barry Warsaw41075852001-09-23 03:18:13 +0000888class TestNonConformant(TestEmailBase):
889 def test_parse_missing_minor_type(self):
890 eq = self.assertEqual
891 msg = self._msgobj('msg_14.txt')
892 eq(msg.get_type(), 'text')
893 eq(msg.get_main_type(), 'text')
894 self.failUnless(msg.get_subtype() is None)
895
896 def test_bogus_boundary(self):
Barry Warsaw409a4c02002-04-10 21:01:31 +0000897 fp = openfile(findfile('msg_15.txt'))
Barry Warsaw41075852001-09-23 03:18:13 +0000898 try:
899 data = fp.read()
900 finally:
901 fp.close()
902 p = Parser()
903 # Note, under a future non-strict parsing mode, this would parse the
904 # message into the intended message tree.
905 self.assertRaises(Errors.BoundaryError, p.parsestr, data)
906
Barry Warsaw409a4c02002-04-10 21:01:31 +0000907 def test_multipart_no_boundary(self):
908 fp = openfile(findfile('msg_25.txt'))
909 self.assertRaises(Errors.BoundaryError, email.message_from_file, fp)
910
Barry Warsaw41075852001-09-23 03:18:13 +0000911
Barry Warsaw08a534d2001-10-04 17:58:50 +0000912
913# Test RFC 2047 header encoding and decoding
Barry Warsaw41075852001-09-23 03:18:13 +0000914class TestRFC2047(unittest.TestCase):
915 def test_iso_8859_1(self):
916 eq = self.assertEqual
917 s = '=?iso-8859-1?q?this=20is=20some=20text?='
918 eq(Utils.decode(s), 'this is some text')
919 s = '=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?='
Barry Warsaw409a4c02002-04-10 21:01:31 +0000920 eq(Utils.decode(s), u'Keld J\xf8rn Simonsen')
Barry Warsaw41075852001-09-23 03:18:13 +0000921 s = '=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=' \
922 '=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?='
923 eq(Utils.decode(s), 'If you can read this you understand the example.')
924 s = '=?iso-8859-8?b?7eXs+SDv4SDp7Oj08A==?='
925 eq(Utils.decode(s),
926 u'\u05dd\u05d5\u05dc\u05e9 \u05df\u05d1 \u05d9\u05dc\u05d8\u05e4\u05e0')
927 s = '=?iso-8859-1?q?this=20is?= =?iso-8859-1?q?some=20text?='
Barry Warsaw409a4c02002-04-10 21:01:31 +0000928 eq(Utils.decode(s), u'this issome text')
929 s = '=?iso-8859-1?q?this=20is_?= =?iso-8859-1?q?some=20text?='
Barry Warsaw41075852001-09-23 03:18:13 +0000930 eq(Utils.decode(s), u'this is some text')
931
932 def test_encode_header(self):
933 eq = self.assertEqual
934 s = 'this is some text'
935 eq(Utils.encode(s), '=?iso-8859-1?q?this=20is=20some=20text?=')
936 s = 'Keld_J\xf8rn_Simonsen'
937 eq(Utils.encode(s), '=?iso-8859-1?q?Keld_J=F8rn_Simonsen?=')
938 s1 = 'If you can read this yo'
939 s2 = 'u understand the example.'
940 eq(Utils.encode(s1, encoding='b'),
941 '=?iso-8859-1?b?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=')
942 eq(Utils.encode(s2, charset='iso-8859-2', encoding='b'),
943 '=?iso-8859-2?b?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=')
944
945
Barry Warsaw08a534d2001-10-04 17:58:50 +0000946
947# Test the MIMEMessage class
Barry Warsaw65279d02001-09-26 05:47:08 +0000948class TestMIMEMessage(TestEmailBase):
Barry Warsaw41075852001-09-23 03:18:13 +0000949 def setUp(self):
950 fp = openfile('msg_11.txt')
951 self._text = fp.read()
952 fp.close()
953
954 def test_type_error(self):
Barry Warsaw65279d02001-09-26 05:47:08 +0000955 self.assertRaises(TypeError, MIMEMessage, 'a plain string')
Barry Warsaw41075852001-09-23 03:18:13 +0000956
957 def test_valid_argument(self):
958 eq = self.assertEqual
Barry Warsaw2c685062002-06-02 19:09:27 +0000959 unless = self.failUnless
Barry Warsaw41075852001-09-23 03:18:13 +0000960 subject = 'A sub-message'
961 m = Message()
962 m['Subject'] = subject
Barry Warsaw65279d02001-09-26 05:47:08 +0000963 r = MIMEMessage(m)
Barry Warsaw41075852001-09-23 03:18:13 +0000964 eq(r.get_type(), 'message/rfc822')
Barry Warsaw2c685062002-06-02 19:09:27 +0000965 payload = r.get_payload()
966 unless(type(payload), ListType)
967 eq(len(payload), 1)
968 subpart = payload[0]
969 unless(subpart is m)
970 eq(subpart['subject'], subject)
971
972 def test_bad_multipart(self):
973 eq = self.assertEqual
974 msg1 = Message()
975 msg1['Subject'] = 'subpart 1'
976 msg2 = Message()
977 msg2['Subject'] = 'subpart 2'
978 r = MIMEMessage(msg1)
979 self.assertRaises(Errors.MultipartConversionError, r.attach, msg2)
Barry Warsaw41075852001-09-23 03:18:13 +0000980
981 def test_generate(self):
982 # First craft the message to be encapsulated
983 m = Message()
984 m['Subject'] = 'An enclosed message'
Barry Warsaw2c685062002-06-02 19:09:27 +0000985 m.set_payload('Here is the body of the message.\n')
Barry Warsaw65279d02001-09-26 05:47:08 +0000986 r = MIMEMessage(m)
Barry Warsaw41075852001-09-23 03:18:13 +0000987 r['Subject'] = 'The enclosing message'
988 s = StringIO()
989 g = Generator(s)
Barry Warsaw2c685062002-06-02 19:09:27 +0000990 g.flatten(r)
Barry Warsaw65279d02001-09-26 05:47:08 +0000991 self.assertEqual(s.getvalue(), """\
992Content-Type: message/rfc822
993MIME-Version: 1.0
994Subject: The enclosing message
995
996Subject: An enclosed message
997
998Here is the body of the message.
999""")
1000
1001 def test_parse_message_rfc822(self):
1002 eq = self.assertEqual
Barry Warsaw2c685062002-06-02 19:09:27 +00001003 unless = self.failUnless
Barry Warsaw65279d02001-09-26 05:47:08 +00001004 msg = self._msgobj('msg_11.txt')
1005 eq(msg.get_type(), 'message/rfc822')
Barry Warsaw2c685062002-06-02 19:09:27 +00001006 payload = msg.get_payload()
1007 unless(isinstance(payload, ListType))
1008 eq(len(payload), 1)
1009 submsg = payload[0]
Barry Warsaw65279d02001-09-26 05:47:08 +00001010 self.failUnless(isinstance(submsg, Message))
1011 eq(submsg['subject'], 'An enclosed message')
1012 eq(submsg.get_payload(), 'Here is the body of the message.\n')
1013
1014 def test_dsn(self):
1015 eq = self.assertEqual
1016 unless = self.failUnless
1017 # msg 16 is a Delivery Status Notification, see RFC XXXX
1018 msg = self._msgobj('msg_16.txt')
1019 eq(msg.get_type(), 'multipart/report')
1020 unless(msg.is_multipart())
1021 eq(len(msg.get_payload()), 3)
1022 # Subpart 1 is a text/plain, human readable section
1023 subpart = msg.get_payload(0)
1024 eq(subpart.get_type(), 'text/plain')
1025 eq(subpart.get_payload(), """\
1026This report relates to a message you sent with the following header fields:
1027
1028 Message-id: <002001c144a6$8752e060$56104586@oxy.edu>
1029 Date: Sun, 23 Sep 2001 20:10:55 -0700
1030 From: "Ian T. Henry" <henryi@oxy.edu>
1031 To: SoCal Raves <scr@socal-raves.org>
1032 Subject: [scr] yeah for Ians!!
1033
1034Your message cannot be delivered to the following recipients:
1035
1036 Recipient address: jangel1@cougar.noc.ucla.edu
1037 Reason: recipient reached disk quota
1038
1039""")
1040 # Subpart 2 contains the machine parsable DSN information. It
1041 # consists of two blocks of headers, represented by two nested Message
1042 # objects.
1043 subpart = msg.get_payload(1)
1044 eq(subpart.get_type(), 'message/delivery-status')
1045 eq(len(subpart.get_payload()), 2)
1046 # message/delivery-status should treat each block as a bunch of
1047 # headers, i.e. a bunch of Message objects.
1048 dsn1 = subpart.get_payload(0)
1049 unless(isinstance(dsn1, Message))
1050 eq(dsn1['original-envelope-id'], '0GK500B4HD0888@cougar.noc.ucla.edu')
1051 eq(dsn1.get_param('dns', header='reporting-mta'), '')
1052 # Try a missing one <wink>
1053 eq(dsn1.get_param('nsd', header='reporting-mta'), None)
1054 dsn2 = subpart.get_payload(1)
1055 unless(isinstance(dsn2, Message))
1056 eq(dsn2['action'], 'failed')
1057 eq(dsn2.get_params(header='original-recipient'),
1058 [('rfc822', ''), ('jangel1@cougar.noc.ucla.edu', '')])
1059 eq(dsn2.get_param('rfc822', header='final-recipient'), '')
1060 # Subpart 3 is the original message
1061 subpart = msg.get_payload(2)
1062 eq(subpart.get_type(), 'message/rfc822')
Barry Warsaw2c685062002-06-02 19:09:27 +00001063 payload = subpart.get_payload()
1064 unless(isinstance(payload, ListType))
1065 eq(len(payload), 1)
1066 subsubpart = payload[0]
Barry Warsaw65279d02001-09-26 05:47:08 +00001067 unless(isinstance(subsubpart, Message))
1068 eq(subsubpart.get_type(), 'text/plain')
1069 eq(subsubpart['message-id'],
1070 '<002001c144a6$8752e060$56104586@oxy.edu>')
Barry Warsaw41075852001-09-23 03:18:13 +00001071
Barry Warsaw1f0fa922001-10-19 04:08:59 +00001072 def test_epilogue(self):
1073 fp = openfile('msg_21.txt')
1074 try:
1075 text = fp.read()
1076 finally:
1077 fp.close()
1078 msg = Message()
1079 msg['From'] = 'aperson@dom.ain'
1080 msg['To'] = 'bperson@dom.ain'
1081 msg['Subject'] = 'Test'
1082 msg.preamble = 'MIME message\n'
1083 msg.epilogue = 'End of MIME message\n'
1084 msg1 = MIMEText('One')
1085 msg2 = MIMEText('Two')
1086 msg.add_header('Content-Type', 'multipart/mixed', boundary='BOUNDARY')
Barry Warsaw2c685062002-06-02 19:09:27 +00001087 msg.attach(msg1)
1088 msg.attach(msg2)
Barry Warsaw1f0fa922001-10-19 04:08:59 +00001089 sfp = StringIO()
1090 g = Generator(sfp)
Barry Warsaw2c685062002-06-02 19:09:27 +00001091 g.flatten(msg)
Barry Warsaw1f0fa922001-10-19 04:08:59 +00001092 self.assertEqual(sfp.getvalue(), text)
1093
Barry Warsaw41075852001-09-23 03:18:13 +00001094
Barry Warsaw08a534d2001-10-04 17:58:50 +00001095
1096# A general test of parser->model->generator idempotency. IOW, read a message
1097# in, parse it into a message object tree, then without touching the tree,
1098# regenerate the plain text. The original text and the transformed text
1099# should be identical. Note: that we ignore the Unix-From since that may
1100# contain a changed date.
Barry Warsawb6a92132002-06-28 23:49:33 +00001101class TestIdempotent(TestEmailBase):
Barry Warsaw41075852001-09-23 03:18:13 +00001102 def _msgobj(self, filename):
1103 fp = openfile(filename)
1104 try:
1105 data = fp.read()
1106 finally:
1107 fp.close()
Barry Warsaw65279d02001-09-26 05:47:08 +00001108 msg = email.message_from_string(data)
1109 return msg, data
Barry Warsaw41075852001-09-23 03:18:13 +00001110
1111 def _idempotent(self, msg, text):
Barry Warsawb6a92132002-06-28 23:49:33 +00001112 eq = self.ndiffAssertEqual
Barry Warsaw41075852001-09-23 03:18:13 +00001113 s = StringIO()
1114 g = Generator(s, maxheaderlen=0)
Barry Warsaw2c685062002-06-02 19:09:27 +00001115 g.flatten(msg)
Barry Warsaw41075852001-09-23 03:18:13 +00001116 eq(text, s.getvalue())
1117
1118 def test_parse_text_message(self):
1119 eq = self.assertEquals
1120 msg, text = self._msgobj('msg_01.txt')
1121 eq(msg.get_type(), 'text/plain')
1122 eq(msg.get_main_type(), 'text')
1123 eq(msg.get_subtype(), 'plain')
Barry Warsaw65279d02001-09-26 05:47:08 +00001124 eq(msg.get_params()[1], ('charset', 'us-ascii'))
Barry Warsaw41075852001-09-23 03:18:13 +00001125 eq(msg.get_param('charset'), 'us-ascii')
1126 eq(msg.preamble, None)
1127 eq(msg.epilogue, None)
1128 self._idempotent(msg, text)
1129
1130 def test_parse_untyped_message(self):
1131 eq = self.assertEquals
1132 msg, text = self._msgobj('msg_03.txt')
1133 eq(msg.get_type(), None)
1134 eq(msg.get_params(), None)
1135 eq(msg.get_param('charset'), None)
1136 self._idempotent(msg, text)
1137
1138 def test_simple_multipart(self):
1139 msg, text = self._msgobj('msg_04.txt')
1140 self._idempotent(msg, text)
1141
1142 def test_MIME_digest(self):
1143 msg, text = self._msgobj('msg_02.txt')
1144 self._idempotent(msg, text)
1145
Barry Warsaw19698172002-06-29 15:23:39 +00001146 def test_long_header(self):
1147 msg, text = self._msgobj('msg_27.txt')
1148 self._idempotent(msg, text)
1149
Barry Warsawb6a92132002-06-28 23:49:33 +00001150## def test_MIME_digest_with_part_headers(self):
1151## msg, text = self._msgobj('msg_28.txt')
1152## self._idempotent(msg, text)
1153
Barry Warsaw41075852001-09-23 03:18:13 +00001154 def test_mixed_with_image(self):
1155 msg, text = self._msgobj('msg_06.txt')
1156 self._idempotent(msg, text)
Tim Peters527e64f2001-10-04 05:36:56 +00001157
Barry Warsaw41075852001-09-23 03:18:13 +00001158 def test_multipart_report(self):
1159 msg, text = self._msgobj('msg_05.txt')
1160 self._idempotent(msg, text)
Barry Warsaw65279d02001-09-26 05:47:08 +00001161
1162 def test_dsn(self):
1163 msg, text = self._msgobj('msg_16.txt')
1164 self._idempotent(msg, text)
Tim Peters527e64f2001-10-04 05:36:56 +00001165
Barry Warsaw1f0fa922001-10-19 04:08:59 +00001166 def test_preamble_epilogue(self):
1167 msg, text = self._msgobj('msg_21.txt')
1168 self._idempotent(msg, text)
1169
Barry Warsaw763af412002-01-27 06:48:47 +00001170 def test_multipart_one_part(self):
1171 msg, text = self._msgobj('msg_23.txt')
1172 self._idempotent(msg, text)
1173
Barry Warsaw409a4c02002-04-10 21:01:31 +00001174 def test_multipart_no_parts(self):
1175 msg, text = self._msgobj('msg_24.txt')
1176 self._idempotent(msg, text)
1177
Barry Warsaw41075852001-09-23 03:18:13 +00001178 def test_content_type(self):
1179 eq = self.assertEquals
Barry Warsaw2c685062002-06-02 19:09:27 +00001180 unless = self.failUnless
Barry Warsaw41075852001-09-23 03:18:13 +00001181 # Get a message object and reset the seek pointer for other tests
1182 msg, text = self._msgobj('msg_05.txt')
1183 eq(msg.get_type(), 'multipart/report')
1184 # Test the Content-Type: parameters
1185 params = {}
Barry Warsaw65279d02001-09-26 05:47:08 +00001186 for pk, pv in msg.get_params():
Barry Warsaw41075852001-09-23 03:18:13 +00001187 params[pk] = pv
1188 eq(params['report-type'], 'delivery-status')
Barry Warsaw65279d02001-09-26 05:47:08 +00001189 eq(params['boundary'], 'D1690A7AC1.996856090/mail.example.com')
Barry Warsaw41075852001-09-23 03:18:13 +00001190 eq(msg.preamble, 'This is a MIME-encapsulated message.\n\n')
1191 eq(msg.epilogue, '\n\n')
1192 eq(len(msg.get_payload()), 3)
1193 # Make sure the subparts are what we expect
1194 msg1 = msg.get_payload(0)
1195 eq(msg1.get_type(), 'text/plain')
1196 eq(msg1.get_payload(), 'Yadda yadda yadda\n')
1197 msg2 = msg.get_payload(1)
1198 eq(msg2.get_type(), None)
1199 eq(msg2.get_payload(), 'Yadda yadda yadda\n')
1200 msg3 = msg.get_payload(2)
1201 eq(msg3.get_type(), 'message/rfc822')
1202 self.failUnless(isinstance(msg3, Message))
Barry Warsaw2c685062002-06-02 19:09:27 +00001203 payload = msg3.get_payload()
1204 unless(isinstance(payload, ListType))
1205 eq(len(payload), 1)
1206 msg4 = payload[0]
1207 unless(isinstance(msg4, Message))
Barry Warsaw41075852001-09-23 03:18:13 +00001208 eq(msg4.get_payload(), 'Yadda yadda yadda\n')
1209
1210 def test_parser(self):
1211 eq = self.assertEquals
Barry Warsaw2c685062002-06-02 19:09:27 +00001212 unless = self.failUnless
Barry Warsaw41075852001-09-23 03:18:13 +00001213 msg, text = self._msgobj('msg_06.txt')
1214 # Check some of the outer headers
1215 eq(msg.get_type(), 'message/rfc822')
Barry Warsaw2c685062002-06-02 19:09:27 +00001216 # Make sure the payload is a list of exactly one sub-Message, and that
1217 # that submessage has a type of text/plain
1218 payload = msg.get_payload()
1219 unless(isinstance(payload, ListType))
1220 eq(len(payload), 1)
1221 msg1 = payload[0]
Barry Warsaw41075852001-09-23 03:18:13 +00001222 self.failUnless(isinstance(msg1, Message))
1223 eq(msg1.get_type(), 'text/plain')
1224 self.failUnless(isinstance(msg1.get_payload(), StringType))
1225 eq(msg1.get_payload(), '\n')
Barry Warsaw41075852001-09-23 03:18:13 +00001226
Barry Warsaw08a534d2001-10-04 17:58:50 +00001227
1228# Test various other bits of the package's functionality
Barry Warsaw41075852001-09-23 03:18:13 +00001229class TestMiscellaneous(unittest.TestCase):
1230 def test_message_from_string(self):
1231 fp = openfile('msg_01.txt')
1232 try:
1233 text = fp.read()
1234 finally:
1235 fp.close()
1236 msg = email.message_from_string(text)
1237 s = StringIO()
1238 # Don't wrap/continue long headers since we're trying to test
1239 # idempotency.
1240 g = Generator(s, maxheaderlen=0)
Barry Warsaw2c685062002-06-02 19:09:27 +00001241 g.flatten(msg)
Barry Warsaw41075852001-09-23 03:18:13 +00001242 self.assertEqual(text, s.getvalue())
1243
1244 def test_message_from_file(self):
1245 fp = openfile('msg_01.txt')
1246 try:
1247 text = fp.read()
1248 fp.seek(0)
1249 msg = email.message_from_file(fp)
1250 s = StringIO()
1251 # Don't wrap/continue long headers since we're trying to test
1252 # idempotency.
1253 g = Generator(s, maxheaderlen=0)
Barry Warsaw2c685062002-06-02 19:09:27 +00001254 g.flatten(msg)
Barry Warsaw41075852001-09-23 03:18:13 +00001255 self.assertEqual(text, s.getvalue())
1256 finally:
1257 fp.close()
1258
1259 def test_message_from_string_with_class(self):
1260 unless = self.failUnless
1261 fp = openfile('msg_01.txt')
1262 try:
1263 text = fp.read()
1264 finally:
1265 fp.close()
1266 # Create a subclass
1267 class MyMessage(Message):
1268 pass
Tim Peters527e64f2001-10-04 05:36:56 +00001269
Barry Warsaw41075852001-09-23 03:18:13 +00001270 msg = email.message_from_string(text, MyMessage)
1271 unless(isinstance(msg, MyMessage))
1272 # Try something more complicated
1273 fp = openfile('msg_02.txt')
1274 try:
1275 text = fp.read()
1276 finally:
1277 fp.close()
1278 msg = email.message_from_string(text, MyMessage)
1279 for subpart in msg.walk():
1280 unless(isinstance(subpart, MyMessage))
1281
Barry Warsaw41075852001-09-23 03:18:13 +00001282 def test_message_from_file_with_class(self):
1283 unless = self.failUnless
1284 # Create a subclass
1285 class MyMessage(Message):
1286 pass
Tim Peters527e64f2001-10-04 05:36:56 +00001287
Barry Warsaw41075852001-09-23 03:18:13 +00001288 fp = openfile('msg_01.txt')
1289 try:
1290 msg = email.message_from_file(fp, MyMessage)
1291 finally:
1292 fp.close()
1293 unless(isinstance(msg, MyMessage))
1294 # Try something more complicated
1295 fp = openfile('msg_02.txt')
1296 try:
1297 msg = email.message_from_file(fp, MyMessage)
1298 finally:
1299 fp.close()
1300 for subpart in msg.walk():
1301 unless(isinstance(subpart, MyMessage))
1302
Barry Warsawfee435a2001-10-09 19:23:57 +00001303 def test__all__(self):
1304 module = __import__('email')
1305 all = module.__all__
1306 all.sort()
Barry Warsaw16f90552002-04-16 05:06:42 +00001307 self.assertEqual(all, ['Charset', 'Encoders', 'Errors', 'Generator',
1308 'Header', 'Iterators', 'MIMEAudio',
1309 'MIMEBase', 'MIMEImage', 'MIMEMessage',
Barry Warsaw409a4c02002-04-10 21:01:31 +00001310 'MIMEText', 'Message', 'Parser',
Barry Warsaw16f90552002-04-16 05:06:42 +00001311 'Utils', 'base64MIME',
Barry Warsaw409a4c02002-04-10 21:01:31 +00001312 'message_from_file', 'message_from_string',
1313 'quopriMIME'])
Barry Warsawfee435a2001-10-09 19:23:57 +00001314
Barry Warsaw75edc6a2001-11-09 17:46:17 +00001315 def test_formatdate(self):
Barry Warsaw409a4c02002-04-10 21:01:31 +00001316 now = time.time()
1317 self.assertEqual(Utils.parsedate(Utils.formatdate(now))[:6],
1318 time.gmtime(now)[:6])
Barry Warsaw75edc6a2001-11-09 17:46:17 +00001319
Barry Warsaw4586d2c2001-11-19 18:38:42 +00001320 def test_formatdate_localtime(self):
Barry Warsaw409a4c02002-04-10 21:01:31 +00001321 now = time.time()
1322 self.assertEqual(
1323 Utils.parsedate(Utils.formatdate(now, localtime=1))[:6],
1324 time.localtime(now)[:6])
Barry Warsaw75a40fc2001-11-19 16:31:06 +00001325
1326 def test_parsedate_none(self):
Barry Warsaw19c10ca2001-11-13 18:01:37 +00001327 self.assertEqual(Utils.parsedate(''), None)
1328
Barry Warsaweae36ac2001-12-20 16:37:27 +00001329 def test_parseaddr_empty(self):
1330 self.assertEqual(Utils.parseaddr('<>'), ('', ''))
Barry Warsaw409a4c02002-04-10 21:01:31 +00001331 self.assertEqual(Utils.formataddr(Utils.parseaddr('<>')), '')
1332
1333 def test_noquote_dump(self):
1334 self.assertEqual(
1335 Utils.formataddr(('A Silly Person', 'person@dom.ain')),
1336 'A Silly Person <person@dom.ain>')
1337
1338 def test_escape_dump(self):
1339 self.assertEqual(
1340 Utils.formataddr(('A (Very) Silly Person', 'person@dom.ain')),
1341 r'"A \(Very\) Silly Person" <person@dom.ain>')
1342 a = r'A \(Special\) Person'
1343 b = 'person@dom.ain'
1344 self.assertEqual(Utils.parseaddr(Utils.formataddr((a, b))), (a, b))
1345
1346 def test_quote_dump(self):
1347 self.assertEqual(
1348 Utils.formataddr(('A Silly; Person', 'person@dom.ain')),
1349 r'"A Silly; Person" <person@dom.ain>')
1350
1351 def test_fix_eols(self):
1352 eq = self.assertEqual
1353 eq(Utils.fix_eols('hello'), 'hello')
1354 eq(Utils.fix_eols('hello\n'), 'hello\r\n')
1355 eq(Utils.fix_eols('hello\r'), 'hello\r\n')
1356 eq(Utils.fix_eols('hello\r\n'), 'hello\r\n')
1357 eq(Utils.fix_eols('hello\n\r'), 'hello\r\n\r\n')
1358
1359 def test_charset_richcomparisons(self):
1360 eq = self.assertEqual
1361 ne = self.failIfEqual
1362 cset1 = Charset()
1363 cset2 = Charset()
1364 eq(cset1, 'us-ascii')
1365 eq(cset1, 'US-ASCII')
1366 eq(cset1, 'Us-AsCiI')
1367 eq('us-ascii', cset1)
1368 eq('US-ASCII', cset1)
1369 eq('Us-AsCiI', cset1)
1370 ne(cset1, 'usascii')
1371 ne(cset1, 'USASCII')
1372 ne(cset1, 'UsAsCiI')
1373 ne('usascii', cset1)
1374 ne('USASCII', cset1)
1375 ne('UsAsCiI', cset1)
1376 eq(cset1, cset2)
1377 eq(cset2, cset1)
Barry Warsaweae36ac2001-12-20 16:37:27 +00001378
Barry Warsaw4be9ecc2002-05-22 01:52:10 +00001379 def test_getaddresses(self):
1380 eq = self.assertEqual
1381 eq(Utils.getaddresses(['aperson@dom.ain (Al Person)',
1382 'Bud Person <bperson@dom.ain>']),
1383 [('Al Person', 'aperson@dom.ain'),
1384 ('Bud Person', 'bperson@dom.ain')])
1385
Barry Warsaw41075852001-09-23 03:18:13 +00001386
Barry Warsaw08a534d2001-10-04 17:58:50 +00001387
1388# Test the iterator/generators
Barry Warsaw41075852001-09-23 03:18:13 +00001389class TestIterators(TestEmailBase):
1390 def test_body_line_iterator(self):
1391 eq = self.assertEqual
1392 # First a simple non-multipart message
1393 msg = self._msgobj('msg_01.txt')
1394 it = Iterators.body_line_iterator(msg)
Barry Warsawd1de6ea2001-10-04 18:18:37 +00001395 lines = list(it)
Barry Warsaw41075852001-09-23 03:18:13 +00001396 eq(len(lines), 6)
1397 eq(EMPTYSTRING.join(lines), msg.get_payload())
1398 # Now a more complicated multipart
1399 msg = self._msgobj('msg_02.txt')
1400 it = Iterators.body_line_iterator(msg)
Barry Warsawd1de6ea2001-10-04 18:18:37 +00001401 lines = list(it)
Barry Warsaw41075852001-09-23 03:18:13 +00001402 eq(len(lines), 43)
Barry Warsaw08a534d2001-10-04 17:58:50 +00001403 eq(EMPTYSTRING.join(lines), openfile('msg_19.txt').read())
Barry Warsaw41075852001-09-23 03:18:13 +00001404
1405 def test_typed_subpart_iterator(self):
1406 eq = self.assertEqual
1407 msg = self._msgobj('msg_04.txt')
1408 it = Iterators.typed_subpart_iterator(msg, 'text')
Barry Warsaw409a4c02002-04-10 21:01:31 +00001409 lines = []
1410 subparts = 0
1411 for subpart in it:
1412 subparts += 1
1413 lines.append(subpart.get_payload())
1414 eq(subparts, 2)
Barry Warsaw41075852001-09-23 03:18:13 +00001415 eq(EMPTYSTRING.join(lines), """\
1416a simple kind of mirror
1417to reflect upon our own
1418a simple kind of mirror
1419to reflect upon our own
1420""")
1421
Barry Warsawcdc632c2001-10-15 04:39:02 +00001422 def test_typed_subpart_iterator_default_type(self):
1423 eq = self.assertEqual
1424 msg = self._msgobj('msg_03.txt')
1425 it = Iterators.typed_subpart_iterator(msg, 'text', 'plain')
1426 lines = []
1427 subparts = 0
1428 for subpart in it:
1429 subparts += 1
1430 lines.append(subpart.get_payload())
1431 eq(subparts, 1)
1432 eq(EMPTYSTRING.join(lines), """\
1433
1434Hi,
1435
1436Do you like this message?
1437
1438-Me
1439""")
Barry Warsaw41075852001-09-23 03:18:13 +00001440
Barry Warsaw409a4c02002-04-10 21:01:31 +00001441
Barry Warsaw08a534d2001-10-04 17:58:50 +00001442
Barry Warsawbf7a59d2001-10-11 15:44:50 +00001443class TestParsers(unittest.TestCase):
1444 def test_header_parser(self):
1445 eq = self.assertEqual
1446 # Parse only the headers of a complex multipart MIME document
1447 p = HeaderParser()
1448 fp = openfile('msg_02.txt')
1449 msg = p.parse(fp)
1450 eq(msg['from'], 'ppp-request@zzz.org')
1451 eq(msg['to'], 'ppp@zzz.org')
1452 eq(msg.get_type(), 'multipart/mixed')
1453 eq(msg.is_multipart(), 0)
1454 self.failUnless(isinstance(msg.get_payload(), StringType))
1455
Barry Warsaw409a4c02002-04-10 21:01:31 +00001456 def test_whitespace_continuaton(self):
1457 eq = self.assertEqual
1458 # This message contains a line after the Subject: header that has only
1459 # whitespace, but it is not empty!
1460 msg = email.message_from_string("""\
1461From: aperson@dom.ain
1462To: bperson@dom.ain
1463Subject: the next line has a space on it
Barry Warsaw16f90552002-04-16 05:06:42 +00001464\x20
Barry Warsaw409a4c02002-04-10 21:01:31 +00001465Date: Mon, 8 Apr 2002 15:09:19 -0400
1466Message-ID: spam
1467
1468Here's the message body
1469""")
1470 eq(msg['subject'], 'the next line has a space on it\n ')
1471 eq(msg['message-id'], 'spam')
1472 eq(msg.get_payload(), "Here's the message body\n")
1473
Barry Warsawe0d85c82002-05-19 23:52:54 +00001474 def test_crlf_separation(self):
1475 eq = self.assertEqual
1476 fp = openfile('msg_26.txt')
1477 p = Parser()
1478 msg = p.parse(fp)
1479 eq(len(msg.get_payload()), 2)
1480 part1 = msg.get_payload(0)
1481 eq(part1.get_type(), 'text/plain')
1482 eq(part1.get_payload(), 'Simple email with attachment.\r\n\r\n')
1483 part2 = msg.get_payload(1)
1484 eq(part2.get_type(), 'application/riscos')
1485
Barry Warsawb6a92132002-06-28 23:49:33 +00001486## def test_multipart_digest_with_extra_mime_headers(self):
1487## eq = self.assertEqual
1488## fp = openfile('msg_28.txt')
1489## p = Parser()
1490## msg = p.parse(fp)
1491## self.failUnless(msg.is_multipart())
1492## eq(len(msg.get_payload()), 2)
1493## part1 = msg.get_payload(0)
1494## eq(part1.get_type(), 'text/plain')
1495## eq(part1.get_payload(), 'message 1')
1496## part2 = msg.get_payload(1)
1497## eq(part2.get_type(), 'text/plain')
1498## eq(part2.get_payload(), 'message 2')
1499
Barry Warsaw409a4c02002-04-10 21:01:31 +00001500
1501
1502class TestBase64(unittest.TestCase):
1503 def test_len(self):
1504 eq = self.assertEqual
1505 eq(base64MIME.base64_len('hello'),
1506 len(base64MIME.encode('hello', eol='')))
1507 for size in range(15):
1508 if size == 0 : bsize = 0
1509 elif size <= 3 : bsize = 4
1510 elif size <= 6 : bsize = 8
1511 elif size <= 9 : bsize = 12
1512 elif size <= 12: bsize = 16
1513 else : bsize = 20
1514 eq(base64MIME.base64_len('x'*size), bsize)
1515
1516 def test_decode(self):
1517 eq = self.assertEqual
1518 eq(base64MIME.decode(''), '')
1519 eq(base64MIME.decode('aGVsbG8='), 'hello')
1520 eq(base64MIME.decode('aGVsbG8=', 'X'), 'hello')
1521 eq(base64MIME.decode('aGVsbG8NCndvcmxk\n', 'X'), 'helloXworld')
1522
1523 def test_encode(self):
1524 eq = self.assertEqual
1525 eq(base64MIME.encode(''), '')
1526 eq(base64MIME.encode('hello'), 'aGVsbG8=\n')
1527 # Test the binary flag
1528 eq(base64MIME.encode('hello\n'), 'aGVsbG8K\n')
1529 eq(base64MIME.encode('hello\n', 0), 'aGVsbG8NCg==\n')
1530 # Test the maxlinelen arg
1531 eq(base64MIME.encode('xxxx ' * 20, maxlinelen=40), """\
1532eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg
1533eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg
1534eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg
1535eHh4eCB4eHh4IA==
1536""")
1537 # Test the eol argument
1538 eq(base64MIME.encode('xxxx ' * 20, maxlinelen=40, eol='\r\n'), """\
1539eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg\r
1540eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg\r
1541eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg\r
1542eHh4eCB4eHh4IA==\r
1543""")
Barry Warsaw16f90552002-04-16 05:06:42 +00001544
Barry Warsaw409a4c02002-04-10 21:01:31 +00001545 def test_header_encode(self):
1546 eq = self.assertEqual
1547 he = base64MIME.header_encode
1548 eq(he('hello'), '=?iso-8859-1?b?aGVsbG8=?=')
1549 eq(he('hello\nworld'), '=?iso-8859-1?b?aGVsbG8NCndvcmxk?=')
1550 # Test the charset option
1551 eq(he('hello', charset='iso-8859-2'), '=?iso-8859-2?b?aGVsbG8=?=')
1552 # Test the keep_eols flag
1553 eq(he('hello\nworld', keep_eols=1),
1554 '=?iso-8859-1?b?aGVsbG8Kd29ybGQ=?=')
1555 # Test the maxlinelen argument
1556 eq(he('xxxx ' * 20, maxlinelen=40), """\
1557=?iso-8859-1?b?eHh4eCB4eHh4IHh4eHggeHg=?=
1558 =?iso-8859-1?b?eHggeHh4eCB4eHh4IHh4eHg=?=
1559 =?iso-8859-1?b?IHh4eHggeHh4eCB4eHh4IHg=?=
1560 =?iso-8859-1?b?eHh4IHh4eHggeHh4eCB4eHg=?=
1561 =?iso-8859-1?b?eCB4eHh4IHh4eHggeHh4eCA=?=
1562 =?iso-8859-1?b?eHh4eCB4eHh4IHh4eHgg?=""")
1563 # Test the eol argument
1564 eq(he('xxxx ' * 20, maxlinelen=40, eol='\r\n'), """\
1565=?iso-8859-1?b?eHh4eCB4eHh4IHh4eHggeHg=?=\r
1566 =?iso-8859-1?b?eHggeHh4eCB4eHh4IHh4eHg=?=\r
1567 =?iso-8859-1?b?IHh4eHggeHh4eCB4eHh4IHg=?=\r
1568 =?iso-8859-1?b?eHh4IHh4eHggeHh4eCB4eHg=?=\r
1569 =?iso-8859-1?b?eCB4eHh4IHh4eHggeHh4eCA=?=\r
1570 =?iso-8859-1?b?eHh4eCB4eHh4IHh4eHgg?=""")
1571
1572
1573
1574class TestQuopri(unittest.TestCase):
1575 def setUp(self):
1576 self.hlit = [chr(x) for x in range(ord('a'), ord('z')+1)] + \
1577 [chr(x) for x in range(ord('A'), ord('Z')+1)] + \
1578 [chr(x) for x in range(ord('0'), ord('9')+1)] + \
1579 ['!', '*', '+', '-', '/', ' ']
1580 self.hnon = [chr(x) for x in range(256) if chr(x) not in self.hlit]
1581 assert len(self.hlit) + len(self.hnon) == 256
1582 self.blit = [chr(x) for x in range(ord(' '), ord('~')+1)] + ['\t']
1583 self.blit.remove('=')
1584 self.bnon = [chr(x) for x in range(256) if chr(x) not in self.blit]
1585 assert len(self.blit) + len(self.bnon) == 256
1586
1587 def test_header_quopri_check(self):
1588 for c in self.hlit:
1589 self.failIf(quopriMIME.header_quopri_check(c))
1590 for c in self.hnon:
1591 self.failUnless(quopriMIME.header_quopri_check(c))
1592
1593 def test_body_quopri_check(self):
1594 for c in self.blit:
1595 self.failIf(quopriMIME.body_quopri_check(c))
1596 for c in self.bnon:
1597 self.failUnless(quopriMIME.body_quopri_check(c))
1598
1599 def test_header_quopri_len(self):
1600 eq = self.assertEqual
1601 hql = quopriMIME.header_quopri_len
1602 enc = quopriMIME.header_encode
1603 for s in ('hello', 'h@e@l@l@o@'):
1604 # Empty charset and no line-endings. 7 == RFC chrome
1605 eq(hql(s), len(enc(s, charset='', eol=''))-7)
1606 for c in self.hlit:
1607 eq(hql(c), 1)
1608 for c in self.hnon:
1609 eq(hql(c), 3)
1610
1611 def test_body_quopri_len(self):
1612 eq = self.assertEqual
1613 bql = quopriMIME.body_quopri_len
1614 for c in self.blit:
1615 eq(bql(c), 1)
1616 for c in self.bnon:
1617 eq(bql(c), 3)
1618
1619 def test_quote_unquote_idempotent(self):
1620 for x in range(256):
1621 c = chr(x)
1622 self.assertEqual(quopriMIME.unquote(quopriMIME.quote(c)), c)
1623
1624 def test_header_encode(self):
1625 eq = self.assertEqual
1626 he = quopriMIME.header_encode
1627 eq(he('hello'), '=?iso-8859-1?q?hello?=')
1628 eq(he('hello\nworld'), '=?iso-8859-1?q?hello=0D=0Aworld?=')
1629 # Test the charset option
1630 eq(he('hello', charset='iso-8859-2'), '=?iso-8859-2?q?hello?=')
1631 # Test the keep_eols flag
1632 eq(he('hello\nworld', keep_eols=1), '=?iso-8859-1?q?hello=0Aworld?=')
1633 # Test a non-ASCII character
Jack Jansen1476c272002-04-23 10:52:44 +00001634 eq(he('hello\xc7there'), '=?iso-8859-1?q?hello=C7there?=')
Barry Warsaw409a4c02002-04-10 21:01:31 +00001635 # Test the maxlinelen argument
1636 eq(he('xxxx ' * 20, maxlinelen=40), """\
1637=?iso-8859-1?q?xxxx_xxxx_xxxx_xxxx_xx?=
1638 =?iso-8859-1?q?xx_xxxx_xxxx_xxxx_xxxx?=
1639 =?iso-8859-1?q?_xxxx_xxxx_xxxx_xxxx_x?=
1640 =?iso-8859-1?q?xxx_xxxx_xxxx_xxxx_xxx?=
1641 =?iso-8859-1?q?x_xxxx_xxxx_?=""")
1642 # Test the eol argument
1643 eq(he('xxxx ' * 20, maxlinelen=40, eol='\r\n'), """\
1644=?iso-8859-1?q?xxxx_xxxx_xxxx_xxxx_xx?=\r
1645 =?iso-8859-1?q?xx_xxxx_xxxx_xxxx_xxxx?=\r
1646 =?iso-8859-1?q?_xxxx_xxxx_xxxx_xxxx_x?=\r
1647 =?iso-8859-1?q?xxx_xxxx_xxxx_xxxx_xxx?=\r
1648 =?iso-8859-1?q?x_xxxx_xxxx_?=""")
1649
1650 def test_decode(self):
1651 eq = self.assertEqual
1652 eq(quopriMIME.decode(''), '')
1653 eq(quopriMIME.decode('hello'), 'hello')
1654 eq(quopriMIME.decode('hello', 'X'), 'hello')
1655 eq(quopriMIME.decode('hello\nworld', 'X'), 'helloXworld')
1656
1657 def test_encode(self):
1658 eq = self.assertEqual
1659 eq(quopriMIME.encode(''), '')
1660 eq(quopriMIME.encode('hello'), 'hello')
1661 # Test the binary flag
1662 eq(quopriMIME.encode('hello\r\nworld'), 'hello\nworld')
1663 eq(quopriMIME.encode('hello\r\nworld', 0), 'hello\nworld')
1664 # Test the maxlinelen arg
1665 eq(quopriMIME.encode('xxxx ' * 20, maxlinelen=40), """\
1666xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx=
1667 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxx=
1668x xxxx xxxx xxxx xxxx=20""")
1669 # Test the eol argument
1670 eq(quopriMIME.encode('xxxx ' * 20, maxlinelen=40, eol='\r\n'), """\
1671xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx=\r
1672 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxx=\r
1673x xxxx xxxx xxxx xxxx=20""")
1674 eq(quopriMIME.encode("""\
1675one line
1676
1677two line"""), """\
1678one line
1679
1680two line""")
Barry Warsaw16f90552002-04-16 05:06:42 +00001681
Barry Warsaw409a4c02002-04-10 21:01:31 +00001682
1683
1684# Test the Charset class
1685class TestCharset(unittest.TestCase):
1686 def test_idempotent(self):
1687 eq = self.assertEqual
1688 # Make sure us-ascii = no Unicode conversion
1689 c = Charset('us-ascii')
1690 s = 'Hello World!'
1691 sp = c.to_splittable(s)
1692 eq(s, c.from_splittable(sp))
1693 # test 8-bit idempotency with us-ascii
1694 s = '\xa4\xa2\xa4\xa4\xa4\xa6\xa4\xa8\xa4\xaa'
1695 sp = c.to_splittable(s)
1696 eq(s, c.from_splittable(sp))
1697
1698
1699
1700# Test multilingual MIME headers.
Barry Warsawb6a92132002-06-28 23:49:33 +00001701class TestHeader(TestEmailBase):
Barry Warsaw409a4c02002-04-10 21:01:31 +00001702 def test_simple(self):
Barry Warsawb6a92132002-06-28 23:49:33 +00001703 eq = self.ndiffAssertEqual
1704 h = Header('Hello World!')
1705 eq(h.encode(), 'Hello World!')
1706 h.append(' Goodbye World!')
1707 eq(h.encode(), 'Hello World! Goodbye World!')
1708
1709 def test_simple_surprise(self):
1710 eq = self.ndiffAssertEqual
Barry Warsaw409a4c02002-04-10 21:01:31 +00001711 h = Header('Hello World!')
1712 eq(h.encode(), 'Hello World!')
1713 h.append('Goodbye World!')
Barry Warsawb6a92132002-06-28 23:49:33 +00001714 eq(h.encode(), 'Hello World!Goodbye World!')
Barry Warsaw409a4c02002-04-10 21:01:31 +00001715
1716 def test_header_needs_no_decoding(self):
1717 h = 'no decoding needed'
1718 self.assertEqual(decode_header(h), [(h, None)])
1719
1720 def test_long(self):
1721 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.",
1722 maxlinelen=76)
1723 for l in h.encode().split('\n '):
1724 self.failUnless(len(l) <= 76)
1725
1726 def test_multilingual(self):
1727 eq = self.assertEqual
1728 g = Charset("iso-8859-1")
1729 cz = Charset("iso-8859-2")
1730 utf8 = Charset("utf-8")
1731 g_head = "Die Mieter treten hier ein werden mit einem Foerderband komfortabel den Korridor entlang, an s\xfcdl\xfcndischen Wandgem\xe4lden vorbei, gegen die rotierenden Klingen bef\xf6rdert. "
1732 cz_head = "Finan\xe8ni metropole se hroutily pod tlakem jejich d\xf9vtipu.. "
1733 utf8_head = u"\u6b63\u78ba\u306b\u8a00\u3046\u3068\u7ffb\u8a33\u306f\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u4e00\u90e8\u306f\u30c9\u30a4\u30c4\u8a9e\u3067\u3059\u304c\u3001\u3042\u3068\u306f\u3067\u305f\u3089\u3081\u3067\u3059\u3002\u5b9f\u969b\u306b\u306f\u300cWenn ist das Nunstuck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput.\u300d\u3068\u8a00\u3063\u3066\u3044\u307e\u3059\u3002".encode("utf-8")
1734 h = Header(g_head, g)
1735 h.append(cz_head, cz)
1736 h.append(utf8_head, utf8)
1737 enc = h.encode()
1738 eq(enc, """=?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_eine?=
1739 =?iso-8859-1?q?m_Foerderband_komfortabel_den_Korridor_ent?=
1740 =?iso-8859-1?q?lang=2C_an_s=FCdl=FCndischen_Wandgem=E4lden_vorbei?=
1741 =?iso-8859-1?q?=2C_gegen_die_rotierenden_Klingen_bef=F6rdert=2E_?=
1742 =?iso-8859-2?q?Finan=E8ni_metropole_se_hroutil?=
1743 =?iso-8859-2?q?y_pod_tlakem_jejich_d=F9vtipu=2E=2E_?=
1744 =?utf-8?b?5q2j56K644Gr6KiA44GG44Go57+76Kiz44Gv?=
1745 =?utf-8?b?44GV44KM44Gm44GE44G+44Gb44KT44CC5LiA?=
1746 =?utf-8?b?6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM?=
1747 =?utf-8?b?44CB44GC44Go44Gv44Gn44Gf44KJ44KB44Gn?=
1748 =?utf-8?b?44GZ44CC5a6f6Zqb44Gr44Gv44CMV2VubiBpc3QgZGE=?=
1749 =?utf-8?b?cyBOdW5zdHVjayBnaXQgdW5k?=
1750 =?utf-8?b?IFNsb3Rlcm1leWVyPyBKYSEgQmVpaGVyaHVuZCBkYXMgT2Rl?=
1751 =?utf-8?b?ciBkaWUgRmxpcHBlcndhbGR0?=
1752 =?utf-8?b?IGdlcnNwdXQu44CN44Go6KiA44Gj44Gm44GE44G+44GZ44CC?=""")
1753 eq(decode_header(enc),
1754 [(g_head, "iso-8859-1"), (cz_head, "iso-8859-2"),
1755 (utf8_head, "utf-8")])
Barry Warsaw3fdc8892002-06-29 03:27:27 +00001756 # Test for conversion to unicode. BAW: Python 2.1 doesn't support the
1757 # __unicode__() protocol, so do things this way for compatibility.
1758 ustr = h.__unicode__()
1759 # For Python 2.2 and beyond
1760 #ustr = unicode(h)
1761 eq(ustr.encode('utf-8'),
1762 'Die Mieter treten hier ein werden mit einem Foerderband '
1763 'komfortabel den Korridor entlang, an s\xc3\xbcdl\xc3\xbcndischen '
1764 'Wandgem\xc3\xa4lden vorbei, gegen die rotierenden Klingen '
1765 'bef\xc3\xb6rdert. Finan\xc4\x8dni metropole se hroutily pod '
1766 'tlakem jejich d\xc5\xafvtipu.. \xe6\xad\xa3\xe7\xa2\xba\xe3\x81'
1767 '\xab\xe8\xa8\x80\xe3\x81\x86\xe3\x81\xa8\xe7\xbf\xbb\xe8\xa8\xb3'
1768 '\xe3\x81\xaf\xe3\x81\x95\xe3\x82\x8c\xe3\x81\xa6\xe3\x81\x84\xe3'
1769 '\x81\xbe\xe3\x81\x9b\xe3\x82\x93\xe3\x80\x82\xe4\xb8\x80\xe9\x83'
1770 '\xa8\xe3\x81\xaf\xe3\x83\x89\xe3\x82\xa4\xe3\x83\x84\xe8\xaa\x9e'
1771 '\xe3\x81\xa7\xe3\x81\x99\xe3\x81\x8c\xe3\x80\x81\xe3\x81\x82\xe3'
1772 '\x81\xa8\xe3\x81\xaf\xe3\x81\xa7\xe3\x81\x9f\xe3\x82\x89\xe3\x82'
1773 '\x81\xe3\x81\xa7\xe3\x81\x99\xe3\x80\x82\xe5\xae\x9f\xe9\x9a\x9b'
1774 '\xe3\x81\xab\xe3\x81\xaf\xe3\x80\x8cWenn ist das Nunstuck git '
1775 'und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt '
1776 'gersput.\xe3\x80\x8d\xe3\x81\xa8\xe8\xa8\x80\xe3\x81\xa3\xe3\x81'
1777 '\xa6\xe3\x81\x84\xe3\x81\xbe\xe3\x81\x99\xe3\x80\x82')
Barry Warsaw409a4c02002-04-10 21:01:31 +00001778
Barry Warsawe0d85c82002-05-19 23:52:54 +00001779 def test_explicit_maxlinelen(self):
Barry Warsawb6a92132002-06-28 23:49:33 +00001780 eq = self.ndiffAssertEqual
Barry Warsawe0d85c82002-05-19 23:52:54 +00001781 hstr = 'A very long line that must get split to something other than at the 76th character boundary to test the non-default behavior'
1782 h = Header(hstr)
1783 eq(h.encode(), '''\
Barry Warsawb6a92132002-06-28 23:49:33 +00001784A very long line that must get split to something other than at the 76th
1785 character boundary to test the non-default behavior''')
Barry Warsawe0d85c82002-05-19 23:52:54 +00001786 h = Header(hstr, header_name='Subject')
1787 eq(h.encode(), '''\
1788A very long line that must get split to something other than at the
Barry Warsawb6a92132002-06-28 23:49:33 +00001789 76th character boundary to test the non-default behavior''')
Barry Warsawe0d85c82002-05-19 23:52:54 +00001790 h = Header(hstr, maxlinelen=1024, header_name='Subject')
1791 eq(h.encode(), hstr)
1792
Barry Warsawbf7a59d2001-10-11 15:44:50 +00001793
Barry Warsaw9546e792002-06-29 05:58:45 +00001794# Test RFC 2231 header parameters decoding
1795class TestRFC2231(TestEmailBase):
1796 def test_get_param(self):
1797 eq = self.assertEqual
1798 msg = self._msgobj('msg_29.txt')
1799 eq(msg.get_param('title'),
1800 ('us-ascii', 'en', 'This is even more ***fun*** isn\'t it!'))
1801 eq(msg.get_param('title', unquote=0),
1802 ('us-ascii', 'en', '"This is even more ***fun*** isn\'t it!"'))
1803
1804
Barry Warsawbf7a59d2001-10-11 15:44:50 +00001805
Barry Warsawc9ad32c2002-04-15 22:14:06 +00001806def _testclasses():
1807 mod = sys.modules[__name__]
1808 return [getattr(mod, name) for name in dir(mod) if name.startswith('Test')]
1809
1810
Barry Warsaw41075852001-09-23 03:18:13 +00001811def suite():
1812 suite = unittest.TestSuite()
Barry Warsawc9ad32c2002-04-15 22:14:06 +00001813 for testclass in _testclasses():
1814 suite.addTest(unittest.makeSuite(testclass))
Barry Warsaw41075852001-09-23 03:18:13 +00001815 return suite
1816
1817
Barry Warsawc9ad32c2002-04-15 22:14:06 +00001818def test_main():
1819 for testclass in _testclasses():
1820 test_support.run_unittest(testclass)
1821
1822
Barry Warsaw08a534d2001-10-04 17:58:50 +00001823
Guido van Rossum78f0dd32001-12-07 21:07:08 +00001824if __name__ == '__main__':
Barry Warsaw409a4c02002-04-10 21:01:31 +00001825 unittest.main(defaultTest='suite')