from flask import current_app, request, url_for from flask_babel import gettext from markupsafe import Markup, escape def render_text_plain(_subtype, content, _path): return f'
{escape(content)}
\n' def render_text_html(_subtype, _content, path): if path: url = url_for( "read", msgid=request.view_args["msgid"], format="raw", path=".".join(map(str, path)), ) else: url = url_for("read", msgid=request.view_args["msgid"], format="raw") return f'\n' def render_image(subtype, content, _path): return f'\n' def render_multipart_alternative(_subtype, content, path): parts = content["parts"] T = "
" script_url = url_for("static", filename="src/rendermail.js") return f'
\n{T}\n{C}\n
\n' def render_multipart(_subtype, content, path): parts = content["parts"] R = '
\n' for i, p in enumerate(parts): R += "
\n" if ( not p["head"]["content_disposition"] or p["head"]["content_disposition"].lower() == "none" or p["head"]["content_disposition"].lower() == "inline" ): R += mime_render(*to_mime_types(p["head"]), p["body"], path + (i,)) elif p["head"]["content_disposition"].lower() == "attachment": link_text = gettext("Attachment {filename} of type {filetype}").format( filename=p["head"]["filename"], filetype=to_mime_type(p["head"]) ) ref_url = url_for( "read", msgid=request.view_args["msgid"], path=".".join(map(str, [*path, i])), format="raw", ) R += "

" R += f'\n' R += f"{escape(link_text)}\n" R += "

\n" else: current_app.log.warning( "unknown Content-Disposition %s", p["head"]["content_disposition"] ) R += f"

unknown Content-Disposition {p['head']['content_disposition']}

\n" R += "
\n" return R + "
\n" def _format_header(category, value): R = "" if isinstance(value, list) and value: R += f"
{escape(category)}
\n" for v in value: value = ( f'"{v["display_name"]}" <{v["address"]}>' if v["display_name"] else v["address"] ) R += f"
{escape(value)}
\n" return R def render_message(subtype, msg, path): if subtype != "rfc822": current_app.log.warning("unknown message mime-subtype %s", subtype) R = '
' R += '
' R += f"
{escape(gettext('Subject'))}
" R += f"
{escape(msg['head']['subject'])}
\n" R += _format_header(gettext("From"), msg["head"].get("from")) R += _format_header(gettext("To"), msg["head"].get("to")) R += _format_header(gettext("CC"), msg["head"].get("cc")) R += _format_header(gettext("BCC"), msg["head"].get("bcc")) R += f"
{escape(gettext('Date'))}
" R += f"
{escape(msg['head']['date'])}
\n" R += f"
{escape(gettext('Content-Type'))}
" R += f"
{to_mime_type(msg['head']['mime'])}
\n" R += "
\n" R += mime_render(*to_mime_types(msg["head"]["mime"]), msg["body"], path + (0,)) return R + "
\n" MIMERenderSubs = { ("text", "plain"): render_text_plain, ("text", "html"): render_text_html, ("multipart", "alternative"): render_multipart_alternative, "multipart": render_multipart, "message": render_message, "image": render_image, } def mime_render(maintype, subtype, content, path): renderer = MIMERenderSubs.get((maintype, subtype)) or MIMERenderSubs.get(maintype) if not renderer: typ = f"{maintype}/{subtype}" msg = gettext("Unsupported MIME type of {mime_type}.").format(mime_type=typ) return f'

{msg}

\n' return renderer(subtype, content, path) def to_mime_type(mime): return escape(f"{mime['content_maintype']}/{mime['content_subtype']}".lower()) def to_mime_types(mime): return escape(mime["content_maintype"]), escape(mime["content_subtype"]) def format_mail(mail): return Markup(mime_render("message", "rfc822", mail, tuple()))