Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

special-case multipart/signed to match the README #8

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions muttdown/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def convert_one(part, config):
if config.css:
md = '<style>' + config.css + '</style>' + md
md = pynliner.fromString(md)
message = MIMEText(md, 'html', _charset="UTF-8")
message = MIMEText(md, _subtype='html', _charset='utf-8')
return message
except Exception:
raise
Expand All @@ -59,7 +59,7 @@ def _move_headers(source, dest):
del source[k]


def convert_tree(message, config, indent=0):
def convert_tree(message, config, only_new=False):
"""Recursively convert a potentially-multipart tree.

Returns a tuple of (the converted tree, whether any markdown was found)
Expand All @@ -73,6 +73,8 @@ def convert_tree(message, config, indent=0):
if disposition == 'inline' and ct in ('text/plain', 'text/markdown'):
converted = convert_one(message, config)
if converted is not None:
if only_new:
return converted, True
new_tree = MIMEMultipart('alternative')
_move_headers(message, new_tree)
new_tree.attach(message)
Expand All @@ -81,19 +83,29 @@ def convert_tree(message, config, indent=0):
return message, False
else:
if ct == 'multipart/signed':
# if this is a multipart/signed message, then let's just
# recurse into the non-signature part
# find the non-signature part
target_part = None
for part in message.get_payload():
if part.get_content_type() != 'application/pgp-signature':
return convert_tree(part, config, indent=indent + 1)
target_part = part
if target_part is None:
return message, False
converted_part, did_conversion = convert_tree(target_part, config, only_new=True)
if not did_conversion:
return message, False
new_root = MIMEMultipart('alternative', message.get_charset())
_move_headers(message, new_root)
new_root.attach(message)
new_root.attach(converted_part)
return new_root, True
else:
did_conversion = False
new_root = MIMEMultipart(cs, message.get_charset())
if message.preamble:
new_root.preamble = message.preamble
_move_headers(message, new_root)
for part in message.get_payload():
part, did_this_conversion = convert_tree(part, config, indent=indent + 1)
part, did_this_conversion = convert_tree(part, config, only_new=only_new)
did_conversion |= did_this_conversion
new_root.attach(part)
return new_root, did_conversion
Expand Down
27 changes: 27 additions & 0 deletions tests/test_basic.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.message import Message
from email.iterators import _structure

import pytest
from six.moves import StringIO

from muttdown.main import convert_tree
from muttdown.main import process_message
Expand Down Expand Up @@ -88,3 +91,27 @@ def test_with_css(config_with_css):
assert text_part.get_payload(decode=True) == b'!m\n\nThis is a message'
html_part = converted.get_payload()[1]
assert html_part.get_payload(decode=True) == b'<p style="font-family: serif">This is a message</p>'


def test_multipart_signed(basic_config):
msg = MIMEMultipart('signed')
msg['Subject'] = 'Test Message'
msg['From'] = '[email protected]'
msg['To'] = '[email protected]'
msg['Bcc'] = 'bananas'

msg.attach(MIMEText("!m This is the signed message body. \U0001f4a9"))
msg.attach(MIMEApplication("some-signature-here", "pgp-signature", name="signature.asc"))
converted, _ = convert_tree(msg, basic_config)

structure = StringIO()

_structure(converted, fp=structure)
assert structure.getvalue() == '\n'.join([
'multipart/alternative',
' multipart/signed',
' text/plain',
' application/pgp-signature',
' text/html',
''
])