Fix MIME content sub-type handling for non-plain text messages

This commit is contained in:
Piotr F. Mieszkowski 2023-04-22 18:08:19 +02:00
parent bc92d7a31c
commit 61cf50effe
4 changed files with 33 additions and 16 deletions

View File

@ -179,35 +179,42 @@ def _encrypt_all_payloads_mime(message: EmailMessage, gpg_to_cmdline):
boundary = _make_boundary()
if isinstance(message.get_payload(), str):
LOG.debug('Rewrapping a flat, text-only message')
wrapped_payload = _rewrap_payload(message)
encrypted_part.set_payload(wrapped_payload.as_string())
_set_type_and_boundary(message, boundary)
return [pgp_ver_part, _encrypt_payload(encrypted_part, gpg_to_cmdline, True)]
check_nested = True
else:
processed_payloads = _generate_message_from_payloads(message)
encrypted_part.set_payload(processed_payloads.as_string())
_set_type_and_boundary(message, boundary)
return [pgp_ver_part, _encrypt_payload(encrypted_part, gpg_to_cmdline, False)]
check_nested = False
return [pgp_ver_part, _encrypt_payload(encrypted_part, gpg_to_cmdline, check_nested)]
def _rewrap_payload(message: EmailMessage) -> MIMEPart:
# In PGP/MIME (RFC 3156), the payload has to be a valid MIME entity. In
# other words, we need to wrap text/plain message's payload in a new MIME
# other words, we need to wrap text/* message's payload in a new MIME
# entity.
pld = MIMEPart()
pld.set_type(message.get_content_type())
pld.set_content(message.get_content())
wrapper = MIMEPart(policy=SMTPUTF8)
content = message.get_content()
wrapper.set_content(content)
# Make sure all Content-Type parameters are included.
for (k, v) in message.get_params():
pld.set_param(k, v)
wrapper.set_type(message.get_content_type())
return pld
# Copy all Content-Type parameters.
for (pname, pvalue) in message.get_params():
# Skip MIME type that's also returned by get_params().
if not '/' in pname:
wrapper.set_param(pname, pvalue)
return wrapper
def _make_boundary():
@ -242,8 +249,6 @@ def _encrypt_payload(payload: EmailMessage, recipients, check_nested=True, **kwa
if isAttachment:
_append_gpg_extension(payload)
if not (payload.get('Content-Transfer-Encoding') is None):
payload.replace_header('Content-Transfer-Encoding', "7bit")
return payload

View File

@ -90,12 +90,10 @@ class RecipientList:
def emails(self):
"""Return list of recipients."""
LOG.debug('Recipient emails from: %s', self._recipients)
return [r.email() for r in self._recipients]
def keys(self):
"""Return list of GPG identities."""
LOG.debug('Recipient keys from: %s', self._recipients)
return [r.key() for r in self._recipients]
def __iadd__(self, recipient: GpgRecipient):
@ -116,6 +114,10 @@ class RecipientList:
"""
return len(self._recipients)
def __repr__(self):
"""Returns textual object representation."""
return '<RecipientList %d %s>' % (len(self._recipients), ','.join(self.emails()))
def identify_gpg_recipients(recipients, keys: kcache.KeyCache):
"""Split recipient list into GPG and non-GPG ones."""

View File

@ -173,6 +173,14 @@ class EmailTest(unittest.TestCase):
_ = mp.as_string()
self.assertFalse(mp.get_boundary() is None)
def test_content_type_params_include_mime_type(self):
p = email.message.MIMEPart()
p.set_type('text/plain')
p.set_param('charset', 'UTF-8')
p.set_param('format', 'flowed')
self.assertIn(('text/plain', ''), p.get_params())
class RawConfigParserTest(unittest.TestCase):
def test_config_parser_returns_str(self):

View File

@ -36,5 +36,7 @@ class LacreCoreTest(unittest.TestCase):
rewrapped = lacre.core._rewrap_payload(m)
self.assertFalse('Subject' in rewrapped)
self.assertEqual(rewrapped.get_content_type(), m.get_content_type())
self.assertFalse('Subject' in rewrapped,
'only content and content-type should be copied')
self.assertEqual(rewrapped.get_content_type(), 'text/plain',
'rewrapped part should have initial message\'s content-type')