diff --git a/modoboa/lib/tests/sample_messages/multipart-input.txt b/modoboa/lib/tests/sample_messages/multipart-input.txt new file mode 100644 index 000000000..ee0b51430 --- /dev/null +++ b/modoboa/lib/tests/sample_messages/multipart-input.txt @@ -0,0 +1,130 @@ +Return-Path: +X-Original-To: someone@example.net +Delivered-To: me@example.net +Received: from mail.example.net (localhost [127.0.0.1]) + by mail.example.net (Postfix) with ESMTP id 4BD531AF130 + for ; Sun, 17 Dec 2017 03:11:30 +0000 (GMT) +Authentication-Results: mail.example.net (amavisd-new); + dkim=pass (1024-bit key) reason="pass (just generated, assumed good)" + header.d=example.net +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=example.net; h= + content-language:content-type:content-type:mime-version + :user-agent:date:date:message-id:subject:subject:from:from + :received:received; s=mail; t=1513480289; bh=wAmt0wg2Yk04321B + Q63uWL5f67PEBB2EEGONiFUrIvk=; b=UZUzPqH8TUk/bMZm1ofkjwatNCQftZKK + C91uIuX3mmjvngNwbVjx4m7jFZItZscNqigyGJN5HCOl8tC/LJmmWzRdhIisFtLA + uXRiBuWps82rbV4ciFU8N30i9UXvtWvMywwL6pYSESqixiwylzbddjMjKXXdyqaT + DTyR16ZK8IE= +X-Virus-Scanned: amavisd-new at example.net +X-Spam-Flag: NO +X-Spam-Score: -0.988 +X-Spam-Level: +X-Spam-Status: No, score=-0.988 tagged_above=-999 required=3.9 + tests=[ALL_TRUSTED=-1, HTML_MESSAGE=0.001, T_HTML_ATTACH=0.01, + URIBL_BLOCKED=0.001] autolearn=no autolearn_force=no +Received: from mail.example.net ([127.0.0.1]) + by mail.example.net (mail.example.net [127.0.0.1]) (amavisd-new, port 10026) + with LMTP id BSCmm59DxNvB for ; + Sun, 17 Dec 2017 03:11:29 +0000 (GMT) +Received: from [10.0.0.1] (my-pc.example.net [10.0.0.1]) + by mail.example.net (Postfix) with ESMTPSA id 641171AF12D + for ; Sun, 17 Dec 2017 03:11:29 +0000 (GMT) +To: Someone +From: Me +X-Clacks-Overhead: GNU Terry Pratchett +Subject: A test message +Message-ID: <5294ad7a-e966-f1a0-3431-8c4b1b76f5a3@example.net> +Date: Sun, 17 Dec 2017 03:11:33 +0000 +User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 + Thunderbird/52.5.0 +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="------------7EB9EDC10414C906CC3C2CEA" +Content-Language: en-GB + +This is a multi-part message in MIME format. +--------------7EB9EDC10414C906CC3C2CEA +Content-Type: multipart/alternative; + boundary="------------87B289699A48D5D0D61BABCC" + + +--------------87B289699A48D5D0D61BABCC +Content-Type: text/plain; charset=utf-8; format=flowed +Content-Transfer-Encoding: 7bit + +This is an *html* /test /_message._ + +:-) + + +-- +Someone +someone@example.net + + +--------------87B289699A48D5D0D61BABCC +Content-Type: multipart/related; + boundary="------------D585B241EEF60F0F44EC20E5" + + +--------------D585B241EEF60F0F44EC20E5 +Content-Type: text/html; charset=utf-8 +Content-Transfer-Encoding: 7bit + + + + + + + +

This is an html test message.

