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

Various improvements to muttdown #5

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
muttdown
========

`muttdown` is a sendmail-replacement designed for use with the [mutt][] email client which will transparently compile annotated `text/plain` mail into `text/html` using the [Markdown][] standard. It will recursively walk the MIME tree and compile any `text/plain` or `text/markdown` part which begins with the sigil "!m" into Markdown, which it will insert alongside the original in a multipart/alternative container.
`muttdown` is a sendmail-replacement designed for use with the [mutt][] email client which will transparently compile annotated `text/plain` mail into `text/html` using the [Markdown][] standard. It will recursively walk the MIME tree and compile any `text/plain` or `text/markdown` part which begins with the sigil "!m" into Markdown, which it will insert alongside the original in a multipart/alternative container. If a part starts with the sigil '!p' it will simply enclose it in <pre> </pre> tags instead of
formatting it with markdown.

It's also smart enough not to break `multipart/signed`.

Expand Down Expand Up @@ -56,6 +57,16 @@ The `css_file` should be regular CSS styling blocks; we use [pynliner][] to inli

Muttdown can also send its mail using the native `sendmail` if you have that set up (instead of doing SMTP itself). To do so, just leave the smtp options in the config file blank, set the `sendmail` option to the fully-qualified path to your `sendmail` binary, and run muttdown with the `-s` flag

If the `remove_sigil` configuration file option is true, the sigil will also be removed from the plaintext version of the message part.

The `markdown_extensions` configuration file option can be set to a list of Python markdown extensions that will be enabled (e.g. `[markdown.extensions.extra,markdown.extensions.Admonition]`).

The `utf8` option will assume that the message text is UTF8 and create
the HTML version appropriately. This is useful if you receive a lot
of e-mails (which you want to quote in replies) that include UTF-8
characters but don't have the encoding set correctly.


Installation
------------
Install muttdown with `pip install muttdown` or by downloading this package and running `python setup.py install`. You will need the [PyYAML][] and [Python-Markdown][] libraries, as specified in `requirements.txt`.
Expand Down
3 changes: 3 additions & 0 deletions muttdown/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ class Config(object):
'smtp_timeout': 10,
'css_file': None,
'sendmail': '/usr/sbin/sendmail',
'remove_sigil': False,
'markdown_extensions' : [],
'utf8': False,
}

def __init__(self):
Expand Down
47 changes: 36 additions & 11 deletions muttdown/main.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/usr/bin/env python2
from __future__ import print_function

import argparse
Expand All @@ -16,32 +17,54 @@
import markdown
import pynliner

from . import config
from . import __version__
try:
from . import config
from . import __version__
__name__ = 'muttdown'
except ValueError:
import config
__version__ = 'testing'

__name__ = 'muttdown'
def convert_markdown(text,config):
return markdown.markdown(text,config.markdown_extensions,output_format="html5")

def convert_preformat(text,config):
import cgi
return '<pre>\n' + cgi.escape(text) + '\n</pre>\n'

def convert_one(part, config):
try:
text = part.get_payload(None, True)
if not text.startswith('!m'):

if text.startswith('!m'):
converter = convert_markdown
elif text.startswith('!p'):
converter = convert_preformat
else:
return None
text = re.sub('\s*!m\s*', '', text, re.M)
text = re.sub('\s*![pm]\s*', '', text, count=1, flags=re.M)
if config.remove_sigil:
part.set_payload(text)
if config.utf8:
text = unicode(text,'utf8')
if '\n-- \n' in text:
pre_signature, signature = text.split('\n-- \n')
md = markdown.markdown(pre_signature, output_format="html5")
md = converter(pre_signature,config)
md += '\n<div class="signature" style="font-size: small"><p>-- <br />'
md += '<br />'.join(signature.split('\n'))
md += '</p></div>'
else:
md = markdown.markdown(text)
md = converter(text,config)
if config.css:
md = '<style>' + config.css + '</style>' + md
md = '<style>\n' + config.css + '</style>\n' + '<body>\n' + md + '\n</body>\n'
md = pynliner.fromString(md)
message = MIMEText(md, 'html')
if config.utf8:
message = MIMEText(md.encode('utf-8'), 'html', _charset='utf-8')
else:
message = MIMEText(md, 'html')
return message
except Exception:
except Exception as e:
sys.stderr.write('muttdown: '+str(e))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

newline here perhaps

return None


Expand Down Expand Up @@ -116,7 +139,7 @@ def main():
parser = argparse.ArgumentParser(version='%s %s' % (__name__, __version__))
parser.add_argument(
'-c', '--config_file', default=os.path.expanduser('~/.muttdown.yaml'),
type=argparse.FileType('r'), required=True,
type=argparse.FileType('r'), required=False,
help='Path to YAML config file (default %(default)s)'
)
parser.add_argument(
Expand Down Expand Up @@ -153,6 +176,8 @@ def main():

proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, shell=False)
proc.communicate(rebuilt.as_string())
proc.wait()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

communicate waits for the process to end implicitly

sys.exit(proc.returncode)

else:
conn = smtp_connection(c)
Expand Down