"""Decode MIME message"""
import sys, os
+import subprocess
from mimedecode_version import __version__, \
__author__, __copyright__, __license__
+if sys.version_info[0] >= 3:
+ # Replace email.message._formatparam with _formatparam from Python 2.7
+ # to avoid re-encoding non-ascii params.
+ import formatparam_27
me = os.path.basename(sys.argv[0])
""" % (__version__, __copyright__))
if exit: sys.exit(0)
+
def usage(code=0, errormsg=''):
version(0)
sys.stdout.write("""\
output(";")
output(_decode_header(value[1], strip=False))
output(os.linesep)
- output(os.linesep) # End of headers
+ output(os.linesep) # End of headers
def recode_if_needed(s, charset):
- if bytes is str: # Python2
+ if bytes is str: # Python2
if isinstance(s, bytes) and \
charset and charset.lower() != g.default_encoding:
s = s.decode(charset, "replace").\
encode(g.default_encoding, "replace")
- else: # Python3
+ else: # Python3
if isinstance(s, bytes):
s = s.decode(charset, "replace")
return s
# together into the final string.
return ' '.join(rtn)
+
def decode_header(msg, header):
"Decode mail header (if exists) and put it back, if it was encoded"
if header in msg:
value = msg[header]
new_value = _decode_header(value)
- if new_value != value: # do not bother to touch msg if not changed
+ if new_value != value: # do not bother to touch msg if not changed
set_header(msg, header, new_value)
new_value = recode_if_needed(value[2], value[0])
else:
new_value = _decode_header(value)
- if new_value != value: # do not bother to touch msg if not changed
+ if new_value != value: # do not bother to touch msg if not changed
msg.set_param(param, new_value, header)
def _get_exceptions(list):
- return [x[1:].lower() for x in list[1:] if x[0]=='-']
+ return [x[1:].lower() for x in list[1:] if x[0] == '-']
+
def _decode_headers_params(msg, header, decode_all_params, param_list):
if decode_all_params:
for param in param_list:
decode_header_param(msg, header, param)
+
def _remove_headers_params(msg, header, remove_all_params, param_list):
if remove_all_params:
params = msg.get_params(header=header)
msg.del_param(param, header)
else:
value = msg[header]
- if value is None: # No such header
+ if value is None: # No such header
return
- if ';' not in value: # There are no parameters
+ if ';' not in value: # There are no parameters
return
- del msg[header] # Delete all such headers
+ del msg[header] # Delete all such headers
# Get the value without parameters and set it back
msg[header] = value.split(';')[0].strip()
else:
for param in param_list:
msg.del_param(param, header)
+
def decode_headers(msg):
"Decode message headers according to global options"
for header_list in g.remove_headers:
header_list = header_list.split(',')
- if header_list[0] == '*': # Remove all headers except listed
+ if header_list[0] == '*': # Remove all headers except listed
header_list = _get_exceptions(header_list)
for header in msg.keys():
if header.lower() not in header_list:
del msg[header]
- else: # Remove listed headers
+ else: # Remove listed headers
for header in header_list:
del msg[header]
for header_list, param_list in g.remove_headers_params:
header_list = header_list.split(',')
param_list = param_list.split(',')
- remove_all_params = param_list[0] == '*' # Remove all params except listed
+ remove_all_params = param_list[0] == '*' # Remove all params except listed
if remove_all_params:
param_list = _get_exceptions(param_list)
- if header_list[0] == '*': # Remove for all headers except listed
+ if header_list[0] == '*': # Remove for all headers except listed
header_list = _get_exceptions(header_list)
for header in msg.keys():
if header.lower() not in header_list:
_remove_headers_params(msg, header, remove_all_params, param_list)
- else: # Decode for listed headers
+ else: # Decode for listed headers
for header in header_list:
_remove_headers_params(msg, header, remove_all_params, param_list)
for header_list in g.decode_headers:
header_list = header_list.split(',')
- if header_list[0] == '*': # Decode all headers except listed
+ if header_list[0] == '*': # Decode all headers except listed
header_list = _get_exceptions(header_list)
for header in msg.keys():
if header.lower() not in header_list:
decode_header(msg, header)
- else: # Decode listed headers
+ else: # Decode listed headers
for header in header_list:
decode_header(msg, header)
for header_list, param_list in g.decode_header_params:
header_list = header_list.split(',')
param_list = param_list.split(',')
- decode_all_params = param_list[0] == '*' # Decode all params except listed
+ decode_all_params = param_list[0] == '*' # Decode all params except listed
if decode_all_params:
param_list = _get_exceptions(param_list)
- if header_list[0] == '*': # Decode for all headers except listed
+ if header_list[0] == '*': # Decode for all headers except listed
header_list = _get_exceptions(header_list)
for header in msg.keys():
if header.lower() not in header_list:
_decode_headers_params(msg, header, decode_all_params, param_list)
- else: # Decode for listed headers
+ else: # Decode for listed headers
for header in header_list:
_decode_headers_params(msg, header, decode_all_params, param_list)
msg.set_param("charset", charset, "Content-Type")
-caps = None # Globally stored mailcap database; initialized only if needed
+caps = None # Globally stored mailcap database; initialized only if needed
+
def decode_body(msg, s):
"Decode body to plain text using first copiousoutput filter from mailcap"
return s
outfile = open(filename, 'wb')
- if charset and isinstance(s, bytes):
+ if charset and bytes is not str and isinstance(s, bytes): # Python3
s = s.decode(charset, "replace")
if not isinstance(s, bytes):
s = s.encode(g.default_encoding, "replace")
outfile.write(s)
outfile.close()
- pipe = os.popen(command, 'r')
- new_s = pipe.read()
- if pipe.close() is None: # result=0, Ok
+ pipe = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
+ new_s = pipe.stdout.read()
+ pipe.stdout.close()
+ if pipe.wait() == 0: # result=0, Ok
s = new_s
- os.remove(filename)
-
- set_content_type(msg, "text/plain")
- if s is new_s:
+ if bytes is not str and isinstance(s, bytes): # Python3
+ s = s.decode(g.default_encoding, "replace")
+ if charset and not isinstance(s, bytes):
+ s = s.encode(charset, "replace")
+ set_content_type(msg, "text/plain")
msg["X-MIME-Autoconverted"] = "from %s to text/plain by %s id %s" % (content_type, g.host_name, command.split()[0])
else:
msg["X-MIME-Autoconverted"] = "failed conversion from %s to text/plain by %s id %s" % (content_type, g.host_name, command.split()[0])
+ os.remove(filename)
return s
mimetypes = None
+
def _guess_extension(ctype):
global mimetypes
if mimetypes is None:
mimetypes._db.read(user_mime_type)
return mimetypes.guess_extension(ctype)
+
def _save_message(msg, outstring, save_headers=False, save_body=False):
for header, param in (
("Content-Disposition", "filename"),
fname = msg.get_param(param, header=header)
if fname:
if isinstance(fname, tuple):
- fname = fname[2] # Do not recode if it isn't recoded yet
+ fname = fname[2] # Do not recode if it isn't recoded yet
try:
for forbidden in chr(0), '/', '\\':
if forbidden in fname:
encoding = msg["Content-Transfer-Encoding"]
if left_binary or encoding in (None, '', '7bit', '8bit', 'binary'):
outstring = msg.get_payload()
- else: # Decode from transfer ecoding to text or binary form
+ else: # Decode from transfer ecoding to text or binary form
outstring = msg.get_payload(decode=1)
set_header(msg, "Content-Transfer-Encoding", "8bit")
msg["X-MIME-Autoconverted"] = "from %s to 8bit by %s id %s" % (encoding, g.host_name, me)
outstring = totext(msg, outstring)
break
elif content_type in g.binary_mask or \
- content_type in g.decoded_binary_mask:
+ content_type in g.decoded_binary_mask:
output_headers(msg)
output(outstring)
break
if content_type in g.error_mask:
raise ValueError("content type %s prohibited" % ctype)
+
def decode_multipart(msg):
"Decode multipart"
output_headers(msg)
- if msg.preamble: # Preserve the first part, it is probably not a RFC822-message
- output(msg.preamble) # Usually it is just a few lines of text (MIME warning)
+ if msg.preamble: # Preserve the first part, it is probably not a RFC822-message
+ output(msg.preamble) # Usually it is just a few lines of text (MIME warning)
if msg.preamble is not None:
output(os.linesep)
if msg.is_multipart():
decode_multipart(msg)
- elif len(msg): # Simple one-part message (there are headers) - decode it
+ elif len(msg): # Simple one-part message (there are headers) - decode it
decode_part(msg)
- else: # Not a message, just text - copy it literally
+ else: # Not a message, just text - copy it literally
output(msg.as_string())
class GlobalOptions:
from m_lib.defenc import default_encoding
- recode_charset = 1 # recode charset of message body
+ recode_charset = 1 # recode charset of message body
host_name = None
# A list of header/parameter/value triples to set
set_header_param = []
- totext_mask = [] # A list of content-types to decode
- binary_mask = [] # A list of content-types to pass through
- decoded_binary_mask = [] # A list of content-types to pass through (content-transfer-decoded)
- ignore_mask = [] # Ignore (do not decode and do not include into output) but output a warning instead of the body
- fully_ignore_mask = [] # Completely ignore - no headers, no body, no warning
+ totext_mask = [] # A list of content-types to decode
+ binary_mask = [] # A list of content-types to pass through
+ decoded_binary_mask = [] # A list of content-types to pass through (content-transfer-decoded)
+ ignore_mask = [] # Ignore (do not decode and do not include into output) but output a warning instead of the body
+ fully_ignore_mask = [] # Completely ignore - no headers, no body, no warning
error_mask = [] # Raise error if encounter one of these
save_counter = 0
from getopt import getopt, GetoptError
try:
- options, arguments = getopt(sys.argv[1:],
+ options, arguments = getopt(
+ sys.argv[1:],
'hVcCDPH:f:d:p:r:R:b:B:e:I:i:t:O:o:',
['help', 'version', 'host=',
'save-headers=', 'save-body=', 'save-message=',