Skip to content

Commit 5306ba3

Browse files
authored
Merge pull request #31 from thegushi/fix/body-line-iterator-decode
Fix body_line_iterator(decode=True) returning no lines in Python 3.11
2 parents c61ffe6 + 6739650 commit 5306ba3

4 files changed

Lines changed: 29 additions & 5 deletions

File tree

Mailman/Bouncers/AOL.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ def process(msg):
2929
return
3030
addrs = []
3131
found = False
32-
for line in msg.get_payload(decode=True).splitlines():
32+
payload = msg.get_payload(decode=True)
33+
if isinstance(payload, bytes):
34+
payload = payload.decode('us-ascii', errors='replace')
35+
for line in payload.splitlines():
3336
if scre.search(line):
3437
found = True
3538
continue

Mailman/Bouncers/SimpleMatch.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,30 @@
1717

1818
"""Recognizes simple heuristically delimited bounces."""
1919

20+
import io
2021
import re
2122
import email.iterators
2223

2324

25+
def _body_line_iterator(msg):
26+
"""Iterate body lines with Content-Transfer-Encoding properly decoded.
27+
28+
Python 3's body_line_iterator only yields str payloads; get_payload(
29+
decode=True) returns bytes (CTE-decoded). This helper bridges the gap so
30+
quoted-printable and base64 bodies are decoded before pattern matching.
31+
"""
32+
for subpart in email.iterators.typed_subpart_iterator(msg):
33+
payload = subpart.get_payload(decode=True)
34+
if not isinstance(payload, bytes):
35+
continue
36+
charset = subpart.get_content_charset('us-ascii') or 'us-ascii'
37+
try:
38+
text = payload.decode(charset, errors='replace')
39+
except LookupError:
40+
text = payload.decode('us-ascii', errors='replace')
41+
yield from io.StringIO(text)
42+
43+
2444

2545
def _c(pattern):
2646
return re.compile(pattern, re.IGNORECASE)
@@ -224,7 +244,7 @@ def process(msg, patterns=None):
224244
# we process the message multiple times anyway.
225245
for scre, ecre, acre in patterns:
226246
state = 0
227-
for line in email.iterators.body_line_iterator(msg, decode=True):
247+
for line in _body_line_iterator(msg):
228248
if state == 0:
229249
if scre.search(line):
230250
state = 1

Mailman/Bouncers/SimpleWarning.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import email.iterators
2222

2323
from Mailman.Bouncers.BouncerAPI import Stop
24-
from Mailman.Bouncers.SimpleMatch import _c
24+
from Mailman.Bouncers.SimpleMatch import _c, _body_line_iterator
2525

2626

2727

@@ -75,7 +75,7 @@ def process(msg):
7575
addrs = {}
7676
for scre, ecre, acre in patterns:
7777
state = 0
78-
for line in email.iterators.body_line_iterator(msg, decode=True):
78+
for line in _body_line_iterator(msg):
7979
if state == 0:
8080
if scre.search(line):
8181
state = 1

Mailman/Handlers/Tagger.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from email.header import decode_header
2727

2828
from Mailman import Utils
29+
from Mailman.Bouncers.SimpleMatch import _body_line_iterator
2930
from Mailman.Logging.Syslog import syslog
3031
from Mailman.Handlers.CookHeaders import change_header
3132

@@ -97,7 +98,7 @@ def scanbody(msg, numlines=None):
9798
# the first numlines of body text.
9899
lines = []
99100
lineno = 0
100-
reader = list(email.iterators.body_line_iterator(msg, decode=True))
101+
reader = list(_body_line_iterator(msg))
101102
while numlines is None or lineno < numlines:
102103
try:
103104
line = reader.pop(0)

0 commit comments

Comments
 (0)