def readonly_immutability_test(): """We can read the headers and access the body without changing a single char inside the message""" message = scan(BILINGUAL) eq_(u"Simple text. How are you? Как ты поживаешь?", message.headers['Subject']) assert_false(message.was_changed()) eq_(BILINGUAL, message.to_string()) message = scan(ENCLOSED) pmessage = message_from_string(ENCLOSED) # we can read the headers without changing anything eq_(u'"Александр Клижентас☯" <*****@*****.**>', message.headers['To']) eq_('Bob Marley <*****@*****.**>, Jimmy Hendrix <*****@*****.**>', message.parts[1].enclosed.headers['To']) assert_false(message.was_changed()) # we can also read the body without changing anything pbody = pmessage.get_payload()[1].get_payload()[0].get_payload()[0].get_payload(decode=True) pbody = unicode(pbody, 'utf-8') eq_(pbody, message.parts[1].enclosed.parts[0].body) assert_false(message.was_changed()) eq_(ENCLOSED, message.to_string())
def content_types_test(): part = scan(IPHONE) eq_((None, {}), part.content_disposition) assert_false(part.is_attachment()) eq_(('inline', {'filename': 'photo.JPG'}), part.parts[1].content_disposition) assert_false(part.parts[1].is_attachment()) ok_(part.parts[1].is_inline()) part = scan(SPAM_BROKEN_CTYPE) eq_((None, {}), part.content_disposition) part = scan(MAILGUN_PIC) attachment = part.parts[1] eq_('image/png', attachment.detected_content_type) eq_('png', attachment.detected_subtype) eq_('image', attachment.detected_format) ok_(not attachment.is_body()) part = scan(BZ2_ATTACHMENT) attachment = part.parts[1] eq_('application/x-bzip2', attachment.detected_content_type) eq_('x-bzip2', attachment.detected_subtype) eq_('application', attachment.detected_format) ok_(not attachment.is_body())
def read_body_test(): """ Make sure we've set up boundaries correctly and methods that read raw bodies work fine """ part = scan(MULTIPART) eq_(MULTIPART, part._container.read_message()) # the body of the multipart message is everything after the message body = "--bd1" + MULTIPART.split("--bd1", 1)[1] eq_(body, part._container.read_body()) # body of the text part is the value itself eq_('Sasha\r\n', part.parts[0]._container.read_body()) # body of the inner mime part is the part after the headers # and till the outer boundary body = "--bd2\r\n" + MULTIPART.split( "--bd2\r\n", 1)[1].split("--bd1--", 1)[0] eq_(body, part.parts[1]._container.read_body()) # this is a simple message, make sure we can read the body # correctly part = scan(NO_CTYPE) eq_(NO_CTYPE, part._container.read_message()) eq_("Hello,\nI'm just testing message parsing\n\nBR,\nBob", part._container.read_body()) # multipart/related part = scan(RELATIVE) eq_(RELATIVE, part._container.read_message()) eq_("""This is html and text message, thanks\r\n\r\n-- \r\nRegards,\r\nBob\r\n""", part.parts[0]._container.read_body()) # enclosed part = scan(ENCLOSED) eq_(ENCLOSED, part._container.read_message()) body = part.parts[1]._container.read_body() ok_(body.endswith("--===============4360815924781479146==--"))
def preserve_ascii_test(): """Make sure that ascii remains ascii whenever possible""" # should remain ascii message = scan(TEXT_ONLY) message.body = u'Hello, how is it going?' message = scan(message.to_string()) eq_('7bit', message.content_encoding.value)
def message_is_delivery_notification_test(): message = scan(NDN) ok_(message.is_delivery_notification()) message = scan(BOUNCE) ok_(message.is_delivery_notification()) message = scan(IPHONE) assert_false(message.is_delivery_notification())
def missing_newline_test(): mime = "From: Foo <*****@*****.**>\r\nTo: Bar <*****@*****.**>\r\nMIME-Version: 1.0\r\nContent-type: text/html\r\nSubject: API Message\r\nhello, world\r\n.\r\n" message = scan(mime) eq_("hello, world\r\n.\r\n", message.body) # check that works with mixed-style-newlines mime = "From: Foo <*****@*****.**>\r\nTo: Bar <*****@*****.**>\r\nMIME-Version: 1.0\r\nContent-type: text/html\r\nSubject: API Message\nhello, world" message = scan(mime) eq_("hello, world", message.body)
def ascii_to_quoted_printable_test(): # contains unicode chars message = scan(TEXT_ONLY) unicode_value = u'☯Привет! Как дела? Что делаешь?,\n Что новенького?☯' message.body = unicode_value message = scan(message.to_string()) eq_('quoted-printable', message.content_encoding.value) eq_('utf-8', message.content_type.get_charset()) eq_(unicode_value, message.body)
def test_uservoice_case(): message = scan(LONG_LINKS) html = message.body message.body = html val = message.to_string() for line in val.splitlines(): print line ok_(len(line) < 200) message = scan(val) eq_(html, message.body)
def ascii_to_unicode_test(): """Make sure that ascii uprades to unicode whenever needed""" # contains unicode chars message = scan(TEXT_ONLY) unicode_value = u'☯Привет! Как дела? Что делаешь?,\n Что новенького?☯' message.body = unicode_value message = scan(message.to_string()) eq_('base64', message.content_encoding.value) eq_('utf-8', message.content_type.get_charset()) eq_(unicode_value, message.body)
def ascii_to_quoted_printable_test_2(): # contains unicode chars message = scan(TEXT_ONLY) value = u'Hello, how is it going?' * 100 message.body = value message = scan(message.to_string()) eq_('quoted-printable', message.content_encoding.value) eq_('iso-8859-1', message.content_type.get_charset()) eq_('iso-8859-1', message.charset) eq_(value, message.body)
def preserve_content_encoding_test_quoted_printable(): # should remain 8bit unicode_value = u'☯Привет! Как дела? Что делаешь?,\r\n Что новенького?☯' message = scan(QUOTED_PRINTABLE) body = message.parts[0].body message.parts[0].body = body + unicode_value message = scan(message.to_string()) eq_(body + unicode_value, message.parts[0].body) eq_('quoted-printable', message.parts[0].content_encoding.value)
def preserve_content_encoding_test_8bit(): # 8bit messages unicode_value = u'☯Привет! Как дела? Что делаешь?,\n Что новенького?☯' # should remain 8bit message = scan(EIGHT_BIT) message.parts[0].body = unicode_value message = scan(message.to_string()) eq_(unicode_value, message.parts[0].body) eq_('8bit', message.parts[0].content_encoding.value)
def ascii_to_quoted_printable_test(): """Make sure that ascii uprades to quoted-printable if it has long lines""" # contains unicode chars message = scan(TEXT_ONLY) value = u'Hello, how is it going?' * 100 message.body = value message = scan(message.to_string()) eq_('quoted-printable', message.content_encoding.value) eq_('iso-8859-1', message.content_type.get_charset()) eq_('iso-8859-1', message.charset) eq_(value, message.body)
def enclosed_body_alternation_test(): message = scan(ENCLOSED) value = u'☯Привет! Как дела? Что делаешь?, \r\n\r Что новенького?☯' enclosed = message.parts[1].enclosed enclosed.parts[0].body = value out = message.to_string() message = scan(out) enclosed = message.parts[1].enclosed eq_(value, enclosed.parts[0].body)
def weird_bounce_test(): message = scan(WEIRD_BOUNCE) eq_(0, len(message.parts)) eq_('text/plain', message.content_type) message = scan(WEIRD_BOUNCE_2) eq_(0, len(message.parts)) eq_('text/plain', message.content_type) message = scan(WEIRD_BOUNCE_3) eq_(0, len(message.parts)) eq_('text/plain', message.content_type)
def enclosed_inner_part_no_headers_test(): message = scan(TORTURE_PART) enclosed = message.parts[1].enclosed no_headers = enclosed.parts[0] assert_false(no_headers.headers) no_headers.body = no_headers.body + "Mailgun!" message = scan(message.to_string()) enclosed = message.parts[1].enclosed no_headers = enclosed.parts[0] ok_(no_headers.body.endswith("Mailgun!"))
def enclosed_header_inner_alternation_test(): message = scan(ENCLOSED) unicode_value = u'☯Привет! Как дела? Что делаешь?☯' enclosed = message.parts[1].enclosed enclosed.parts[0].headers['Subject'] = unicode_value message2 = scan(message.to_string()) enclosed2 = message2.parts[1].enclosed eq_(unicode_value, enclosed2.parts[0].headers['Subject']) eq_(enclosed.parts[0].body, enclosed2.parts[0].body) eq_(enclosed.parts[1].body, enclosed2.parts[1].body)
def enclosed_first_part_alternation_test(): message = scan(ENCLOSED) message.parts[0].body = 'Hey!\n' out = message.to_string() a = ENCLOSED.split("--===============6195527458677812340==", 2)[2] b = out.split("--===============6195527458677812340==", 2)[2] eq_(a, b, "Enclosed message should not be changed") message2 = scan(out) eq_('Hey!\n', message2.parts[0].body) eq_(message.parts[1].enclosed.parts[1].body, message2.parts[1].enclosed.parts[1].body)
def enclosed_body_alternation_test(): """We've changed the body in the inner part of the message only, the rest was not changed""" message = scan(ENCLOSED) value = u'☯Привет! Как дела? Что делаешь?, \r\n\r Что новенького?☯' enclosed = message.parts[1].enclosed enclosed.parts[0].body = value out = message.to_string() message = scan(out) enclosed = message.parts[1].enclosed eq_(value, enclosed.parts[0].body)
def enclosed_inner_part_no_headers_test(): """We've changed the inner part of the entity that has no headers, make sure that it was processed correctly""" message = scan(TORTURE_PART) enclosed = message.parts[1].enclosed no_headers = enclosed.parts[0] assert_false(no_headers.headers) no_headers.body = no_headers.body + "Mailgun!" message = scan(message.to_string()) enclosed = message.parts[1].enclosed no_headers = enclosed.parts[0] ok_(no_headers.body.endswith("Mailgun!"))
def enclosed_header_inner_alternation_test(): """We've changed the headers in the inner part of the message only, the rest was not changed""" message = scan(ENCLOSED) unicode_value = u'☯Привет! Как дела? Что делаешь?☯' enclosed = message.parts[1].enclosed enclosed.parts[0].headers['Subject'] = unicode_value message2 = scan(message.to_string()) enclosed2 = message2.parts[1].enclosed eq_(unicode_value, enclosed2.parts[0].headers['Subject']) eq_(enclosed.parts[0].body, enclosed2.parts[0].body) eq_(enclosed.parts[1].body, enclosed2.parts[1].body)
def immutability_test(): message = scan(BILINGUAL) eq_(u"Simple text. How are you? Как ты поживаешь?", message.headers['Subject']) eq_(BILINGUAL, message.to_string()) message = scan(TORTURE) eq_('Multi-media mail demonstration ', message.headers['Subject']) eq_(TORTURE, message.to_string()) message = scan(TORTURE) eq_('Multi-media mail demonstration ', message.headers['Subject']) with closing(StringIO()) as out: message.to_stream(out) eq_(TORTURE.rstrip(), out.getvalue().rstrip())
def ndn_2_test(): message = scan(BOUNCE) ok_(message.is_delivery_notification()) eq_(3, len(message.parts)) eq_('text/plain', message.parts[0].content_type) ok_(message.parts[1].content_type.is_delivery_status()) ok_(message.parts[2].content_type.is_message_container())
def torture_message_test(): message = scan(TORTURE) tree = tree_to_string(message).splitlines() expected = TORTURE_PARTS.splitlines() eq_(len(tree), len(expected)) for a, b in zip(expected, tree): eq_(a, b)
def top_level_headers_immutability_test(): message = scan(ENCLOSED) message.headers['Subject'] = u'☯Привет! Как дела? Что делаешь?☯' out = message.to_string() a = ENCLOSED.split("--===============6195527458677812340==", 1)[1] b = out.split("--===============6195527458677812340==", 1)[1] eq_(a, b, "Bodies should not be changed in any way")
def mailbox_full_test(): message = scan(MAILBOX_FULL) ok_(message.is_delivery_notification()) eq_(3, len(message.parts)) eq_('text/plain', message.parts[0].content_type) ok_(message.parts[1].content_type.is_delivery_status()) ok_(message.parts[2].content_type.is_headers_container())
def enclosed_message_test(): message = scan(ENCLOSED) pmessage = message_from_string(ENCLOSED) eq_(C('multipart', 'mixed', dict(boundary='===============6195527458677812340==')), message.content_type) eq_(u'"Александр Клижентас☯" <*****@*****.**>', message.headers['To']) eq_(pmessage.get_payload()[0].get_payload(), message.parts[0].body) enclosed = message.parts[1] penclosed = pmessage.get_payload(1) eq_(('message/rfc822', {'name': u'thanks.eml'},), enclosed.headers['Content-Type']) pbody = penclosed.get_payload()[0].get_payload()[0].get_payload(decode=True) pbody = unicode(pbody, 'utf-8') body = enclosed.enclosed.parts[0].body eq_(pbody, body) body = enclosed.enclosed.parts[1].body pbody = penclosed.get_payload()[0].get_payload()[1].get_payload(decode=True) pbody = unicode(pbody, 'utf-8') eq_(pbody, body)
def create_message_without_headers_test(): message = scan(TEXT_ONLY) for h, v in message.headers.items(): del message.headers[h] assert_false(message.headers, message.headers) assert_raises(EncodingError, message.to_string)
def create_message_without_headers_test(): message = scan(TEXT_ONLY) for h,v in message.headers.items(): del message.headers[h] assert_false(message.headers, message.headers) assert_raises(EncodingError, message.to_string)
def enclosed_message_test(): message = scan(ENCLOSED) pmessage = _email.message_from_string(ENCLOSED) eq_( C('multipart', 'mixed', dict(boundary='===============6195527458677812340==')), message.content_type) eq_(u'"Александр Клижентас☯" <*****@*****.**>', message.headers['To']) eq_(pmessage.get_payload()[0].get_payload(), message.parts[0].body) enclosed = message.parts[1] penclosed = pmessage.get_payload(1) eq_(( 'message/rfc822', { 'name': u'thanks.eml' }, ), enclosed.headers['Content-Type']) pbody = penclosed.get_payload()[0].get_payload()[0].get_payload( decode=True) pbody = pbody.decode('utf-8') body = enclosed.enclosed.parts[0].body eq_(pbody, body) body = enclosed.enclosed.parts[1].body pbody = penclosed.get_payload()[0].get_payload()[1].get_payload( decode=True) pbody = pbody.decode('utf-8') eq_(pbody, body)
def missing_final_boundaries_enclosed_test(): message = scan(ENCLOSED_BROKEN_BOUNDARY) eq_(( 'message/rfc822', { 'name': u'thanks.eml' }, ), message.parts[1].headers['Content-Type'])
def test_non_ascii_content_type(): data = """From: [email protected] To: [email protected] Content-Type: text/点击链接绑定邮箱; charset="us-ascii" Body.""" message = scan(data) assert_raises(DecodingError, lambda x: message.headers, 1)
def no_ctype_headers_and_and_boundaries_test(): """We are ok, when there is no content type and boundaries""" message = scan(NO_CTYPE) eq_(C('text', 'plain', dict(charset='ascii')), message.content_type) pmessage = _email.message_from_string(NO_CTYPE) eq_(message.body, pmessage.get_payload(decode=True).decode('utf-8')) for a, b in zip(NO_CTYPE_HEADERS, message.headers.iteritems()): eq_(a, b)
def set_message_id_test(): # Given message = scan(MULTI_RECEIVED_HEADERS) # When/Then eq_({'[email protected]', '*****@*****.**'}, set(message.references))
def broken_ctype_test(): message = scan(RUSSIAN_ATTACH_YAHOO) attachment = message.parts[1] eq_('image/png', attachment.detected_content_type) eq_('png', attachment.detected_subtype) eq_('image', attachment.detected_format) eq_(u'Картинка с очень, очень длинным предлинным именем преименем таким чт�', attachment.detected_file_name) ok_(not attachment.is_body())
def message_alter_body_and_serialize_test(): message = scan(IPHONE) part = list(message.walk())[2] part.body = u'Привет, Danielle!\n\n' with closing(StringIO()) as out: message.to_stream(out) message1 = scan(out.getvalue()) message2 = scan(message.to_string()) parts = list(message1.walk()) eq_(3, len(parts)) eq_(u'Привет, Danielle!\n\n', parts[2].body) parts = list(message2.walk()) eq_(3, len(parts)) eq_(u'Привет, Danielle!\n\n', parts[2].body)
def parse_then_serialize_malformed_message_test(): """ We don't have to fully parse message headers if they are never accessed. Thus we should be able to parse then serialize a message with malformed headers without crashing, even though we would crash if we fully parsed it. """ serialized = scan(OUTLOOK_EXPRESS).to_string() eq_(OUTLOOK_EXPRESS, serialized)
def test_attachments(): """Content-Type and file name are properly detected """ # pdf attachment, file name in Content-Disposition data = """Content-Type: application/octet-stream; name="J_S III_W-2.pdf" Content-Disposition: attachment; filename*="J_S III_W-2.pdf" Content-Transfer-Encoding: base64 YmxhaGJsYWhibGFo """ part = scan(data) eq_('application/pdf', part.detected_content_type) eq_('J_S III_W-2.pdf', part.detected_file_name) # no attachment data = """Content-Type: text; charset=ISO-8859-1 some plain text here """ part = scan(data) eq_('text/plain', part.detected_content_type) eq_('', part.detected_file_name) # inline attachment, file name in Content-Disposition data = """Content-Type: image/png Content-Transfer-Encoding: base64 Content-Disposition: inline; filename=2.png Content-ID: <FN2JbofqmRT7zIMt8uzW> YmxhaGJsYWhibGFo """ part = scan(data) eq_('image/png', part.detected_content_type) eq_('2.png', part.detected_file_name) # broken mime data = """Content-Type: Нельзя это распарсить адекватно" Content-Disposition: Да и это тоже Content-Transfer-Encoding: base64 YmxhaGJsYWhibGFo """ part = recover(data) eq_('text/plain', part.detected_content_type) eq_('', part.detected_file_name)
def torture_alter_test(): message = scan(TORTURE) unicode_value = u'☯Привет! Как дела? Что делаешь?,\n Что новенького?☯' message.parts[5].enclosed.parts[0].parts[0].body = unicode_value for p in message.walk(): if str(p.content_type) == 'text/plain': p.body = unicode_value p.headers['Mailgun-Altered'] = u'Oh yeah' message = scan(message.to_string()) eq_(unicode_value, message.parts[5].enclosed.parts[0].parts[0].body) tree = tree_to_string(message).splitlines() expected = TORTURE_PARTS.splitlines() eq_(len(tree), len(expected)) for a, b in zip(expected, tree): eq_(a, b)
def content_types_test(): part = scan(IPHONE) eq_((None, {}), part.content_disposition) assert_false(part.is_attachment()) eq_(('inline', {'filename': 'photo.JPG'}), part.parts[1].content_disposition) assert_false(part.parts[1].is_attachment()) ok_(part.parts[1].is_inline()) part = scan(SPAM_BROKEN_CTYPE) eq_((None, {}), part.content_disposition) part = scan(MAILGUN_PIC) attachment = part.parts[1] eq_('image/png', attachment.detected_content_type) eq_('png', attachment.detected_subtype) eq_('image', attachment.detected_format) ok_(not attachment.is_body())
def alter_message_test_size(): # check to make sure size is recalculated after header changed stream_part = scan(IPHONE) size_before = stream_part.size stream_part.headers.add('foo', 'bar') size_after = stream_part.size eq_(size_before, len(IPHONE)) eq_(size_after, len(stream_part.to_string()))
def message_headers_equivalence_test(): """ FallbackMimePart headers match MimePart headers exactly for the same input. """ # Given message = scan(ENCLOSED) fallback_message = create.from_string(ENCLOSED) # When eq_(message.headers.items(), fallback_message.headers.items())
def walk_test(): message = scan(ENCLOSED) expected = [ 'multipart/mixed', 'text/plain', 'message/rfc822', 'multipart/alternative', 'text/plain', 'text/html' ] eq_(expected[1:], [str(p.content_type) for p in message.walk()]) eq_(expected, [str(p.content_type) for p in message.walk(with_self=True)]) eq_(['text/plain', 'message/rfc822'], [str(p.content_type) for p in message.walk(skip_enclosed=True)])
def preserve_formatting_with_new_headers_test(): # MULTIPART contains this header: # Content-Type: multipart/alternative; boundary=bd1 # which will change to this if it is re-serialized: # Content-Type: multipart/alternative; boundary="bd1" message = scan(MULTIPART) message.headers.prepend('X-New-Header', 'Added') new_header, remaining_mime = message.to_string().split('\r\n', 1) eq_('X-New-Header: Added', new_header) eq_(MULTIPART, remaining_mime)
def enclosed_broken_encoding_test(): message = scan(ENCLOSED_BROKEN_ENCODING) for p in message.walk(): try: p.headers['A'] = 'b' except: pass with closing(StringIO()) as out: message.to_stream(out) ok_(out.getvalue())
def ndn_test(): message = scan(NDN) ok_(message.is_delivery_notification()) eq_(3, len(message.parts)) eq_('Returned mail: Cannot send message for 5 days', message.headers['Subject']) eq_('text/plain', message.parts[0].content_type) ok_(message.parts[1].content_type.is_delivery_status()) ok_(message.parts[2].content_type.is_message_container()) eq_('Hello, how are you', message.parts[2].enclosed.headers['Subject'])
def correct_charset_test(): # Given message = scan(TEXT_ONLY) eq_('iso-8859-1', message.charset) # When message.charset = 'utf-8' # Then eq_('utf-8', message.charset) eq_('utf-8', str(message.headers['Content-Type'].get_charset()))
def double_serialization_test(): message = scan(TORTURE) message.headers['Subject'] = u'Поменяли текст ☢' a = message.to_string() b = message.to_string() with closing(StringIO()) as out: message.to_stream(out) c = out.getvalue() eq_(a, b) eq_(b, c)