Author: bodewig
Date: Tue Feb 16 13:59:03 2010
New Revision: 910519

URL: http://svn.apache.org/viewvc?rev=910519&view=rev
Log:
Allow extra fields to violate the recommended structure.  COMPRESS-73

Modified:
    commons/proper/compress/trunk/src/changes/changes.xml
    
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
    commons/proper/compress/trunk/src/site/xdoc/zip.xml

Modified: commons/proper/compress/trunk/src/changes/changes.xml
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/changes/changes.xml?rev=910519&r1=910518&r2=910519&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/changes/changes.xml (original)
+++ commons/proper/compress/trunk/src/changes/changes.xml Tue Feb 16 13:59:03 
2010
@@ -23,6 +23,10 @@
   </properties>
   <body>
     <release version="1.1" date="as in SVN" description="Release 1.1">
+      <action type="fix" date="2010-02-16" issue="COMPRESS-73">
+        ZipArchiveEntry, ZipFile and ZipArchiveInputStream are now
+        more lenient when parsing extra fields.
+      </action>
       <action type="fix" date="2010-02-12">
         Improved exception message if the extra field data in ZIP
         archives cannot be parsed.

Modified: 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java?rev=910519&r1=910518&r2=910519&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
 (original)
+++ 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
 Tue Feb 16 13:59:03 2010
@@ -18,9 +18,11 @@
 package org.apache.commons.compress.archivers.zip;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.zip.ZipException;
 import org.apache.commons.compress.archivers.ArchiveEntry;
 
@@ -28,6 +30,22 @@
  * Extension that adds better handling of extra fields and provides
  * access to the internal and external file attributes.
  *
+ * <p>The extra data is expected to follow the recommendation of
+ * {...@link http://www.pkware.com/documents/casestudies/APPNOTE.TXT
+ * APPNOTE.txt}:</p>
+ * <ul>
+ *   <li>the extra byte array consists of a sequence of extra fields</li>
+ *   <li>each extra fields starts by a two byte header id followed by
+ *   a two byte sequence holding the length of the remainder of
+ *   data.</li>
+ * </ul>
+ *
+ * <p>Any extra data that cannot be parsed by the rules above will be
+ * consumed as "unparseable" extra data and treated differently by the
+ * methods of this class.  Versions prior to Apache Commons Compress
+ * 1.1 would have thrown an exception if any attempt was made to read
+ * or write extra data not conforming to the recommendation.</p>
+ *
  * @NotThreadSafe
  */
 public class ZipArchiveEntry extends java.util.zip.ZipEntry
@@ -54,6 +72,7 @@
     private int platform = PLATFORM_FAT;
     private long externalAttributes = 0;
     private LinkedHashMap/*<ZipShort, ZipExtraField>*/ extraFields = null;
