Return-Path: Delivered-To: apmail-commons-commits-archive@minotaur.apache.org Received: (qmail 35614 invoked from network); 16 Feb 2010 13:59:32 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 16 Feb 2010 13:59:32 -0000 Received: (qmail 44529 invoked by uid 500); 16 Feb 2010 13:59:32 -0000 Delivered-To: apmail-commons-commits-archive@commons.apache.org Received: (qmail 44438 invoked by uid 500); 16 Feb 2010 13:59:31 -0000 Mailing-List: contact commits-help@commons.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@commons.apache.org Delivered-To: mailing list commits@commons.apache.org Received: (qmail 44429 invoked by uid 99); 16 Feb 2010 13:59:31 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 16 Feb 2010 13:59:31 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 16 Feb 2010 13:59:24 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id E39272388A2C; Tue, 16 Feb 2010 13:59:03 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r910519 - in /commons/proper/compress/trunk/src: changes/changes.xml main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java site/xdoc/zip.xml Date: Tue, 16 Feb 2010 13:59:03 -0000 To: commits@commons.apache.org From: bodewig@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20100216135903.E39272388A2C@eris.apache.org> 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 @@ + + ZipArchiveEntry, ZipFile and ZipArchiveInputStream are now + more lenient when parsing extra fields. + 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. * + *

The extra data is expected to follow the recommendation of + * {@link http://www.pkware.com/documents/casestudies/APPNOTE.TXT + * APPNOTE.txt}:

+ *
    + *
  • the extra byte array consists of a sequence of extra fields
  • + *
  • each extra fields starts by a two byte header id followed by + * a two byte sequence holding the length of the remainder of + * data.
  • + *
+ * + *

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.

+ * * @NotThreadSafe */ public class ZipArchiveEntry extends java.util.zip.ZipEntry @@ -54,6 +72,7 @@ private int platform = PLATFORM_FAT; private long externalAttributes = 0; private LinkedHashMap/**/ 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. * *

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. * *

The new extra field will be the first one.

* @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 UnrecognizedExtraField.

+

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 UnparseableExtraFieldData.

+