]> git.phdru.name Git - mimedecode.git/commitdiff
Add options --save-headers/body/message
authorOleg Broytman <phd@phdru.name>
Tue, 11 Mar 2014 16:39:11 +0000 (20:39 +0400)
committerOleg Broytman <phd@phdru.name>
Tue, 11 Mar 2014 16:49:24 +0000 (20:49 +0400)
ANNOUNCE
TODO
mimedecode.docbook
mimedecode.py

index c9bb92f49437e33752c7a2f7f26df688a8ece254..201eccd67b0d0b5e5bcb15b9b2a96f0e567c340f 100644 (file)
--- a/ANNOUNCE
+++ b/ANNOUNCE
@@ -33,46 +33,14 @@ level).
 
    Add option -B to skip content-transfer-decoding binary attachments.
 
-   Add option -O to set the destination directory.
+   Add options --save-headers, --save-body and --save-message to save decoded
+headers/bodies/messages to files.
+
+   Add option -O to set the destination directory for output files.
 
    Fix a minor bug: if a multipart message (or a subpart) lacks any textual
 content - avoid putting an excessive newline.
 
-WHAT'S NEW in version 2.4.0 (2014-03-08)
-
-   Change option -d to accept a comma-separated list of headers:
--d h1,h2,h3,...
-
-   Change option -d to decode all headers and accept a list of exceptions:
--d *,-h1,-h2,...
-
-   Change option -p to accept lists of headers and parameters:
--p h1,h2,h3,..:p1,p2,p3,..
-
-   Allow * and exceptions for -p in the headers and parameters lists:
--p *,-h1,-h2,-h3:p1,p2,p3
--p h1,h2,h3:*,-p1,-p2,-p3
--p *,-h1,-h2,-h3:*,-p1,-p2,-p3
-
-   Change option -r to accept a list of headers: -r h1,h2,h3,...
-
-   Change option -r to remove all headers and accept a list of exceptions:
--r *,-h1,-h2,...
-
-   Change option -R to accept lists of headers and parameters:
--R h1,h2,h3:p1,p2,p3
--R h1,h2,h3:*,-p1,-p2,-p3
--R *,-h1,-h2,-h3:p1,p2,p3
--R *,-h1,-h2,-h3:*,-p1,-p2,-p3
-
-   Publish docs in html format.
-
-   Add ChangeLog.
-
-WHAT'S NEW in version 2.3.7 (2014-02-23)
-
-   Add option -r to remove headers and option -R to remove headers parameters.
-
 
 WHERE TO GET
    Home page: http://phdru.name/Software/Python/#mimedecode
diff --git a/TODO b/TODO
index ec2be24f725bf0566e7db800ffb1b7437b8688cf..c750676196f78bba33aa955ecefc1c58694c13e7 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,7 +1,3 @@
-Add options --save-headers, --save-body and --save-message to save
-decoded headers/bodies/messages to files.
-
-
 Release 2.5.0.
 
 
index 0a690213af43010f8db11d01742d939ebd922938..897f0bc43dee32d3279e4275f3b2e8b949e2a60e 100644 (file)
@@ -92,6 +92,9 @@
       <arg choice="opt">
          <option>-Bbeit mask</option>
       </arg>
+      <arg choice="opt">
+         <option>--save-headers|body|message mask</option>
+      </arg>
       <arg choice="opt">
          <option>-O dest_dir</option>
       </arg>
       </listitem>
    </varlistentry>
 
+   <varlistentry>
+      <term>--save-headers mask</term>
+   </varlistentry>
+
+   <varlistentry>
+      <term>--save-body mask</term>
+   </varlistentry>
+
+   <varlistentry>
+      <term>--save-message mask</term>
+      <listitem>
+         <para>
+            Append mask to a list of content types to save to a file;
+            --save-headers saves only decoded headers of the message (or
+            subpart); --save-body saves only decoded body; --save-message saves
+            the entire message (or subpart).
+         </para>
+      </listitem>
+   </varlistentry>
+
    <varlistentry>
       <term>-O dest_dir</term>
       <listitem>
@@ -494,6 +517,19 @@ cat input_file | mimedecode.py -o output_file</programlisting>
    Initially all 4 lists are empty, so without any additional parameters
 the program always uses the default decoding.
 </para>
