225 lines
9.7 KiB
Diff
225 lines
9.7 KiB
Diff
|
|
From 700a1e2e3572ec2c8766da9acdaf8babce5d8d20 Mon Sep 17 00:00:00 2001
|
|||
|
|
From: Neal Gompa <ngompa13@gmail.com>
|
|||
|
|
Date: Thu, 15 Oct 2020 10:30:07 -0400
|
|||
|
|
Subject: [PATCH] Cover some subject prefix use cases
|
|||
|
|
MIME-Version: 1.0
|
|||
|
|
Content-Type: text/plain; charset=UTF-8
|
|||
|
|
Content-Transfer-Encoding: 8bit
|
|||
|
|
|
|||
|
|
The subject_prefix handler has many bugs and was mostly not covered
|
|||
|
|
for lists with a non-ascii charset.
|
|||
|
|
|
|||
|
|
Co-authored-by: Aurélien Bompard <aurelien@bompard.org>
|
|||
|
|
Signed-off-by: Neal Gompa <ngompa13@gmail.com>
|
|||
|
|
---
|
|||
|
|
src/mailman/handlers/subject_prefix.py | 19 +++--
|
|||
|
|
.../handlers/tests/test_subject_prefix.py | 81 +++++++++++++++++--
|
|||
|
|
2 files changed, 87 insertions(+), 13 deletions(-)
|
|||
|
|
|
|||
|
|
diff --git a/src/mailman/handlers/subject_prefix.py b/src/mailman/handlers/subject_prefix.py
|
|||
|
|
index 95ec0ec2d..10ad07875 100644
|
|||
|
|
--- a/src/mailman/handlers/subject_prefix.py
|
|||
|
|
+++ b/src/mailman/handlers/subject_prefix.py
|
|||
|
|
@@ -39,13 +39,12 @@ def ascii_header(mlist, msgdata, subject, prefix, prefix_pattern, ws):
|
|||
|
|
if charset not in ASCII_CHARSETS:
|
|||
|
|
return None
|
|||
|
|
subject_text = EMPTYSTRING.join(str(subject).splitlines())
|
|||
|
|
+ subject_text = re.sub(prefix_pattern, '', subject_text)
|
|||
|
|
# At this point, the subject may become null if someone posted mail
|
|||
|
|
# with "Subject: [subject prefix]".
|
|||
|
|
if subject_text.strip() == '':
|
|||
|
|
with _.using(mlist.preferred_language.code):
|
|||
|
|
subject_text = _('(no subject)')
|
|||
|
|
- else:
|
|||
|
|
- subject_text = re.sub(prefix_pattern, '', subject_text)
|
|||
|
|
msgdata['stripped_subject'] = subject_text
|
|||
|
|
rematch = re.match(RE_PATTERN, subject_text, re.I)
|
|||
|
|
if rematch:
|
|||
|
|
@@ -83,13 +82,12 @@ def all_same_charset(mlist, msgdata, subject, prefix, prefix_pattern, ws):
|
|||
|
|
if charset != list_charset:
|
|||
|
|
return None
|
|||
|
|
subject_text = EMPTYSTRING.join(chunks)
|
|||
|
|
+ subject_text = re.sub(prefix_pattern, '', subject_text)
|
|||
|
|
# At this point, the subject may become null if someone posted mail
|
|||
|
|
# with "Subject: [subject prefix]".
|
|||
|
|
if subject_text.strip() == '':
|
|||
|
|
- with _.push(mlist.preferred_language.code):
|
|||
|
|
+ with _.using(mlist.preferred_language.code):
|
|||
|
|
subject_text = _('(no subject)')
|
|||
|
|
- else:
|
|||
|
|
- subject_text = re.sub(prefix_pattern, '', subject_text)
|
|||
|
|
msgdata['stripped_subject'] = subject_text
|
|||
|
|
rematch = re.match(RE_PATTERN, subject_text, re.I)
|
|||
|
|
if rematch:
|
|||
|
|
@@ -114,7 +112,7 @@ def mixed_charsets(mlist, msgdata, subject, prefix, prefix_pattern, ws):
|
|||
|
|
list_charset = mlist.preferred_language.charset
|
|||
|
|
chunks = decode_header(subject.encode())
|
|||
|
|
if len(chunks) == 0:
|
|||
|
|
- with _.push(mlist.preferred_language.code):
|
|||
|
|
+ with _.using(mlist.preferred_language.code):
|
|||
|
|
subject_text = _('(no subject)')
|
|||
|
|
chunks = [(prefix, list_charset),
|
|||
|
|
(subject_text, list_charset),
|
|||
|
|
@@ -134,13 +132,20 @@ def mixed_charsets(mlist, msgdata, subject, prefix, prefix_pattern, ws):
|
|||
|
|
chunks.insert(0, ('', 'us-ascii'))
|
|||
|
|
first_text = ''
|
|||
|
|
first_text = re.sub(prefix_pattern, '', first_text).lstrip()
|
|||
|
|
+ if not first_text.strip() and len(chunks) <= 1:
|
|||
|
|
+ with _.using(mlist.preferred_language.code):
|
|||
|
|
+ subject_text = _('(no subject)')
|
|||
|
|
+ chunks = [(prefix.strip(), list_charset),
|
|||
|
|
+ (subject_text, list_charset),
|
|||
|
|
+ ]
|
|||
|
|
+ return make_header(chunks, continuation_ws=ws)
|
|||
|
|
rematch = re.match(RE_PATTERN, first_text, re.I)
|
|||
|
|
if rematch:
|
|||
|
|
first_text = 'Re: ' + first_text[rematch.end():]
|
|||
|
|
chunks[0] = (first_text, chunk_charset)
|
|||
|
|
# The subject text stripped of the prefix, for use in the NNTP gateway.
|
|||
|
|
msgdata['stripped_subject'] = str(make_header(chunks, continuation_ws=ws))
|
|||
|
|
- chunks.insert(0, (prefix, list_charset))
|
|||
|
|
+ chunks.insert(0, (prefix.strip(), list_charset))
|
|||
|
|
return make_header(chunks, continuation_ws=ws)
|
|||
|
|
|
|||
|
|
|
|||
|
|
diff --git a/src/mailman/handlers/tests/test_subject_prefix.py b/src/mailman/handlers/tests/test_subject_prefix.py
|
|||
|
|
index c2a257035..1d8bc175d 100644
|
|||
|
|
--- a/src/mailman/handlers/tests/test_subject_prefix.py
|
|||
|
|
+++ b/src/mailman/handlers/tests/test_subject_prefix.py
|
|||
|
|
@@ -33,6 +33,13 @@ class TestSubjectPrefix(unittest.TestCase):
|
|||
|
|
def setUp(self):
|
|||
|
|
self._mlist = create_list('test@example.com')
|
|||
|
|
self._process = config.handlers['subject-prefix'].process
|
|||
|
|
+ language_manager = getUtility(ILanguageManager)
|
|||
|
|
+ if 'xx' not in language_manager:
|
|||
|
|
+ language_manager.add('xx', 'utf-8', 'Freedonia')
|
|||
|
|
+
|
|||
|
|
+ def tearDown(self):
|
|||
|
|
+ # The LanguageManager may need a 'remove' method.
|
|||
|
|
+ del getUtility(ILanguageManager)._languages['xx']
|
|||
|
|
|
|||
|
|
def test_isdigest(self):
|
|||
|
|
# If the message is destined for the digest, the Subject header does
|
|||
|
|
@@ -114,6 +121,14 @@ class TestSubjectPrefix(unittest.TestCase):
|
|||
|
|
self._process(self._mlist, msg, {})
|
|||
|
|
self.assertEqual(str(msg['subject']), '[Test] A test message')
|
|||
|
|
|
|||
|
|
+ def test_multiline_subject_non_ascii_list(self):
|
|||
|
|
+ # The subject appears on multiple lines on a non-ascii list.
|
|||
|
|
+ self._mlist.preferred_language = 'xx'
|
|||
|
|
+ msg = Message()
|
|||
|
|
+ msg['Subject'] = '\n A test message'
|
|||
|
|
+ self._process(self._mlist, msg, {})
|
|||
|
|
+ self.assertEqual(str(msg['subject']), '[Test] A test message')
|
|||
|
|
+
|
|||
|
|
def test_i18n_prefix(self):
|
|||
|
|
# The Subject header is encoded, but the prefix is still added.
|
|||
|
|
msg = Message()
|
|||
|
|
@@ -130,7 +145,7 @@ class TestSubjectPrefix(unittest.TestCase):
|
|||
|
|
msg['Subject'] = '[Test] '
|
|||
|
|
self._process(self._mlist, msg, {})
|
|||
|
|
subject = msg['subject']
|
|||
|
|
- self.assertEqual(str(subject), '[Test] ')
|
|||
|
|
+ self.assertEqual(str(subject), '[Test] (no subject)')
|
|||
|
|
|
|||
|
|
def test_prefix_only_all_same(self):
|
|||
|
|
# Incoming subject is only the prefix.
|
|||
|
|
@@ -141,7 +156,7 @@ class TestSubjectPrefix(unittest.TestCase):
|
|||
|
|
self._process(self._mlist, msg, {})
|
|||
|
|
self._mlist.preferred_language.charset = old_charset
|
|||
|
|
subject = msg['subject']
|
|||
|
|
- self.assertEqual(str(subject), '[Test] ')
|
|||
|
|
+ self.assertEqual(str(subject), '[Test] (no subject)')
|
|||
|
|
|
|||
|
|
def test_prefix_only_mixed(self):
|
|||
|
|
# Incoming subject is only the prefix.
|
|||
|
|
@@ -149,7 +164,7 @@ class TestSubjectPrefix(unittest.TestCase):
|
|||
|
|
msg['Subject'] = '=?utf-8?Q?[Test]_?='
|
|||
|
|
self._process(self._mlist, msg, {})
|
|||
|
|
subject = msg['subject']
|
|||
|
|
- self.assertEqual(str(subject), '[Test] ')
|
|||
|
|
+ self.assertEqual(str(subject), '[Test] (no subject)')
|
|||
|
|
|
|||
|
|
def test_re_only(self):
|
|||
|
|
# Incoming subject is only Re:.
|
|||
|
|
@@ -198,15 +213,13 @@ class TestSubjectPrefix(unittest.TestCase):
|
|||
|
|
def test_decode_header_returns_string(self):
|
|||
|
|
# Under some circumstances, email.header.decode_header() returns a
|
|||
|
|
# string value. Ensure we can handle that.
|
|||
|
|
- manager = getUtility(ILanguageManager)
|
|||
|
|
- manager.add('xx', 'iso-8859-1', 'Xlandia')
|
|||
|
|
self._mlist.preferred_language = 'xx'
|
|||
|
|
msg = Message()
|
|||
|
|
msg['Subject'] = 'Plain text'
|
|||
|
|
self._process(self._mlist, msg, {})
|
|||
|
|
subject = msg['subject']
|
|||
|
|
self.assertEqual(subject.encode(),
|
|||
|
|
- '=?iso-8859-1?q?=5BTest=5D_?= Plain text')
|
|||
|
|
+ '=?utf-8?b?W1Rlc3Rd?= Plain text')
|
|||
|
|
|
|||
|
|
def test_unknown_encoded_subject(self):
|
|||
|
|
msg = Message()
|
|||
|
|
@@ -215,3 +228,59 @@ class TestSubjectPrefix(unittest.TestCase):
|
|||
|
|
subject = msg['subject']
|
|||
|
|
self.assertEqual(str(subject),
|
|||
|
|
'[Test] Non-ascii subject - fran<61>ais')
|
|||
|
|
+
|
|||
|
|
+ def test_non_ascii_list(self):
|
|||
|
|
+ # The mailing list has a non-ascii language
|
|||
|
|
+ self._mlist.preferred_language = 'xx'
|
|||
|
|
+ msg = Message()
|
|||
|
|
+ msg['Subject'] = 'A test message'
|
|||
|
|
+ self._process(self._mlist, msg, {})
|
|||
|
|
+ self.assertEqual(str(msg['subject']), '[Test] A test message')
|
|||
|
|
+
|
|||
|
|
+ def test_no_subject(self):
|
|||
|
|
+ # The email has no subject
|
|||
|
|
+ msg = Message()
|
|||
|
|
+ msg['Subject'] = ''
|
|||
|
|
+ self._process(self._mlist, msg, {})
|
|||
|
|
+ self.assertEqual(str(msg['subject']), '[Test] (no subject)')
|
|||
|
|
+
|
|||
|
|
+ def test_no_subject_non_ascii_list(self):
|
|||
|
|
+ # The email has no subject on a non-ascii list
|
|||
|
|
+ self._mlist.preferred_language = 'xx'
|
|||
|
|
+ msg = Message()
|
|||
|
|
+ msg['Subject'] = ''
|
|||
|
|
+ self._process(self._mlist, msg, {})
|
|||
|
|
+ self.assertEqual(str(msg['subject']), '[Test] (no subject)')
|
|||
|
|
+
|
|||
|
|
+ def test_no_real_subject(self):
|
|||
|
|
+ # The email has no subject
|
|||
|
|
+ msg = Message()
|
|||
|
|
+ msg['Subject'] = '[Test] '
|
|||
|
|
+ self._process(self._mlist, msg, {})
|
|||
|
|
+ self.assertEqual(str(msg['subject']), '[Test] (no subject)')
|
|||
|
|
+
|
|||
|
|
+ def test_no_real_subject_non_ascii_list(self):
|
|||
|
|
+ # The email has no subject on a non-ascii list
|
|||
|
|
+ self._mlist.preferred_language = 'xx'
|
|||
|
|
+ msg = Message()
|
|||
|
|
+ msg['Subject'] = '[Test] '
|
|||
|
|
+ self._process(self._mlist, msg, {})
|
|||
|
|
+ self.assertEqual(str(msg['subject']), '[Test] (no subject)')
|
|||
|
|
+
|
|||
|
|
+ def test_non_ascii_subject_and_list(self):
|
|||
|
|
+ # The mailing list has a non-ascii language and the subject is
|
|||
|
|
+ # non-ascii with the same encoding.
|
|||
|
|
+ self._mlist.preferred_language = 'xx'
|
|||
|
|
+ msg = Message()
|
|||
|
|
+ msg['Subject'] = '=?utf-8?q?d=C3=A9sirable?='
|
|||
|
|
+ self._process(self._mlist, msg, {})
|
|||
|
|
+ self.assertEqual(str(msg['subject']), '[Test] d\xe9sirable')
|
|||
|
|
+
|
|||
|
|
+ def test_non_ascii_empty_subject_and_non_ascii_list(self):
|
|||
|
|
+ # The mailing list has a non-ascii language and the subject is
|
|||
|
|
+ # non-ascii with the same encoding, but actually empty.
|
|||
|
|
+ self._mlist.preferred_language = 'xx'
|
|||
|
|
+ msg = Message()
|
|||
|
|
+ msg['Subject'] = '=?utf-8?q?[Test]_?='
|
|||
|
|
+ self._process(self._mlist, msg, {})
|
|||
|
|
+ self.assertEqual(str(msg['subject']), '[Test] (no subject)')
|
|||
|
|
--
|
|||
|
|
2.28.0
|
|||
|
|
|