PageRenderTime 732ms CodeModel.GetById 121ms app.highlight 427ms RepoModel.GetById 116ms app.codeStats 1ms

/Lib/email/test/test_email_renamed.py

http://unladen-swallow.googlecode.com/
Python | 3284 lines | 3205 code | 41 blank | 38 comment | 17 complexity | bdf971ce6de3457afd007a1b0a5daa4a MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1# Copyright (C) 2001-2007 Python Software Foundation
   2# Contact: email-sig@python.org
   3# email package unit tests
   4
   5import os
   6import sys
   7import time
   8import base64
   9import difflib
  10import unittest
  11import warnings
  12from cStringIO import StringIO
  13
  14import email
  15
  16from email.charset import Charset
  17from email.header import Header, decode_header, make_header
  18from email.parser import Parser, HeaderParser
  19from email.generator import Generator, DecodedGenerator
  20from email.message import Message
  21from email.mime.application import MIMEApplication
  22from email.mime.audio import MIMEAudio
  23from email.mime.text import MIMEText
  24from email.mime.image import MIMEImage
  25from email.mime.base import MIMEBase
  26from email.mime.message import MIMEMessage
  27from email.mime.multipart import MIMEMultipart
  28from email import utils
  29from email import errors
  30from email import encoders
  31from email import iterators
  32from email import base64mime
  33from email import quoprimime
  34
  35from test.test_support import findfile, run_unittest
  36from email.test import __file__ as landmark
  37
  38
  39NL = '\n'
  40EMPTYSTRING = ''
  41SPACE = ' '
  42
  43
  44
  45def openfile(filename, mode='r'):
  46    path = os.path.join(os.path.dirname(landmark), 'data', filename)
  47    return open(path, mode)
  48
  49
  50
  51# Base test class
  52class TestEmailBase(unittest.TestCase):
  53    def ndiffAssertEqual(self, first, second):
  54        """Like failUnlessEqual except use ndiff for readable output."""
  55        if first <> second:
  56            sfirst = str(first)
  57            ssecond = str(second)
  58            diff = difflib.ndiff(sfirst.splitlines(), ssecond.splitlines())
  59            fp = StringIO()
  60            print >> fp, NL, NL.join(diff)
  61            raise self.failureException, fp.getvalue()
  62
  63    def _msgobj(self, filename):
  64        fp = openfile(findfile(filename))
  65        try:
  66            msg = email.message_from_file(fp)
  67        finally:
  68            fp.close()
  69        return msg
  70
  71
  72
  73# Test various aspects of the Message class's API
  74class TestMessageAPI(TestEmailBase):
  75    def test_get_all(self):
  76        eq = self.assertEqual
  77        msg = self._msgobj('msg_20.txt')
  78        eq(msg.get_all('cc'), ['ccc@zzz.org', 'ddd@zzz.org', 'eee@zzz.org'])
  79        eq(msg.get_all('xx', 'n/a'), 'n/a')
  80
  81    def test_getset_charset(self):
  82        eq = self.assertEqual
  83        msg = Message()
  84        eq(msg.get_charset(), None)
  85        charset = Charset('iso-8859-1')
  86        msg.set_charset(charset)
  87        eq(msg['mime-version'], '1.0')
  88        eq(msg.get_content_type(), 'text/plain')
  89        eq(msg['content-type'], 'text/plain; charset="iso-8859-1"')
  90        eq(msg.get_param('charset'), 'iso-8859-1')
  91        eq(msg['content-transfer-encoding'], 'quoted-printable')
  92        eq(msg.get_charset().input_charset, 'iso-8859-1')
  93        # Remove the charset
  94        msg.set_charset(None)
  95        eq(msg.get_charset(), None)
  96        eq(msg['content-type'], 'text/plain')
  97        # Try adding a charset when there's already MIME headers present
  98        msg = Message()
  99        msg['MIME-Version'] = '2.0'
 100        msg['Content-Type'] = 'text/x-weird'
 101        msg['Content-Transfer-Encoding'] = 'quinted-puntable'
 102        msg.set_charset(charset)
 103        eq(msg['mime-version'], '2.0')
 104        eq(msg['content-type'], 'text/x-weird; charset="iso-8859-1"')
 105        eq(msg['content-transfer-encoding'], 'quinted-puntable')
 106
 107    def test_set_charset_from_string(self):
 108        eq = self.assertEqual
 109        msg = Message()
 110        msg.set_charset('us-ascii')
 111        eq(msg.get_charset().input_charset, 'us-ascii')
 112        eq(msg['content-type'], 'text/plain; charset="us-ascii"')
 113
 114    def test_set_payload_with_charset(self):
 115        msg = Message()
 116        charset = Charset('iso-8859-1')
 117        msg.set_payload('This is a string payload', charset)
 118        self.assertEqual(msg.get_charset().input_charset, 'iso-8859-1')
 119
 120    def test_get_charsets(self):
 121        eq = self.assertEqual
 122
 123        msg = self._msgobj('msg_08.txt')
 124        charsets = msg.get_charsets()
 125        eq(charsets, [None, 'us-ascii', 'iso-8859-1', 'iso-8859-2', 'koi8-r'])
 126
 127        msg = self._msgobj('msg_09.txt')
 128        charsets = msg.get_charsets('dingbat')
 129        eq(charsets, ['dingbat', 'us-ascii', 'iso-8859-1', 'dingbat',
 130                      'koi8-r'])
 131
 132        msg = self._msgobj('msg_12.txt')
 133        charsets = msg.get_charsets()
 134        eq(charsets, [None, 'us-ascii', 'iso-8859-1', None, 'iso-8859-2',
 135                      'iso-8859-3', 'us-ascii', 'koi8-r'])
 136
 137    def test_get_filename(self):
 138        eq = self.assertEqual
 139
 140        msg = self._msgobj('msg_04.txt')
 141        filenames = [p.get_filename() for p in msg.get_payload()]
 142        eq(filenames, ['msg.txt', 'msg.txt'])
 143
 144        msg = self._msgobj('msg_07.txt')
 145        subpart = msg.get_payload(1)
 146        eq(subpart.get_filename(), 'dingusfish.gif')
 147
 148    def test_get_filename_with_name_parameter(self):
 149        eq = self.assertEqual
 150
 151        msg = self._msgobj('msg_44.txt')
 152        filenames = [p.get_filename() for p in msg.get_payload()]
 153        eq(filenames, ['msg.txt', 'msg.txt'])
 154
 155    def test_get_boundary(self):
 156        eq = self.assertEqual
 157        msg = self._msgobj('msg_07.txt')
 158        # No quotes!
 159        eq(msg.get_boundary(), 'BOUNDARY')
 160
 161    def test_set_boundary(self):
 162        eq = self.assertEqual
 163        # This one has no existing boundary parameter, but the Content-Type:
 164        # header appears fifth.
 165        msg = self._msgobj('msg_01.txt')
 166        msg.set_boundary('BOUNDARY')
 167        header, value = msg.items()[4]
 168        eq(header.lower(), 'content-type')
 169        eq(value, 'text/plain; charset="us-ascii"; boundary="BOUNDARY"')
 170        # This one has a Content-Type: header, with a boundary, stuck in the
 171        # middle of its headers.  Make sure the order is preserved; it should
 172        # be fifth.
 173        msg = self._msgobj('msg_04.txt')
 174        msg.set_boundary('BOUNDARY')
 175        header, value = msg.items()[4]
 176        eq(header.lower(), 'content-type')
 177        eq(value, 'multipart/mixed; boundary="BOUNDARY"')
 178        # And this one has no Content-Type: header at all.
 179        msg = self._msgobj('msg_03.txt')
 180        self.assertRaises(errors.HeaderParseError,
 181                          msg.set_boundary, 'BOUNDARY')
 182
 183    def test_get_decoded_payload(self):
 184        eq = self.assertEqual
 185        msg = self._msgobj('msg_10.txt')
 186        # The outer message is a multipart
 187        eq(msg.get_payload(decode=True), None)
 188        # Subpart 1 is 7bit encoded
 189        eq(msg.get_payload(0).get_payload(decode=True),
 190           'This is a 7bit encoded message.\n')
 191        # Subpart 2 is quopri
 192        eq(msg.get_payload(1).get_payload(decode=True),
 193           '\xa1This is a Quoted Printable encoded message!\n')
 194        # Subpart 3 is base64
 195        eq(msg.get_payload(2).get_payload(decode=True),
 196           'This is a Base64 encoded message.')
 197        # Subpart 4 has no Content-Transfer-Encoding: header.
 198        eq(msg.get_payload(3).get_payload(decode=True),
 199           'This has no Content-Transfer-Encoding: header.\n')
 200
 201    def test_get_decoded_uu_payload(self):
 202        eq = self.assertEqual
 203        msg = Message()
 204        msg.set_payload('begin 666 -\n+:&5L;&\\@=V]R;&0 \n \nend\n')
 205        for cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
 206            msg['content-transfer-encoding'] = cte
 207            eq(msg.get_payload(decode=True), 'hello world')
 208        # Now try some bogus data
 209        msg.set_payload('foo')
 210        eq(msg.get_payload(decode=True), 'foo')
 211
 212    def test_decoded_generator(self):
 213        eq = self.assertEqual
 214        msg = self._msgobj('msg_07.txt')
 215        fp = openfile('msg_17.txt')
 216        try:
 217            text = fp.read()
 218        finally:
 219            fp.close()
 220        s = StringIO()
 221        g = DecodedGenerator(s)
 222        g.flatten(msg)
 223        eq(s.getvalue(), text)
 224
 225    def test__contains__(self):
 226        msg = Message()
 227        msg['From'] = 'Me'
 228        msg['to'] = 'You'
 229        # Check for case insensitivity
 230        self.failUnless('from' in msg)
 231        self.failUnless('From' in msg)
 232        self.failUnless('FROM' in msg)
 233        self.failUnless('to' in msg)
 234        self.failUnless('To' in msg)
 235        self.failUnless('TO' in msg)
 236
 237    def test_as_string(self):
 238        eq = self.assertEqual
 239        msg = self._msgobj('msg_01.txt')
 240        fp = openfile('msg_01.txt')
 241        try:
 242            text = fp.read()
 243        finally:
 244            fp.close()
 245        eq(text, msg.as_string())
 246        fullrepr = str(msg)
 247        lines = fullrepr.split('\n')
 248        self.failUnless(lines[0].startswith('From '))
 249        eq(text, NL.join(lines[1:]))
 250
 251    def test_bad_param(self):
 252        msg = email.message_from_string("Content-Type: blarg; baz; boo\n")
 253        self.assertEqual(msg.get_param('baz'), '')
 254
 255    def test_missing_filename(self):
 256        msg = email.message_from_string("From: foo\n")
 257        self.assertEqual(msg.get_filename(), None)
 258
 259    def test_bogus_filename(self):
 260        msg = email.message_from_string(
 261        "Content-Disposition: blarg; filename\n")
 262        self.assertEqual(msg.get_filename(), '')
 263
 264    def test_missing_boundary(self):
 265        msg = email.message_from_string("From: foo\n")
 266        self.assertEqual(msg.get_boundary(), None)
 267
 268    def test_get_params(self):
 269        eq = self.assertEqual
 270        msg = email.message_from_string(
 271            'X-Header: foo=one; bar=two; baz=three\n')
 272        eq(msg.get_params(header='x-header'),
 273           [('foo', 'one'), ('bar', 'two'), ('baz', 'three')])
 274        msg = email.message_from_string(
 275            'X-Header: foo; bar=one; baz=two\n')
 276        eq(msg.get_params(header='x-header'),
 277           [('foo', ''), ('bar', 'one'), ('baz', 'two')])
 278        eq(msg.get_params(), None)
 279        msg = email.message_from_string(
 280            'X-Header: foo; bar="one"; baz=two\n')
 281        eq(msg.get_params(header='x-header'),
 282           [('foo', ''), ('bar', 'one'), ('baz', 'two')])
 283
 284    def test_get_param_liberal(self):
 285        msg = Message()
 286        msg['Content-Type'] = 'Content-Type: Multipart/mixed; boundary = "CPIMSSMTPC06p5f3tG"'
 287        self.assertEqual(msg.get_param('boundary'), 'CPIMSSMTPC06p5f3tG')
 288
 289    def test_get_param(self):
 290        eq = self.assertEqual
 291        msg = email.message_from_string(
 292            "X-Header: foo=one; bar=two; baz=three\n")
 293        eq(msg.get_param('bar', header='x-header'), 'two')
 294        eq(msg.get_param('quuz', header='x-header'), None)
 295        eq(msg.get_param('quuz'), None)
 296        msg = email.message_from_string(
 297            'X-Header: foo; bar="one"; baz=two\n')
 298        eq(msg.get_param('foo', header='x-header'), '')
 299        eq(msg.get_param('bar', header='x-header'), 'one')
 300        eq(msg.get_param('baz', header='x-header'), 'two')
 301        # XXX: We are not RFC-2045 compliant!  We cannot parse:
 302        # msg["Content-Type"] = 'text/plain; weird="hey; dolly? [you] @ <\\"home\\">?"'
 303        # msg.get_param("weird")
 304        # yet.
 305
 306    def test_get_param_funky_continuation_lines(self):
 307        msg = self._msgobj('msg_22.txt')
 308        self.assertEqual(msg.get_payload(1).get_param('name'), 'wibble.JPG')
 309
 310    def test_get_param_with_semis_in_quotes(self):
 311        msg = email.message_from_string(
 312            'Content-Type: image/pjpeg; name="Jim&amp;&amp;Jill"\n')
 313        self.assertEqual(msg.get_param('name'), 'Jim&amp;&amp;Jill')
 314        self.assertEqual(msg.get_param('name', unquote=False),
 315                         '"Jim&amp;&amp;Jill"')
 316
 317    def test_has_key(self):
 318        msg = email.message_from_string('Header: exists')
 319        self.failUnless(msg.has_key('header'))
 320        self.failUnless(msg.has_key('Header'))
 321        self.failUnless(msg.has_key('HEADER'))
 322        self.failIf(msg.has_key('headeri'))
 323
 324    def test_set_param(self):
 325        eq = self.assertEqual
 326        msg = Message()
 327        msg.set_param('charset', 'iso-2022-jp')
 328        eq(msg.get_param('charset'), 'iso-2022-jp')
 329        msg.set_param('importance', 'high value')
 330        eq(msg.get_param('importance'), 'high value')
 331        eq(msg.get_param('importance', unquote=False), '"high value"')
 332        eq(msg.get_params(), [('text/plain', ''),
 333                              ('charset', 'iso-2022-jp'),
 334                              ('importance', 'high value')])
 335        eq(msg.get_params(unquote=False), [('text/plain', ''),
 336                                       ('charset', '"iso-2022-jp"'),
 337                                       ('importance', '"high value"')])
 338        msg.set_param('charset', 'iso-9999-xx', header='X-Jimmy')
 339        eq(msg.get_param('charset', header='X-Jimmy'), 'iso-9999-xx')
 340
 341    def test_del_param(self):
 342        eq = self.assertEqual
 343        msg = self._msgobj('msg_05.txt')
 344        eq(msg.get_params(),
 345           [('multipart/report', ''), ('report-type', 'delivery-status'),
 346            ('boundary', 'D1690A7AC1.996856090/mail.example.com')])
 347        old_val = msg.get_param("report-type")
 348        msg.del_param("report-type")
 349        eq(msg.get_params(),
 350           [('multipart/report', ''),
 351            ('boundary', 'D1690A7AC1.996856090/mail.example.com')])
 352        msg.set_param("report-type", old_val)
 353        eq(msg.get_params(),
 354           [('multipart/report', ''),
 355            ('boundary', 'D1690A7AC1.996856090/mail.example.com'),
 356            ('report-type', old_val)])
 357
 358    def test_del_param_on_other_header(self):
 359        msg = Message()
 360        msg.add_header('Content-Disposition', 'attachment', filename='bud.gif')
 361        msg.del_param('filename', 'content-disposition')
 362        self.assertEqual(msg['content-disposition'], 'attachment')
 363
 364    def test_set_type(self):
 365        eq = self.assertEqual
 366        msg = Message()
 367        self.assertRaises(ValueError, msg.set_type, 'text')
 368        msg.set_type('text/plain')
 369        eq(msg['content-type'], 'text/plain')
 370        msg.set_param('charset', 'us-ascii')
 371        eq(msg['content-type'], 'text/plain; charset="us-ascii"')
 372        msg.set_type('text/html')
 373        eq(msg['content-type'], 'text/html; charset="us-ascii"')
 374
 375    def test_set_type_on_other_header(self):
 376        msg = Message()
 377        msg['X-Content-Type'] = 'text/plain'
 378        msg.set_type('application/octet-stream', 'X-Content-Type')
 379        self.assertEqual(msg['x-content-type'], 'application/octet-stream')
 380
 381    def test_get_content_type_missing(self):
 382        msg = Message()
 383        self.assertEqual(msg.get_content_type(), 'text/plain')
 384
 385    def test_get_content_type_missing_with_default_type(self):
 386        msg = Message()
 387        msg.set_default_type('message/rfc822')
 388        self.assertEqual(msg.get_content_type(), 'message/rfc822')
 389
 390    def test_get_content_type_from_message_implicit(self):
 391        msg = self._msgobj('msg_30.txt')
 392        self.assertEqual(msg.get_payload(0).get_content_type(),
 393                         'message/rfc822')
 394
 395    def test_get_content_type_from_message_explicit(self):
 396        msg = self._msgobj('msg_28.txt')
 397        self.assertEqual(msg.get_payload(0).get_content_type(),
 398                         'message/rfc822')
 399
 400    def test_get_content_type_from_message_text_plain_implicit(self):
 401        msg = self._msgobj('msg_03.txt')
 402        self.assertEqual(msg.get_content_type(), 'text/plain')
 403
 404    def test_get_content_type_from_message_text_plain_explicit(self):
 405        msg = self._msgobj('msg_01.txt')
 406        self.assertEqual(msg.get_content_type(), 'text/plain')
 407
 408    def test_get_content_maintype_missing(self):
 409        msg = Message()
 410        self.assertEqual(msg.get_content_maintype(), 'text')
 411
 412    def test_get_content_maintype_missing_with_default_type(self):
 413        msg = Message()
 414        msg.set_default_type('message/rfc822')
 415        self.assertEqual(msg.get_content_maintype(), 'message')
 416
 417    def test_get_content_maintype_from_message_implicit(self):
 418        msg = self._msgobj('msg_30.txt')
 419        self.assertEqual(msg.get_payload(0).get_content_maintype(), 'message')
 420
 421    def test_get_content_maintype_from_message_explicit(self):
 422        msg = self._msgobj('msg_28.txt')
 423        self.assertEqual(msg.get_payload(0).get_content_maintype(), 'message')
 424
 425    def test_get_content_maintype_from_message_text_plain_implicit(self):
 426        msg = self._msgobj('msg_03.txt')
 427        self.assertEqual(msg.get_content_maintype(), 'text')
 428
 429    def test_get_content_maintype_from_message_text_plain_explicit(self):
 430        msg = self._msgobj('msg_01.txt')
 431        self.assertEqual(msg.get_content_maintype(), 'text')
 432
 433    def test_get_content_subtype_missing(self):
 434        msg = Message()
 435        self.assertEqual(msg.get_content_subtype(), 'plain')
 436
 437    def test_get_content_subtype_missing_with_default_type(self):
 438        msg = Message()
 439        msg.set_default_type('message/rfc822')
 440        self.assertEqual(msg.get_content_subtype(), 'rfc822')
 441
 442    def test_get_content_subtype_from_message_implicit(self):
 443        msg = self._msgobj('msg_30.txt')
 444        self.assertEqual(msg.get_payload(0).get_content_subtype(), 'rfc822')
 445
 446    def test_get_content_subtype_from_message_explicit(self):
 447        msg = self._msgobj('msg_28.txt')
 448        self.assertEqual(msg.get_payload(0).get_content_subtype(), 'rfc822')
 449
 450    def test_get_content_subtype_from_message_text_plain_implicit(self):
 451        msg = self._msgobj('msg_03.txt')
 452        self.assertEqual(msg.get_content_subtype(), 'plain')
 453
 454    def test_get_content_subtype_from_message_text_plain_explicit(self):
 455        msg = self._msgobj('msg_01.txt')
 456        self.assertEqual(msg.get_content_subtype(), 'plain')
 457
 458    def test_get_content_maintype_error(self):
 459        msg = Message()
 460        msg['Content-Type'] = 'no-slash-in-this-string'
 461        self.assertEqual(msg.get_content_maintype(), 'text')
 462
 463    def test_get_content_subtype_error(self):
 464        msg = Message()
 465        msg['Content-Type'] = 'no-slash-in-this-string'
 466        self.assertEqual(msg.get_content_subtype(), 'plain')
 467
 468    def test_replace_header(self):
 469        eq = self.assertEqual
 470        msg = Message()
 471        msg.add_header('First', 'One')
 472        msg.add_header('Second', 'Two')
 473        msg.add_header('Third', 'Three')
 474        eq(msg.keys(), ['First', 'Second', 'Third'])
 475        eq(msg.values(), ['One', 'Two', 'Three'])
 476        msg.replace_header('Second', 'Twenty')
 477        eq(msg.keys(), ['First', 'Second', 'Third'])
 478        eq(msg.values(), ['One', 'Twenty', 'Three'])
 479        msg.add_header('First', 'Eleven')
 480        msg.replace_header('First', 'One Hundred')
 481        eq(msg.keys(), ['First', 'Second', 'Third', 'First'])
 482        eq(msg.values(), ['One Hundred', 'Twenty', 'Three', 'Eleven'])
 483        self.assertRaises(KeyError, msg.replace_header, 'Fourth', 'Missing')
 484
 485    def test_broken_base64_payload(self):
 486        x = 'AwDp0P7//y6LwKEAcPa/6Q=9'
 487        msg = Message()
 488        msg['content-type'] = 'audio/x-midi'
 489        msg['content-transfer-encoding'] = 'base64'
 490        msg.set_payload(x)
 491        self.assertEqual(msg.get_payload(decode=True), x)
 492
 493
 494
 495# Test the email.encoders module
 496class TestEncoders(unittest.TestCase):
 497    def test_encode_empty_payload(self):
 498        eq = self.assertEqual
 499        msg = Message()
 500        msg.set_charset('us-ascii')
 501        eq(msg['content-transfer-encoding'], '7bit')
 502
 503    def test_default_cte(self):
 504        eq = self.assertEqual
 505        msg = MIMEText('hello world')
 506        eq(msg['content-transfer-encoding'], '7bit')
 507
 508    def test_default_cte(self):
 509        eq = self.assertEqual
 510        # With no explicit _charset its us-ascii, and all are 7-bit
 511        msg = MIMEText('hello world')
 512        eq(msg['content-transfer-encoding'], '7bit')
 513        # Similar, but with 8-bit data
 514        msg = MIMEText('hello \xf8 world')
 515        eq(msg['content-transfer-encoding'], '8bit')
 516        # And now with a different charset
 517        msg = MIMEText('hello \xf8 world', _charset='iso-8859-1')
 518        eq(msg['content-transfer-encoding'], 'quoted-printable')
 519
 520
 521
 522# Test long header wrapping
 523class TestLongHeaders(TestEmailBase):
 524    def test_split_long_continuation(self):
 525        eq = self.ndiffAssertEqual
 526        msg = email.message_from_string("""\
 527Subject: bug demonstration
 528\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
 529\tmore text
 530
 531test
 532""")
 533        sfp = StringIO()
 534        g = Generator(sfp)
 535        g.flatten(msg)
 536        eq(sfp.getvalue(), """\
 537Subject: bug demonstration
 538\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
 539\tmore text
 540
 541test
 542""")
 543
 544    def test_another_long_almost_unsplittable_header(self):
 545        eq = self.ndiffAssertEqual
 546        hstr = """\
 547bug demonstration
 548\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
 549\tmore text"""
 550        h = Header(hstr, continuation_ws='\t')
 551        eq(h.encode(), """\
 552bug demonstration
 553\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
 554\tmore text""")
 555        h = Header(hstr)
 556        eq(h.encode(), """\
 557bug demonstration
 558 12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
 559 more text""")
 560
 561    def test_long_nonstring(self):
 562        eq = self.ndiffAssertEqual
 563        g = Charset("iso-8859-1")
 564        cz = Charset("iso-8859-2")
 565        utf8 = Charset("utf-8")
 566        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. "
 567        cz_head = "Finan\xe8ni metropole se hroutily pod tlakem jejich d\xf9vtipu.. "
 568        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")
 569        h = Header(g_head, g, header_name='Subject')
 570        h.append(cz_head, cz)
 571        h.append(utf8_head, utf8)
 572        msg = Message()
 573        msg['Subject'] = h
 574        sfp = StringIO()
 575        g = Generator(sfp)
 576        g.flatten(msg)
 577        eq(sfp.getvalue(), """\
 578Subject: =?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_einem_Foerd?=
 579 =?iso-8859-1?q?erband_komfortabel_den_Korridor_entlang=2C_an_s=FCdl=FCndi?=
 580 =?iso-8859-1?q?schen_Wandgem=E4lden_vorbei=2C_gegen_die_rotierenden_Kling?=
 581 =?iso-8859-1?q?en_bef=F6rdert=2E_?= =?iso-8859-2?q?Finan=E8ni_met?=
 582 =?iso-8859-2?q?ropole_se_hroutily_pod_tlakem_jejich_d=F9vtipu=2E=2E_?=
 583 =?utf-8?b?5q2j56K644Gr6KiA44GG44Go57+76Kiz44Gv44GV44KM44Gm44GE?=
 584 =?utf-8?b?44G+44Gb44KT44CC5LiA6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM44CB?=
 585 =?utf-8?b?44GC44Go44Gv44Gn44Gf44KJ44KB44Gn44GZ44CC5a6f6Zqb44Gr44Gv44CM?=
 586 =?utf-8?q?Wenn_ist_das_Nunstuck_git_und_Slotermeyer=3F_Ja!_Beiherhund_das?=
 587 =?utf-8?b?IE9kZXIgZGllIEZsaXBwZXJ3YWxkdCBnZXJzcHV0LuOAjeOBqOiogOOBow==?=
 588 =?utf-8?b?44Gm44GE44G+44GZ44CC?=
 589
 590""")
 591        eq(h.encode(), """\
 592=?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_einem_Foerd?=
 593 =?iso-8859-1?q?erband_komfortabel_den_Korridor_entlang=2C_an_s=FCdl=FCndi?=
 594 =?iso-8859-1?q?schen_Wandgem=E4lden_vorbei=2C_gegen_die_rotierenden_Kling?=
 595 =?iso-8859-1?q?en_bef=F6rdert=2E_?= =?iso-8859-2?q?Finan=E8ni_met?=
 596 =?iso-8859-2?q?ropole_se_hroutily_pod_tlakem_jejich_d=F9vtipu=2E=2E_?=
 597 =?utf-8?b?5q2j56K644Gr6KiA44GG44Go57+76Kiz44Gv44GV44KM44Gm44GE?=
 598 =?utf-8?b?44G+44Gb44KT44CC5LiA6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM44CB?=
 599 =?utf-8?b?44GC44Go44Gv44Gn44Gf44KJ44KB44Gn44GZ44CC5a6f6Zqb44Gr44Gv44CM?=
 600 =?utf-8?q?Wenn_ist_das_Nunstuck_git_und_Slotermeyer=3F_Ja!_Beiherhund_das?=
 601 =?utf-8?b?IE9kZXIgZGllIEZsaXBwZXJ3YWxkdCBnZXJzcHV0LuOAjeOBqOiogOOBow==?=
 602 =?utf-8?b?44Gm44GE44G+44GZ44CC?=""")
 603
 604    def test_long_header_encode(self):
 605        eq = self.ndiffAssertEqual
 606        h = Header('wasnipoop; giraffes="very-long-necked-animals"; '
 607                   'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"',
 608                   header_name='X-Foobar-Spoink-Defrobnit')
 609        eq(h.encode(), '''\
 610wasnipoop; giraffes="very-long-necked-animals";
 611 spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''')
 612
 613    def test_long_header_encode_with_tab_continuation(self):
 614        eq = self.ndiffAssertEqual
 615        h = Header('wasnipoop; giraffes="very-long-necked-animals"; '
 616                   'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"',
 617                   header_name='X-Foobar-Spoink-Defrobnit',
 618                   continuation_ws='\t')
 619        eq(h.encode(), '''\
 620wasnipoop; giraffes="very-long-necked-animals";
 621\tspooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''')
 622
 623    def test_header_splitter(self):
 624        eq = self.ndiffAssertEqual
 625        msg = MIMEText('')
 626        # It'd be great if we could use add_header() here, but that doesn't
 627        # guarantee an order of the parameters.
 628        msg['X-Foobar-Spoink-Defrobnit'] = (
 629            'wasnipoop; giraffes="very-long-necked-animals"; '
 630            'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"')
 631        sfp = StringIO()
 632        g = Generator(sfp)
 633        g.flatten(msg)
 634        eq(sfp.getvalue(), '''\
 635Content-Type: text/plain; charset="us-ascii"
 636MIME-Version: 1.0
 637Content-Transfer-Encoding: 7bit
 638X-Foobar-Spoink-Defrobnit: wasnipoop; giraffes="very-long-necked-animals";
 639\tspooge="yummy"; hippos="gargantuan"; marshmallows="gooey"
 640
 641''')
 642
 643    def test_no_semis_header_splitter(self):
 644        eq = self.ndiffAssertEqual
 645        msg = Message()
 646        msg['From'] = 'test@dom.ain'
 647        msg['References'] = SPACE.join(['<%d@dom.ain>' % i for i in range(10)])
 648        msg.set_payload('Test')
 649        sfp = StringIO()
 650        g = Generator(sfp)
 651        g.flatten(msg)
 652        eq(sfp.getvalue(), """\
 653From: test@dom.ain
 654References: <0@dom.ain> <1@dom.ain> <2@dom.ain> <3@dom.ain> <4@dom.ain>
 655\t<5@dom.ain> <6@dom.ain> <7@dom.ain> <8@dom.ain> <9@dom.ain>
 656
 657Test""")
 658
 659    def test_no_split_long_header(self):
 660        eq = self.ndiffAssertEqual
 661        hstr = 'References: ' + 'x' * 80
 662        h = Header(hstr, continuation_ws='\t')
 663        eq(h.encode(), """\
 664References: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx""")
 665
 666    def test_splitting_multiple_long_lines(self):
 667        eq = self.ndiffAssertEqual
 668        hstr = """\
 669from 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)
 670\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)
 671\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)
 672"""
 673        h = Header(hstr, continuation_ws='\t')
 674        eq(h.encode(), """\
 675from babylon.socal-raves.org (localhost [127.0.0.1]);
 676\tby babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81;
 677\tfor <mailman-admin@babylon.socal-raves.org>;
 678\tSat, 2 Feb 2002 17:00:06 -0800 (PST)
 679\tfrom babylon.socal-raves.org (localhost [127.0.0.1]);
 680\tby babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81;
 681\tfor <mailman-admin@babylon.socal-raves.org>;
 682\tSat, 2 Feb 2002 17:00:06 -0800 (PST)
 683\tfrom babylon.socal-raves.org (localhost [127.0.0.1]);
 684\tby babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81;
 685\tfor <mailman-admin@babylon.socal-raves.org>;
 686\tSat, 2 Feb 2002 17:00:06 -0800 (PST)""")
 687
 688    def test_splitting_first_line_only_is_long(self):
 689        eq = self.ndiffAssertEqual
 690        hstr = """\
 691from modemcable093.139-201-24.que.mc.videotron.ca ([24.201.139.93] helo=cthulhu.gerg.ca)
 692\tby kronos.mems-exchange.org with esmtp (Exim 4.05)
 693\tid 17k4h5-00034i-00
 694\tfor test@mems-exchange.org; Wed, 28 Aug 2002 11:25:20 -0400"""
 695        h = Header(hstr, maxlinelen=78, header_name='Received',
 696                   continuation_ws='\t')
 697        eq(h.encode(), """\
 698from modemcable093.139-201-24.que.mc.videotron.ca ([24.201.139.93]
 699\thelo=cthulhu.gerg.ca)
 700\tby kronos.mems-exchange.org with esmtp (Exim 4.05)
 701\tid 17k4h5-00034i-00
 702\tfor test@mems-exchange.org; Wed, 28 Aug 2002 11:25:20 -0400""")
 703
 704    def test_long_8bit_header(self):
 705        eq = self.ndiffAssertEqual
 706        msg = Message()
 707        h = Header('Britische Regierung gibt', 'iso-8859-1',
 708                    header_name='Subject')
 709        h.append('gr\xfcnes Licht f\xfcr Offshore-Windkraftprojekte')
 710        msg['Subject'] = h
 711        eq(msg.as_string(), """\
 712Subject: =?iso-8859-1?q?Britische_Regierung_gibt?= =?iso-8859-1?q?gr=FCnes?=
 713 =?iso-8859-1?q?_Licht_f=FCr_Offshore-Windkraftprojekte?=
 714
 715""")
 716
 717    def test_long_8bit_header_no_charset(self):
 718        eq = self.ndiffAssertEqual
 719        msg = Message()
 720        msg['Reply-To'] = 'Britische Regierung gibt gr\xfcnes Licht f\xfcr Offshore-Windkraftprojekte <a-very-long-address@example.com>'
 721        eq(msg.as_string(), """\
 722Reply-To: Britische Regierung gibt gr\xfcnes Licht f\xfcr Offshore-Windkraftprojekte <a-very-long-address@example.com>
 723
 724""")
 725
 726    def test_long_to_header(self):
 727        eq = self.ndiffAssertEqual
 728        to = '"Someone Test #A" <someone@eecs.umich.edu>,<someone@eecs.umich.edu>,"Someone Test #B" <someone@umich.edu>, "Someone Test #C" <someone@eecs.umich.edu>, "Someone Test #D" <someone@eecs.umich.edu>'
 729        msg = Message()
 730        msg['To'] = to
 731        eq(msg.as_string(0), '''\
 732To: "Someone Test #A" <someone@eecs.umich.edu>, <someone@eecs.umich.edu>,
 733\t"Someone Test #B" <someone@umich.edu>,
 734\t"Someone Test #C" <someone@eecs.umich.edu>,
 735\t"Someone Test #D" <someone@eecs.umich.edu>
 736
 737''')
 738
 739    def test_long_line_after_append(self):
 740        eq = self.ndiffAssertEqual
 741        s = 'This is an example of string which has almost the limit of header length.'
 742        h = Header(s)
 743        h.append('Add another line.')
 744        eq(h.encode(), """\
 745This is an example of string which has almost the limit of header length.
 746 Add another line.""")
 747
 748    def test_shorter_line_with_append(self):
 749        eq = self.ndiffAssertEqual
 750        s = 'This is a shorter line.'
 751        h = Header(s)
 752        h.append('Add another sentence. (Surprise?)')
 753        eq(h.encode(),
 754           'This is a shorter line. Add another sentence. (Surprise?)')
 755
 756    def test_long_field_name(self):
 757        eq = self.ndiffAssertEqual
 758        fn = 'X-Very-Very-Very-Long-Header-Name'
 759        gs = "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. "
 760        h = Header(gs, 'iso-8859-1', header_name=fn)
 761        # BAW: this seems broken because the first line is too long
 762        eq(h.encode(), """\
 763=?iso-8859-1?q?Die_Mieter_treten_hier_?=
 764 =?iso-8859-1?q?ein_werden_mit_einem_Foerderband_komfortabel_den_Korridor_?=
 765 =?iso-8859-1?q?entlang=2C_an_s=FCdl=FCndischen_Wandgem=E4lden_vorbei=2C_g?=
 766 =?iso-8859-1?q?egen_die_rotierenden_Klingen_bef=F6rdert=2E_?=""")
 767
 768    def test_long_received_header(self):
 769        h = 'from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) by hrothgar.la.mastaler.com (tmda-ofmipd) with ESMTP; Wed, 05 Mar 2003 18:10:18 -0700'
 770        msg = Message()
 771        msg['Received-1'] = Header(h, continuation_ws='\t')
 772        msg['Received-2'] = h
 773        self.assertEqual(msg.as_string(), """\
 774Received-1: from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) by
 775\throthgar.la.mastaler.com (tmda-ofmipd) with ESMTP;
 776\tWed, 05 Mar 2003 18:10:18 -0700
 777Received-2: from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) by
 778\throthgar.la.mastaler.com (tmda-ofmipd) with ESMTP;
 779\tWed, 05 Mar 2003 18:10:18 -0700
 780
 781""")
 782
 783    def test_string_headerinst_eq(self):
 784        h = '<15975.17901.207240.414604@sgigritzmann1.mathematik.tu-muenchen.de> (David Bremner\'s message of "Thu, 6 Mar 2003 13:58:21 +0100")'
 785        msg = Message()
 786        msg['Received-1'] = Header(h, header_name='Received-1',
 787                                   continuation_ws='\t')
 788        msg['Received-2'] = h
 789        self.assertEqual(msg.as_string(), """\
 790Received-1: <15975.17901.207240.414604@sgigritzmann1.mathematik.tu-muenchen.de>
 791\t(David Bremner's message of "Thu, 6 Mar 2003 13:58:21 +0100")
 792Received-2: <15975.17901.207240.414604@sgigritzmann1.mathematik.tu-muenchen.de>
 793\t(David Bremner's message of "Thu, 6 Mar 2003 13:58:21 +0100")
 794
 795""")
 796
 797    def test_long_unbreakable_lines_with_continuation(self):
 798        eq = self.ndiffAssertEqual
 799        msg = Message()
 800        t = """\
 801 iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9
 802 locQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp"""
 803        msg['Face-1'] = t
 804        msg['Face-2'] = Header(t, header_name='Face-2')
 805        eq(msg.as_string(), """\
 806Face-1: iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9
 807\tlocQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp
 808Face-2: iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9
 809 locQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp
 810
 811""")
 812
 813    def test_another_long_multiline_header(self):
 814        eq = self.ndiffAssertEqual
 815        m = '''\
 816Received: from siimage.com ([172.25.1.3]) by zima.siliconimage.com with Microsoft SMTPSVC(5.0.2195.4905);
 817\tWed, 16 Oct 2002 07:41:11 -0700'''
 818        msg = email.message_from_string(m)
 819        eq(msg.as_string(), '''\
 820Received: from siimage.com ([172.25.1.3]) by zima.siliconimage.com with
 821\tMicrosoft SMTPSVC(5.0.2195.4905); Wed, 16 Oct 2002 07:41:11 -0700
 822
 823''')
 824
 825    def test_long_lines_with_different_header(self):
 826        eq = self.ndiffAssertEqual
 827        h = """\
 828List-Unsubscribe: <https://lists.sourceforge.net/lists/listinfo/spamassassin-talk>,
 829        <mailto:spamassassin-talk-request@lists.sourceforge.net?subject=unsubscribe>"""
 830        msg = Message()
 831        msg['List'] = h
 832        msg['List'] = Header(h, header_name='List')
 833        eq(msg.as_string(), """\
 834List: List-Unsubscribe: <https://lists.sourceforge.net/lists/listinfo/spamassassin-talk>,
 835\t<mailto:spamassassin-talk-request@lists.sourceforge.net?subject=unsubscribe>
 836List: List-Unsubscribe: <https://lists.sourceforge.net/lists/listinfo/spamassassin-talk>,
 837 <mailto:spamassassin-talk-request@lists.sourceforge.net?subject=unsubscribe>
 838
 839""")
 840
 841
 842
 843# Test mangling of "From " lines in the body of a message
 844class TestFromMangling(unittest.TestCase):
 845    def setUp(self):
 846        self.msg = Message()
 847        self.msg['From'] = 'aaa@bbb.org'
 848        self.msg.set_payload("""\
 849From the desk of A.A.A.:
 850Blah blah blah
 851""")
 852
 853    def test_mangled_from(self):
 854        s = StringIO()
 855        g = Generator(s, mangle_from_=True)
 856        g.flatten(self.msg)
 857        self.assertEqual(s.getvalue(), """\
 858From: aaa@bbb.org
 859
 860>From the desk of A.A.A.:
 861Blah blah blah
 862""")
 863
 864    def test_dont_mangle_from(self):
 865        s = StringIO()
 866        g = Generator(s, mangle_from_=False)
 867        g.flatten(self.msg)
 868        self.assertEqual(s.getvalue(), """\
 869From: aaa@bbb.org
 870
 871From the desk of A.A.A.:
 872Blah blah blah
 873""")
 874
 875
 876
 877# Test the basic MIMEAudio class
 878class TestMIMEAudio(unittest.TestCase):
 879    def setUp(self):
 880        # Make sure we pick up the audiotest.au that lives in email/test/data.
 881        # In Python, there's an audiotest.au living in Lib/test but that isn't
 882        # included in some binary distros that don't include the test
 883        # package.  The trailing empty string on the .join() is significant
 884        # since findfile() will do a dirname().
 885        datadir = os.path.join(os.path.dirname(landmark), 'data', '')
 886        fp = open(findfile('audiotest.au', datadir), 'rb')
 887        try:
 888            self._audiodata = fp.read()
 889        finally:
 890            fp.close()
 891        self._au = MIMEAudio(self._audiodata)
 892
 893    def test_guess_minor_type(self):
 894        self.assertEqual(self._au.get_content_type(), 'audio/basic')
 895
 896    def test_encoding(self):
 897        payload = self._au.get_payload()
 898        self.assertEqual(base64.decodestring(payload), self._audiodata)
 899
 900    def test_checkSetMinor(self):
 901        au = MIMEAudio(self._audiodata, 'fish')
 902        self.assertEqual(au.get_content_type(), 'audio/fish')
 903
 904    def test_add_header(self):
 905        eq = self.assertEqual
 906        unless = self.failUnless
 907        self._au.add_header('Content-Disposition', 'attachment',
 908                            filename='audiotest.au')
 909        eq(self._au['content-disposition'],
 910           'attachment; filename="audiotest.au"')
 911        eq(self._au.get_params(header='content-disposition'),
 912           [('attachment', ''), ('filename', 'audiotest.au')])
 913        eq(self._au.get_param('filename', header='content-disposition'),
 914           'audiotest.au')
 915        missing = []
 916        eq(self._au.get_param('attachment', header='content-disposition'), '')
 917        unless(self._au.get_param('foo', failobj=missing,
 918                                  header='content-disposition') is missing)
 919        # Try some missing stuff
 920        unless(self._au.get_param('foobar', missing) is missing)
 921        unless(self._au.get_param('attachment', missing,
 922                                  header='foobar') is missing)
 923
 924
 925
 926# Test the basic MIMEImage class
 927class TestMIMEImage(unittest.TestCase):
 928    def setUp(self):
 929        fp = openfile('PyBanner048.gif')
 930        try:
 931            self._imgdata = fp.read()
 932        finally:
 933            fp.close()
 934        self._im = MIMEImage(self._imgdata)
 935
 936    def test_guess_minor_type(self):
 937        self.assertEqual(self._im.get_content_type(), 'image/gif')
 938
 939    def test_encoding(self):
 940        payload = self._im.get_payload()
 941        self.assertEqual(base64.decodestring(payload), self._imgdata)
 942
 943    def test_checkSetMinor(self):
 944        im = MIMEImage(self._imgdata, 'fish')
 945        self.assertEqual(im.get_content_type(), 'image/fish')
 946
 947    def test_add_header(self):
 948        eq = self.assertEqual
 949        unless = self.failUnless
 950        self._im.add_header('Content-Disposition', 'attachment',
 951                            filename='dingusfish.gif')
 952        eq(self._im['content-disposition'],
 953           'attachment; filename="dingusfish.gif"')
 954        eq(self._im.get_params(header='content-disposition'),
 955           [('attachment', ''), ('filename', 'dingusfish.gif')])
 956        eq(self._im.get_param('filename', header='content-disposition'),
 957           'dingusfish.gif')
 958        missing = []
 959        eq(self._im.get_param('attachment', header='content-disposition'), '')
 960        unless(self._im.get_param('foo', failobj=missing,
 961                                  header='content-disposition') is missing)
 962        # Try some missing stuff
 963        unless(self._im.get_param('foobar', missing) is missing)
 964        unless(self._im.get_param('attachment', missing,
 965                                  header='foobar') is missing)
 966
 967
 968
 969# Test the basic MIMEApplication class
 970class TestMIMEApplication(unittest.TestCase):
 971    def test_headers(self):
 972        eq = self.assertEqual
 973        msg = MIMEApplication('\xfa\xfb\xfc\xfd\xfe\xff')
 974        eq(msg.get_content_type(), 'application/octet-stream')
 975        eq(msg['content-transfer-encoding'], 'base64')
 976
 977    def test_body(self):
 978        eq = self.assertEqual
 979        bytes = '\xfa\xfb\xfc\xfd\xfe\xff'
 980        msg = MIMEApplication(bytes)
 981        eq(msg.get_payload(), '+vv8/f7/')
 982        eq(msg.get_payload(decode=True), bytes)
 983
 984
 985
 986# Test the basic MIMEText class
 987class TestMIMEText(unittest.TestCase):
 988    def setUp(self):
 989        self._msg = MIMEText('hello there')
 990
 991    def test_types(self):
 992        eq = self.assertEqual
 993        unless = self.failUnless
 994        eq(self._msg.get_content_type(), 'text/plain')
 995        eq(self._msg.get_param('charset'), 'us-ascii')
 996        missing = []
 997        unless(self._msg.get_param('foobar', missing) is missing)
 998        unless(self._msg.get_param('charset', missing, header='foobar')
 999               is missing)