+ :-)
+
+
+
-- 
+someone
+someone@example.net
+ + + +--------------D585B241EEF60F0F44EC20E5 +Content-Type: image/png; + name="image.png" +Content-Transfer-Encoding: base64 +Content-ID: +Content-Disposition: inline; + filename="image.png" + +iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAACXBIWXMAAAsTAAALEwEAmpwY +AAAAF0lEQVQY02NgYGD4//8/ThKfHASMRN0AfaqVa6MQxIQAAAAASUVORK5CYII= +--------------D585B241EEF60F0F44EC20E5-- + +--------------87B289699A48D5D0D61BABCC-- + +--------------7EB9EDC10414C906CC3C2CEA +Content-Type: text/html; + name="test.html" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; + filename="test.html" + +//48AHMAcABhAG4APgA8AGIAPgBTAG8AbQBlACAAPABpAD4ASABUAE0ATAA8AC8AaQA+ACAA +dABlAHgAdAA8AC8AYgA+ACwAIABhACAAPABhACAAaAByAGUAZgA9ACIAaAB0AHQAcAA6AC8A +LwBlAHgAYQBtAHAAbABlAC4AbgBlAHQAIgAgAHQAYQByAGcAZQB0AD0AIgBfAGIAbABhAG4A +awAiACAAcgBlAGwAPQAiAG4AbwBmAG8AbABsAG8AdwAiAD4AbABpAG4AawA8AC8AYQA+ACwA +IABhAG4AJgAjADEAMwA7AA0ACgBlAG0AYgBlAGQAZABlAGQAIABpAG0AYQBnAGUAIABhAG4A +ZAAgAGEAbgAgAGUAeAB0AGUAcgBuAGEAbAAgAGkAbQBhAGcAZQAuADwAYgByAD4AJgAjADEA +MwA7AA0ACgA8AGkAbQBnACAAcwByAGMAPQAiAGQAYQB0AGEAOgBpAG0AYQBnAGUALwBwAG4A +ZwA7AGIAYQBzAGUANgA0ACwAaQBWAEIATwBSAHcAMABLAEcAZwBvAEEAQQBBAEEATgBTAFUA +aABFAFUAZwBBAEEAQQBBAG8AQQBBAEEAQQBLAEMAQQBJAEEAQQBBAEEAQwBVAEYAagBxAEEA +QQBBAEEAQwBYAEIASQBXAFgATQBBAEEAQQBzAFQAQQBBAEEATABFAHcARQBBAG0AcAB3AFkA +QQBBAEEAQQBGADAAbABFAFEAVgBRAFkAMAAyAE4AZwBZAEcARAA0AC8ALwA4AC8AVABoAEsA +ZgBIAEEAUwBNAFIATgAwAEEAZgBhAHEAVgBhADYATQBRAHgASQBRAEEAQQBBAEEAQQBTAFUA +VgBPAFIASwA1AEMAWQBJAEkAPQAiACAAdABhAHIAZwBlAHQAPQAiAF8AYgBsAGEAbgBrACIA +PgA8AGIAcgA+ACYAIwAxADMAOwANAAoAPABpAG0AZwAgAHMAcgBjAD0AIgBoAHQAdABwADoA +LwAvAGUAeABhAG0AcABsAGUALgBuAGUAdAAvAGkAbQBhAGcAZQAuAHAAbgBnACIAIAB0AGEA +cgBnAGUAdAA9ACIAXwBiAGwAYQBuAGsAIgA+ADwAYgByAD4AJgAjADEAMwA7AA0ACgBOAGkA +ZgB0AHkAIQA8AC8AcwBwAGEAbgA+AA0ACgA= +--------------7EB9EDC10414C906CC3C2CEA-- diff --git a/modoboa/lib/tests/sample_messages/multipart-output-html_links.txt b/modoboa/lib/tests/sample_messages/multipart-output-html_links.txt new file mode 100644 index 000000000..5c831152f --- /dev/null +++ b/modoboa/lib/tests/sample_messages/multipart-output-html_links.txt @@ -0,0 +1,16 @@ +
+ + + + + +

This is an html test message.

+ :-)
+
+
+
-- 
+someone
+someone@example.net
+ +

+��

\ No newline at end of file diff --git a/modoboa/lib/tests/sample_messages/multipart-output-html_nolinks.txt b/modoboa/lib/tests/sample_messages/multipart-output-html_nolinks.txt new file mode 100644 index 000000000..e14a409da --- /dev/null +++ b/modoboa/lib/tests/sample_messages/multipart-output-html_nolinks.txt @@ -0,0 +1,16 @@ +
+ + + + + +

