def test_create_mime_with_inline_attachment(): text = randUnicode(length=1024) html = randUnicode(length=1024) attachments = [] for _ in range(4): content = randStr(size=4096) _filename = randUnicode() f = FileStorage(stream=StringIO.StringIO(content), filename=_filename, content_type="image/jpeg") att = Attachment.fromFileStorage(f, AttachmentMetadata(f.filename, f.content_type)) att.metadata.content_disposition = AttachmentType.INLINE attachments.append(att) raw_mime = createMime(text, html, attachments) assert raw_mime.content_type.value == u"multipart/alternative" # 1 part is for text/plain content, and one for multipart/related assert set(["(multipart/related)", "(text/plain)"]) == set(map(lambda p: str(p), raw_mime.parts)) assert text == filter(lambda p:str(p) == "(text/plain)", raw_mime.parts)[0].body related_part = filter(lambda p: str(p) == "(multipart/related)", raw_mime.parts)[0] # one for html and rest inline atts assert len(related_part.parts) == len(attachments) + 1 assert html == filter(lambda p:str(p) == "(text/html)", related_part.parts)[0].body attachment_parts = filter(lambda p: p.content_disposition[0] == AttachmentType.INLINE, related_part.parts) assert set(map(lambda att: att.metadata.filename, attachments)) == set(map(lambda att_part: att_part.content_disposition[1]["filename"], attachment_parts)) assert set(map(lambda att: att.content.content, attachments)) == set(map(lambda att_part: att_part.body, attachment_parts)) assert len(re.findall("=\n", raw_mime.to_string())) == 0
def test_attachment_from_file_storage(): blob = randStream() filename = randUnicode() content_type = "y/x" metadata = AttachmentMetadata(filename, content_type) _file = FileStorage(stream=blob, filename=filename, content_type=content_type) input_file = copy.deepcopy(_file) attachment = Attachment.fromFileStorage(input_file, metadata) assert attachment.content.content == _file.read() assert attachment.metadata.filename == filename assert attachment.metadata.content_type == content_type assert attachment.metadata.content_disposition == AttachmentType.ATTACHMENT # test content_type correction blob = randStream() filename = randUnicode() content_type = "badcontenttype" metadata = AttachmentMetadata(filename, content_type) _file = FileStorage(stream=blob, filename=filename, content_type=content_type) input_file = copy.deepcopy(_file) attachment = Attachment.fromFileStorage(input_file, metadata) assert attachment.content.content == _file.read() assert attachment.metadata.filename == filename assert attachment.metadata.content_type == "application/octet-stream" assert attachment.metadata.content_disposition == AttachmentType.ATTACHMENT
def test_unknown_content_type_with_no_disposition(): root_mime = mime.create.multipart("mixed") text_1 = mime.create.text("plain", randUnicode(length=1332)) root_mime.append(text_1) att = mime.create.attachment(u"image/png", randStr(size=8722), randUnicode(), AttachmentType.ATTACHMENT) root_mime.append(att) content = randUnicode(length=1523) unknown_part = mime.create.text("plain", content) unknown_part.headers["Content-Type"] = mime.message.ContentType( u"xx", u"yy") root_mime.append(unknown_part) text, html, attachments = parseMime(root_mime) assert text == text_1.body # non alternate text gets added to html assert html == u"<br>".join( [u"<div data-preveil-text>{}</div>".format(text_1.body)]) assert len(attachments) == 2 # assert that the bad content type has translated in as a regular attachment assert map(lambda att: att.metadata.content_disposition, attachments) == [AttachmentType.ATTACHMENT] * 2 # all attachment still has the right content assert map(lambda att: att.content.content, attachments) == map( lambda p: p.body, filter(lambda p: p.content_type.value != u"text/plain", root_mime.parts))
def test_from_mime(): # create email from separated mime and test if it get reconstructed ok root_mime = mime.create.multipart("mixed") text_1 = mime.create.text("plain", randUnicode(length=3)) root_mime.append(text_1) attachments = [] for _ in range(2): a = mime.create.attachment("image/png", randStr(size=10), randUnicode(), AttachmentType.INLINE) attachments.append(a) a.to_string() root_mime.append(a) text_2 = mime.create.text("plain", randUnicode(length=3)) root_mime.append(text_2) for _ in range(3): a = mime.create.attachment("video/mp4", randStr(size=15), randUnicode(), AttachmentType.ATTACHMENT) attachments.append(a) a.to_string() root_mime.append(a) root_mime.headers["Message-Id"] = u"<{}>".format(EmailHelpers.newMessageId()) email = EmailV1.fromMime(root_mime.to_string(), [], {"user_id": u"*****@*****.**", "display_name": u"S B"}) # check if the attachments have been all separated properly body_mime = mime.from_string(email.body.content) assert len(attachments) == len(filter(lambda p: p.content_type.value == DUMMY_CONTENT_TYPE , body_mime.parts)) # check att hashes are properly inserted as filenames assert map(lambda a: HexEncode(Sha256Sum(a.to_string())), attachments) == map(lambda p: p.content_disposition[1]["filename"], filter(lambda p: p.content_type.value == DUMMY_CONTENT_TYPE , body_mime.parts))
def test_parse_mime_regular_attachment(): root_mime = mime.create.multipart("mixed") text_1 = mime.create.text("plain", randUnicode(length=1)) root_mime.append(text_1) for _ in range(3): att = mime.create.attachment(u"image/png", randStr(size=12355), randUnicode(), AttachmentType.ATTACHMENT) root_mime.append(att) text, html, attachments = parseMime(root_mime) assert text == text_1.body # non alternate text gets added to html assert html == u"<br>".join( [u"<div data-preveil-text>{}</div>".format(text_1.body)]) # all are regular atts assert all( map( lambda att: att.metadata.content_disposition == AttachmentType. ATTACHMENT, attachments)) # all the atts exist w right content assert map(lambda att: att.content.content, attachments) == map( lambda att_part: att_part.body, filter(lambda p: p.content_disposition[0] == AttachmentType.ATTACHMENT, root_mime.parts)) #add some nested atts nested_mime = mime.create.multipart("mixed") html_1 = mime.create.text("html", randUnicode(length=454)) nested_mime.append(html_1) for _ in range(5): att = mime.create.attachment(u"application/octet-stream", randStr(size=545), randUnicode(length=12), AttachmentType.ATTACHMENT) nested_mime.append(att) root_mime.append(nested_mime) text, html, attachments = parseMime(root_mime) assert text == text_1.body # non alternate text gets added to html assert html == u"<br>".join( [u"<div data-preveil-text>{}</div>".format(text_1.body), html_1.body]) # all are regular atts assert all( map( lambda att: att.metadata.content_disposition == AttachmentType. ATTACHMENT, attachments)) # all the atts exist w right content assert map(lambda att: att.content.content, attachments) == map( lambda att_part: att_part.body, filter(lambda p: p.content_disposition[0] == AttachmentType.ATTACHMENT, root_mime.parts + nested_mime.parts))
def test_case_insensitive_dict(): # test initialization k = randUnicode() sensitive_dict = { 1: randUnicode(), k: randUnicode(), randomize_casing(k): randUnicode(), (1, randUnicode(), randUnicode()): randUnicode() } insensitive_dict = CaseInsensitiveDict(sensitive_dict) assert len(sensitive_dict) == 4 assert len(insensitive_dict) == 3 # test addition for k in insensitive_dict.keys(): for i in xrange(20): insensitive_dict[randomize_casing(k)] = randUnicode() assert len(insensitive_dict) == 3 # test access for k in insensitive_dict.keys(): for i in xrange(20): assert insensitive_dict[k] == insensitive_dict[randomize_casing(k)] # deletion for k in insensitive_dict.keys(): del insensitive_dict[randomize_casing(k)] assert len(insensitive_dict) == 0
def test_attachment_to_mime(): filename = randUnicode() content_type = "image/png" content_disposition = randUnicode() content_id = randUnicode() metadata = AttachmentMetadata(filename, content_type, content_disposition, content_id) blob = randStr(size=1024) attachement = Attachment(metadata, Content(blob)) att_mime = attachement.toMime() assert att_mime.headers.get("Content-Id") == content_id assert att_mime.headers.get("Content-Disposition") == (content_disposition, {"filename": filename}) assert att_mime.headers.get("Content-Type") == content_type assert att_mime.body == blob
def test_parse_mime_text_html(): raw_mime = mime.create.text("html", randUnicode(length=2)) text, html, attachments = parseMime(raw_mime) assert text == u"" assert html == raw_mime.body assert attachments == []
def test_attachment_reconstruction(): raw_message = """Received: ConsoleMessageDelivery From: "(Secure) iOS Dev" <*****@*****.**> Content-Type: multipart/related; boundary="Apple-Mail=_B3D4A2AE-CBE5-47E8-86E4-5052190755A6"; type="text/plain" Subject: Message-Id: <*****@*****.**> Date: Mon, 27 Jun 2016 10:43:03 -0400 To: iOS Dev <*****@*****.**> Mime-Version: 1.0 (Mac OS X Mail 9.3 \(3124\)) --Apple-Mail=_B3D4A2AE-CBE5-47E8-86E4-5052190755A6 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=us-ascii blah blah blah --Apple-Mail=_B3D4A2AE-CBE5-47E8-86E4-5052190755A6 Content-Transfer-Encoding: base64 Content-Disposition: dummy; filename="handle" Content-Type: dummy/dummy; name="handle" cGxhY2Vob2xkZXIgZm9yIGFuIGF0dGFjaG1lbnQ= """ attachment_content = randUnicode() raw_attachment = "Content-Type: text/plain; name=\"test.txt\"\r\nContent-Disposition: attachment; filename=\"test.txt\"\r\n\r\n{}".format(attachment_content) message = mime.from_string(raw_message) attachment = mime.from_string(raw_attachment) attachments = {"handle": attachment} status, restored_message = EmailV1.restoreAttachments(message, attachments) assert status assert len(restored_message.parts) == 2 assert restored_message.parts[1] == attachment
def test_parse_mime_inline_attachment(): root_mime = mime.create.multipart("related") html_1 = mime.create.text("html", randUnicode(length=10)) root_mime.append(html_1) for _ in range(6): att = mime.create.attachment(u"image/png", randStr(size=235), randUnicode(), AttachmentType.INLINE) root_mime.append(att) text, html, attachments = parseMime(root_mime) assert text == u"" #the 3 attachments should be inline and have content id assigned to them assert len(attachments) == 6 assert all([ att.metadata.content_disposition == AttachmentType.INLINE for att in attachments ]) matches = re.findall("<div data-preveil-inline>(.*?)</div>", html, flags=re.DOTALL) # all are inline assert len(matches) == len(attachments) # make sure all the content ids are included content_ids = [re.findall('src="(.*?)"', img)[0] for img in matches] content_ids = [re.subn("cid:", "<", ci)[0] + ">" for ci in content_ids] assert content_ids == [att.metadata.content_id for att in attachments] # test when inline atts have content_id root_mime = mime.create.multipart("alternative") text_1 = mime.create.text("plain", randUnicode(length=19)) root_mime.append(text_1) related_mime = mime.create.multipart("related") html_1 = mime.create.text("html", randUnicode(length=15)) related_mime.append(html_1) att = mime.create.attachment(u"application/octet-stream", randStr(size=421), randUnicode(), AttachmentType.INLINE) cid = EmailHelpers.newMessageId() att.headers["Content-Id"] = cid related_mime.append(att) root_mime.append(related_mime) text, html, attachments = parseMime(root_mime) assert len(attachments) == 1 assert cid == attachments[0].metadata.content_id assert len(re.findall("data-preveil-inline", html)) == 0
def test_parse_mime_only_plaintext(): raw_mime = mime.create.text("plain", randUnicode(length=32321)) text, html, attachments = parseMime(raw_mime) assert text == raw_mime.body # if not alternative, text should be wrapped inside html assert html == u"<div data-preveil-text>{}</div>".format(raw_mime.body) assert attachments == []
def test_invalid_content_disposition(): root_mime = mime.create.multipart("mixed") text_1 = mime.create.text("plain", randUnicode(length=1653)) root_mime.append(text_1) att = mime.create.attachment(u"image/png", randStr(size=2052), randUnicode(), u"invalidCD") root_mime.append(att) text, html, attachments = parseMime(root_mime) assert text == text_1.body # non alternate text gets added to html assert html == u"<br>".join([u"<div data-preveil-text>{}</div>".format(text_1.body)]) assert len(attachments) == 1 # assert that the bad CD has translated as a regular attachment assert attachments[0].metadata.content_disposition == AttachmentType.ATTACHMENT # all attachment still has the right content assert attachments[0].content.content == filter(lambda p: p.content_disposition[0] == u"invalidCD", root_mime.parts)[0].body
def test_create_plain_mime(): # html/text can't be None with pytest.raises(EmailException) as e: createMime(None, u"html", []) with pytest.raises(EmailException) as e: createMime(u"text", None, []) text = randUnicode(length=1024) html = randUnicode(length=1024) raw_mime = createMime(text, html, []) assert raw_mime.content_type.value == u"multipart/alternative" assert len(raw_mime.parts) == 1 + 1 assert set(["(text/html)", "(text/plain)"]) == set(map(lambda p: str(p), raw_mime.parts)) assert text == filter(lambda p:str(p) == "(text/plain)", raw_mime.parts)[0].body assert html == filter(lambda p:str(p) == "(text/html)", raw_mime.parts)[0].body # test that quotable encoding always has the correct softline break as "=\r\n" assert len(re.findall("=\n", raw_mime.to_string())) == 0
def __init__(self, subject=None, sender=None, user_key=None, symm_key=PVKeyFactory.newSymmKey()): self.subject = subject if subject else randUnicode(5) self.sender = sender if sender else randUnicode(5) self.tos = [recipient() for _ in range(3)] self.ccs = [recipient() for _ in range(3)] self.body = { "block_ids": [randUnicode(5) for _ in range(3)], "snippet": randUnicode(5), "size": randint(0, 100), } self.attachments = [] self.other_headers = {} self.symm_key = symm_key self.user_key = user_key if user_key else PVKeyFactory.newUserKey( key_version=0)
def test_attachment_to_mime(): filename = randUnicode() content_type = "image/png" content_disposition = randUnicode() content_id = randUnicode() metadata = AttachmentMetadata(filename, content_type, content_disposition, content_id) blob = randStr(size=1024) attachement = Attachment(metadata, Content(blob)) att_mime = attachement.toMime() assert att_mime.headers.get("Content-Id") == content_id assert att_mime.headers.get("Content-Disposition") == (content_disposition, { "filename": filename }) assert att_mime.headers.get("Content-Type") == content_type assert att_mime.body == blob
def make_group_recipients(counts=2): groups = {} for _ in range(counts): alias = randUnicode(5) members = [recipient() for _ in range(randint(1, 4))] groups[alias] = { "alias": "{}@alias.test.preveil.com".format(alias), "users": members } return groups
def test_parse_mime_nested_alternative_and_text(): root_mime = mime.create.multipart("mixed") text_1 = mime.create.text("plain", randUnicode(length=1)) root_mime.append(text_1) alternate_mime = mime.create.multipart("alternative") text_2 = mime.create.text("plain", randUnicode(length=2)) html_2 = mime.create.text("html", randUnicode(length=3)) alternate_mime.append(text_2) alternate_mime.append(html_2) root_mime.append(alternate_mime) text_3 = mime.create.text("plain", randUnicode(length=5)) root_mime.append(text_3) text, html, attachments = parseMime(root_mime) assert text == u"\n".join([text_1.body, text_2.body, text_3.body]) # non alternate text gets added to html assert html == u"<br>".join([u"<div data-preveil-text>{}</div>".format(text_1.body), html_2.body, u"<div data-preveil-text>{}</div>".format(text_3.body)]) assert attachments == []
def test_from_mime(): # create email from separated mime and test if it get reconstructed ok root_mime = mime.create.multipart("mixed") text_1 = mime.create.text("plain", randUnicode(length=3)) root_mime.append(text_1) attachments = [] for _ in range(2): a = mime.create.attachment("image/png", randStr(size=10), randUnicode(), AttachmentType.INLINE) attachments.append(a) a.to_string() root_mime.append(a) text_2 = mime.create.text("plain", randUnicode(length=3)) root_mime.append(text_2) for _ in range(3): a = mime.create.attachment("video/mp4", randStr(size=15), randUnicode(), AttachmentType.ATTACHMENT) attachments.append(a) a.to_string() root_mime.append(a) root_mime.headers["Message-Id"] = u"<{}>".format( EmailHelpers.newMessageId()) email = EmailV1.fromMime(root_mime.to_string(), [], overwrite_sender={ "user_id": u"*****@*****.**", "display_name": u"S B" }) # check if the attachments have been all separated properly body_mime = mime.from_string(email.body.content) assert len(attachments) == len( filter(lambda p: p.content_type.value == DUMMY_CONTENT_TYPE, body_mime.parts)) # check att hashes are properly inserted as filenames assert map(lambda a: HexEncode(Sha256Sum(a.to_string())), attachments) == map( lambda p: p.content_disposition[1]["filename"], filter(lambda p: p.content_type.value == DUMMY_CONTENT_TYPE, body_mime.parts))
def test_parse_mime_inline_attachment(): root_mime = mime.create.multipart("related") html_1 = mime.create.text("html", randUnicode(length=10)) root_mime.append(html_1) for _ in range(6): att = mime.create.attachment(u"image/png", randStr(size=235), randUnicode(), AttachmentType.INLINE) root_mime.append(att) text, html, attachments = parseMime(root_mime) assert text == u"" #the 3 attachments should be inline and have content id assigned to them assert len(attachments) == 6 assert all([att.metadata.content_disposition == AttachmentType.INLINE for att in attachments]) matches = re.findall("<div data-preveil-inline>(.*?)</div>", html, flags=re.DOTALL) # all are inline assert len(matches) == len(attachments) # make sure all the content ids are included content_ids = [re.findall('src="(.*?)"',img)[0] for img in matches] content_ids = [re.subn("cid:", "<", ci)[0] + ">" for ci in content_ids] assert content_ids == [att.metadata.content_id for att in attachments] # test when inline atts have content_id root_mime = mime.create.multipart("alternative") text_1 = mime.create.text("plain", randUnicode(length=19)) root_mime.append(text_1) related_mime = mime.create.multipart("related") html_1 = mime.create.text("html", randUnicode(length=15)) related_mime.append(html_1) att = mime.create.attachment(u"application/octet-stream", randStr(size=421), randUnicode(), AttachmentType.INLINE) cid = EmailHelpers.newMessageId() att.headers["Content-Id"] = cid related_mime.append(att) root_mime.append(related_mime) text, html, attachments = parseMime(root_mime) assert len(attachments) == 1 assert cid == attachments[0].metadata.content_id assert len(re.findall("data-preveil-inline", html)) == 0
def test_attachment_metadata(): filename = randUnicode() content_type = randUnicode() content_disposition = randUnicode() content_id = randUnicode() size = random.randint(0, 1000000000) metadata = AttachmentMetadata(filename, content_type, content_disposition, content_id, size) assert metadata.toDict() == { "filename": filename, "content_type" : content_type, "content_id": content_id, "content_disposition": content_disposition, "size": size } metadata = AttachmentMetadata(filename, None, None, None, None) assert metadata.toDict() == { "filename": filename, "content_type" : u"application/octet-stream", "content_id": None, "content_disposition": AttachmentType.ATTACHMENT, "size": None }
def test_create_plain_mime(): # html/text can't be None with pytest.raises(EmailException) as e: createMime(None, u"html", []) with pytest.raises(EmailException) as e: createMime(u"text", None, []) text = randUnicode(length=1024) html = randUnicode(length=1024) raw_mime = createMime(text, html, []) assert raw_mime.content_type.value == u"multipart/alternative" assert len(raw_mime.parts) == 1 + 1 assert set(["(text/html)", "(text/plain)"]) == set(map(lambda p: str(p), raw_mime.parts)) assert text == filter(lambda p: str(p) == "(text/plain)", raw_mime.parts)[0].body assert html == filter(lambda p: str(p) == "(text/html)", raw_mime.parts)[0].body # test that quotable encoding always has the correct softline break as "=\r\n" assert len(re.findall("=\n", raw_mime.to_string())) == 0
def test_create_mime_with_regular_attachment(): text = randUnicode(length=1024) html = randUnicode(length=1024) attachments = [] for _ in range(4): content = randStr(size=4096) _filename = randUnicode() f = FileStorage(stream=StringIO.StringIO(content), filename=_filename, content_type="image/jpeg") attachments.append( Attachment.fromFileStorage( f, AttachmentMetadata(f.filename, f.content_type))) raw_mime = createMime(text, html, attachments) assert raw_mime.content_type.value == u"multipart/mixed" # 1 part is for text/html content, and one for each attachment assert len(raw_mime.parts) == len(attachments) + 1 body_parts = filter( lambda p: p.content_type.value == u"multipart/alternative", raw_mime.parts) assert len(body_parts) == 1 body_part = body_parts[0] assert set(["(text/html)", "(text/plain)"]) == set(map(lambda p: str(p), body_part.parts)) attachment_parts = filter( lambda p: p.content_disposition[0] == AttachmentType.ATTACHMENT, raw_mime.parts) assert set(map(lambda att: att.metadata.content_disposition, attachments)) == set( map(lambda att_part: att_part.content_disposition[0], attachment_parts)) assert set(map(lambda att: att.metadata.filename, attachments)) == set( map(lambda att_part: att_part.content_disposition[1]["filename"], attachment_parts)) assert set(map(lambda att: att.content.content, attachments)) == set( map(lambda att_part: att_part.body, attachment_parts)) assert len(re.findall("=\n", raw_mime.to_string())) == 0
def test_create_mime_with_regular_attachment(): text = randUnicode(length=1024) html = randUnicode(length=1024) attachments = [] for _ in range(4): content = randStr(size=4096) _filename = randUnicode() f = FileStorage(stream=StringIO.StringIO(content), filename=_filename, content_type="image/jpeg") attachments.append(Attachment.fromFileStorage(f, AttachmentMetadata(f.filename, f.content_type))) raw_mime = createMime(text, html, attachments) assert raw_mime.content_type.value == u"multipart/mixed" # 1 part is for text/html content, and one for each attachment assert len(raw_mime.parts) == len(attachments) + 1 body_parts = filter(lambda p: p.content_type.value == u"multipart/alternative", raw_mime.parts) assert len(body_parts) == 1 body_part = body_parts[0] assert set(["(text/html)", "(text/plain)"]) == set(map(lambda p: str(p), body_part.parts)) attachment_parts = filter(lambda p: p.content_disposition[0] == AttachmentType.ATTACHMENT, raw_mime.parts) assert set(map(lambda att: att.metadata.content_disposition, attachments)) == set(map(lambda att_part: att_part.content_disposition[0], attachment_parts)) assert set(map(lambda att: att.metadata.filename, attachments)) == set(map(lambda att_part: att_part.content_disposition[1]["filename"], attachment_parts)) assert set(map(lambda att: att.content.content, attachments)) == set(map(lambda att_part: att_part.body, attachment_parts)) assert len(re.findall("=\n", raw_mime.to_string())) == 0
def test_parse_mime_nested_alternative_and_text(): root_mime = mime.create.multipart("mixed") text_1 = mime.create.text("plain", randUnicode(length=1)) root_mime.append(text_1) alternate_mime = mime.create.multipart("alternative") text_2 = mime.create.text("plain", randUnicode(length=2)) html_2 = mime.create.text("html", randUnicode(length=3)) alternate_mime.append(text_2) alternate_mime.append(html_2) root_mime.append(alternate_mime) text_3 = mime.create.text("plain", randUnicode(length=5)) root_mime.append(text_3) text, html, attachments = parseMime(root_mime) assert text == u"\n".join([text_1.body, text_2.body, text_3.body]) # non alternate text gets added to html assert html == u"<br>".join([ u"<div data-preveil-text>{}</div>".format(text_1.body), html_2.body, u"<div data-preveil-text>{}</div>".format(text_3.body) ]) assert attachments == []
def test_attachment_metadata(): filename = randUnicode() content_type = randUnicode() content_disposition = randUnicode() content_id = randUnicode() size = random.randint(0, 1000000000) metadata = AttachmentMetadata(filename, content_type, content_disposition, content_id, size) assert metadata.toDict() == { "filename": filename, "content_type": content_type, "content_id": content_id, "content_disposition": content_disposition, "size": size } metadata = AttachmentMetadata(filename, None, None, None, None) assert metadata.toDict() == { "filename": filename, "content_type": u"application/octet-stream", "content_id": None, "content_disposition": AttachmentType.ATTACHMENT, "size": None }
def test_invalid_content_disposition(): root_mime = mime.create.multipart("mixed") text_1 = mime.create.text("plain", randUnicode(length=1653)) root_mime.append(text_1) att = mime.create.attachment(u"image/png", randStr(size=2052), randUnicode(), u"invalidCD") root_mime.append(att) text, html, attachments = parseMime(root_mime) assert text == text_1.body # non alternate text gets added to html assert html == u"<br>".join( [u"<div data-preveil-text>{}</div>".format(text_1.body)]) assert len(attachments) == 1 # assert that the bad CD has translated as a regular attachment assert attachments[ 0].metadata.content_disposition == AttachmentType.ATTACHMENT # all attachment still has the right content assert attachments[0].content.content == filter( lambda p: p.content_disposition[0] == u"invalidCD", root_mime.parts)[0].body
def test_create_mime_with_inline_attachment(): text = randUnicode(length=1024) html = randUnicode(length=1024) attachments = [] for _ in range(4): content = randStr(size=4096) _filename = randUnicode() f = FileStorage(stream=StringIO.StringIO(content), filename=_filename, content_type="image/jpeg") att = Attachment.fromFileStorage( f, AttachmentMetadata(f.filename, f.content_type)) att.metadata.content_disposition = AttachmentType.INLINE attachments.append(att) raw_mime = createMime(text, html, attachments) assert raw_mime.content_type.value == u"multipart/alternative" # 1 part is for text/plain content, and one for multipart/related assert set(["(multipart/related)", "(text/plain)"]) == set(map(lambda p: str(p), raw_mime.parts)) assert text == filter(lambda p: str(p) == "(text/plain)", raw_mime.parts)[0].body related_part = filter(lambda p: str(p) == "(multipart/related)", raw_mime.parts)[0] # one for html and rest inline atts assert len(related_part.parts) == len(attachments) + 1 assert html == filter(lambda p: str(p) == "(text/html)", related_part.parts)[0].body attachment_parts = filter( lambda p: p.content_disposition[0] == AttachmentType.INLINE, related_part.parts) assert set(map(lambda att: att.metadata.filename, attachments)) == set( map(lambda att_part: att_part.content_disposition[1]["filename"], attachment_parts)) assert set(map(lambda att: att.content.content, attachments)) == set( map(lambda att_part: att_part.body, attachment_parts)) assert len(re.findall("=\n", raw_mime.to_string())) == 0
def test_parse_mime_regular_attachment(): root_mime = mime.create.multipart("mixed") text_1 = mime.create.text("plain", randUnicode(length=1)) root_mime.append(text_1) for _ in range(3): att = mime.create.attachment(u"image/png", randStr(size=12355), randUnicode(), AttachmentType.ATTACHMENT) root_mime.append(att) text, html, attachments = parseMime(root_mime) assert text == text_1.body # non alternate text gets added to html assert html == u"<br>".join([u"<div data-preveil-text>{}</div>".format(text_1.body)]) # all are regular atts assert all(map(lambda att: att.metadata.content_disposition == AttachmentType.ATTACHMENT, attachments)) # all the atts exist w right content assert map(lambda att: att.content.content, attachments) == map(lambda att_part: att_part.body, filter(lambda p: p.content_disposition[0] == AttachmentType.ATTACHMENT, root_mime.parts)) #add some nested atts nested_mime = mime.create.multipart("mixed") html_1 = mime.create.text("html", randUnicode(length=454)) nested_mime.append(html_1) for _ in range(5): att = mime.create.attachment(u"application/octet-stream", randStr(size=545), randUnicode(length=12), AttachmentType.ATTACHMENT) nested_mime.append(att) root_mime.append(nested_mime) text, html, attachments = parseMime(root_mime) assert text == text_1.body # non alternate text gets added to html assert html == u"<br>".join([u"<div data-preveil-text>{}</div>".format(text_1.body), html_1.body]) # all are regular atts assert all(map(lambda att: att.metadata.content_disposition == AttachmentType.ATTACHMENT, attachments)) # all the atts exist w right content assert map(lambda att: att.content.content, attachments) == map(lambda att_part: att_part.body, filter(lambda p: p.content_disposition[0] == AttachmentType.ATTACHMENT, root_mime.parts + nested_mime.parts))
def test_unknown_content_type_with_no_disposition(): root_mime = mime.create.multipart("mixed") text_1 = mime.create.text("plain", randUnicode(length=1332)) root_mime.append(text_1) att = mime.create.attachment(u"image/png", randStr(size=8722), randUnicode(), AttachmentType.ATTACHMENT) root_mime.append(att) content = randUnicode(length=1523) unknown_part = mime.create.text("plain", content) unknown_part.headers["Content-Type"] = mime.message.ContentType(u"xx", u"yy") root_mime.append(unknown_part) text, html, attachments = parseMime(root_mime) assert text == text_1.body # non alternate text gets added to html assert html == u"<br>".join([u"<div data-preveil-text>{}</div>".format(text_1.body)]) assert len(attachments) == 2 # assert that the bad content type has translated in as a regular attachment assert map(lambda att: att.metadata.content_disposition, attachments) == [AttachmentType.ATTACHMENT]*2 # all attachment still has the right content assert map(lambda att: att.content.content, attachments) == map(lambda p: p.body, filter(lambda p: p.content_type.value != u"text/plain", root_mime.parts))
def test_master_init_config(): master = MasterConfig() assert master.initialized == False # should raise KeyError for missing enviroment variables org_env, mocked_vars = os.environ, [] while len(master.config_keys) - len(mocked_vars) > 0: with pytest.raises(KeyError): master.initConfig() mocked_vars.append(master.config_keys[len(mocked_vars)]) os.environ.update({ k.replace("-", "_").upper() if k != "mode" else "PREVEIL_MODE": str(randUnicode(5)) for k in mocked_vars }) # should just go through when all the required keys are in env master.initConfig() os.environ = org_env master = MasterConfig() assert master.initialized == False # should raise for not having `config_file` with pytest.raises(ValueError): master.initConfig(mode=u"dev") # should raise for not having `dev` mode in `config_file` with pytest.raises(ValueError): master.initConfig(mode=u"dev", config_file=TEST_CONFIG_PATH) master.initConfig(mode=u"test", config_file=TEST_CONFIG_PATH) assert master.initialized == True status, confs = readYAMLConfig(TEST_CONFIG_PATH) assert status confs = confs["test"] for k in set(master.config_keys) - set(["mode", "working-dir", "root-dir" ]): assert confs[k] == master.getConfigByKey(k) assert master.getConfigByKey("mode") == "test" expected_root = unicode(os.path.join(os.getenv("SystemDrive") + "\\", "PreVeilData")) \ if sys.platform == "win32" \ else unicode(os.path.join("/", "var", "preveil")) assert master.getConfigByKey("root-dir") == expected_root assert master.getConfigByKey("working-dir") == os.path.join( expected_root, "daemon", "modes", "test")
def test_to_mime(): from_account, to_account = User(), User() # with one attachment attachments = [FileStorage( stream=StringIO.StringIO(os.urandom(1024)), filename=randUnicode() + ".jpg", content_type="image/jpeg")] email = create_email_v1( from_account, [to_account], [], [], u"S S", u"text", u"html", attachments, None, [], [], []) raw_mime = email.toMime() assert raw_mime.content_type.is_multipart() == True parts = [] for part in raw_mime.walk(with_self=True): parts.append(part) assert parts[0].content_type == "multipart/mixed" assert parts[1].content_type == "multipart/alternative" assert parts[2].content_type == "text/plain" assert parts[2].body == "text" assert parts[3].content_type == "text/html" assert parts[3].body == "html" assert parts[4].content_type == u"image/jpeg" #with multiple atts attachments = [ FileStorage( stream=StringIO.StringIO(os.urandom(1024)), filename=randUnicode(), content_type="application/zip"), FileStorage( stream=StringIO.StringIO(os.urandom(1024)), filename=randUnicode(), content_type="audio/jpeg"), FileStorage( stream=StringIO.StringIO(os.urandom(1024)), filename=randUnicode(), content_type="application/pdf"), FileStorage( stream=StringIO.StringIO(randUnicode()), filename=randUnicode(), content_type="text/plain"), ] email = create_email_v1(from_account, [to_account], [], [], u"subject", u"a", u"b", attachments, None, []) raw_mime = email.toMime() assert raw_mime.content_type.is_multipart() parts = [] for part in raw_mime.walk(with_self=True): parts.append(part) assert parts[0].content_type == "multipart/mixed" assert parts[1].content_type == "multipart/alternative" assert parts[2].content_type == "text/plain" assert parts[2].body == "a" assert parts[3].content_type == "text/html" assert parts[3].body == "b" assert parts[4].content_type == "application/zip" assert parts[5].content_type == "audio/jpeg" assert parts[6].content_type == "application/pdf" assert parts[7].content_type == "text/plain"
def __init__(self, protocol_version, private_metadata): self.protocol_version = protocol_version self.private_metadata = private_metadata self.id = randUnicode(5) self.version = randUnicode(5) self.thread_id = randUnicode(5) self.mailbox_id = randUnicode(5) self.uid = 0 self.message_id = randUnicode(5) self.in_reply_to = randUnicode(5) self.rev_id = 0 self.is_deleted = False self.references = [] self.flags = [] self.timestamp = '2019-08-16T16:14:48'
def test_attachment_reconstruction(): raw_message = """Received: ConsoleMessageDelivery From: "(Secure) iOS Dev" <*****@*****.**> Content-Type: multipart/related; boundary="Apple-Mail=_B3D4A2AE-CBE5-47E8-86E4-5052190755A6"; type="text/plain" Subject: Message-Id: <*****@*****.**> Date: Mon, 27 Jun 2016 10:43:03 -0400 To: iOS Dev <*****@*****.**> Mime-Version: 1.0 (Mac OS X Mail 9.3 \(3124\)) --Apple-Mail=_B3D4A2AE-CBE5-47E8-86E4-5052190755A6 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=us-ascii blah blah blah --Apple-Mail=_B3D4A2AE-CBE5-47E8-86E4-5052190755A6 Content-Transfer-Encoding: base64 Content-Disposition: dummy; filename="handle" Content-Type: dummy/dummy; name="handle" cGxhY2Vob2xkZXIgZm9yIGFuIGF0dGFjaG1lbnQ= """ attachment_content = randUnicode() raw_attachment = "Content-Type: text/plain; name=\"test.txt\"\r\nContent-Disposition: attachment; filename=\"test.txt\"\r\n\r\n{}".format( attachment_content) message = mime.from_string(raw_message) attachment = mime.from_string(raw_attachment) attachments = {"handle": attachment} status, restored_message = EmailV1.restoreAttachments(message, attachments) assert status assert len(restored_message.parts) == 2 assert restored_message.parts[1] == attachment
def test_server_message_parser_V5(): sender_key_version = random.randint(0, 100) recipient_key_version = random.randint(0, 100) sender_user_key = H.PVKeyFactory.newUserKey(key_version=sender_key_version) sender = H.UserData(randUnicode(6), randUnicode(6), randUnicode(5), [sender_user_key.public_user_key], None) pvm = MockPrivateMetadataV5(sender=sender.user_id, user_key=sender_user_key) signature, encrypted_pvm = pvm.signAndEncrypt() wrapped_key = b64enc( pvm.user_key.public_user_key.public_key.seal(pvm.symm_key.serialize())) recipients = [recipient() for _ in range(2)] bad_wrapped_recipients = b64enc( pvm.user_key.public_user_key.public_key.seal( H.utf8Encode(H.jdumps(recipients)))) msg = MockServerMessageV5(encrypted_pvm, signature, sender_key_version, recipient_key_version, wrapped_key, bad_wrapped_recipients) u = H.UserData(randUnicode(6), randUnicode(6), randUnicode(5), [H.PVKeyFactory.newUserKey(key_version=0).public_user_key], None) with pytest.raises(H.EmailException): # bad wrapped recipients decrypted_msg = decryptServerMessage(msg.toDict(), pvm.user_key.encryption_key, pvm.symm_key) msg.wrapped_recipients = b64enc( pvm.user_key.public_user_key.public_key.seal( H.utf8Encode(H.jdumps(pvm.tos + pvm.ccs)))) decrypted_msg = decryptServerMessage(msg.toDict(), pvm.user_key.encryption_key, pvm.symm_key) assert verifyServerMessage(decrypted_msg, sender.public_user_key.verify_key) decrypted_msg["collection_id"] = sender.mail_cid email = EmailFactory.fromServerMessage(u.user_id, decrypted_msg, wrapped_key, recipient_key_version, None) assert email.server_attr.collection_id == sender.mail_cid _verifyEmail(email, pvm, msg) # bccs can figure out themselves as being bcced assert email.bccs == [{"user_id": u.user_id, "display_name": u.user_id}] # no bcc to = H.UserData(pvm.tos[0]["user_id"], randUnicode(6), randUnicode(5), [H.PVKeyFactory.newUserKey(key_version=0).public_user_key], None) email = EmailFactory.fromServerMessage(to.user_id, decrypted_msg, wrapped_key, recipient_key_version, None) _verifyEmail(email, pvm, msg) assert email.bccs == [] # sender can see all bccs bccs = [recipient() for _ in range(3)] msg.wrapped_bccs = b64enc( pvm.user_key.public_user_key.public_key.seal( H.utf8Encode(H.jdumps(bccs)))) decrypted_msg = decryptServerMessage(msg.toDict(), pvm.user_key.encryption_key, pvm.symm_key) assert verifyServerMessage(decrypted_msg, sender.public_user_key.verify_key) decrypted_msg["collection_id"] = sender.mail_cid email = EmailFactory.fromServerMessage(sender.user_id, decrypted_msg, wrapped_key, recipient_key_version, None) _verifyEmail(email, pvm, msg) assert H.CaseInsensitiveSet(map(lambda u: u["user_id"], email.bccs)) == H.CaseInsensitiveSet( map(lambda u: u["user_id"], bccs)) # wrong verify key bad_user_key = H.PVKeyFactory.newUserKey(key_version=1) assert not verifyServerMessage(decrypted_msg, bad_user_key.public_user_key.verify_key) for f in [getSender, getWrappedKey]: with pytest.raises(H.EmailException): # corrupted message decrypted_msg = {} f(decrypted_msg)
def test_server_message_parser_V6(): sender_key_version = random.randint(0, 100) recipient_key_version = random.randint(0, 100) sender_user_key = H.PVKeyFactory.newUserKey(key_version=sender_key_version) sender = H.UserData(randUnicode(6), randUnicode(6), randUnicode(5), [sender_user_key.public_user_key], None) pvm = MockPrivateMetadataV6(sender=sender.user_id, user_key=sender_user_key) signature, encrypted_pvm = pvm.signAndEncrypt() wrapped_key = b64enc( pvm.user_key.public_user_key.public_key.seal(pvm.symm_key.serialize())) tos_groups_flatten = flatten_recipient_groups(pvm.tos_groups) ccs_groups_flatten = flatten_recipient_groups(pvm.ccs_groups) wrapped_recipients = b64enc( pvm.user_key.public_user_key.public_key.seal( H.utf8Encode( H.jdumps(pvm.tos + pvm.ccs + tos_groups_flatten + ccs_groups_flatten)))) msg = MockServerMessageV6(encrypted_pvm, signature, sender_key_version, recipient_key_version, wrapped_key, wrapped_recipients) decrypted_msg = decryptServerMessage(msg.toDict(), pvm.user_key.encryption_key, pvm.symm_key) assert verifyServerMessage(decrypted_msg, sender.public_user_key.verify_key) decrypted_msg["collection_id"] = sender.mail_cid email = EmailFactory.fromServerMessage(sender.user_id, decrypted_msg, wrapped_key, recipient_key_version, None) assert email.server_attr.collection_id == sender.mail_cid _verifyEmail(email, pvm, msg) # sender can see all bccs bccs = [recipient() for _ in range(3)] msg.wrapped_bccs = b64enc( pvm.user_key.public_user_key.public_key.seal( H.utf8Encode(H.jdumps(bccs)))) decrypted_msg = decryptServerMessage(msg.toDict(), pvm.user_key.encryption_key, pvm.symm_key) assert verifyServerMessage(decrypted_msg, sender.public_user_key.verify_key) decrypted_msg["collection_id"] = sender.mail_cid email = EmailFactory.fromServerMessage(sender.user_id, decrypted_msg, wrapped_key, recipient_key_version, None) _verifyEmail(email, pvm, msg) assert H.CaseInsensitiveSet(map(lambda u: u["user_id"], email.bccs)) == H.CaseInsensitiveSet( map(lambda u: u["user_id"], bccs)) # bcc is a sub group of one of the tos_groups bccs = pvm.tos_groups[random.randint(0, len(pvm.tos_groups) - 1)]["users"] msg.wrapped_bccs = b64enc( pvm.user_key.public_user_key.public_key.seal( H.utf8Encode(H.jdumps(bccs)))) decrypted_msg = decryptServerMessage(msg.toDict(), pvm.user_key.encryption_key, pvm.symm_key) assert verifyServerMessage(decrypted_msg, sender.public_user_key.verify_key) decrypted_msg["collection_id"] = sender.mail_cid email = EmailFactory.fromServerMessage(sender.user_id, decrypted_msg, wrapped_key, recipient_key_version, None) _verifyEmail(email, pvm, msg) assert H.CaseInsensitiveSet(map(lambda u: u["user_id"], email.bccs)) == H.CaseInsensitiveSet( map(lambda u: u["user_id"], bccs))
def test_to_mime(): from_account, to_account = User(), User() # with no attachments # FileStorage(stream=StringIO.StringIO(os.urandom(1024)), filename=randUnicode() + ".jpg", content_type="image/jpeg") attachments = [] email = create_email_v4(from_account, [to_account], [], [], u"S S", u"text", u"html", attachments, None, [], [], []) raw_mime = email.toMime() assert raw_mime.content_type.is_multipart() parts = [] for part in raw_mime.walk(with_self=True): parts.append(part) assert parts[0].content_type == "multipart/alternative" assert parts[1].content_type == "text/plain" assert parts[1].body == "text" assert parts[2].content_type == "text/html" assert parts[2].body == "html" # with multiple atts attachments = [ FileStorage(stream=StringIO.StringIO(os.urandom(1024)), filename=randUnicode(), content_type="image/png"), FileStorage(stream=StringIO.StringIO(os.urandom(1024)), filename=randUnicode(), content_type="video/mp4"), FileStorage(stream=StringIO.StringIO(os.urandom(1024)), filename=randUnicode(), content_type="application/octet-stream"), FileStorage(stream=StringIO.StringIO(randUnicode()), filename=randUnicode(), content_type="text/html"), ] email = create_email_v4(from_account, [to_account], [], [], u"subject", u"a", u"b", attachments, None, []) raw_mime = email.toMime() assert raw_mime.content_type.is_multipart() parts = [] for part in raw_mime.walk(with_self=True): parts.append(part) assert parts[0].content_type == "multipart/mixed" assert parts[1].content_type == "multipart/alternative" assert parts[2].content_type == "text/plain" assert parts[2].body == "a" assert parts[3].content_type == "text/html" assert parts[3].body == "b" assert parts[4].content_type == "image/png" assert parts[5].content_type == "video/mp4" assert parts[6].content_type == "application/octet-stream" assert parts[7].content_type == "text/html" # lets make first attachment inline email.attachments[0].metadata.content_disposition = AttachmentType.INLINE raw_mime = email.toMime() assert raw_mime.content_type.is_multipart() parts = [] for part in raw_mime.walk(with_self=True): parts.append(part) assert parts[0].content_type == "multipart/mixed" assert parts[1].content_type == "multipart/alternative" assert parts[2].content_type == "text/plain" assert parts[2].body == "a" assert parts[3].content_type == "multipart/related" assert parts[4].content_type == "text/html" assert parts[4].body == "b" assert parts[5].content_type == "image/png" assert parts[6].content_type == "video/mp4" assert parts[7].content_type == "application/octet-stream" assert parts[8].content_type == "text/html"
def __init__(self): self.user_id = randUnicode() self.display_name = randUnicode()
def test_to_mime(): from_account, to_account = User(), User() # with one attachment attachments = [ FileStorage(stream=StringIO.StringIO(os.urandom(1024)), filename=randUnicode() + ".jpg", content_type="image/jpeg") ] email = create_email_v1(from_account, [to_account], [], [], u"S S", u"text", u"html", attachments, None, [], [], []) raw_mime = email.toMime() assert raw_mime.content_type.is_multipart() parts = [] for part in raw_mime.walk(with_self=True): parts.append(part) assert parts[0].content_type == "multipart/mixed" assert parts[1].content_type == "multipart/alternative" assert parts[2].content_type == "text/plain" assert parts[2].body == "text" assert parts[3].content_type == "text/html" assert parts[3].body == "html" assert parts[4].content_type == u"image/jpeg" # with multiple atts attachments = [ FileStorage(stream=StringIO.StringIO(os.urandom(1024)), filename=randUnicode(), content_type="application/zip"), FileStorage(stream=StringIO.StringIO(os.urandom(1024)), filename=randUnicode(), content_type="audio/jpeg"), FileStorage(stream=StringIO.StringIO(os.urandom(1024)), filename=randUnicode(), content_type="application/pdf"), FileStorage(stream=StringIO.StringIO(randUnicode()), filename=randUnicode(), content_type="text/plain"), ] email = create_email_v1(from_account, [to_account], [], [], u"subject", u"a", u"b", attachments, None, []) raw_mime = email.toMime() assert raw_mime.content_type.is_multipart() parts = [] for part in raw_mime.walk(with_self=True): parts.append(part) assert parts[0].content_type == "multipart/mixed" assert parts[1].content_type == "multipart/alternative" assert parts[2].content_type == "text/plain" assert parts[2].body == "a" assert parts[3].content_type == "text/html" assert parts[3].body == "b" assert parts[4].content_type == "application/zip" assert parts[5].content_type == "audio/jpeg" assert parts[6].content_type == "application/pdf" assert parts[7].content_type == "text/plain"
def recipient(): return {"user_id": randUnicode(5), "key_version": randint(0, 100)}
def test_create_mime_headers_validity(): text = randUnicode(length=2012) html = randUnicode(length=2014) attachments = [] for _ in range(4): content = randStr(size=2017) _filename = randUnicode() f = FileStorage(stream=StringIO.StringIO(content), filename=_filename, content_type="image/jpeg") attachments.append( Attachment.fromFileStorage( f, AttachmentMetadata(f.filename, f.content_type))) message_id = randUnicode() _time = time.time() tos = [{ "user_id": randUnicode() + "@x.com", "display_name": randUnicode() } for _ in range(12)] ccs = [{ "user_id": randUnicode() + "@x.com", "display_name": randUnicode() } for _ in range(4)] bccs = [{ "user_id": randUnicode() + "@x.com", "display_name": randUnicode() } for _ in range(3)] references = [randUnicode() for _ in range(5)] in_reply_to = randUnicode() subject = randUnicode() reply_tos = [{ "user_id": randUnicode() + "@x.com", "display_name": randUnicode() } for _ in range(2)] sender = { "user_id": randUnicode() + "@x.com", "display_name": randUnicode() } raw_mime = createMime(text, html, attachments, message_id, _time, subject, tos, ccs, bccs, reply_tos, sender, in_reply_to, references) assert message_id == raw_mime.headers.get("Message-Id") assert tos == [{ "user_id": to.address, "display_name": to.display_name } for to in addresslib.address.parse_list(raw_mime.headers.get("To"))] assert ccs == [{ "user_id": cc.address, "display_name": cc.display_name } for cc in addresslib.address.parse_list(raw_mime.headers.get("Cc"))] assert bccs == [{ "user_id": bcc.address, "display_name": bcc.display_name } for bcc in addresslib.address.parse_list(raw_mime.headers.get("Bcc"))] assert reply_tos == [{ "user_id": rpt.address, "display_name": rpt.display_name } for rpt in addresslib.address.parse_list( raw_mime.headers.get("Reply-To"))] assert "{} <{}>".format(sender["display_name"], sender["user_id"]) == raw_mime.headers.get("From") assert subject == raw_mime.headers.get("Subject") assert references == raw_mime.headers.get("references").split(" ") assert in_reply_to == raw_mime.headers.get("in-reply-to") assert email.utils.formatdate(_time) == raw_mime.headers.get("Date") assert len(re.findall("=\n", raw_mime.to_string())) == 0
def test_create_mime_headers_validity(): text = randUnicode(length=2012) html = randUnicode(length=2014) attachments = [] for _ in range(4): content = randStr(size=2017) _filename = randUnicode() f = FileStorage(stream=StringIO.StringIO(content), filename=_filename, content_type="image/jpeg") attachments.append(Attachment.fromFileStorage(f, AttachmentMetadata(f.filename, f.content_type))) message_id = randUnicode() _time = time.time() tos = [{"user_id": randUnicode()+"@x.com", "display_name": randUnicode()} for _ in range(12)] ccs = [{"user_id": randUnicode()+"@x.com", "display_name": randUnicode()} for _ in range(4)] bccs = [{"user_id": randUnicode()+"@x.com", "display_name": randUnicode()} for _ in range(3)] references = [randUnicode() for _ in range(5)] in_reply_to = randUnicode() subject = randUnicode() reply_tos = [{"user_id": randUnicode()+"@x.com", "display_name": randUnicode()} for _ in range(2)] sender = {"user_id": randUnicode()+"@x.com", "display_name": randUnicode()} raw_mime = createMime(text, html, attachments, message_id, _time, subject, tos, ccs, bccs, reply_tos, sender, in_reply_to, references) assert message_id == raw_mime.headers.get("Message-Id") assert tos == [{"user_id": to.address, "display_name": to.display_name} for to in addresslib.address.parse_list(raw_mime.headers.get("To"))] assert ccs == [{"user_id": cc.address, "display_name": cc.display_name} for cc in addresslib.address.parse_list(raw_mime.headers.get("Cc"))] assert bccs == [{"user_id": bcc.address, "display_name": bcc.display_name} for bcc in addresslib.address.parse_list(raw_mime.headers.get("Bcc"))] assert reply_tos == [{"user_id": rpt.address, "display_name": rpt.display_name} for rpt in addresslib.address.parse_list(raw_mime.headers.get("Reply-To"))] assert "{} <{}>".format(sender["display_name"], sender["user_id"]) == raw_mime.headers.get("From") assert subject == raw_mime.headers.get("Subject") assert references == raw_mime.headers.get("references").split(" ") assert in_reply_to == raw_mime.headers.get("in-reply-to") assert email.utils.formatdate(_time) == raw_mime.headers.get("Date") assert len(re.findall("=\n", raw_mime.to_string())) == 0