1000
1001    def test_payload(self):
1002        self.assertEqual(self._msg.get_payload(), 'hello there')
1003        self.failUnless(not self._msg.is_multipart())
1004
1005    def test_charset(self):
1006        eq = self.assertEqual
1007        msg = MIMEText('hello there', _charset='us-ascii')
1008        eq(msg.get_charset().input_charset, 'us-ascii')
1009        eq(msg['content-type'], 'text/plain; charset="us-ascii"')
1010
1011
1012
1013# Test complicated multipart/* messages
1014class TestMultipart(TestEmailBase):
1015    def setUp(self):
1016        fp = openfile('PyBanner048.gif')
1017        try:
1018            data = fp.read()
1019        finally:
1020            fp.close()
1021
1022        container = MIMEBase('multipart', 'mixed', boundary='BOUNDARY')
1023        image = MIMEImage(data, name='dingusfish.gif')
1024        image.add_header('content-disposition', 'attachment',
1025                         filename='dingusfish.gif')
1026        intro = MIMEText('''\
1027Hi there,
1028
1029This is the dingus fish.
1030''')
1031        container.attach(intro)
1032        container.attach(image)
1033        container['From'] = 'Barry <barry@digicool.com>'
1034        container['To'] = 'Dingus Lovers <cravindogs@cravindogs.com>'
1035        container['Subject'] = 'Here is your dingus fish'
1036
1037        now = 987809702.54848599
1038        timetuple = time.localtime(now)
1039        if timetuple[-1] == 0:
1040            tzsecs = time.timezone
1041        else:
1042            tzsecs = time.altzone
1043        if tzsecs > 0:
1044            sign = '-'
1045        else:
1046            sign = '+'
1047        tzoffset = ' %s%04d' % (sign, tzsecs / 36)
1048        container['Date'] = time.strftime(
1049            '%a, %d %b %Y %H:%M:%S',
1050            time.localtime(now)) + tzoffset
1051        self._msg = container
1052        self._im = image
1053        self._txt = intro
1054
1055    def test_hierarchy(self):
1056        # convenience
1057        eq = self.assertEqual
1058        unless = self.failUnless
1059        raises = self.assertRaises
1060        # tests
1061        m = self._msg
1062        unless(m.is_multipart())
1063        eq(m.get_content_type(), 'multipart/mixed')
1064        eq(len(m.get_payload()), 2)
1065        raises(IndexError, m.get_payload, 2)
1066        m0 = m.get_payload(0)
1067        m1 = m.get_payload(1)
1068        unless(m0 is self._txt)
1069        unless(m1 is self._im)
1070        eq(m.get_payload(), [m0, m1])
1071        unless(not m0.is_multipart())
1072        unless(not m1.is_multipart())
1073
1074    def test_empty_multipart_idempotent(self):
1075        text = """\
1076Content-Type: multipart/mixed; boundary="BOUNDARY"
1077MIME-Version: 1.0
1078Subject: A subject
1079To: aperson@dom.ain
1080From: bperson@dom.ain
1081
1082
1083--BOUNDARY
1084
1085
1086--BOUNDARY--
1087"""
1088        msg = Parser().parsestr(text)
1089        self.ndiffAssertEqual(text, msg.as_string())
1090
1091    def test_no_parts_in_a_multipart_with_none_epilogue(self):
1092        outer = MIMEBase('multipart', 'mixed')
1093        outer['Subject'] = 'A subject'
1094        outer['To'] = 'aperson@dom.ain'
1095        outer['From'] = 'bperson@dom.ain'
1096        outer.set_boundary('BOUNDARY')
1097        self.ndiffAssertEqual(outer.as_string(), '''\
1098Content-Type: multipart/mixed; boundary="BOUNDARY"
1099MIME-Version: 1.0
1100Subject: A subject
1101To: aperson@dom.ain
1102From: bperson@dom.ain
1103
1104--BOUNDARY
1105
1106--BOUNDARY--''')
1107
1108    def test_no_parts_in_a_multipart_with_empty_epilogue(self):
1109        outer = MIMEBase('multipart', 'mixed')
1110        outer['Subject'] = 'A subject'
1111        outer['To'] = 'aperson@dom.ain'
1112        outer['From'] = 'bperson@dom.ain'
1113        outer.preamble = ''
1114        outer.epilogue = ''
1115        outer.set_boundary('BOUNDARY')
1116        self.ndiffAssertEqual(outer.as_string(), '''\
1117Content-Type: multipart/mixed; boundary="BOUNDARY"
1118MIME-Version: 1.0
1119Subject: A subject
1120To: aperson@dom.ain
1121From: bperson@dom.ain
1122
1123
1124--BOUNDARY
1125
1126--BOUNDARY--
1127''')
1128
1129    def test_one_part_in_a_multipart(self):
1130        eq = self.ndiffAssertEqual
1131        outer = MIMEBase('multipart', 'mixed')
1132        outer['Subject'] = 'A subject'
1133        outer['To'] = 'aperson@dom.ain'
1134        outer['From'] = 'bperson@dom.ain'
1135        outer.set_boundary('BOUNDARY')
1136        msg = MIMEText('hello world')
1137        outer.attach(msg)
1138        eq(outer.as_string(), '''\
1139Content-Type: multipart/mixed; boundary="BOUNDARY"
1140MIME-Version: 1.0
1141Subject: A subject
1142To: aperson@dom.ain
1143From: bperson@dom.ain
1144
1145--BOUNDARY
1146Content-Type: text/plain; charset="us-ascii"
1147MIME-Version: 1.0
1148Content-Transfer-Encoding: 7bit
1149
1150hello world
1151--BOUNDARY--''')
1152
1153    def test_seq_parts_in_a_multipart_with_empty_preamble(self):
1154        eq = self.ndiffAssertEqual
1155        outer = MIMEBase('multipart', 'mixed')
1156        outer['Subject'] = 'A subject'
1157        outer['To'] = 'aperson@dom.ain'
1158        outer['From'] = 'bperson@dom.ain'
1159        outer.preamble = ''
1160        msg = MIMEText('hello world')
1161        outer.attach(msg)
1162        outer.set_boundary('BOUNDARY')
1163        eq(outer.as_string(), '''\
1164Content-Type: multipart/mixed; boundary="BOUNDARY"
1165MIME-Version: 1.0
1166Subject: A subject
1167To: aperson@dom.ain
1168From: bperson@dom.ain
1169
1170
1171--BOUNDARY
1172Content-Type: text/plain; charset="us-ascii"
1173MIME-Version: 1.0
1174Content-Transfer-Encoding: 7bit
1175
1176hello world
1177--BOUNDARY--''')
1178
1179
1180    def test_seq_parts_in_a_multipart_with_none_preamble(self):
1181        eq = self.ndiffAssertEqual
1182        outer = MIMEBase('multipart', 'mixed')
1183        outer['Subject'] = 'A subject'
1184        outer['To'] = 'aperson@dom.ain'
1185        outer['From'] = 'bperson@dom.ain'
1186        outer.preamble = None
1187        msg = MIMEText('hello world')
1188        outer.attach(msg)
1189        outer.set_boundary('BOUNDARY')
1190        eq(outer.as_string(), '''\
1191Content-Type: multipart/mixed; boundary="BOUNDARY"
1192MIME-Version: 1.0
1193Subject: A subject
1194To: aperson@dom.ain
1195From: bperson@dom.ain
1196
1197--BOUNDARY
1198Content-Type: text/plain; charset="us-ascii"
1199MIME-Version: 1.0
1200Content-Transfer-Encoding: 7bit
1201
1202hello world
1203--BOUNDARY--''')
1204
1205
1206    def test_seq_parts_in_a_multipart_with_none_epilogue(self):
1207        eq = self.ndiffAssertEqual
1208        outer = MIMEBase('multipart', 'mixed')
1209        outer['Subject'] = 'A subject'
1210        outer['To'] = 'aperson@dom.ain'
1211        outer['From'] = 'bperson@dom.ain'
1212        outer.epilogue = None
1213        msg = MIMEText('hello world')
1214        outer.attach(msg)
1215        outer.set_boundary('BOUNDARY')
1216        eq(outer.as_string(), '''\
1217Content-Type: multipart/mixed; boundary="BOUNDARY"
1218MIME-Version: 1.0
1219Subject: A subject
1220To: aperson@dom.ain
1221From: bperson@dom.ain
1222
1223--BOUNDARY
1224Content-Type: text/plain; charset="us-ascii"
1225MIME-Version: 1.0
1226Content-Transfer-Encoding: 7bit
1227
1228hello world
1229--BOUNDARY--''')
1230
1231
1232    def test_seq_parts_in_a_multipart_with_empty_epilogue(self):
1233        eq = self.ndiffAssertEqual
1234        outer = MIMEBase('multipart', 'mixed')
1235        outer['Subject'] = 'A subject'
1236        outer['To'] = 'aperson@dom.ain'
1237        outer['From'] = 'bperson@dom.ain'
1238        outer.epilogue = ''
1239        msg = MIMEText('hello world')
1240        outer.attach(msg)
1241        outer.set_boundary('BOUNDARY')
1242        eq(outer.as_string(), '''\
1243Content-Type: multipart/mixed; boundary="BOUNDARY"
1244MIME-Version: 1.0
1245Subject: A subject
1246To: aperson@dom.ain
1247From: bperson@dom.ain
1248
1249--BOUNDARY
1250Content-Type: text/plain; charset="us-ascii"
1251MIME-Version: 1.0
1252Content-Transfer-Encoding: 7bit
1253
1254hello world
1255--BOUNDARY--
1256''')
1257
1258
1259    def test_seq_parts_in_a_multipart_with_nl_epilogue(self):
1260        eq = self.ndiffAssertEqual
1261        outer = MIMEBase('multipart', 'mixed')
1262        outer['Subject'] = 'A subject'
1263        outer['To'] = 'aperson@dom.ain'
1264        outer['From'] = 'bperson@dom.ain'
1265        outer.epilogue = '\n'
1266        msg = MIMEText('hello world')
1267        outer.attach(msg)
1268        outer.set_boundary('BOUNDARY')
1269        eq(outer.as_string(), '''\
1270Content-Type: multipart/mixed; boundary="BOUNDARY"
1271MIME-Version: 1.0
1272Subject: A subject
1273To: aperson@dom.ain
1274From: bperson@dom.ain
1275
1276--BOUNDARY
1277Content-Type: text/plain; charset="us-ascii"
1278MIME-Version: 1.0
1279Content-Transfer-Encoding: 7bit
1280
1281hello world
1282--BOUNDARY--
1283
1284''')
1285
1286    def test_message_external_body(self):
1287        eq = self.assertEqual
1288        msg = self._msgobj('msg_36.txt')
1289        eq(len(msg.get_payload()), 2)
1290        msg1 = msg.get_payload(1)
1291        eq(msg1.get_content_type(), 'multipart/alternative')
1292        eq(len(msg1.get_payload()), 2)
1293        for subpart in msg1.get_payload():
1294            eq(subpart.get_content_type(), 'message/external-body')
1295            eq(len(subpart.get_payload()), 1)
1296            subsubpart = subpart.get_payload(0)
1297            eq(subsubpart.get_content_type(), 'text/plain')
1298
1299    def test_double_boundary(self):
1300        # msg_37.txt is a multipart that contains two dash-boundary's in a
1301        # row.  Our interpretation of RFC 2046 calls for ignoring the second
1302        # and subsequent boundaries.
1303        msg = self._msgobj('msg_37.txt')
1304        self.assertEqual(len(msg.get_payload()), 3)
1305
1306    def test_nested_inner_contains_outer_boundary(self):
1307        eq = self.ndiffAssertEqual
1308        # msg_38.txt has an inner part that contains outer boundaries.  My
1309        # interpretation of RFC 2046 (based on sections 5.1 and 5.1.2) say
1310        # these are illegal and should be interpreted as unterminated inner
1311        # parts.
1312        msg = self._msgobj('msg_38.txt')
1313        sfp = StringIO()
1314        iterators._structure(msg, sfp)
1315        eq(sfp.getvalue(), """\
1316multipart/mixed
1317    multipart/mixed
1318        multipart/alternative
1319            text/plain
1320        text/plain
1321    text/plain
1322    text/plain
1323""")
1324
1325    def test_nested_with_same_boundary(self):
1326        eq = self.ndiffAssertEqual
1327        # msg 39.txt is similarly evil in that it's got inner parts that use
1328        # the same boundary as outer parts.  Again, I believe the way this is
1329        # parsed is closest to the spirit of RFC 2046
1330        msg = self._msgobj('msg_39.txt')
1331        sfp = StringI

Large files files are truncated, but you can click here to view the full file