]> git.phdru.name Git - mimedecode.git/blobdiff - mimedecode.py
Make option -i to work with multipart messages
[mimedecode.git] / mimedecode.py
index e63aedcc8664c82bdf89ae915d3ac59666e2c4a0..7d96a3d50a3eacb030265ae0846e7509843793d6 100755 (executable)
@@ -1,10 +1,9 @@
 #! /usr/bin/env python
 """Decode MIME message"""
 
-from mimedecode_version import __version__, __author__, __copyright__, __license__
-
 import sys, os
-import email
+from mimedecode_version import __version__, \
+    __author__, __copyright__, __license__
 
 me = os.path.basename(sys.argv[0])
 
@@ -21,17 +20,17 @@ def usage(code=0, errormsg=''):
         Usage: %s [-h|--help] [-V|--version] [-cCDP] [-H|--host=hostname] [-f charset] [-d header1[,h2,...]|*[,-h1,...]] [-p header1[,h2,h3,...]:param1[,p2,p3,...]] [-r header1[,h2,...]|*[,-h1,...]] [-R header1[,h2,h3,...]:param1[,p2,p3,...]] [--set-header header:value] [--set-param header:param=value] [-Bbeit mask] [--save-headers|body|message mask] [-O dest_dir] [-o output_file] [input_file [output_file]]
 """ % me)
     if errormsg:
-        sys.stderr.write(errormsg + '\n')
+        sys.stderr.write(errormsg + os.linesep)
     sys.exit(code)
 
 
 def output_headers(msg):
     unix_from = msg.get_unixfrom()
     if unix_from:
-        output(unix_from + '\n')
+        output(unix_from + os.linesep)
     for key, value in msg.items():
-        output("%s: %s\n" % (key, value))
-    output("\n") # End of headers
+        output("%s: %s%s" % (key, value, os.linesep))
+    output(os.linesep) # End of headers
 
 
 def recode_if_needed(s, charset):
@@ -74,9 +73,6 @@ def decode_header(msg, header):
             set_header(msg, header, new_value)
 
 
-def _decode_header_param(s):
-    return recode_if_needed(s[2], s[0])
-
 def decode_header_param(msg, header, param):
     "Decode mail header's parameter (if exists) and put it back, if it was encoded"
 
@@ -84,7 +80,7 @@ def decode_header_param(msg, header, param):
         value = msg.get_param(param, header=header)
         if value:
             if isinstance(value, tuple):
-                new_value = _decode_header_param(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
@@ -226,9 +222,9 @@ def decode_body(msg, s):
     if not command:
         return s
 
-    file = open(filename, 'w')
-    file.write(s)
-    file.close()
+    outfile = open(filename, 'wb')
+    outfile.write(s)
+    outfile.close()
 
     pipe = os.popen(command, 'r')
     s = pipe.read()
@@ -266,6 +262,18 @@ def totext(msg, instring):
     return s
 
 
+mimetypes = None
+
+def _guess_extension(ctype):
+    global mimetypes
+    if mimetypes is None:
+        import mimetypes
+        mimetypes.init()
+        user_mime_type = os.path.expanduser('~/.mime.types')
+        if os.path.exists(user_mime_type):
+            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"),
@@ -273,16 +281,27 @@ def _save_message(msg, outstring, save_headers=False, save_body=False):
     ):
         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
+            try:
+                    for forbidden in chr(0), '/', '\\':
+                        if forbidden in fname:
+                            raise ValueError
+            except ValueError:
+                continue
             fname = '-' + fname
             break
     else:
         fname = ''
     g.save_counter += 1
     fname = str(g.save_counter) + fname
+    if '.' not in fname:
+        ext = _guess_extension(msg.get_content_type())
+        if ext: fname += ext
 
     global output
     save_output = output
-    outfile = open(os.path.join(g.destination_dir, fname), 'w')
+    outfile = open_output_file(fname)
     output = outfile.write
     if save_headers:
         output_headers(msg)
@@ -302,8 +321,7 @@ def decode_part(msg):
     ctype = msg.get_content_type()
     if ctype:
         masks.append(ctype)
-    mtype = msg.get_content_maintype()
-    if mtype:
+        mtype = ctype.split('/')[0]
         masks.append(mtype + '/*')
     masks.append('*/*')
 
@@ -335,7 +353,7 @@ def decode_part(msg):
             break
         elif content_type in g.ignore_mask:
             output_headers(msg)
-            output("\nMessage body of type %s skipped.\n" % ctype)
+            output("%sMessage body of type %s skipped.%s" % (os.linesep, ctype, os.linesep))
             break
         elif content_type in g.error_mask:
             break
@@ -346,9 +364,9 @@ def decode_part(msg):
     for content_type in masks:
         if content_type in g.save_headers_mask:
             _save_message(msg, outstring, save_headers=True, save_body=False)
-        elif content_type in g.save_body_mask:
+        if content_type in g.save_body_mask:
             _save_message(msg, outstring, save_headers=False, save_body=True)
-        elif content_type in g.save_message_mask:
+        if content_type in g.save_message_mask:
             _save_message(msg, outstring, save_headers=True, save_body=True)
 
     for content_type in masks:
@@ -359,29 +377,45 @@ def decode_multipart(msg):
     "Decode multipart"
 
     decode_headers(msg)
+    boundary = msg.get_boundary()
+
+    masks = []
+    ctype = msg.get_content_type()
+    if ctype:
+        masks.append(ctype)
+        mtype = ctype.split('/')[0]
+        masks.append(mtype + '/*')
+    masks.append('*/*')
+
+    for content_type in masks:
+        if content_type in g.ignore_mask:
+            output_headers(msg)
+            output("%sMessage body of type %s skipped.%s" % (os.linesep, ctype, os.linesep))
+            if boundary:
+                output("%s--%s--%s" % (os.linesep, boundary, os.linesep))
+            return
+
     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 is not None:
-        output("\n")
+        output(os.linesep)
 
     first_subpart = True
-    boundary = msg.get_boundary()
-
     for subpart in msg.get_payload():
         if boundary:
             if first_subpart:
                 first_subpart = False
             else:
-                output("\n")
-            output("--%s\n" % boundary)
+                output(os.linesep)
+            output("--%s%s" % (boundary, os.linesep))
 
         # Recursively decode all parts of the subpart
         decode_message(subpart)
 
     if boundary:
-        output("\n--%s--\n" % boundary)
+        output("%s--%s--%s" % (os.linesep, boundary, os.linesep))
 
     if msg.epilogue:
         output(msg.epilogue)
@@ -398,6 +432,19 @@ def decode_message(msg):
         output(msg.as_string())
 
 
+def open_output_file(filename):
+    fullpath = os.path.abspath(os.path.join(g.destination_dir, filename))
+    full_dir = os.path.dirname(fullpath)
+    create = not os.path.isdir(full_dir)
+    if create:
+        os.makedirs(full_dir)
+    try:
+        return open(fullpath, 'wb')
+    except:
+        if create:
+            os.removedirs(full_dir)
+
+
 class GlobalOptions:
     from m_lib.defenc import default_encoding
     recode_charset = 1 # recode charset of message body
@@ -524,7 +571,7 @@ if __name__ == "__main__":
         g.input_filename = '-'
         infile = sys.stdin
         if g.output_filename:
-            outfile = open(os.path.join(g.destination_dir, g.output_filename), 'w')
+            outfile = open_output_file(g.output_filename)
         else:
             g.output_filename = '-'
             outfile = sys.stdout
@@ -537,7 +584,7 @@ if __name__ == "__main__":
             infile = open(arguments[0], 'r')
         if la == 1:
             if g.output_filename:
-                outfile = open(os.path.join(g.destination_dir, g.output_filename), 'w')
+                outfile = open_output_file(g.output_filename)
             else:
                 g.output_filename = '-'
                 outfile = sys.stdout
@@ -549,7 +596,7 @@ if __name__ == "__main__":
                 outfile = sys.stdout
             else:
                 g.output_filename = arguments[1]
-                outfile = open(os.path.join(g.destination_dir, g.output_filename), 'w')
+                outfile = open_output_file(g.output_filename)
     else:
         usage(1, 'Too many arguments')
 
@@ -565,10 +612,11 @@ if __name__ == "__main__":
     g.outfile = outfile
     output = outfile.write
 
+    import email
     msg = email.message_from_file(infile)
 
     for header, value in g.set_header_value:
-        msg[header] = value
+        set_header(msg, header, value)
 
     for header, param, value in g.set_header_param:
         if header in msg: