Author: bodewig
Date: Tue Feb 16 14:41:11 2010
New Revision: 910537

URL: http://svn.apache.org/viewvc?rev=910537&view=rev
Log:
accept a wider range of existing zip archives by being more lenient when 
parsing extra fields

Added:
    ant/core/trunk/src/main/org/apache/tools/zip/UnparseableExtraFieldData.java 
  (contents, props changed)
      - copied, changed from r910483, 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnparseableExtraFieldData.java
    ant/core/trunk/src/main/org/apache/tools/zip/ZipUtil.java   (contents, 
props changed)
      - copied, changed from r910483, 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java
Removed:
    ant/core/trunk/src/tests/antunit/taskdefs/zip/Bugzilla-46559.zip
Modified:
    ant/core/trunk/WHATSNEW
    ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/Zip.java
    
ant/core/trunk/src/main/org/apache/tools/ant/types/resources/ZipResource.java
    ant/core/trunk/src/main/org/apache/tools/zip/ExtraFieldUtils.java   
(contents, props changed)
    ant/core/trunk/src/main/org/apache/tools/zip/UnrecognizedExtraField.java   
(contents, props changed)
    ant/core/trunk/src/main/org/apache/tools/zip/ZipEntry.java   (contents, 
props changed)
    ant/core/trunk/src/tests/antunit/taskdefs/unzip-test.xml
    
ant/core/trunk/src/tests/junit/org/apache/tools/zip/ExtraFieldUtilsTest.java   
(contents, props changed)

Modified: ant/core/trunk/WHATSNEW
URL: 
http://svn.apache.org/viewvc/ant/core/trunk/WHATSNEW?rev=910537&r1=910536&r2=910537&view=diff
==============================================================================
--- ant/core/trunk/WHATSNEW (original)
+++ ant/core/trunk/WHATSNEW Tue Feb 16 14:41:11 2010
@@ -31,6 +31,9 @@
  * Project provides new get methods that return copies instead of the
    live maps of task and type definitions, references and targets.
 
+ * Ant is now more lenient with ZIP extra fields and will be able to
+   read archives that it failed to read in earlier versions.
+
 Changes from Ant 1.8.0RC1 TO Ant 1.8.0
 ======================================
 

Modified: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/Zip.java
URL: 
http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/Zip.java?rev=910537&r1=910536&r2=910537&view=diff
==============================================================================
--- ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/Zip.java (original)
+++ ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/Zip.java Tue Feb 16 
14:41:11 2010
@@ -1026,7 +1026,7 @@
                 try {
                     is = zf.getInputStream(ze);
                     zipFile(is, zOut, prefix + name, ze.getTime(),
-                            fromArchive, mode, ze.getExtraFields());
+                            fromArchive, mode, ze.getExtraFields(true));
                 } finally {
                     doCompress = oldCompress;
                     FileUtils.close(is);

Modified: 
ant/core/trunk/src/main/org/apache/tools/ant/types/resources/ZipResource.java
URL: 
http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/types/resources/ZipResource.java?rev=910537&r1=910536&r2=910537&view=diff
==============================================================================
--- 
ant/core/trunk/src/main/org/apache/tools/ant/types/resources/ZipResource.java 
(original)
+++ 
ant/core/trunk/src/main/org/apache/tools/ant/types/resources/ZipResource.java 
Tue Feb 16 14:41:11 2010
@@ -219,7 +219,7 @@
         setDirectory(e.isDirectory());
         setSize(e.getSize());
         setMode(e.getUnixMode());
-        extras = e.getExtraFields();
+        extras = e.getExtraFields(true);
         method = e.getMethod();
     }
 

Modified: ant/core/trunk/src/main/org/apache/tools/zip/ExtraFieldUtils.java
URL: 
http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/zip/ExtraFieldUtils.java?rev=910537&r1=910536&r2=910537&view=diff
==============================================================================
--- ant/core/trunk/src/main/org/apache/tools/zip/ExtraFieldUtils.java (original)
+++ ant/core/trunk/src/main/org/apache/tools/zip/ExtraFieldUtils.java Tue Feb 
16 14:41:11 2010
@@ -92,18 +92,19 @@
 
     /**
      * Split the array into ExtraFields and populate them with the
-     * given data as local file data.
+     * given data as local file data, throwing an exception if the
+     * data cannot be parsed.
      * @param data an array of bytes as it appears in local file data
      * @return an array of ExtraFields
      * @throws ZipException on error
      */
     public static ZipExtraField[] parse(byte[] data) throws ZipException {
-        return parse(data, true);
+        return parse(data, true, UnparseableExtraField.THROW);
     }
 
     /**
      * Split the array into ExtraFields and populate them with the
-     * given data.
+     * given data, throwing an exception if the data cannot be parsed.
      * @param data an array of bytes
      * @param local whether data originates from the local file data
      * or the central directory
@@ -113,14 +114,60 @@
      */
     public static ZipExtraField[] parse(byte[] data, boolean local)
         throws ZipException {
+        return parse(data, local, UnparseableExtraField.THROW);
+    }
+
+    /**
+     * Split the array into ExtraFields and populate them with the
+     * given data.
+     * @param data an array of bytes
+     * @param local whether data originates from the local file data
+     * or the central directory
+     * @param onUnparseableData what to do if the extra field data
+     * cannot be parsed.
+     * @return an array of ExtraFields
+     * @throws ZipException on error
+     * @since Ant 1.8.1
+     */
+    public static ZipExtraField[] parse(byte[] data, boolean local,
+                                        UnparseableExtraField 
onUnparseableData)
+        throws ZipException {
         List v = new ArrayList();
         int start = 0;
+        LOOP:
         while (start <= data.length - WORD) {
             ZipShort headerId = new ZipShort(data, start);
             int length = (new ZipShort(data, start + 2)).getValue();
             if (start + WORD + length > data.length) {
-                throw new ZipException("data starting at " + start
-                    + " is in unknown format");
+                switch(onUnparseableData.getKey()) {
+                case UnparseableExtraField.THROW_KEY:
+                    throw new ZipException("bad extra field starting at "
+                                           + start + ".  Block length of "
+                                           + length + " bytes exceeds 
remaining"
+                                           + " data of "
+                                           + (data.length - start - WORD)
+                                           + " bytes.");
+                case UnparseableExtraField.READ_KEY:
+                    UnparseableExtraFieldData field =
+                        new UnparseableExtraFieldData();
+                    if (local) {
+                        field.parseFromLocalFileData(data, start,
+                                                     data.length - start);
+                    } else {
+                        field.parseFromCentralDirectoryData(data, start,
+                                                            data.length - 
start);
+                    }
+                    v.add(field);
+                    /*FALLTHROUGH*/
+                case UnparseableExtraField.SKIP_KEY:
+                    // since we cannot parse the data we must assume
+                    // the extra field consumes the whole rest of the
+                    // available data
+                    break LOOP;
+                default:
+                    throw new ZipException("unknown UnparseableExtraField key: 
"
+                                           + onUnparseableData.getKey());
+                }
             }
             try {
                 ZipExtraField ze = createExtraField(headerId);
@@ -152,13 +199,19 @@
      * @since 1.1
      */
     public static byte[] mergeLocalFileDataData(ZipExtraField[] data) {
-        int sum = WORD * data.length;
+        final boolean lastIsUnparseableHolder = data.length > 0
+            && data[data.length - 1] instanceof UnparseableExtraFieldData;
+        int regularExtraFieldCount =
+            lastIsUnparseableHolder ? data.length - 1 : data.length;
+
+        int sum = WORD * regularExtraFieldCount;
         for (int i = 0; i < data.length; i++) {
             sum += data[i].getLocalFileDataLength().getValue();
         }
+
         byte[] result = new byte[sum];
         int start = 0;
-        for (int i = 0; i < data.length; i++) {
+        for (int i = 0; i < regularExtraFieldCount; i++) {
             System.arraycopy(data[i].getHeaderId().getBytes(),
                              0, result, start, 2);
             System.arraycopy(data[i].getLocalFileDataLength().getBytes(),
@@ -167,6 +220,10 @@
             System.arraycopy(local, 0, result, start + WORD, local.length);
             start += (local.length + WORD);
         }
+        if (lastIsUnparseableHolder) {
+            byte[] local = data[data.length - 1].getLocalFileDataData();
+            System.arraycopy(local, 0, result, start, local.length);
+        }
         return result;
     }
 
@@ -177,13 +234,18 @@
      * @since 1.1
      */
     public static byte[] mergeCentralDirectoryData(ZipExtraField[] data) {
-        int sum = WORD * data.length;
+        final boolean lastIsUnparseableHolder = data.length > 0
+            && data[data.length - 1] instanceof UnparseableExtraFieldData;
+        int regularExtraFieldCount =
+            lastIsUnparseableHolder ? data.length - 1 : data.length;
+
+        int sum = WORD * regularExtraFieldCount;
         for (int i = 0; i < data.length; i++) {
             sum += data[i].getCentralDirectoryLength().getValue();
         }
         byte[] result = new byte[sum];
         int start = 0;
-        for (int i = 0; i < data.length; i++) {
+        for (int i = 0; i < regularExtraFieldCount; i++) {
             System.arraycopy(data[i].getHeaderId().getBytes(),
                              0, result, start, 2);
             System.arraycopy(data[i].getCentralDirectoryLength().getBytes(),
@@ -192,6 +254,60 @@
             System.arraycopy(local, 0, result, start + WORD, local.length);
             start += (local.length + WORD);
         }
+        if (lastIsUnparseableHolder) {
+            byte[] local = data[data.length - 1].getCentralDirectoryData();
+            System.arraycopy(local, 0, result, start, local.length);
+        }
         return result;
     }
+
+    /**
+     * "enum" for the possible actions to take if the extra field
+     * cannot be parsed.
+     */
+    public static final class UnparseableExtraField {
+        /**
+         * Key for "throw an exception" action.
+         */
+        public static final int THROW_KEY = 0;
+        /**
+         * Key for "skip" action.
+         */
+        public static final int SKIP_KEY = 1;
+        /**
+         * Key for "read" action.
+         */
+        public static final int READ_KEY = 2;
+
+        /**
+         * Throw an exception if field cannot be parsed.
+         */
+        public static final UnparseableExtraField THROW
+            = new UnparseableExtraField(THROW_KEY);
+
+        /**
+         * Skip the extra field entirely and don't make its data
+         * available - effectively removing the extra field data.
+         */
+        public static final UnparseableExtraField SKIP
+            = new UnparseableExtraField(SKIP_KEY);
+
+        /**
+         * Read the extra field data into an instance of {...@link
+         * UnparseableExtraFieldData UnparseableExtraFieldData}.
+         */
+        public static final UnparseableExtraField READ
+            = new UnparseableExtraField(READ_KEY);
+
+        private final int key;
+
+        private UnparseableExtraField(int k) {
+            key = k;
+        }
+
+        /**
+         * Key of the action to take.
+         */
+        public int getKey() { return key; }
+    }
 }

Propchange: ant/core/trunk/src/main/org/apache/tools/zip/ExtraFieldUtils.java
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Tue Feb 16 14:41:11 2010
@@ -1 +1,2 @@
+/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java:910483-910521
 
/commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java:745528,746933,748133,749524,749603,749855,749859

Copied: 
ant/core/trunk/src/main/org/apache/tools/zip/UnparseableExtraFieldData.java 
(from r910483, 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnparseableExtraFieldData.java)
URL: 
http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/zip/UnparseableExtraFieldData.java?p2=ant/core/trunk/src/main/org/apache/tools/zip/UnparseableExtraFieldData.java&p1=commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnparseableExtraFieldData.java&r1=910483&r2=910537&rev=910537&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnparseableExtraFieldData.java
 (original)
+++ ant/core/trunk/src/main/org/apache/tools/zip/UnparseableExtraFieldData.java 
Tue Feb 16 14:41:11 2010
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.commons.compress.archivers.zip;
+package org.apache.tools.zip;
 
 /**
  * Wrapper for extra field data that doesn't conform to the recommended format 
of header-tag + size + data.
@@ -25,9 +25,11 @@
  * {...@link http://www.pkware.com/documents/casestudies/APPNOTE.TXT
  * APPNOTE.TXT}.  Since it isn't used anywhere except to satisfy the
  * ZipExtraField contract it shouldn't matter anyway.</p>
- * @NotThreadSafe
+ * @since Ant 1.8.1
  */
-public final class UnparseableExtraFieldData implements ZipExtraField {
+public final class UnparseableExtraFieldData
+    implements CentralDirectoryParsingZipExtraField {
+
     private static final ZipShort HEADER_ID = new ZipShort(0xACC1);
 
     private byte[] localFileData;

Propchange: 
ant/core/trunk/src/main/org/apache/tools/zip/UnparseableExtraFieldData.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
ant/core/trunk/src/main/org/apache/tools/zip/UnparseableExtraFieldData.java
------------------------------------------------------------------------------
    svn:mergeinfo = 

Modified: 
ant/core/trunk/src/main/org/apache/tools/zip/UnrecognizedExtraField.java
URL: 
http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/zip/UnrecognizedExtraField.java?rev=910537&r1=910536&r2=910537&view=diff
==============================================================================
--- ant/core/trunk/src/main/org/apache/tools/zip/UnrecognizedExtraField.java 
(original)
+++ ant/core/trunk/src/main/org/apache/tools/zip/UnrecognizedExtraField.java 
Tue Feb 16 14:41:11 2010
@@ -66,7 +66,7 @@
      * @param data the field data to use
      */
     public void setLocalFileDataData(byte[] data) {
-        localData = copy(data);
+        localData = ZipUtil.copy(data);
     }
 
     /**
@@ -82,7 +82,7 @@
      * @return the local data
      */
     public byte[] getLocalFileDataData() {
-        return copy(localData);
+        return ZipUtil.copy(localData);
     }
 
     /**
@@ -98,7 +98,7 @@
      * @param data the data to use
      */
     public void setCentralDirectoryData(byte[] data) {
-        centralData = copy(data);
+        centralData = ZipUtil.copy(data);
     }
 
     /**
@@ -119,7 +119,7 @@
      */
     public byte[] getCentralDirectoryData() {
         if (centralData != null) {
-            return copy(centralData);
+            return ZipUtil.copy(centralData);
         }
         return getLocalFileDataData();
     }
@@ -151,12 +151,4 @@
         }
     }
 
-    private static byte[] copy(byte[] from) {
-        if (from != null) {
-            byte[] to = new byte[from.length];
-            System.arraycopy(from, 0, to, 0, to.length);
-            return to;
-        }
-        return null;
-    }
 }

Propchange: 
ant/core/trunk/src/main/org/apache/tools/zip/UnrecognizedExtraField.java
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Tue Feb 16 14:41:11 2010
@@ -1 +1,2 @@
+/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnrecognizedExtraField.java:910483-910521
 
/commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/UnrecognizedExtraField.java:746933,748133,749603,749855,749859

Modified: ant/core/trunk/src/main/org/apache/tools/zip/ZipEntry.java
URL: 
http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/zip/ZipEntry.java?rev=910537&r1=910536&r2=910537&view=diff
==============================================================================
--- ant/core/trunk/src/main/org/apache/tools/zip/ZipEntry.java (original)
+++ ant/core/trunk/src/main/org/apache/tools/zip/ZipEntry.java Tue Feb 16 
14:41:11 2010
@@ -18,13 +18,32 @@
 
 package org.apache.tools.zip;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.zip.ZipException;
 
 /**
  * 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>
+ *
  */
 public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
 
@@ -37,6 +56,7 @@
     private int platform = PLATFORM_FAT;
     private long externalAttributes = 0;
     private LinkedHashMap/*<ZipShort, ZipExtraField>*/ extraFields = null;
+    private UnparseableExtraFieldData unparseableExtra = null;
     private String name = null;
 
     /**
@@ -58,7 +78,9 @@
         super(entry);
         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();
@@ -75,7 +97,7 @@
         this((java.util.zip.ZipEntry) entry);
         setInternalAttributes(entry.getInternalAttributes());
         setExternalAttributes(entry.getExternalAttributes());
-        setExtraFields(entry.getExtraFields());
+        setExtraFields(entry.getExtraFields(true));
     }
 
     /**
@@ -93,10 +115,9 @@
     public Object clone() {
         ZipEntry e = (ZipEntry) 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;
     }
 
@@ -194,26 +215,46 @@
     public void setExtraFields(ZipExtraField[] fields) {
         extraFields = new LinkedHashMap();
         for (int i = 0; i < fields.length; i++) {
-            extraFields.put(fields[i].getHeaderId(), fields[i]);
+            if (fields[i] instanceof UnparseableExtraFieldData) {
+                unparseableExtra = (UnparseableExtraFieldData) fields[i];
+            } else {
+                extraFields.put(fields[i].getHeaderId(), fields[i]);
+            }
         }
         setExtra();
     }
 
     /**
+     * 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
      * @since 1.1
      */
-    public ZipExtraField[] getExtraFields() {
+    public ZipExtraField[] getExtraFields(boolean includeUnparseable) {
         if (extraFields == null) {
-            return new ZipExtraField[0];
+            return !includeUnparseable || unparseableExtra == null
+                ? new ZipExtraField[0]
+                : new ZipExtraField[] { unparseableExtra };
+        }
+        List result = new ArrayList(extraFields.values());
+        if (includeUnparseable && unparseableExtra != null) {
+            result.add(unparseableExtra);
         }
-        ZipExtraField[] result = new ZipExtraField[extraFields.size()];
-        return (ZipExtraField[]) extraFields.values().toArray(result);
+        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
@@ -222,15 +263,19 @@
      * @since 1.1
      */
     public void addExtraField(ZipExtraField ze) {
-        if (extraFields == null) {
-            extraFields = new LinkedHashMap();
+        if (ze instanceof UnparseableExtraFieldData) {
+            unparseableExtra = (UnparseableExtraFieldData) ze;
+        } else {
+            if (extraFields == null) {
+                extraFields = new LinkedHashMap();
+            }
+            extraFields.put(ze.getHeaderId(), ze);
         }
-        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>
@@ -238,18 +283,22 @@
      * @since 1.1
      */
     public void addAsFirstExtraField(ZipExtraField ze) {
-        LinkedHashMap copy = extraFields;
-        extraFields = new LinkedHashMap();
-        extraFields.put(ze.getHeaderId(), ze);
-        if (copy != null) {
-            copy.remove(ze.getHeaderId());
-            extraFields.putAll(copy);
+        if (ze instanceof UnparseableExtraFieldData) {
+            unparseableExtra = (UnparseableExtraFieldData) ze;
+        } else {
+            LinkedHashMap copy = extraFields;
+            extraFields = new LinkedHashMap();
+            extraFields.put(ze.getHeaderId(), ze);
+            if (copy != null) {
+                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
      * @since 1.1
      */
@@ -264,6 +313,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.
@@ -276,7 +336,18 @@
     }
 
     /**
-     * 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
      * @since 1.1
@@ -284,10 +355,14 @@
      */
     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 (Exception e) {
-            throw new RuntimeException(e.getMessage(), e);
+            // actually this is not be possible as of Ant 1.8.1
+            throw new RuntimeException("Error parsing extra fields for entry: "
+                                       + getName() + " - " + e.getMessage(), 
e);
         }
     }
 
@@ -300,7 +375,7 @@
      * @since 1.1
      */
     protected void setExtra() {
-        
super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields()));
+        
super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields(true)));
     }
 
     /**
@@ -308,7 +383,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 (Exception e) {
             throw new RuntimeException(e.getMessage(), e);
@@ -331,7 +408,7 @@
      * @since 1.1
      */
     public byte[] getCentralDirectoryExtra() {
-        return ExtraFieldUtils.mergeCentralDirectoryData(getExtraFields());
+        return ExtraFieldUtils.mergeCentralDirectoryData(getExtraFields(true));
     }
 
     /**
@@ -413,7 +490,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 {

Propchange: ant/core/trunk/src/main/org/apache/tools/zip/ZipEntry.java
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Tue Feb 16 14:41:11 2010
@@ -1,2 +1,3 @@
+/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java:910483-910521
 
/commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java:747850,749603
 
/commons/sandbox/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipEntry.java:746933,748133,749524,749855,749859

Copied: ant/core/trunk/src/main/org/apache/tools/zip/ZipUtil.java (from 
r910483, 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java)
URL: 
http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/zip/ZipUtil.java?p2=ant/core/trunk/src/main/org/apache/tools/zip/ZipUtil.java&p1=commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java&r1=910483&r2=910537&rev=910537&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java
 (original)
+++ ant/core/trunk/src/main/org/apache/tools/zip/ZipUtil.java Tue Feb 16 
14:41:11 2010
@@ -15,159 +15,14 @@
  *  limitations under the License.
  *
  */
-package org.apache.commons.compress.archivers.zip;
-
-import java.io.IOException;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.zip.CRC32;
+package org.apache.tools.zip;
 
 /**
  * Utility class for handling DOS and Java time conversions.
- * @Immutable
+ * @since Ant 1.8.1
  */
 public abstract class ZipUtil {
     /**
-     * Smallest date/time ZIP can handle.
-     */
-    private static final byte[] DOS_TIME_MIN = ZipLong.getBytes(0x00002100L);
-
-    /**
-     * Convert a Date object to a DOS date/time field.
-     * @param time the <code>Date</code> to convert
-     * @return the date as a <code>ZipLong</code>
-     */
-    public static ZipLong toDosTime(Date time) {
-        return new ZipLong(toDosTime(time.getTime()));
-    }
-
-    /**
-     * Convert a Date object to a DOS date/time field.
-     *
-     * <p>Stolen from InfoZip's <code>fileio.c</code></p>
-     * @param t number of milliseconds since the epoch
-     * @return the date as a byte array
-     */
-    public static byte[] toDosTime(long t) {
-        Calendar c = Calendar.getInstance();
-        c.setTimeInMillis(t);
-
-        int year = c.get(Calendar.YEAR);
-        if (year < 1980) {
-            return (byte[]) DOS_TIME_MIN.clone(); // stop callers from 
changing the array
-        }
-        int month = c.get(Calendar.MONTH) + 1;
-        long value =  ((year - 1980) << 25)
-            |         (month << 21)
-            |         (c.get(Calendar.DAY_OF_MONTH) << 16)
-            |         (c.get(Calendar.HOUR_OF_DAY) << 11)
-            |         (c.get(Calendar.MINUTE) << 5)
-            |         (c.get(Calendar.SECOND) >> 1);
-        return ZipLong.getBytes(value);
-    }
-
-    /**
-     * Assumes a negative integer really is a positive integer that
-     * has wrapped around and re-creates the original value.
-     * @param i the value to treat as unsigned int.
-     * @return the unsigned int as a long.
-     */
-    public static long adjustToLong(int i) {
-        if (i < 0) {
-            return 2 * ((long) Integer.MAX_VALUE) + 2 + i;
-        } else {
-            return i;
-        }
-    }
-
-    /**
-     * Convert a DOS date/time field to a Date object.
-     *
-     * @param zipDosTime contains the stored DOS time.
-     * @return a Date instance corresponding to the given time.
-     */
-    public static Date fromDosTime(ZipLong zipDosTime) {
-        long dosTime = zipDosTime.getValue();
-        return new Date(dosToJavaTime(dosTime));
-    }
-
-    /**
-     * Converts DOS time to Java time (number of milliseconds since
-     * epoch).
-     */
-    public static long dosToJavaTime(long dosTime) {
-        Calendar cal = Calendar.getInstance();
-        // CheckStyle:MagicNumberCheck OFF - no point
-        cal.set(Calendar.YEAR, (int) ((dosTime >> 25) & 0x7f) + 1980);
-        cal.set(Calendar.MONTH, (int) ((dosTime >> 21) & 0x0f) - 1);
-        cal.set(Calendar.DATE, (int) (dosTime >> 16) & 0x1f);
-        cal.set(Calendar.HOUR_OF_DAY, (int) (dosTime >> 11) & 0x1f);
-        cal.set(Calendar.MINUTE, (int) (dosTime >> 5) & 0x3f);
-        cal.set(Calendar.SECOND, (int) (dosTime << 1) & 0x3e);
-        // CheckStyle:MagicNumberCheck ON
-        return cal.getTime().getTime();
-    }
-
-    /**
-     * If the entry has Unicode*ExtraFields and the CRCs of the
-     * names/comments match those of the extra fields, transfer the
-     * known Unicode values from the extra field.
-     */
-    static void setNameAndCommentFromExtraFields(ZipArchiveEntry ze,
-                                                 byte[] originalNameBytes,
-                                                 byte[] commentBytes) {
-        UnicodePathExtraField name = (UnicodePathExtraField)
-            ze.getExtraField(UnicodePathExtraField.UPATH_ID);
-        String originalName = ze.getName();
-        String newName = getUnicodeStringIfOriginalMatches(name,
-                                                           originalNameBytes);
-        if (newName != null && !originalName.equals(newName)) {
-            ze.setName(newName);
-        }
-
-        if (commentBytes != null && commentBytes.length > 0) {
-            UnicodeCommentExtraField cmt = (UnicodeCommentExtraField)
-                ze.getExtraField(UnicodeCommentExtraField.UCOM_ID);
-            String newComment =
-                getUnicodeStringIfOriginalMatches(cmt, commentBytes);
-            if (newComment != null) {
-                ze.setComment(newComment);
-            }
-        }
-    }
-
-    /**
-     * If the stored CRC matches the one of the given name, return the
-     * Unicode name of the given field.
-     *
-     * <p>If the field is null or the CRCs don't match, return null
-     * instead.</p>
-     */
-    private static 
-        String getUnicodeStringIfOriginalMatches(AbstractUnicodeExtraField f,
-                                                 byte[] orig) {
-        if (f != null) {
-            CRC32 crc32 = new CRC32();
-            crc32.update(orig);
-            long origCRC32 = crc32.getValue();
-
-            if (origCRC32 == f.getNameCRC32()) {
-                try {
-                    return ZipEncodingHelper
-                        .UTF8_ZIP_ENCODING.decode(f.getUnicodeName());
-                } catch (IOException ex) {
-                    // UTF-8 unsupported?  should be impossible the
-                    // Unicode*ExtraField must contain some bad bytes
-
-                    // TODO log this anywhere?
-                    return null;
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
      * Create a copy of the given array - or return null if the
      * argument is null.
      */

Propchange: ant/core/trunk/src/main/org/apache/tools/zip/ZipUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ant/core/trunk/src/main/org/apache/tools/zip/ZipUtil.java
------------------------------------------------------------------------------
    svn:mergeinfo = 

Modified: ant/core/trunk/src/tests/antunit/taskdefs/unzip-test.xml
URL: 
http://svn.apache.org/viewvc/ant/core/trunk/src/tests/antunit/taskdefs/unzip-test.xml?rev=910537&r1=910536&r2=910537&view=diff
==============================================================================
--- ant/core/trunk/src/tests/antunit/taskdefs/unzip-test.xml (original)
+++ ant/core/trunk/src/tests/antunit/taskdefs/unzip-test.xml Tue Feb 16 
14:41:11 2010
@@ -59,7 +59,7 @@
           >
     <mkdir dir="${input}"/>
     <mkdir dir="${output}"/>
-    <copy file="zip/Bugzilla-46559.zip" tofile="${input}/test.zip"/>
+    <copy file="broken_cd.zip" tofile="${input}/test.zip"/>
     <au:expectfailure>
       <unzip src="${input}/test.zip" dest="${output}"/>
     </au:expectfailure>

Modified: 
ant/core/trunk/src/tests/junit/org/apache/tools/zip/ExtraFieldUtilsTest.java
URL: 
http://svn.apache.org/viewvc/ant/core/trunk/src/tests/junit/org/apache/tools/zip/ExtraFieldUtilsTest.java?rev=910537&r1=910536&r2=910537&view=diff
==============================================================================
--- 
ant/core/trunk/src/tests/junit/org/apache/tools/zip/ExtraFieldUtilsTest.java 
(original)
+++ 
ant/core/trunk/src/tests/junit/org/apache/tools/zip/ExtraFieldUtilsTest.java 
Tue Feb 16 14:41:11 2010
@@ -18,6 +18,7 @@
 
 package org.apache.tools.zip;
 
+import java.util.Arrays;
 import junit.framework.TestCase;
 
 /**
@@ -78,11 +79,65 @@
             fail("data should be invalid");
         } catch (Exception e) {
             assertEquals("message",
-                         "data starting at "+(4+aLocal.length)+" is in unknown 
format",
+                         "bad extra field starting at "+(4 + aLocal.length)
+                         + ".  Block length of 1 bytes exceeds remaining data 
of 0 bytes.",
                          e.getMessage());
         }
     }
 
+    public void testParseWithRead() throws Exception {
+        ZipExtraField[] ze =
+            ExtraFieldUtils.parse(data, true,
+                                  ExtraFieldUtils.UnparseableExtraField.READ);
+        assertEquals("number of fields", 2, ze.length);
+        assertTrue("type field 1", ze[0] instanceof AsiExtraField);
+        assertEquals("mode field 1", 040755,
+                     ((AsiExtraField) ze[0]).getMode());
+        assertTrue("type field 2", ze[1] instanceof UnrecognizedExtraField);
+        assertEquals("data length field 2", 1,
+                     ze[1].getLocalFileDataLength().getValue());
+
+        byte[] data2 = new byte[data.length-1];
+        System.arraycopy(data, 0, data2, 0, data2.length);
+        ze = ExtraFieldUtils.parse(data2, true,
+                                   ExtraFieldUtils.UnparseableExtraField.READ);
+        assertEquals("number of fields", 2, ze.length);
+        assertTrue("type field 1", ze[0] instanceof AsiExtraField);
+        assertEquals("mode field 1", 040755,
+                     ((AsiExtraField) ze[0]).getMode());
+        assertTrue("type field 2", ze[1] instanceof UnparseableExtraFieldData);
+        assertEquals("data length field 2", 4,
+                     ze[1].getLocalFileDataLength().getValue());
+        byte[] expectedData = new byte[4];
+        for (int i = 0; i < 4; i++) {
+            assertEquals("byte number " + i,
+                         data2[data.length - 5 + i],
+                         ze[1].getLocalFileDataData()[i]);
+        }
+    }
+
+    public void testParseWithSkip() throws Exception {
+        ZipExtraField[] ze =
+            ExtraFieldUtils.parse(data, true,
+                                  ExtraFieldUtils.UnparseableExtraField.SKIP);
+        assertEquals("number of fields", 2, ze.length);
+        assertTrue("type field 1", ze[0] instanceof AsiExtraField);
+        assertEquals("mode field 1", 040755,
+                     ((AsiExtraField) ze[0]).getMode());
+        assertTrue("type field 2", ze[1] instanceof UnrecognizedExtraField);
+        assertEquals("data length field 2", 1,
+                     ze[1].getLocalFileDataLength().getValue());
+
+        byte[] data2 = new byte[data.length-1];
+        System.arraycopy(data, 0, data2, 0, data2.length);
+        ze = ExtraFieldUtils.parse(data2, true,
+                                   ExtraFieldUtils.UnparseableExtraField.SKIP);
+        assertEquals("number of fields", 1, ze.length);
+        assertTrue("type field 1", ze[0] instanceof AsiExtraField);
+        assertEquals("mode field 1", 040755,
+                     ((AsiExtraField) ze[0]).getMode());
+    }
+
     /**
      * Test merge methods
      */
@@ -111,4 +166,30 @@
         }
 
     }
+
+    public void testMergeWithUnparseableData() throws Exception {
+        ZipExtraField d = new UnparseableExtraFieldData();
+        d.parseFromLocalFileData(new byte[] {1, 0, 1, 0}, 0, 4);
+        byte[] local =
+            ExtraFieldUtils.mergeLocalFileDataData(new ZipExtraField[] {a, d});
+        assertEquals("local length", data.length - 1, local.length);
+        for (int i = 0; i < local.length; i++) {
+            assertEquals("local byte " + i, data[i], local[i]);
+        }
+
+        byte[] dCentral = d.getCentralDirectoryData();
+        byte[] data2 = new byte[4 + aLocal.length + dCentral.length];
+        System.arraycopy(data, 0, data2, 0, 4 + aLocal.length + 2);
+        System.arraycopy(dCentral, 0, data2,
+                         4 + aLocal.length, dCentral.length);
+
+
+        byte[] central =
+            ExtraFieldUtils.mergeCentralDirectoryData(new ZipExtraField[] {a, 
d});
+        assertEquals("central length", data2.length, central.length);
+        for (int i = 0; i < central.length; i++) {
+            assertEquals("central byte " + i, data2[i], central[i]);
+        }
+
+    }
 }

Propchange: 
ant/core/trunk/src/tests/junit/org/apache/tools/zip/ExtraFieldUtilsTest.java
------------------------------------------------------------------------------
--- svn:mergeinfo (added)
+++ svn:mergeinfo Tue Feb 16 14:41:11 2010
@@ -0,0 +1,2 @@
+/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtilsTest.java:910483-910521
+/commons/sandbox/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtilsTest.java:749906-749907


Reply via email to