From 999865c661c045fd8bec166c451d35c2bae6ee3d Mon Sep 17 00:00:00 2001 From: Wassilios Lytras Date: Fri, 22 Jul 2022 16:33:52 +0200 Subject: [PATCH 1/4] Fix github action build fail due to: https://stackoverflow.com/questions/71673404/importerror-cannot-import-name-unicodefun-from-click --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 59a607f..71bda72 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ "pylint==2.12.1", "pylama==8.3.7", "pylama-pylint==3.1.1", - "black==21.12b0", + "black==22.6.0", "pytest-black==0.3.12", ] From 85d0b28666a9533aa9dcc3a40ed8de4a38ee0ca1 Mon Sep 17 00:00:00 2001 From: Wassilios Lytras Date: Fri, 22 Jul 2022 20:10:09 +0200 Subject: [PATCH 2/4] Added partner setting to force canonicalize binary. --- pyas2lib/as2.py | 5 ++++- pyas2lib/utils.py | 9 +++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pyas2lib/as2.py b/pyas2lib/as2.py index 9eadd4e..cd2d915 100644 --- a/pyas2lib/as2.py +++ b/pyas2lib/as2.py @@ -177,6 +177,8 @@ class Partner: :param mdn_confirm_text: The text to be used in the MDN for successfully processed messages received from this partner. + :param canonicalize_as_binary: force binary canonicalization for this partner + """ as2_name: str @@ -194,6 +196,7 @@ class Partner: mdn_digest_alg: str = None mdn_confirm_text: str = MDN_CONFIRM_TEXT ignore_self_signed: bool = True + canonicalize_as_binary: bool = False def __post_init__(self): """Run the post initialisation checks for this class.""" @@ -638,7 +641,7 @@ def parse(self, raw_content, find_org_cb, find_partner_cb, find_message_cb=None) # Verify the message, first using raw message and if it fails # then convert to canonical form and try again - mic_content = canonicalize(self.payload) + mic_content = canonicalize(self.payload, canonicalize_as_binary=self.sender.canonicalize_as_binary) verify_cert = self.sender.load_verify_cert() self.digest_alg = verify_message(mic_content, signature, verify_cert) diff --git a/pyas2lib/utils.py b/pyas2lib/utils.py index 5947c55..5a2e999 100644 --- a/pyas2lib/utils.py +++ b/pyas2lib/utils.py @@ -48,8 +48,8 @@ def _handle_text(self, msg): newline replacements. """ if ( - msg.get_content_type() == "application/octet-stream" - or msg.get("Content-Transfer-Encoding") == "binary" + msg.get_content_type() == "application/octet-stream" + or msg.get("Content-Transfer-Encoding") == "binary" ): payload = msg.get_payload(decode=True) if payload is None: @@ -75,15 +75,16 @@ def mime_to_bytes(msg: message.Message, email_policy: policy.Policy = policy.HTT return fp.getvalue() -def canonicalize(email_message: message.Message): +def canonicalize(email_message: message.Message, canonicalize_as_binary: bool = False): """ Function to convert an email Message to standard format string/ :param email_message: email.message.Message to be converted to standard string + :param canonicalize_as_binary: force binary canonicalization :return: the standard representation of the email message in bytes """ - if email_message.get("Content-Transfer-Encoding") == "binary": + if email_message.get("Content-Transfer-Encoding") == "binary" or canonicalize_as_binary: message_header = "" message_body = email_message.get_payload(decode=True) for k, v in email_message.items(): From 0b3aeae7f4efd20c1e1a130cf094166255fc7bfb Mon Sep 17 00:00:00 2001 From: Wassilios Lytras Date: Fri, 22 Jul 2022 20:13:02 +0200 Subject: [PATCH 3/4] Formatted with black --- pyas2lib/as2.py | 5 ++++- pyas2lib/utils.py | 9 ++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pyas2lib/as2.py b/pyas2lib/as2.py index cd2d915..cb058ad 100644 --- a/pyas2lib/as2.py +++ b/pyas2lib/as2.py @@ -641,7 +641,10 @@ def parse(self, raw_content, find_org_cb, find_partner_cb, find_message_cb=None) # Verify the message, first using raw message and if it fails # then convert to canonical form and try again - mic_content = canonicalize(self.payload, canonicalize_as_binary=self.sender.canonicalize_as_binary) + mic_content = canonicalize( + self.payload, + canonicalize_as_binary=self.sender.canonicalize_as_binary, + ) verify_cert = self.sender.load_verify_cert() self.digest_alg = verify_message(mic_content, signature, verify_cert) diff --git a/pyas2lib/utils.py b/pyas2lib/utils.py index 5a2e999..48ad8dd 100644 --- a/pyas2lib/utils.py +++ b/pyas2lib/utils.py @@ -48,8 +48,8 @@ def _handle_text(self, msg): newline replacements. """ if ( - msg.get_content_type() == "application/octet-stream" - or msg.get("Content-Transfer-Encoding") == "binary" + msg.get_content_type() == "application/octet-stream" + or msg.get("Content-Transfer-Encoding") == "binary" ): payload = msg.get_payload(decode=True) if payload is None: @@ -84,7 +84,10 @@ def canonicalize(email_message: message.Message, canonicalize_as_binary: bool = :return: the standard representation of the email message in bytes """ - if email_message.get("Content-Transfer-Encoding") == "binary" or canonicalize_as_binary: + if ( + email_message.get("Content-Transfer-Encoding") == "binary" + or canonicalize_as_binary + ): message_header = "" message_body = email_message.get_payload(decode=True) for k, v in email_message.items(): From 69b13d5801c598673870925a3c419cd95bc3f51e Mon Sep 17 00:00:00 2001 From: Wassilios Lytras Date: Fri, 3 May 2024 10:00:30 +0200 Subject: [PATCH 4/4] Add specific error when MDN received, but Original Message was not found. Related to https://github.com/abhishek-ram/django-pyas2/issues/45 and will be implemented/used in django-pyas2. --- pyas2lib/as2.py | 5 +++++ pyas2lib/tests/test_advanced.py | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/pyas2lib/as2.py b/pyas2lib/as2.py index cb320a3..9c74e54 100644 --- a/pyas2lib/as2.py +++ b/pyas2lib/as2.py @@ -943,6 +943,11 @@ def parse(self, raw_content, find_message_cb): # Call the find message callback which should return a Message instance orig_message = find_message_cb(self.orig_message_id, orig_recipient) + if not orig_message: + status = "failed/Failure" + details_status = "original-message-not-found" + return status, details_status + # Extract the headers and save it mdn_headers = {} for k, v in self.payload.items(): diff --git a/pyas2lib/tests/test_advanced.py b/pyas2lib/tests/test_advanced.py index fefda8d..a0aca23 100644 --- a/pyas2lib/tests/test_advanced.py +++ b/pyas2lib/tests/test_advanced.py @@ -396,6 +396,33 @@ def test_mdn_not_found(self): self.assertEqual(status, "failed/Failure") self.assertEqual(detailed_status, "mdn-not-found") + def test_mdn_original_message_not_found(self): + """Test that the MDN parser raises MDN not found when a non MDN message is passed.""" + self.partner.mdn_mode = as2.SYNCHRONOUS_MDN + self.out_message = as2.Message(self.org, self.partner) + self.out_message.build(self.test_data) + + # Parse the generated AS2 message as the partner + raw_out_message = ( + self.out_message.headers_str + b"\r\n" + self.out_message.content + ) + in_message = as2.Message() + _, _, mdn = in_message.parse( + raw_out_message, + find_org_cb=self.find_org, + find_partner_cb=self.find_partner, + find_message_cb=lambda x, y: False, + ) + + # Parse the MDN + out_mdn = as2.Mdn() + status, detailed_status = out_mdn.parse( + mdn.headers_str + b"\r\n" + mdn.content, find_message_cb=lambda x, y: False + ) + + self.assertEqual(status, "failed/Failure") + self.assertEqual(detailed_status, "original-message-not-found") + def test_unsigned_mdn_sent_error(self): """Test the case where a signed mdn was expected but unsigned mdn was returned.""" self.partner.mdn_mode = as2.SYNCHRONOUS_MDN