# Copyright (C) 2011-2012 Patrick Totzke # Copyright (C) 2021 Anton Khirnov # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import mailcap from tempfile import NamedTemporaryFile from urwid.util import detected_encoding from ..settings.const import settings def _parse_nametemplate(template): """this returns a prefix and suffix to be used in the tempfile module for a given mailcap nametemplate string""" nt_list = template.split('%s') template_prefix = '' template_suffix = '' if len(nt_list) == 2: template_suffix = nt_list[1] template_prefix = nt_list[0] else: template_suffix = template return (template_prefix, template_suffix) class MailcapHandler: """ A handler for externally processing a MIME part with given payload and content-type ctype. Must be used as a context manager, since it may create a temporary file that needs to be deleted. If the handler requires a file, then this function will create a temporary file and write the payload into it. Otherwise, the payload needs to be provided on stdin. """ _entry = None _payload = None _ctype = None _params = None _fname = None _need_tmpfile = None _tmpfile = None def __init__(self, payload, ctype, params, filename, field_key): # find the mime handler _, self._entry = settings.mailcap_find_match(ctype, key = field_key) if self._entry is None: return if isinstance(payload, str): payload = payload.encode(detected_encoding, errors = 'backslashreplace') self._payload = payload self._payload = payload self._ctype = ctype self._params = tuple('='.join(p) for p in params) self._fname = filename # in case the mailcap defined command contains no '%s', # we pipe the files content to the handling command via stdin self._need_tmpfile = '%s' in self._entry['view'] def __bool__(self): return self._entry is not None def __enter__(self): """ The context manager manages the temporary file, if one is needed. """ if not self._need_tmpfile: return nametemplate = self._entry.get('nametemplate', '%s') prefix, suffix = _parse_nametemplate(nametemplate) fn_hook = settings.get_hook('sanitize_attachment_filename') if fn_hook: prefix, suffix = fn_hook(self._fname, prefix, suffix) tmpfile = NamedTemporaryFile(prefix = prefix, suffix = suffix) try: tmpfile.write(self._payload) tmpfile.flush() self._tmpfile = tmpfile except: tmpfile.close() raise def __exit__(self, exc_type, exc_value, tb): if self._tmpfile: self._tmpfile.close() self._tmpfile = None @property def cmd(self): """ Shell command to run (str) """ fname = self._tmpfile.name if self._need_tmpfile else None return mailcap.subst(self._entry['view'], self._ctype, plist = self._params, filename = fname) @property def stdin(self): if self._need_tmpfile: return None return self._payload @property def needs_terminal(self): return self._entry.get('needsterminal') is not None @property def copious_output(self): return self._entry.get('copiousoutput') is not None