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
|