summaryrefslogtreecommitdiff
path: root/alot/utils/mailcap.py
blob: f95a0a71aeaac1783801b036ecaa9f57cc148905 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# Copyright (C) 2011-2012  Patrick Totzke <patricktotzke@gmail.com>
# Copyright (C) 2021  Anton Khirnov <anton@khirnov.net>
# 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