+
+<para>
+  The 3 save list options (--save-headers/body/message) are similar. They make
+  the program to save every non-multipart subpart (only headers, or body, or
+  the entire subpart) that corresponds to the given mask to a file. Before
+  saving the message (or the subpart) is decoded according to all other options
+  and placed to the output stream as usual. Filename for the file is created
+  using "filename" parameter from the Content-Disposition header, or "name"
+  parameter from the Content-Type header if one of those exist; a serial
+  counter is prepended to the filename to avoid collisions; if there are no
+  name/filename parameters, the filename is just the serial counter. The file
+  is saved in the directory set with -O (default is the current directory).
+</para>
 </refsect1>
 
 
index e832017b33670d0cd605c1650eb00c89b21d91a8..b461284e1e60270faa2f613ea3b4a707f991e321 100755 (executable)
@@ -18,7 +18,7 @@ Broytman mimedecode.py version %s, %s
 def usage(code=0, errormsg=''):
     version(0)
     sys.stdout.write("""\
-        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] [-O dest_dir] [-o output_file] [input_file [output_file]]
+        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')
@@ -266,6 +266,33 @@ def totext(msg, instring):
 
     output_headers(msg)
     output(s)
+    return s
+
+
+def _save_message(msg, outstring, save_headers=False, save_body=False):
+    for header, param in (
+        ("Content-Disposition", "filename"),
+        ("Content-Type", "name"),
+    ):
+        fname = msg.get_param(param, header=header)
+        if fname:
+            fname = '-' + fname
+            break
+    else:
+        fname = ''
+    g.save_counter += 1
+    fname = str(g.save_counter) + fname
+
+    global output
+    save_output = output
+    outfile = open(os.path.join(g.destination_dir, fname), 'w')
+    output = outfile.write
+    if save_headers:
+        output_headers(msg)
+    if save_body:
+        output(outstring)
+    outfile.close()
+    output = save_output
 
 
 def decode_part(msg):
@@ -299,23 +326,30 @@ def decode_part(msg):
 
     for content_type in masks:
         if content_type in g.totext_mask:
-            totext(msg, outstring)
-            return
+            outstring = totext(msg, outstring)
+            break
         elif content_type in g.binary_mask or \
              content_type in g.decoded_binary_mask:
             output_headers(msg)
             output(outstring)
-            return
+            break
         elif content_type in g.ignore_mask:
             output_headers(msg)
             output("\nMessage body of type `%s' skipped.\n" % content_type)
-            return
+            break
         elif content_type in g.error_mask:
             raise ValueError, "content type `%s' prohibited" % content_type
+    else:
+        # Neither content type nor masks were listed - decode by default
+        outstring = totext(msg, outstring)
 
-    # Neither content type nor masks were listed - decode by default
-    totext(msg, outstring)
-
+    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:
+            _save_message(msg, outstring, save_headers=False, save_body=True)
+        elif content_type in g.save_message_mask:
+            _save_message(msg, outstring, save_headers=True, save_body=True)
 
 def decode_multipart(msg):
     "Decode multipart"
@@ -392,6 +426,11 @@ class GlobalOptions:
     ignore_mask = [] # Ignore (skip, do not decode and do not include into output)
     error_mask = []  # Raise error if encounter one of these
 
+    save_counter = 0
+    save_headers_mask = []
+    save_body_mask = []
+    save_message_mask = []
+
     input_filename = None
     output_filename = None
     destination_dir = os.curdir
@@ -405,7 +444,9 @@ def get_opts():
     try:
         options, arguments = getopt(sys.argv[1:],
             'hVcCDPH:f:d:p:r:R:b:B:e:i:t:O:o:',
-            ['help', 'version', 'host=', 'set-header=', 'set-param='])
+            ['help', 'version', 'host=',
+             'save-headers=', 'save-body=', 'save-message=',
+             'set-header=', 'set-param='])
     except GetoptError:
         usage(1)
 
@@ -455,6 +496,12 @@ def get_opts():
             g.ignore_mask.append(value)
         elif option == '-e':
             g.error_mask.append(value)
+        elif option == '--save-headers':
+            g.save_headers_mask.append(value)
+        elif option == '--save-body':
+            g.save_body_mask.append(value)
+        elif option == '--save-message':
+            g.save_message_mask.append(value)
         elif option == '-O':
             g.destination_dir = value
         elif option == '-o':