From ddfb55472b20af351e2c08d902688816bfe73b70 Mon Sep 17 00:00:00 2001 From: starlet-dx <15929766099@163.com> Date: Sun, 23 Jul 2023 15:35:54 +0800 Subject: [PATCH] Fix test fails caused by python update to 3.11.4 --- ...coroutines-and-use-async-def-instead.patch | 120 ++++++++++ Support-Python-3.11.patch | 58 +++++ ...idity-at-RCPT-command-in-LMTP-runner.patch | 217 ++++++++++++++++++ mailman.spec | 11 +- 4 files changed, 405 insertions(+), 1 deletion(-) create mode 100644 Remove-old-style-coroutines-and-use-async-def-instead.patch create mode 100644 Support-Python-3.11.patch create mode 100644 Verify-recipient-validity-at-RCPT-command-in-LMTP-runner.patch diff --git a/Remove-old-style-coroutines-and-use-async-def-instead.patch b/Remove-old-style-coroutines-and-use-async-def-instead.patch new file mode 100644 index 0000000..3fe9402 --- /dev/null +++ b/Remove-old-style-coroutines-and-use-async-def-instead.patch @@ -0,0 +1,120 @@ +From 38c794d9ed71f25ee138760461f66abc29dfa81c Mon Sep 17 00:00:00 2001 +From: Abhilash Raj +Date: Sun, 20 Dec 2020 05:26:18 +0000 +Subject: [PATCH] Remove old style coroutines and use async def instead. + +--- + src/mailman/testing/mta.py | 39 ++++++++++++++++---------------------- + 1 file changed, 16 insertions(+), 23 deletions(-) + +diff --git a/src/mailman/testing/mta.py b/src/mailman/testing/mta.py +index aede4a24da..aa6543056d 100644 +--- a/src/mailman/testing/mta.py ++++ b/src/mailman/testing/mta.py +@@ -18,7 +18,6 @@ + """Fake MTA for testing purposes.""" + from __future__ import generator_stop + +-import asyncio + import smtplib + + from aiosmtpd.controller import Controller +@@ -54,14 +53,12 @@ class ConnectionCountingHandler(MessageHandler): + def handle_message(self, message): + self._msg_queue.put(message) + +- @asyncio.coroutine +- def handle_EHLO(self, server, session, envelope, hostname): ++ async def handle_EHLO(self, server, session, envelope, hostname): + session.host_name = hostname +- yield from server.push('250-AUTH PLAIN') ++ await server.push('250-AUTH PLAIN') + return '250 HELP' + +- @asyncio.coroutine +- def handle_RSET(self, server, session, envelope): ++ async def handle_RSET(self, server, session, envelope): + self.connection_count = 0 + return '250 OK' + +@@ -83,8 +80,7 @@ class ConnectionCountingSMTP(SMTP): + # same though, so it's fine to stash this value away there. + self.event_handler.connection_count += 1 + +- @asyncio.coroutine +- def smtp_AUTH(self, arg): ++ async def smtp_AUTH(self, arg): + """Record that the AUTH occurred.""" + args = arg.split() + if args[0].lower() == 'plain': +@@ -94,25 +90,24 @@ class ConnectionCountingSMTP(SMTP): + # which must be equal to the base 64 equivalent of the + # expected login string "testuser:testpass". + if response == 'AHRlc3R1c2VyAHRlc3RwYXNz': +- yield from self.push('235 Ok') ++ await self.push('235 Ok') + self._oob_queue.put(response) + else: +- yield from self.push('571 Bad authentication') ++ await self.push('571 Bad authentication') + else: + assert len(args) == 1, args + # Send a challenge and set us up to wait for the response. +- yield from self.push('334 ') ++ await self.push('334 ') + self._waiting_for_auth_response = True + else: +- yield from self.push('571 Bad authentication') ++ await self.push('571 Bad authentication') + +- @asyncio.coroutine +- def smtp_STAT(self, arg): ++ async def smtp_STAT(self, arg): + """Cause the server to send statistics to its controller.""" + # Do not count the connection caused by the STAT connect. + self.event_handler.connection_count -= 1 + self._oob_queue.put(self.event_handler.connection_count) +- yield from self.push('250 Ok') ++ await self.push('250 Ok') + + def _next_error(self, command): + """Return the next error for the SMTP command, if there is one. +@@ -139,29 +134,27 @@ class ConnectionCountingSMTP(SMTP): + return code + return None + +- @asyncio.coroutine +- def smtp_RCPT(self, arg): ++ async def smtp_RCPT(self, arg): + """For testing, sometimes cause a non-25x response.""" + code = self._next_error('rcpt') + if code is None: + # Everything's cool. +- yield from super().smtp_RCPT(arg) ++ await super().smtp_RCPT(arg) + else: + # The test suite wants this to fail. The message corresponds to + # the exception we expect smtplib.SMTP to raise. +- yield from self.push('%d Error: SMTPRecipientsRefused' % code) ++ await self.push('%d Error: SMTPRecipientsRefused' % code) + +- @asyncio.coroutine +- def smtp_MAIL(self, arg): ++ async def smtp_MAIL(self, arg): + """For testing, sometimes cause a non-25x response.""" + code = self._next_error('mail') + if code is None: + # Everything's cool. +- yield from super().smtp_MAIL(arg) ++ await super().smtp_MAIL(arg) + else: + # The test suite wants this to fail. The message corresponds to + # the exception we expect smtplib.SMTP to raise. +- yield from self.push('%d Error: SMTPResponseException' % code) ++ await self.push('%d Error: SMTPResponseException' % code) + + + class ConnectionCountingController(Controller): +-- +GitLab + diff --git a/Support-Python-3.11.patch b/Support-Python-3.11.patch new file mode 100644 index 0000000..3d15a3d --- /dev/null +++ b/Support-Python-3.11.patch @@ -0,0 +1,58 @@ +From 1f2b19c8ad7235f5632a170c91e766462884813e Mon Sep 17 00:00:00 2001 +From: starlet-dx <15929766099@163.com> +Date: Sun, 23 Jul 2023 15:19:02 +0800 +Subject: [PATCH 1/1] Support Python 3.11 + +Refer: +https://gitlab.com/mailman/mailman/-/commit/1954815f32fea4d9d920cdc74f63bcc24d3b6c49 +--- + src/mailman/runners/lmtp.py | 17 ++++++++++++----- + 1 file changed, 12 insertions(+), 5 deletions(-) + +diff --git a/src/mailman/runners/lmtp.py b/src/mailman/runners/lmtp.py +index a653275..0fc45bf 100644 +--- a/src/mailman/runners/lmtp.py ++++ b/src/mailman/runners/lmtp.py +@@ -35,7 +35,6 @@ so that the peer mail server can provide better diagnostics. + """ + + import email +-import asyncio + import logging + + from aiosmtpd.controller import Controller +@@ -126,9 +125,13 @@ def split_recipient(address): + + + class LMTPHandler: +- @asyncio.coroutine ++ async def handle_RCPT(self, server, session, envelope, to, rcpt_options): ++ # Use a helper function to use the transactional wrapper on since it ++ # doesn't yet work on awaitables (async def funcs.) ++ return self._handle_RCPT(server, session, envelope, to, rcpt_options) ++ + @transactional +- def handle_RCPT(self, server, session, envelope, to, rcpt_options): ++ def _handle_RCPT(self, server, session, envelope, to, rcpt_options): + listnames = set(getUtility(IListManager).names) + try: + to = parseaddr(to)[1].lower() +@@ -164,9 +167,13 @@ class LMTPHandler: + config.db.abort() + return ERR_550 + +- @asyncio.coroutine ++ async def handle_DATA(self, server, session, envelope): ++ # Use a helper function to use the transactional wrapper on since it ++ # doesn't yet work on awaitables (async def funcs.) ++ return self._handle_DATA(server, session, envelope) ++ + @transactional +- def handle_DATA(self, server, session, envelope): ++ def _handle_DATA(self, server, session, envelope): + try: + # Refresh the list of list names every time we process a message + # since the set of mailing lists could have changed. +-- +2.30.0 + diff --git a/Verify-recipient-validity-at-RCPT-command-in-LMTP-runner.patch b/Verify-recipient-validity-at-RCPT-command-in-LMTP-runner.patch new file mode 100644 index 0000000..5438052 --- /dev/null +++ b/Verify-recipient-validity-at-RCPT-command-in-LMTP-runner.patch @@ -0,0 +1,217 @@ +From b9387743835821e8327e73aa502cb01f2f83dc97 Mon Sep 17 00:00:00 2001 +From: Patrick Cernko +Date: Wed, 19 Oct 2022 23:27:10 +0000 +Subject: [PATCH] Verify recipient validity at RCPT command in LMTP runner (2nd + try) + +Verify recipient validity at RCPT command in LMTP runner (reviewed merge request c2ddff05a4f405fa46fb792cc69912829c1cbf83, rejected 2 years ago) +* returning '250 ok' to correctly accept valid recipients +* reorganized code a bit to better match existing recipient verification code in handle_DATA() +* fixed unit tests (verified with `tox -e py39-nocov` on Debian/bullseye) + +See !671 for the original merge request by @foxcpp. Also see !126. + +Fixes #14 +--- + src/mailman/docs/NEWS.rst | 5 +++ + src/mailman/docs/mta.rst | 38 ++++++++++++++++++++++ + src/mailman/runners/lmtp.py | 38 ++++++++++++++++++++++ + src/mailman/runners/tests/test_lmtp.py | 45 ++++++++++++++++++++------ + 4 files changed, 117 insertions(+), 9 deletions(-) + +diff --git a/src/mailman/docs/mta.rst b/src/mailman/docs/mta.rst +index bd2df65948..cc85910b2c 100644 +--- a/src/mailman/docs/mta.rst ++++ b/src/mailman/docs/mta.rst +@@ -253,6 +253,8 @@ which are local, you may need ``local_recipient_maps`` as above. Note that + these can be ``regexp`` tables rather than ``hash`` tables. See the + ``Transport maps`` section above. + ++Starting with version 3.3.6, it is possible to use Mailman's LMTP ++service with Postfix' ``reject_unverified_recipient``. + + Postfix documentation + --------------------- +@@ -381,6 +383,42 @@ two lines to the ``mailman3_transport:`` section. + headers_remove = message-id + headers_add = "Message-ID: ${if def:header_message-id:{$h_message-id:}{}}" + ++Alternative setup using callout verification ++-------------------------------------------- ++ ++Starting with version 3.3.6, you can rely on Mailman's responce on ++``RCPT TO:`` LMTP command if mailman would accept the recipient ++address as valid. This can be used in Exim to validate recipients ++using callout verification. ++:: ++ ++ # /etc/exim4/conf.d/main/25_mm3_macros ++ # The colon-separated list of domains served by Mailman. ++ domainlist mm_domains = list.example.net ++ # The port of your Mailman's LMTP service ++ MM3_LMTP_PORT = 8024 ++ ++ # /etc/exim4/local_rcpt_callout (create file or append if already exists) ++ # Make callout verification for all domains served by Mailman. ++ *@+mm_domains ++ ++ # /etc/exim4/conf.d/router/455_mm3_router ++ mailman3_router: ++ driver = accept ++ domains = +mm_domains ++ # no further conditions, valid recipients are verified in ++ # acl_check_rcpt using callout verification ++ transport = mailman3_transport ++ ++ # /etc/exim4/conf.d/transport/55_mm3_transport ++ mailman3_transport: ++ driver = smtp ++ protocol = lmtp ++ allow_localhost ++ hosts = localhost ++ port = MM3_LMTP_PORT ++ rcpt_include_affixes = true ++ + Exim 4 documentation + -------------------- + +diff --git a/src/mailman/runners/lmtp.py b/src/mailman/runners/lmtp.py +index 53318f7120..b5b5b181ef 100644 +--- a/src/mailman/runners/lmtp.py ++++ b/src/mailman/runners/lmtp.py +@@ -126,6 +126,44 @@ def split_recipient(address): + + + class LMTPHandler: ++ @asyncio.coroutine ++ @transactional ++ def handle_RCPT(self, server, session, envelope, to, rcpt_options): ++ listnames = set(getUtility(IListManager).names) ++ try: ++ to = parseaddr(to)[1].lower() ++ local, subaddress, domain = split_recipient(to) ++ if subaddress is not None: ++ # Check that local-subaddress is not an actual list name. ++ listname = '{}-{}@{}'.format(local, subaddress, domain) ++ if listname in listnames: ++ local = '{}-{}'.format(local, subaddress) ++ subaddress = None ++ listname = '{}@{}'.format(local, domain) ++ if listname not in listnames: ++ return ERR_550 ++ canonical_subaddress = SUBADDRESS_NAMES.get(subaddress) ++ if subaddress is None: ++ # The message is destined for the mailing list. ++ # nothing to do here, just keep code similar to handle_DATA ++ pass ++ elif canonical_subaddress is None: ++ # The subaddress was bogus. ++ slog.error('unknown sub-address: %s', subaddress) ++ return ERR_550 ++ else: ++ # A valid subaddress. ++ # nothing to do here, just keep code similar to handle_DATA ++ pass ++ # recipient validated, just do the same as aiosmtpd.LMTP would do ++ envelope.rcpt_tos.append(to) ++ envelope.rcpt_options.extend(rcpt_options) ++ return '250 Ok' ++ except Exception: ++ slog.exception('Address verification: %s', to) ++ config.db.abort() ++ return ERR_550 ++ + @asyncio.coroutine + @transactional + def handle_DATA(self, server, session, envelope): +diff --git a/src/mailman/runners/tests/test_lmtp.py b/src/mailman/runners/tests/test_lmtp.py +index 3d0f8e0e5d..5200228028 100644 +--- a/src/mailman/runners/tests/test_lmtp.py ++++ b/src/mailman/runners/tests/test_lmtp.py +@@ -114,7 +114,7 @@ Message-ID: + + def test_nonexistent_mailing_list(self): + # Trying to post to a nonexistent mailing list is an error. +- with self.assertRaises(smtplib.SMTPDataError) as cm: ++ with self.assertRaises(smtplib.SMTPRecipientsRefused) as cm: + self._lmtp.sendmail('anne@example.com', + ['notalist@example.com'], """\ + From: anne.person@example.com +@@ -123,13 +123,22 @@ Subject: An interesting message + Message-ID: + + """) +- self.assertEqual(cm.exception.smtp_code, 550) +- self.assertEqual(cm.exception.smtp_error, ++ # smtplib.SMTPRecipientsRefused.args contains a list of errors (for ++ # each RCPT TO), thus we should have only one error ++ self.assertEqual(len(cm.exception.args), 1) ++ args0 = cm.exception.args[0] ++ # each error should be a dict with the corresponding email address ++ # as key ++ self.assertTrue('notalist@example.com' in args0) ++ errorval = args0['notalist@example.com'] ++ # errorval must be a tuple of (code, errorstr) ++ self.assertEqual(errorval[0], 550) ++ self.assertEqual(errorval[1], + b'Requested action not taken: mailbox unavailable') + + def test_nonexistent_domain(self): + # Trying to post to a nonexistent domain is an error. +- with self.assertRaises(smtplib.SMTPDataError) as cm: ++ with self.assertRaises(smtplib.SMTPRecipientsRefused) as cm: + self._lmtp.sendmail('anne@example.com', + ['test@x.example.com'], """\ + From: anne.person@example.com +@@ -138,8 +147,17 @@ Subject: An interesting message + Message-ID: + + """) +- self.assertEqual(cm.exception.smtp_code, 550) +- self.assertEqual(cm.exception.smtp_error, ++ # smtplib.SMTPRecipientsRefused.args contains a list of errors (for ++ # each RCPT TO), thus we should have only one error ++ self.assertEqual(len(cm.exception.args), 1) ++ args0 = cm.exception.args[0] ++ # each error should be a dict with the corresponding email address ++ # as key ++ self.assertTrue('test@x.example.com' in args0) ++ errorval = args0['test@x.example.com'] ++ # errorval must be a tuple of (code, errorstr) ++ self.assertEqual(errorval[0], 550) ++ self.assertEqual(errorval[1], + b'Requested action not taken: mailbox unavailable') + + def test_alias_domain(self): +@@ -168,7 +186,7 @@ X-MailFrom: anne@example.com + + def test_missing_subaddress(self): + # Trying to send a message to a bogus subaddress is an error. +- with self.assertRaises(smtplib.SMTPDataError) as cm: ++ with self.assertRaises(smtplib.SMTPRecipientsRefused) as cm: + self._lmtp.sendmail('anne@example.com', + ['test-bogus@example.com'], """\ + From: anne.person@example.com +@@ -177,8 +195,17 @@ Subject: An interesting message + Message-ID: + + """) +- self.assertEqual(cm.exception.smtp_code, 550) +- self.assertEqual(cm.exception.smtp_error, ++ # smtplib.SMTPRecipientsRefused.args contains a list of errors (for ++ # each RCPT TO), thus we should have only one error ++ self.assertEqual(len(cm.exception.args), 1) ++ args0 = cm.exception.args[0] ++ # each error should be a dict with the corresponding email address ++ # as key ++ self.assertTrue('test-bogus@example.com' in args0) ++ errorval = args0['test-bogus@example.com'] ++ # errorval must be a tuple of (code, errorstr) ++ self.assertEqual(errorval[0], 550) ++ self.assertEqual(errorval[1], + b'Requested action not taken: mailbox unavailable') + + def test_mailing_list_with_subaddress(self): +-- +GitLab + diff --git a/mailman.spec b/mailman.spec index 8e96183..8bd002e 100644 --- a/mailman.spec +++ b/mailman.spec @@ -7,7 +7,7 @@ Name: mailman Version: 3.3.2 -Release: 9 +Release: 10 Epoch: 3 Summary: The GNU mailing list manager License: GPLv3 @@ -32,6 +32,12 @@ Patch17: 0001-fix-tests-assertion-error.patch Patch18: support-sqlalchemy-1-4.patch #Refer: https://gitlab.com/mailman/mailman/-/merge_requests/929 Patch19: Require-authheaders-0.14.0-and-adjust-tests-accordingly.patch +#Refer: https://gitlab.com/mailman/mailman/-/commit/38c794d9ed71f25ee138760461f66abc29dfa81c +Patch20: Remove-old-style-coroutines-and-use-async-def-instead.patch +#Refer: https://gitlab.com/mailman/mailman/-/commit/b9387743835821e8327e73aa502cb01f2f83dc97 +Patch21: Verify-recipient-validity-at-RCPT-command-in-LMTP-runner.patch +#Refer: https://gitlab.com/mailman/mailman/-/commit/1954815f32fea4d9d920cdc74f63bcc24d3b6c49 +Patch22: Support-Python-3.11.patch BuildArch: noarch BuildRequires: glibc-langpack-en BuildRequires: python%{python3_pkgversion}-devel >= 3.5 python%{python3_pkgversion}-setuptools @@ -213,6 +219,9 @@ done %{_datadir}/selinux/*/mailman3.pp %changelog +* Sun Jul 23 2023 yaoxin - 3:3.3.2-10 +- Fix test fails caused by python update to 3.11.4 + * Wed Mar 08 2023 yaoxin - 3:3.3.2-9 - Add ExecStartPost option to mailman3.service for fix error message when mailman3 starting -- Gitee