From 5cb88d559ae462dad80d346d44f12dcc9eb3ec10 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Tue, 26 Jan 2021 10:26:27 +0100 Subject: Rewrite mailcap handling. Add a class that encapsulates the handler and is responsible for managing the temporary file, if one is needed. Use this class for both rendering inline content and displaying attachments externally. External attachments are now wrapped in an asyncio task that is added to a pool of tasks managed by ui. --- alot/db/message.py | 67 +++++++++++++++++++----------------------------------- 1 file changed, 23 insertions(+), 44 deletions(-) (limited to 'alot/db') diff --git a/alot/db/message.py b/alot/db/message.py index ff8cca70..e10e29a5 100644 --- a/alot/db/message.py +++ b/alot/db/message.py @@ -2,77 +2,54 @@ # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file -from contextlib import ExitStack import email import email.charset as charset import email.policy import logging -import mailcap import os -import tempfile +import subprocess from datetime import datetime +from urwid.util import detected_encoding + from .attachment import Attachment from .. import crypto from .. import helper from ..errors import GPGProblem -from ..helper import parse_mailcap_nametemplate -from ..helper import split_commandstring from ..settings.const import settings +from ..utils.mailcap import MailcapHandler charset.add_charset('utf-8', charset.QP, charset.QP, 'utf-8') _APP_PGP_SIG = 'application/pgp-signature' _APP_PGP_ENC = 'application/pgp-encrypted' -def _render_part_external(raw_payload, ctype, params, field_key='copiousoutput'): +def _render_part_external(payload, ctype, params, filename): """ renders a non-multipart email part into displayable plaintext by piping its payload through an external script. The handler itself is determined by the mailcap entry for this part's ctype. """ - rendered_payload = None - # get mime handler - _, entry = settings.mailcap_find_match(ctype, key=field_key) - if entry is None: - return None - - if isinstance(raw_payload, str): - raw_payload = raw_payload.encode('utf-8') - - with ExitStack() as stack: - # read parameter, create handler command - parms = tuple('='.join(p) for p in params) - - # in case the mailcap defined command contains no '%s', - # we pipe the files content to the handling command via stdin - if '%s' in entry['view']: - # open tempfile, respect mailcaps nametemplate - nametemplate = entry.get('nametemplate', '%s') - prefix, suffix = parse_mailcap_nametemplate(nametemplate) + h = MailcapHandler(payload, ctype, params, filename, 'copiousoutput') + if not h or h.needs_terminal: + return - tmpfile = stack.enter_context(tempfile.NamedTemporaryFile(prefix = prefix, suffix = suffix)) + def decode(buf): + return buf.decode(detected_encoding, errors = 'backslashreplace') - tmpfile.write(raw_payload) - tmpfile.flush() + with h: + logging.debug('Rendering part %s: %s', ctype, h.cmd) - tempfile_name = tmpfile.name - stdin = None - else: - tempfile_name = None - stdin = raw_payload - - # create and call external command - cmd = mailcap.subst(entry['view'], ctype, - filename = tempfile_name, plist = parms) - logging.debug('command: %s', cmd) - logging.debug('parms: %s', str(parms)) - cmdlist = split_commandstring(cmd) - # call handler - stdout, _, _ = helper.call_cmd(cmdlist, stdin=stdin) + try: + result = subprocess.run(h.cmd, shell = True, check = True, + capture_output = True, input = h.stdin) + except subprocess.CalledProcessError as e: + logging.error('Calling mailcap handler "%s" failed with code %d: %s', + h.cmd, e.returncode, decode(e.stderr)) + return None - return stdout + return decode(result.stdout) class _MessageHeaders: _msg = None @@ -170,7 +147,9 @@ class _MimeTree: content = self._part.get_content() # try procesing content with an external program - rendered = _render_part_external(content, self.content_type, self._part.get_params()) + rendered = _render_part_external(content, self.content_type, + self._part.get_params(), + 'copiousoutput') if rendered: return rendered -- cgit v1.2.3