+    private UnparseableExtraFieldData unparseableExtra = null;
     private String name = null;
 
     /**
@@ -75,7 +94,9 @@
         setName(entry.getName());
         byte[] extra = entry.getExtra();
         if (extra != null) {
-            setExtraFields(ExtraFieldUtils.parse(extra));
+            setExtraFields(ExtraFieldUtils.parse(extra, true,
+                                                 ExtraFieldUtils
+                                                 .UnparseableExtraField.READ));
         } else {
             // initializes extra data to an empty byte array
             setExtra();
@@ -92,7 +113,7 @@
         this((java.util.zip.ZipEntry) entry);
         setInternalAttributes(entry.getInternalAttributes());
         setExternalAttributes(entry.getExternalAttributes());
-        setExtraFields(entry.getExtraFields());
+        setExtraFields(entry.getExtraFields(true));
     }
 
     /**
@@ -118,10 +139,9 @@
     public Object clone() {
         ZipArchiveEntry e = (ZipArchiveEntry) super.clone();
 
-        e.extraFields = extraFields != null ? (LinkedHashMap) 
extraFields.clone() : null;
         e.setInternalAttributes(getInternalAttributes());
         e.setExternalAttributes(getExternalAttributes());
-        e.setExtraFields(getExtraFields());
+        e.setExtraFields(getExtraFields(true));
         return e;
     }
 
@@ -246,25 +266,45 @@
     public void setExtraFields(ZipExtraField[] fields) {
         extraFields = new LinkedHashMap();
         for (int i = 0; i < fields.length; i++) {
+            if (fields[i] instanceof UnparseableExtraFieldData) {
+                unparseableExtra = (UnparseableExtraFieldData) fields[i];
+            } else {
             extraFields.put(fields[i].getHeaderId(), fields[i]);
+            }
         }
         setExtra();
     }
 
     /**
-     * Retrieves extra fields.
+     * Retrieves all extra fields that have been parsed successfully.
      * @return an array of the extra fields
      */
     public ZipExtraField[] getExtraFields() {
+        return getExtraFields(false);
+    }
+
+    /**
+     * Retrieves extra fields.
+     * @param includeUnparseable whether to also return unparseable
+     * extra fields as {...@link UnparseableExtraFieldData} if such data
+     * exists.
+     * @return an array of the extra fields
+     */
+    public ZipExtraField[] getExtraFields(boolean includeUnparseable) {
         if (extraFields == null) {
-            return new ZipExtraField[0];
+            return !includeUnparseable || unparseableExtra == null
+                ? new ZipExtraField[0]
+                : new ZipExtraField[] { unparseableExtra };
         }
-        ZipExtraField[] result = new ZipExtraField[extraFields.size()];
-        return (ZipExtraField[]) extraFields.values().toArray(result);
+        List result = new ArrayList(extraFields.values());
+        if (includeUnparseable && unparseableExtra != null) {
+            result.add(unparseableExtra);
+        }
+        return (ZipExtraField[]) result.toArray(new ZipExtraField[0]);
     }
 
     /**
-     * Adds an extra fields - replacing an already present extra field
+     * Adds an extra field - replacing an already present extra field
      * of the same type.
      *
      * <p>If no extra field of the same type exists, the field will be
@@ -272,21 +312,28 @@
      * @param ze an extra field
      */
     public void addExtraField(ZipExtraField ze) {
+        if (ze instanceof UnparseableExtraFieldData) {
+            unparseableExtra = (UnparseableExtraFieldData) ze;
+        } else {
         if (extraFields == null) {
             extraFields = new LinkedHashMap();
         }
         extraFields.put(ze.getHeaderId(), ze);
+        }
         setExtra();
     }
 
     /**
-     * Adds an extra fields - replacing an already present extra field
+     * Adds an extra field - replacing an already present extra field
      * of the same type.
      *
      * <p>The new extra field will be the first one.</p>
      * @param ze an extra field
      */
     public void addAsFirstExtraField(ZipExtraField ze) {
+        if (ze instanceof UnparseableExtraFieldData) {
+            unparseableExtra = (UnparseableExtraFieldData) ze;
+        } else {
         LinkedHashMap copy = extraFields;
         extraFields = new LinkedHashMap();
         extraFields.put(ze.getHeaderId(), ze);
@@ -294,11 +341,12 @@
             copy.remove(ze.getHeaderId());
             extraFields.putAll(copy);
         }
+        }
         setExtra();
     }
 
     /**
-     * Remove an extra fields.
+     * Remove an extra field.
      * @param type the type of extra field to remove
      */
     public void removeExtraField(ZipShort type) {
@@ -312,6 +360,17 @@
     }
 
     /**
+     * Removes unparseable extra field data.
+     */
+    public void removeUnparseableExtraFieldData() {
+        if (unparseableExtra == null) {
+            throw new java.util.NoSuchElementException();
+        }
+        unparseableExtra = null;
+        setExtra();
+    }
+
+    /**
      * Looks up an extra field by its header id.
      *
      * @return null if no such field exists.
@@ -324,16 +383,30 @@
     }
 
     /**
-     * Throws an Exception if extra data cannot be parsed into extra fields.
+     * Looks up extra field data that couldn't be parsed correctly.
+     *
+     * @return null if no such field exists.
+     */
+    public UnparseableExtraFieldData getUnparseableExtraFieldData() {
+        return unparseableExtra;
+    }
+
+    /**
+     * Parses the given bytes as extra field data and consumes any
+     * unparseable data as an {...@link UnparseableExtraFieldData}
+     * instance.
      * @param extra an array of bytes to be parsed into extra fields
      * @throws RuntimeException if the bytes cannot be parsed
      * @throws RuntimeException on error
      */
     public void setExtra(byte[] extra) throws RuntimeException {
         try {
-            ZipExtraField[] local = ExtraFieldUtils.parse(extra, true);
+            ZipExtraField[] local =
+                ExtraFieldUtils.parse(extra, true,
+                                      
ExtraFieldUtils.UnparseableExtraField.READ);
             mergeExtraFields(local, true);
         } catch (ZipException e) {
+            // actually this is not be possible as of Commons Compress 1.1
             throw new RuntimeException("Error parsing extra fields for entry: "
                                        + getName() + " - " + e.getMessage(), 
e);
         }
@@ -346,7 +419,7 @@
      * modify super's data directly.
      */
     protected void setExtra() {
-        
super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields()));
+        
super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields(true)));
     }
 
     /**
@@ -354,7 +427,9 @@
      */
     public void setCentralDirectoryExtra(byte[] b) {
         try {
-            ZipExtraField[] central = ExtraFieldUtils.parse(b, false);
+            ZipExtraField[] central =
+                ExtraFieldUtils.parse(b, false,
+                                      
ExtraFieldUtils.UnparseableExtraField.READ);
             mergeExtraFields(central, false);
         } catch (ZipException e) {
             throw new RuntimeException(e.getMessage(), e);
@@ -375,7 +450,7 @@
      * @return the central directory extra data
      */
     public byte[] getCentralDirectoryExtra() {
-        return ExtraFieldUtils.mergeCentralDirectoryData(getExtraFields());
+        return ExtraFieldUtils.mergeCentralDirectoryData(getExtraFields(true));
     }
 
     /**
@@ -429,7 +504,12 @@
             setExtraFields(f);
         } else {
             for (int i = 0; i < f.length; i++) {
-                ZipExtraField existing = getExtraField(f[i].getHeaderId());
+                ZipExtraField existing;
+                if (f[i] instanceof UnparseableExtraFieldData) {
+                    existing = unparseableExtra;
+                } else {
+                    existing = getExtraField(f[i].getHeaderId());
+                }
                 if (existing == null) {
                     addExtraField(f[i]);
                 } else {

Modified: commons/proper/compress/trunk/src/site/xdoc/zip.xml
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/site/xdoc/zip.xml?rev=910519&r1=910518&r2=910519&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/site/xdoc/zip.xml (original)
+++ commons/proper/compress/trunk/src/site/xdoc/zip.xml Tue Feb 16 13:59:03 2010
@@ -99,6 +99,13 @@
           the package, any other extra field will be stored
           as <code>UnrecognizedExtraField</code>.</p>
 
+        <p>Prior to version 1.1 of this library trying to read an
+          archive with extra fields that didn't follow the recommended
+          structure for those fields would cause Compress to throw an
+          exception.  Starting with version 1.1 these extra fields
+          will now be read
+          as <code>UnparseableExtraFieldData</code>.</p>
+
       </subsection>
 
       <subsection name="Encoding" id="encoding">


Reply via email to