This is an html test message.

+ :-)
+
+
+
-- 
+someone
+someone@example.net
+ +

+��

\ No newline at end of file diff --git a/modoboa/lib/tests/sample_messages/multipart-output-plain_nolinks.txt b/modoboa/lib/tests/sample_messages/multipart-output-plain_nolinks.txt new file mode 100644 index 000000000..8795edadc --- /dev/null +++ b/modoboa/lib/tests/sample_messages/multipart-output-plain_nolinks.txt @@ -0,0 +1,7 @@ +
This is an *html* /test /_message._
+
+:-)
+
+-- 
+Someone
+someone@example.net
\ No newline at end of file diff --git a/modoboa/lib/tests/sample_messages/text_plain-input.txt b/modoboa/lib/tests/sample_messages/text_plain-input.txt new file mode 100644 index 000000000..bfbf9aca8 --- /dev/null +++ b/modoboa/lib/tests/sample_messages/text_plain-input.txt @@ -0,0 +1,51 @@ +Return-Path: +X-Original-To: me@example.net Delivered-To: someone@example.net +Received: from mail.example.net (localhost [127.0.0.1]) + by mail.example.net (Postfix) with ESMTP id A142E1AF12D + for ; Sun, 17 Dec 2017 13:10:10 +0000 (GMT) +Authentication-Results: mail.example.net (amavisd-new); + dkim=pass (1024-bit key) reason="pass (just generated, assumed good)" + header.d=example.net +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=example.net; h= + content-transfer-encoding:content-language:content-type + :content-type:mime-version:user-agent:date:date:message-id + :subject:subject:from:from:received:received; s=mail; t= + 1513516210; bh=yyFHETi2jtS7bFMEIArr53VnJMNNjltuXCFTOqs/cL0=; b=X + nMo4pMsaH7EO9A55i0SOv5RGP4Sp4eukaXXQJgMWahcfUn4mM2+pSdc+8ZMaXX7y + ITQdCzI0LL9WGfX7bbMA49hYtIELjQ/AochqABTUsMxnq0Sgf1FE37bA3NS1pBtp + TBKehpGjr86y0V247Okmf2faEkO9dMvmwWz8QBI0N4= +X-Virus-Scanned: amavisd-new at example.net +X-Spam-Flag: NO +X-Spam-Score: -0.999 +X-Spam-Level: +X-Spam-Status: No, score=-0.999 tagged_above=-999 required=3.9 + tests=[ALL_TRUSTED=-1, URIBL_BLOCKED=0.001] + autolearn=no autolearn_force=no +Received: from mail.example.net ([127.0.0.1]) + by mail.example.net (mail.example.net [127.0.0.1]) (amavisd-new, port 10026) + with LMTP id j0UYMNn7ZprP for ; + Sun, 17 Dec 2017 13:10:10 +0000 (GMT) +Received: from [10.0.0.1] (my-pc.example.net [10.0.0.1]) + by mail.example.net (Postfix) with ESMTPSA id E66201AF132 + for ; Sun, 17 Dec 2017 13:10:09 +0000 (GMT) +To: Me +From: Someone +X-Clacks-Overhead: GNU Terry Pratchett +Subject: =?UTF-8?Q?T=c3=a9st_message?= +Message-ID: <8626caea-4c05-8dc5-c687-bcfb7a329d18@example.net> +Date: Sun, 17 Dec 2017 13:10:14 +0000 +User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 + Thunderbird/52.5.0 +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf-8; format=flowed +Content-Language: en-GB +Content-Transfer-Encoding: 7bit +X-rAnDoM-cAsE-hEaDeR: fOoBaR +X-UPPER-CASE-HEADER: FOOBAR +x-lower-case-header: foobar + +This is a plain text e-mail message. + +-- +Someone +someone@example.net diff --git a/modoboa/lib/tests/sample_messages/text_plain-output-plain_nolinks.txt b/modoboa/lib/tests/sample_messages/text_plain-output-plain_nolinks.txt new file mode 100644 index 000000000..e027961d8 --- /dev/null +++ b/modoboa/lib/tests/sample_messages/text_plain-output-plain_nolinks.txt @@ -0,0 +1,5 @@ +
This is a plain text e-mail message.
+
+-- 
+Someone
+someone@example.net
\ No newline at end of file diff --git a/modoboa/lib/tests/test_email_utils.py b/modoboa/lib/tests/test_email_utils.py new file mode 100644 index 000000000..e3e267059 --- /dev/null +++ b/modoboa/lib/tests/test_email_utils.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- + +"""Tests for email_utils.""" + +from __future__ import unicode_literals + +import os + +from django.test import SimpleTestCase +from django.utils.encoding import smart_bytes, smart_text + +from ..email_utils import Email + +SAMPLES_DIR = os.path.realpath( + os.path.join(os.path.dirname(__file__), "sample_messages")) + + +class EmailTestImplementation(Email): + + def _fetch_message(self): + message_path = os.path.join(SAMPLES_DIR, "%s-input.txt" % self.mailid) + assert os.path.isfile(message_path), "%s does not exist." % message_path + + with open(message_path, "rb") as fp: + mail_text = smart_bytes(fp.read()) + + return mail_text + + +class EmailTests(SimpleTestCase): + """Tests for modoboa.lib.email_utils.Email + + When writing new sample messages use the following naming convention for + the sample files stored in sample_messages: + + input: {message_id}-input.txt + output: {message_id}-output-{dformat}_{no,}links.txt + """ + + def _get_expected_output(self, message_id, **kwargs): + ext = kwargs["dformat"] if "dformat" in kwargs else "plain" + ext += "_links" if "links" in kwargs and kwargs["links"] else "_nolinks" + message_path = os.path.join(SAMPLES_DIR, + "%s-output-%s.txt" % (message_id, ext)) + assert os.path.isfile(message_path), "%s does not exist." % message_path + + with open(message_path, "rb") as fp: + # output should always be unicode (py2) or str (py3) + mail_text = smart_text(fp.read()) + + return mail_text + + def _test_email(self, message_id, **kwargs): + """Boiler plate code for testing e-mails.""" + expected_output = self._get_expected_output(message_id, **kwargs) + output = EmailTestImplementation(message_id, **kwargs).body + self.assertEqual(output, expected_output) + + def test_invalid_links_value(self): + """modoboa-amavis set links = "0"; in python "0" == True, fixed in PR 79 + https://github.com/modoboa/modoboa-amavis/pull/79""" + with self.assertRaises(TypeError) as cm: + EmailTestImplementation("text_plain", links="0") + + ex_message = cm.exception.messages + self.assertEqual(ex_message, + "links == \"0\" is not valid, did you mean True or " + "False?") + + def test_links_value_for_webmail(self): + """modoboa-webmail sets links = 0 or 1 + TODO: this should be fixed in modoboa-webmail to use True/False""" + email = EmailTestImplementation("text_plain", links=0) + self.assertFalse(email.links) + + email = EmailTestImplementation("text_plain", links=1) + self.assertTrue(email.links) + + def test_headers(self): + email = EmailTestImplementation("text_plain") + # test header names are case insensative + self.assertEqual(email.msg["x-upper-case-header"], "FOOBAR") + self.assertEqual(email.msg["X-Lower-Case-Header"], "foobar") + self.assertEqual(email.msg["x-random-case-header"], "fOoBaR") + + expected_output = [ + {"name": "From", "value": "Someone "}, + {"name": "To", "value": "Me "}, + {"name": "Cc", "value": ""}, + {"name": "Date", "value": "Sun, 17 Dec 2017 13:10:14 +0000"}, + {"name": "Subject", "value": "Tést message"}, + ] + self.assertEqual(email.headers, expected_output) + + def test_email_text_plain(self): + self._test_email("text_plain") + + def test_email_multipart_as_text(self): + """display the text/plain part of a multipart message""" + self._test_email("multipart") + + def test_email_multipart_with_links(self): + """display the text/html part of a multipart message""" + self._test_email("multipart", dformat="html", links=True) + + def test_email_multipart_without_links(self): + """display the text/html part of a multipart message with links removed""" + self._test_email("multipart", dformat="html", links=False)