diff --git a/python_multipart/decoders.py b/python_multipart/decoders.py index c4cdfb6..2e13858 100644 --- a/python_multipart/decoders.py +++ b/python_multipart/decoders.py @@ -142,8 +142,9 @@ def write(self, data: bytes) -> int: # If the last 2 characters have an '=' sign in it, then we won't be # able to decode the encoded value and we'll need to save it for the # next decoding step. - if data[-2:].find(b"=") != -1: - enc, rest = data[:-2], data[-2:] + idx = data.rfind(b"=", max(0, len(data) - 2)) + if idx != -1: + enc, rest = data[:idx], data[idx:] else: enc = data rest = b"" diff --git a/tests/test_quoted_printable_chunks.py b/tests/test_quoted_printable_chunks.py new file mode 100644 index 0000000..f520506 --- /dev/null +++ b/tests/test_quoted_printable_chunks.py @@ -0,0 +1,30 @@ +import binascii + +import pytest + +from python_multipart.decoders import QuotedPrintableDecoder + + +class Sink: + def __init__(self) -> None: + self.b = b"" + + def write(self, data: bytes) -> int: + self.b += data + return len(data) + + +@pytest.mark.parametrize( + "data", + [b"=41=42", b"a=41=42=43z", b"=41=\n=42", b"x=3D=3Dy"], +) +def test_chunked_matches_whole(data: bytes) -> None: + whole = binascii.a2b_qp(data) + for i in range(len(data) + 1): + for j in range(i, len(data) + 1): + sink = Sink() + decoder = QuotedPrintableDecoder(sink) + for part in (data[:i], data[i:j], data[j:]): + decoder.write(part) + decoder.finalize() + assert sink.b == whole, (i, j)