diff --git a/WHATSNEW b/WHATSNEW index 1771d11a5..f6fe64fa0 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -10,6 +10,9 @@ Changes that could break older environments: rmic to use a new compiler a lot easier but may break custom versions of this task that rely on the old implementation. +* several Zip methods have changed their signature as we now use a Zip + package of our own that handles Unix permissions for directories. + Other changes: -------------- diff --git a/src/main/org/apache/tools/ant/taskdefs/Ear.java b/src/main/org/apache/tools/ant/taskdefs/Ear.java index a52e6b676..cac185cab 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Ear.java +++ b/src/main/org/apache/tools/ant/taskdefs/Ear.java @@ -55,10 +55,10 @@ package org.apache.tools.ant.taskdefs; import org.apache.tools.ant.*; import org.apache.tools.ant.types.ZipFileSet; +import org.apache.tools.zip.*; import java.io.*; import java.util.Vector; -import java.util.zip.*; /** * Creates a EAR archive. Based on WAR task diff --git a/src/main/org/apache/tools/ant/taskdefs/Jar.java b/src/main/org/apache/tools/ant/taskdefs/Jar.java index d69dd4289..8134e6661 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Jar.java +++ b/src/main/org/apache/tools/ant/taskdefs/Jar.java @@ -56,9 +56,9 @@ package org.apache.tools.ant.taskdefs; import org.apache.tools.ant.*; import org.apache.tools.ant.types.ZipFileSet; +import org.apache.tools.zip.*; import java.io.*; -import java.util.zip.*; /** * Creates a JAR archive. diff --git a/src/main/org/apache/tools/ant/taskdefs/War.java b/src/main/org/apache/tools/ant/taskdefs/War.java index 98f23bcde..7e12d545e 100644 --- a/src/main/org/apache/tools/ant/taskdefs/War.java +++ b/src/main/org/apache/tools/ant/taskdefs/War.java @@ -56,10 +56,10 @@ package org.apache.tools.ant.taskdefs; import org.apache.tools.ant.*; import org.apache.tools.ant.types.ZipFileSet; +import org.apache.tools.zip.*; import java.io.*; import java.util.Vector; -import java.util.zip.*; /** * Creates a WAR archive. diff --git a/src/main/org/apache/tools/ant/taskdefs/Zip.java b/src/main/org/apache/tools/ant/taskdefs/Zip.java index 2acbf56d6..4536406e4 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Zip.java +++ b/src/main/org/apache/tools/ant/taskdefs/Zip.java @@ -59,10 +59,12 @@ import java.util.Hashtable; import java.util.Stack; import java.util.StringTokenizer; import java.util.Vector; -import java.util.zip.*; +import java.util.zip.CRC32; +import java.util.zip.ZipInputStream; import org.apache.tools.ant.*; import org.apache.tools.ant.types.*; import org.apache.tools.ant.util.*; +import org.apache.tools.zip.*; /** * Create a ZIP archive. @@ -280,7 +282,7 @@ public class Zip extends MatchingTask { try { in = new ZipInputStream(new FileInputStream(zipSrc)); - while ((entry = in.getNextEntry()) != null) { + while ((entry = new ZipEntry(in.getNextEntry())) != null) { String vPath = entry.getName(); if (zipScanner.match(vPath)) { addParentDirs(null, vPath, zOut, prefix); @@ -414,6 +416,10 @@ public class Zip extends MatchingTask { ze.setMethod (ZipEntry.STORED); // This is faintly ridiculous: ze.setCrc (emptyCrc); + + // this is 040775 | MS-DOS directory flag in reverse byte order + ze.setExternalAttributes(0x41FD0010L); + zOut.putNextEntry (ze); } diff --git a/src/main/org/apache/tools/zip/AsiExtraField.java b/src/main/org/apache/tools/zip/AsiExtraField.java new file mode 100644 index 000000000..111c6d905 --- /dev/null +++ b/src/main/org/apache/tools/zip/AsiExtraField.java @@ -0,0 +1,366 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.zip; + +import java.util.zip.CRC32; +import java.util.zip.ZipException; + +/** + * Adds Unix file permission and UID/GID fields as well as symbolic + * link handling. + * + *

This class uses the ASi extra field in the format: + *

+ *         Value         Size            Description
+ *         -----         ----            -----------
+ * (Unix3) 0x756e        Short           tag for this extra block type
+ *         TSize         Short           total data size for this block
+ *         CRC           Long            CRC-32 of the remaining data
+ *         Mode          Short           file permissions
+ *         SizDev        Long            symlink'd size OR major/minor dev num
+ *         UID           Short           user ID
+ *         GID           Short           group ID
+ *         (var.)        variable        symbolic link filename
+ * 
+ * taken from appnote.iz (Info-ZIP note, 981119) found at ftp://ftp.uu.net/pub/archiving/zip/doc/

+ + * + *

Short is two bytes and Long is four bytes in big endian byte and + * word order, device numbers are currently not supported.

+ * + * @author Stefan Bodewig + * @version $Revision$ + */ +public class AsiExtraField implements ZipExtraField, UnixStat, Cloneable { + + private final static ZipShort HEADER_ID = new ZipShort(0x756E); + + /** + * Standard Unix stat(2) file mode. + * + * @since 1.1 + */ + private int mode = 0; + /** + * User ID. + * + * @since 1.1 + */ + private int uid = 0; + /** + * Group ID. + * + * @since 1.1 + */ + private int gid = 0; + /** + * File this entry points to, if it is a symbolic link. + * + *

empty string - if entry is not a symbolic link.

+ * + * @since 1.1 + */ + private String link = ""; + /** + * Is this an entry for a directory? + * + * @since 1.1 + */ + private boolean dirFlag = false; + + /** + * Instance used to calculate checksums. + * + * @since 1.1 + */ + private CRC32 crc = new CRC32(); + + public AsiExtraField() { + } + + /** + * The Header-ID. + * + * @since 1.1 + */ + public ZipShort getHeaderId() { + return HEADER_ID; + } + + /** + * Length of the extra field in the local file data - without + * Header-ID or length specifier. + * + * @since 1.1 + */ + public ZipShort getLocalFileDataLength() { + return new ZipShort( 4 // CRC + + 2 // Mode + + 4 // SizDev + + 2 // UID + + 2 // GID + + getLinkedFile().getBytes().length); + } + + /** + * Delegate to local file data. + * + * @since 1.1 + */ + public ZipShort getCentralDirectoryLength() { + return getLocalFileDataLength(); + } + + /** + * The actual data to put into local file data - without Header-ID + * or length specifier. + * + * @since 1.1 + */ + public byte[] getLocalFileDataData() { + // CRC will be added later + byte[] data = new byte[getLocalFileDataLength().getValue() - 4]; + System.arraycopy((new ZipShort(getMode())).getBytes(), 0, data, 0, 2); + + byte[] linkArray = getLinkedFile().getBytes(); + System.arraycopy((new ZipLong(linkArray.length)).getBytes(), + 0, data, 2, 4); + + System.arraycopy((new ZipShort(getUserId())).getBytes(), + 0, data, 6, 2); + System.arraycopy((new ZipShort(getGroupId())).getBytes(), + 0, data, 8, 2); + + System.arraycopy(linkArray, 0, data, 10, linkArray.length); + + crc.reset(); + crc.update(data); + long checksum = crc.getValue(); + + byte[] result = new byte[data.length + 4]; + System.arraycopy((new ZipLong(checksum)).getBytes(), 0, result, 0, 4); + System.arraycopy(data, 0, result, 4, data.length); + return result; + } + + /** + * Delegate to local file data. + * + * @since 1.1 + */ + public byte[] getCentralDirectoryData() { + return getLocalFileDataData(); + } + + /** + * Set the user id. + * + * @since 1.1 + */ + public void setUserId(int uid) { + this.uid = uid; + } + + /** + * Get the user id. + * + * @since 1.1 + */ + public int getUserId() { + return uid; + } + + /** + * Set the group id. + * + * @since 1.1 + */ + public void setGroupId(int gid) { + this.gid = gid; + } + + /** + * Get the group id. + * + * @since 1.1 + */ + public int getGroupId() { + return gid; + } + + /** + * Indicate that this entry is a symbolic link to the given filename. + * + * @param name Name of the file this entry links to, empty String + * if it is not a symbolic link. + * + * @since 1.1 + */ + public void setLinkedFile(String name) { + link = name; + mode = getMode(mode); + } + + /** + * Name of linked file + * + * @return name of the file this entry links to if it is a + * symbolic link, the empty string otherwise. + * + * @since 1.1 + */ + public String getLinkedFile() { + return link; + } + + /** + * Is this entry a symbolic link? + * + * @since 1.1 + */ + public boolean isLink() { + return getLinkedFile().length() != 0; + } + + /** + * File mode of this file. + * + * @since 1.1 + */ + public void setMode(int mode) { + this.mode = getMode(mode); + } + + /** + * File mode of this file. + * + * @since 1.1 + */ + public int getMode() { + return mode; + } + + /** + * Indicate whether this entry is a directory. + * + * @since 1.1 + */ + public void setDirectory(boolean dirFlag) { + this.dirFlag = dirFlag; + mode = getMode(mode); + } + + /** + * Is this entry a directory? + * + * @since 1.1 + */ + public boolean isDirectory() { + return dirFlag && !isLink(); + } + + /** + * Populate data from this array as if it was in local file data. + * + * @since 1.1 + */ + public void parseFromLocalFileData(byte[] data, int offset, int length) + throws ZipException { + + long givenChecksum = (new ZipLong(data, offset)).getValue(); + byte[] tmp = new byte[length-4]; + System.arraycopy(data, offset+4, tmp, 0, length-4); + crc.reset(); + crc.update(tmp); + long realChecksum = crc.getValue(); + if (givenChecksum != realChecksum) { + throw new ZipException("bad CRC checksum " + + Long.toHexString(givenChecksum) + + " instead of " + + Long.toHexString(realChecksum)); + } + + int newMode = (new ZipShort(tmp, 0)).getValue(); + byte[] linkArray = new byte[(int) (new ZipLong(tmp, 2)).getValue()]; + uid = (new ZipShort(tmp, 6)).getValue(); + gid = (new ZipShort(tmp, 8)).getValue(); + + if (linkArray.length == 0) { + link = ""; + } else { + System.arraycopy(tmp, 10, linkArray, 0, linkArray.length); + link = new String(linkArray); + } + setDirectory((newMode & DIR_FLAG) != 0); + setMode(newMode); + } + + /** + * Get the file mode for given permissions with the correct file type. + * + * @since 1.1 + */ + protected int getMode(int mode) { + int type = FILE_FLAG; + if (isLink()) { + type = LINK_FLAG; + } else if (isDirectory()) { + type = DIR_FLAG; + } + return type | (mode & PERM_MASK); + } + +} diff --git a/src/main/org/apache/tools/zip/ExtraFieldUtils.java b/src/main/org/apache/tools/zip/ExtraFieldUtils.java new file mode 100644 index 000000000..005e98809 --- /dev/null +++ b/src/main/org/apache/tools/zip/ExtraFieldUtils.java @@ -0,0 +1,203 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.zip; + +import java.util.Hashtable; +import java.util.Vector; +import java.util.zip.ZipException; + +/** + * ZipExtraField related methods + * + * @author Stefan Bodewig + * @version $Revision$ + */ +public class ExtraFieldUtils { + + /** + * Static registry of known extra fields. + * + * @since 1.1 + */ + private static Hashtable implementations; + + static { + implementations = new Hashtable(); + register(AsiExtraField.class); + } + + /** + * Register a ZipExtraField implementation. + * + *

The given class must have a no-arg constructor and implement + * the {@link ZipExtraField ZipExtraField interface}.

+ * + * @since 1.1 + */ + public static void register(Class c) { + try { + ZipExtraField ze = (ZipExtraField) c.newInstance(); + implementations.put(ze.getHeaderId(), c); + } catch (ClassCastException cc) { + throw new RuntimeException(c + + " doesn\'t implement ZipExtraField"); + } catch (InstantiationException ie) { + throw new RuntimeException(c + " is not a concrete class"); + } catch (IllegalAccessException ie) { + throw new RuntimeException(c + + "\'s no-arg constructor is not public"); + } + } + + /** + * Create an instance of the approriate ExtraField, falls back to + * {@link UnrecognizedExtraField UnrecognizedExtraField}. + * + * @since 1.1 + */ + public static ZipExtraField createExtraField(ZipShort headerId) + throws InstantiationException, IllegalAccessException { + Class c = (Class) implementations.get(headerId); + if (c != null) { + return (ZipExtraField) c.newInstance(); + } + UnrecognizedExtraField u = new UnrecognizedExtraField(); + u.setHeaderId(headerId); + return u; + } + + /** + * Split the array into ExtraFields and populate them with the + * give data. + * + * @since 1.1 + */ + public static ZipExtraField[] parse(byte[] data) throws ZipException { + Vector v = new Vector(); + int start = 0; + while (start <= data.length-4) { + ZipShort headerId = new ZipShort(data, start); + int length = (new ZipShort(data, start+2)).getValue(); + if (start+4+length > data.length) { + throw new ZipException("data starting at "+start+" is in unknown format"); + } + try { + ZipExtraField ze = createExtraField(headerId); + ze.parseFromLocalFileData(data, start+4, length); + v.addElement(ze); + } catch (InstantiationException ie) { + throw new ZipException(ie.getMessage()); + } catch (IllegalAccessException iae) { + throw new ZipException(iae.getMessage()); + } + start += (length+4); + } + if (start != data.length) { // array not exhausted + throw new ZipException("data starting at "+start+" is in unknown format"); + } + + ZipExtraField[] result = new ZipExtraField[v.size()]; + v.copyInto(result); + return result; + } + + /** + * Merges the local file data fields of the given ZipExtraFields. + * + * @since 1.1 + */ + public static byte[] mergeLocalFileDataData(ZipExtraField[] data) { + int sum = 4*data.length; + for (int i=0; i. + */ + +package org.apache.tools.zip; + +/** + * Constants from stat.h on Unix systems. + * + * @author Stefan Bodewig + * @version $Revision$ + */ +public interface UnixStat { + + /** + * Bits used for permissions (and sticky bit) + * + * @since 1.1 + */ + public static final int PERM_MASK = 07777; + /** + * Indicates symbolic links. + * + * @since 1.1 + */ + public static final int LINK_FLAG = 0120000; + /** + * Indicates plain files. + * + * @since 1.1 + */ + public static final int FILE_FLAG = 0100000; + /** + * Indicates directories. + * + * @since 1.1 + */ + public static final int DIR_FLAG = 040000; + + // ---------------------------------------------------------- + // somewhat arbitrary choices that are quite common for shared + // installations + // ----------------------------------------------------------- + + /** + * Default permissions for symbolic links. + * + * @since 1.1 + */ + public static final int DEFAULT_LINK_PERM = 0777; + /** + * Default permissions for directories. + * + * @since 1.1 + */ + public static final int DEFAULT_DIR_PERM = 0755; + /** + * Default permissions for plain files. + * + * @since 1.1 + */ + public static final int DEFAULT_FILE_PERM = 0644; +} diff --git a/src/main/org/apache/tools/zip/UnrecognizedExtraField.java b/src/main/org/apache/tools/zip/UnrecognizedExtraField.java new file mode 100644 index 000000000..d15c37106 --- /dev/null +++ b/src/main/org/apache/tools/zip/UnrecognizedExtraField.java @@ -0,0 +1,131 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.zip; + +/** + * Simple placeholder for all those extra fields we don't want to deal + * with. + * + *

Assumes local file data and central directory entries are + * identical - unless told the opposite.

+ * + * @author Stefan Bodewig + * @version $Revision$ + */ +public class UnrecognizedExtraField implements ZipExtraField { + + /** + * The Header-ID. + * + * @since 1.1 + */ + private ZipShort headerId; + + public void setHeaderId(ZipShort headerId) { + this.headerId = headerId; + } + + public ZipShort getHeaderId() {return headerId;} + + /** + * Extra field data in local file data - without + * Header-ID or length specifier. + * + * @since 1.1 + */ + private byte[] localData; + + public void setLocalFileDataData(byte[] data) { + localData = data; + } + + public ZipShort getLocalFileDataLength() { + return new ZipShort(localData.length); + } + + public byte[] getLocalFileDataData() {return localData;} + + /** + * Extra field data in central directory - without + * Header-ID or length specifier. + * + * @since 1.1 + */ + private byte[] centralData; + + public void setCentralDirectoryData(byte[] data) { + centralData = data; + } + + public ZipShort getCentralDirectoryLength() { + if (centralData != null) { + return new ZipShort(centralData.length); + } + return getLocalFileDataLength(); + } + + public byte[] getCentralDirectoryData() { + if (centralData != null) { + return centralData; + } + return getLocalFileDataData(); + } + + public void parseFromLocalFileData(byte[] data, int offset, int length) { + byte[] tmp = new byte[length]; + System.arraycopy(data, offset, tmp, 0, length); + setLocalFileDataData(tmp); + } +} diff --git a/src/main/org/apache/tools/zip/ZipEntry.java b/src/main/org/apache/tools/zip/ZipEntry.java new file mode 100644 index 000000000..46bae0be3 --- /dev/null +++ b/src/main/org/apache/tools/zip/ZipEntry.java @@ -0,0 +1,272 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.zip; + +import java.util.Vector; +import java.util.zip.ZipException; + +/** + * Extension that adds better handling of extra fields and provides + * access to the internal and external file attributes. + * + * @author Stefan Bodewig + * @version $Revision$ + */ +public class ZipEntry extends java.util.zip.ZipEntry { + + private int internalAttributes = 0; + private long externalAttributes = 0; + private Vector extraFields = new Vector(); + + /** + * Creates a new zip entry with the specified name. + * + * @since 1.1 + */ + public ZipEntry(String name) { + super(name); + } + + /** + * Creates a new zip entry with fields taken from the specified zip entry. + * + * @since 1.1 + */ + public ZipEntry(java.util.zip.ZipEntry entry) throws ZipException { + super(entry); + byte[] extra = entry.getExtra(); + if (extra != null) { + setExtraFields(ExtraFieldUtils.parse(extra)); + } else { + // initializes extra data to an empty byte array + setExtra(); + } + } + + /** + * Creates a new zip entry with fields taken from the specified zip entry. + * + * @since 1.1 + */ + public ZipEntry(ZipEntry entry) { + super(entry); + setInternalAttributes(entry.getInternalAttributes()); + setExternalAttributes(entry.getExternalAttributes()); + setExtraFields(entry.getExtraFields()); + } + + /** + * Overwrite clone + * + * @since 1.1 + */ + public Object clone() { + ZipEntry e = null; + try { + e = new ZipEntry((java.util.zip.ZipEntry) super.clone()); + } catch (Exception ex) { + // impossible as extra data is in correct format + ex.printStackTrace(); + } + e.setInternalAttributes(getInternalAttributes()); + e.setExternalAttributes(getExternalAttributes()); + e.setExtraFields(getExtraFields()); + return e; + } + + /** + * Retrieves the internal file attributes. + * + * @since 1.1 + */ + public int getInternalAttributes() { + return internalAttributes; + } + + /** + * Sets the internal file attributes. + * + * @since 1.1 + */ + public void setInternalAttributes(int value) { + internalAttributes = value; + } + + /** + * Retrieves the external file attributes. + * + * @since 1.1 + */ + public long getExternalAttributes() { + return externalAttributes; + } + + /** + * Sets the external file attributes. + * + * @since 1.1 + */ + public void setExternalAttributes(long value) { + externalAttributes = value; + } + + /** + * Replaces all currently attached extra fields with the new array. + * + * @since 1.1 + */ + public void setExtraFields(ZipExtraField[] fields) { + extraFields.removeAllElements(); + for (int i=0; i. + */ + +package org.apache.tools.zip; + +import java.util.zip.ZipException; + +/** + * General format of extra field data. + * + *

Extra fields usually apper twice per file, once in the local + * file data and once in the central directory. Usually they are the + * same, but they don't have to be. {@link + * java.util.zip.ZipOutputStream java.util.zip.ZipOutputStream} will + * only use write the local file data at both places.

+ * + * @author Stefan Bodewig + * @version $Revision$ + */ +public interface ZipExtraField { + + /** + * The Header-ID. + * + * @since 1.1 + */ + public ZipShort getHeaderId(); + + /** + * Length of the extra field in the local file data - without + * Header-ID or length specifier. + * + * @since 1.1 + */ + public ZipShort getLocalFileDataLength(); + + /** + * Length of the extra field in the central directory - without + * Header-ID or length specifier. + * + * @since 1.1 + */ + public ZipShort getCentralDirectoryLength(); + + /** + * The actual data to put into local file data - without Header-ID + * or length specifier. + * + * @since 1.1 + */ + public byte[] getLocalFileDataData(); + + /** + * The actual data to put central directory - without Header-ID or + * length specifier. + * + * @since 1.1 + */ + public byte[] getCentralDirectoryData(); + + /** + * Populate data from this array as if it was in local file data. + * + * @since 1.1 + */ + public void parseFromLocalFileData(byte[] data, int offset, int length) + throws ZipException; +} diff --git a/src/main/org/apache/tools/zip/ZipLong.java b/src/main/org/apache/tools/zip/ZipLong.java new file mode 100644 index 000000000..565bf03d7 --- /dev/null +++ b/src/main/org/apache/tools/zip/ZipLong.java @@ -0,0 +1,142 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.zip; + +/** + * Utility class that represents a four byte integer with conversion + * rules for the big endian byte order of ZIP files. + * + * @author Stefan Bodewig + * @version $Revision$ + */ +public class ZipLong implements Cloneable { + + private long value; + + /** + * Create instance from a number. + * + * @since 1.1 + */ + public ZipLong(long value) { + this.value = value; + } + + /** + * Create instance from bytes. + * + * @since 1.1 + */ + public ZipLong (byte[] bytes) { + this(bytes, 0); + } + + /** + * Create instance from the four bytes starting at offset. + * + * @since 1.1 + */ + public ZipLong (byte[] bytes, int offset) { + value = (bytes[offset+3] << 24) & 0xFF000000l; + value += (bytes[offset+2] << 16) & 0xFF0000; + value += (bytes[offset+1] << 8) & 0xFF00; + value += (bytes[offset] & 0xFF); + } + + /** + * Get value as two bytes in big endian byte order. + * + * @since 1.1 + */ + public byte[] getBytes() { + byte[] result = new byte[4]; + result[0] = (byte) ((value & 0xFF)); + result[1] = (byte) ((value & 0xFF00) >> 8); + result[2] = (byte) ((value & 0xFF0000) >> 16); + result[3] = (byte) ((value & 0xFF000000l) >> 24); + return result; + } + + /** + * Get value as Java int. + * + * @since 1.1 + */ + public long getValue() { + return value; + } + + /** + * Override to make two instances with same value equal. + * + * @since 1.1 + */ + public boolean equals(Object o) { + if (o == null || !(o instanceof ZipLong)) { + return false; + } + return value == ((ZipLong) o).getValue(); + } + + /** + * Override to make two instances with same value equal. + * + * @since 1.1 + */ + public int hashCode() { + return (int) value; + } + +}// ZipLong diff --git a/src/main/org/apache/tools/zip/ZipOutputStream.java b/src/main/org/apache/tools/zip/ZipOutputStream.java new file mode 100644 index 000000000..e7a5e689b --- /dev/null +++ b/src/main/org/apache/tools/zip/ZipOutputStream.java @@ -0,0 +1,619 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.zip; + +import java.io.*; +import java.util.Date; +import java.util.Hashtable; +import java.util.Vector; +import java.util.zip.CRC32; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.ZipException; + +/** + * Reimplementation of {@link java.util.zip.ZipOutputStream + * java.util.zip.ZipOutputStream} that does handle the extended + * functionality of this package, especially internal/external file + * attributes and extra fields with different layouts for local file + * data and central directory entries. + * + *

This implementation will use a Data Descriptor to store size and + * CRC information for DEFLATED entries, this means, you don't need to + * calculate them yourself. Unfortunately this is not possible for + * the STORED method, here setting the CRC and uncompressed size + * information is required before {@link #putNextEntry putNextEntry} + * will be called.

+ * + * @author Stefan Bodewig + * @version $Revision$ + */ +public class ZipOutputStream extends DeflaterOutputStream { + + /** + * Current entry. + * + * @since 1.1 + */ + private ZipEntry entry; + + /** + * The file comment. + * + * @since 1.1 + */ + private String comment = ""; + + /** + * Compression level for next entry. + * + * @since 1.1 + */ + private int level = Deflater.DEFAULT_COMPRESSION; + + /** + * Default compression method for next entry. + * + * @since 1.1 + */ + private int method = DEFLATED; + + /** + * List of ZipEntries written so far. + * + * @since 1.1 + */ + private Vector entries = new Vector(); + + /** + * CRC instance to avoid parsing DEFLATED data twice. + * + * @since 1.1 + */ + private CRC32 crc = new CRC32(); + + /** + * Count the bytes written to out. + * + * @since 1.1 + */ + private long written = 0; + + /** + * Data for current entry started here. + * + * @since 1.1 + */ + private long dataStart = 0; + + /** + * Start of central directory. + * + * @since 1.1 + */ + private ZipLong cdOffset = new ZipLong(0); + + /** + * Length of central directory. + * + * @since 1.1 + */ + private ZipLong cdLength = new ZipLong(0); + + /** + * Helper, a 0 as ZipShort. + * + * @since 1.1 + */ + private static final byte[] ZERO = {0, 0}; + + /** + * Helper, a 0 as ZipLong. + * + * @since 1.1 + */ + private static final byte[] LZERO = {0, 0, 0, 0}; + + /** + * Holds the offsets of the LFH starts for each entry + * + * @since 1.1 + */ + private Hashtable offsets = new Hashtable(); + + /** + * Compression method for deflated entries. + * + * @since 1.1 + */ + public static final int DEFLATED = ZipEntry.DEFLATED; + + /** + * Compression method for deflated entries. + * + * @since 1.1 + */ + public static final int STORED = ZipEntry.STORED; + + /** + * Creates a new ZIP OutputStream filtering the underlying stream. + * + * @since 1.1 + */ + public ZipOutputStream(OutputStream out) { + super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true)); + } + + /* + * Found out by experiment, that DeflaterOutputStream.close() + * will call finish() - so we don't need to override close + * ourselves.

+ */ + + /** + * Finishs writing the contents and closes this as well as the + * underlying stream. + * + * @since 1.1 + */ + public void finish() throws IOException { + closeEntry(); + cdOffset = new ZipLong(written); + for (int i=0; iDefault is Deflater.DEFAULT_COMPRESSION.

+ * + * @since 1.1 + */ + public void setLevel(int level) { + this.level = level; + } + + /** + * Sets the default compression method for subsequent entries. + * + *

Default is DEFLATED.

+ * + * @since 1.1 + */ + public void setMethod(int method) { + this.method = method; + } + + /** + * Writes bytes to ZIP entry. + * + *

Override is necessary to support STORED entries, as well as + * calculationg CRC automatically for DEFLATED entries.

+ */ + public void write(byte[] b, int offset, int length) throws IOException { + if (entry.getMethod() == DEFLATED) { + super.write(b, offset, length); + } else { + out.write(b, offset, length); + written += length; + } + crc.update(b, offset, length); + } + + /* + * Various ZIP constants + */ + /** + * local file header signature + * + * @since 1.1 + */ + protected static final ZipLong LFH_SIG = new ZipLong(0X04034B50L); + /** + * data descriptor signature + * + * @since 1.1 + */ + protected static final ZipLong DD_SIG = new ZipLong(0X08074B50L); + /** + * central file header signature + * + * @since 1.1 + */ + protected static final ZipLong CFH_SIG = new ZipLong(0X02014B50L); + /** + * end of central dir signature + * + * @since 1.1 + */ + protected static final ZipLong EOCD_SIG = new ZipLong(0X06054B50L); + + /** + * Writes the local file header entry + * + * @since 1.1 + */ + protected void writeLocalFileHeader(ZipEntry ze) throws IOException { + offsets.put(ze, new ZipLong(written)); + + out.write(LFH_SIG.getBytes()); + written += 4; + + // version needed to extract + // general purpose bit flag + if (ze.getMethod() == DEFLATED) { + // requires version 2 as we are going to store length info + // in the data descriptor + out.write((new ZipShort(20)).getBytes()); + + // bit3 set to signal, we use a data descriptor + out.write((new ZipShort(8)).getBytes()); + } else { + out.write((new ZipShort(10)).getBytes()); + out.write(ZERO); + } + written += 4; + + // compression method + out.write((new ZipShort(ze.getMethod())).getBytes()); + written += 2; + + // last mod. time and date + out.write(toDosTime(new Date(ze.getTime())).getBytes()); + written += 4; + + // CRC + // compressed length + // uncompressed length + if (ze.getMethod() == DEFLATED) { + out.write(LZERO); + out.write(LZERO); + out.write(LZERO); + } else { + out.write((new ZipLong(ze.getCrc())).getBytes()); + out.write((new ZipLong(ze.getSize())).getBytes()); + out.write((new ZipLong(ze.getSize())).getBytes()); + } + written += 12; + + // file name length + byte[] name = ze.getName().getBytes(); + out.write((new ZipShort(name.length)).getBytes()); + written += 2; + + // extra field length + byte[] extra = ze.getLocalFileDataExtra(); + out.write((new ZipShort(extra.length)).getBytes()); + written += 2; + + // file name + out.write(name); + written += name.length; + + // extra field + out.write(extra); + written += extra.length; + + dataStart = written; + } + + /** + * Writes the data descriptor entry + * + * @since 1.1 + */ + protected void writeDataDescriptor(ZipEntry ze) throws IOException { + if (ze.getMethod() != DEFLATED) { + return; + } + out.write(DD_SIG.getBytes()); + out.write((new ZipLong(entry.getCrc())).getBytes()); + out.write((new ZipLong(entry.getCompressedSize())).getBytes()); + out.write((new ZipLong(entry.getSize())).getBytes()); + written += 16; + } + + /** + * Writes the central file header entry + * + * @since 1.1 + */ + protected void writeCentralFileHeader(ZipEntry ze) throws IOException { + out.write(CFH_SIG.getBytes()); + written += 4; + + // version made by + out.write((new ZipShort(20)).getBytes()); + written += 2; + + // version needed to extract + // general purpose bit flag + if (ze.getMethod() == DEFLATED) { + // requires version 2 as we are going to store length info + // in the data descriptor + out.write((new ZipShort(20)).getBytes()); + + // bit3 set to signal, we use a data descriptor + out.write((new ZipShort(8)).getBytes()); + } else { + out.write((new ZipShort(10)).getBytes()); + out.write(ZERO); + } + written += 4; + + // compression method + out.write((new ZipShort(ze.getMethod())).getBytes()); + written += 2; + + // last mod. time and date + out.write(toDosTime(new Date(ze.getTime())).getBytes()); + written += 4; + + // CRC + // compressed length + // uncompressed length + out.write((new ZipLong(ze.getCrc())).getBytes()); + out.write((new ZipLong(ze.getCompressedSize())).getBytes()); + out.write((new ZipLong(ze.getSize())).getBytes()); + written += 12; + + // file name length + byte[] name = ze.getName().getBytes(); + out.write((new ZipShort(name.length)).getBytes()); + written += 2; + + // extra field length + byte[] extra = ze.getCentralDirectoryExtra(); + out.write((new ZipShort(extra.length)).getBytes()); + written += 2; + + // file comment length + String comm = ze.getComment(); + if (comm == null) { + comm = ""; + } + byte[] comment = comm.getBytes(); + out.write((new ZipShort(comment.length)).getBytes()); + written += 2; + + // disk number start + out.write(ZERO); + written += 2; + + // internal file attributes + out.write((new ZipShort(ze.getInternalAttributes())).getBytes()); + written += 2; + + // external file attributes + out.write((new ZipLong(ze.getExternalAttributes())).getBytes()); + written += 4; + + // relative offset of LFH + out.write(((ZipLong) offsets.get(ze)).getBytes()); + written += 4; + + // file name + out.write(name); + written += name.length; + + // extra field + out.write(extra); + written += extra.length; + + // file comment + out.write(comment); + written += comment.length; + } + + /** + * Writes the "End of central dir record" + * + * @since 1.1 + */ + protected void writeCentralDirectoryEnd() throws IOException { + out.write(EOCD_SIG.getBytes()); + + // disk numbers + out.write(ZERO); + out.write(ZERO); + + // number of entries + byte[] num = (new ZipShort(entries.size())).getBytes(); + out.write(num); + out.write(num); + + // length and location of CD + out.write(cdLength.getBytes()); + out.write(cdOffset.getBytes()); + + // ZIP file comment + byte[] data = comment.getBytes(); + out.write((new ZipShort(data.length)).getBytes()); + out.write(data); + } + + /** + * Smallest date/time ZIP can handle. + * + * @since 1.1 + */ + private static final ZipLong DOS_TIME_MIN = new ZipLong(0x00002100L); + + /** + * Convert a Date object to a DOS date/time field. + * + *

Stolen from InfoZip's fileio.c

+ * + * @since 1.1 + */ + protected static ZipLong toDosTime(Date time) { + int year = time.getYear() + 1900; + int month = time.getMonth() + 1; + if (year < 1980) { + return DOS_TIME_MIN; + } + long value = ((year - 1980) << 25) + | (month << 21) + | (time.getDate() << 16) + | (time.getHours() << 11) + | (time.getMinutes() << 5) + | (time.getSeconds() >> 1); + + byte[] result = new byte[4]; + result[0] = (byte) ((value & 0xFF)); + result[1] = (byte) ((value & 0xFF00) >> 8); + result[2] = (byte) ((value & 0xFF0000) >> 16); + result[3] = (byte) ((value & 0xFF000000l) >> 24); + return new ZipLong(result); + } + +} diff --git a/src/main/org/apache/tools/zip/ZipShort.java b/src/main/org/apache/tools/zip/ZipShort.java new file mode 100644 index 000000000..b4abf9fec --- /dev/null +++ b/src/main/org/apache/tools/zip/ZipShort.java @@ -0,0 +1,138 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.zip; + +/** + * Utility class that represents a two byte integer with conversion + * rules for the big endian byte order of ZIP files. + * + * @author Stefan Bodewig + * @version $Revision$ + */ +public class ZipShort implements Cloneable { + + private int value; + + /** + * Create instance from a number. + * + * @since 1.1 + */ + public ZipShort (int value) { + this.value = value; + } + + /** + * Create instance from bytes. + * + * @since 1.1 + */ + public ZipShort (byte[] bytes) { + this(bytes, 0); + } + + /** + * Create instance from the two bytes starting at offset. + * + * @since 1.1 + */ + public ZipShort (byte[] bytes, int offset) { + value = (bytes[offset+1] << 8) & 0xFF00; + value += (bytes[offset] & 0xFF); + } + + /** + * Get value as two bytes in big endian byte order. + * + * @since 1.1 + */ + public byte[] getBytes() { + byte[] result = new byte[2]; + result[0] = (byte) (value & 0xFF); + result[1] = (byte) ((value & 0xFF00) >> 8); + return result; + } + + /** + * Get value as Java int. + * + * @since 1.1 + */ + public int getValue() { + return value; + } + + /** + * Override to make two instances with same value equal. + * + * @since 1.1 + */ + public boolean equals(Object o) { + if (o == null || !(o instanceof ZipShort)) { + return false; + } + return value == ((ZipShort) o).getValue(); + } + + /** + * Override to make two instances with same value equal. + * + * @since 1.1 + */ + public int hashCode() { + return value; + } + +}// ZipShort diff --git a/src/testcases/org/apache/tools/zip/AsiExtraFieldTest.java b/src/testcases/org/apache/tools/zip/AsiExtraFieldTest.java new file mode 100644 index 000000000..5c09c7cb3 --- /dev/null +++ b/src/testcases/org/apache/tools/zip/AsiExtraFieldTest.java @@ -0,0 +1,178 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.zip; + +import junit.framework.TestCase; + +/** + * JUnit 3 testcases for org.apache.tools.zip.AsiExtraField. + * + * @author Stefan Bodewig + */ +public class AsiExtraFieldTest extends TestCase implements UnixStat { + public AsiExtraFieldTest(String name) { + super(name); + } + + /** + * Test file mode magic. + */ + public void testModes() { + AsiExtraField a = new AsiExtraField(); + a.setMode(0123); + assertEquals("plain file", 0100123, a.getMode()); + a.setDirectory(true); + assertEquals("directory", 040123, a.getMode()); + a.setLinkedFile("test"); + assertEquals("symbolic link", 0120123, a.getMode()); + } + + /** + * Test content. + */ + public void testContent() { + AsiExtraField a = new AsiExtraField(); + a.setMode(0123); + a.setUserId(5); + a.setGroupId(6); + byte[] b = a.getLocalFileDataData(); + + // CRC manually calculated, sorry + byte[] expect = {(byte)0xC6, 0x02, 0x78, (byte)0xB6, // CRC + 0123, (byte)0x80, // mode + 0, 0, 0, 0, // link length + 5, 0, 6, 0}; // uid, gid + assertEquals("no link", expect.length, b.length); + for (int i=0; i. + */ + +package org.apache.tools.zip; + +import junit.framework.TestCase; + +/** + * JUnit 3 testcases for org.apache.tools.zip.ExtraFieldUtils. + * + * @author Stefan Bodewig + */ +public class ExtraFieldUtilsTest extends TestCase implements UnixStat { + public ExtraFieldUtilsTest(String name) { + super(name); + } + + private AsiExtraField a; + private UnrecognizedExtraField dummy; + private byte[] data; + private byte[] aLocal; + + public void setUp() { + a = new AsiExtraField(); + a.setMode(0755); + a.setDirectory(true); + dummy = new UnrecognizedExtraField(); + dummy.setHeaderId(new ZipShort(1)); + dummy.setLocalFileDataData(new byte[0]); + dummy.setCentralDirectoryData(new byte[] {0}); + + aLocal = a.getLocalFileDataData(); + byte[] dummyLocal = dummy.getLocalFileDataData(); + data = new byte[4 + aLocal.length + 4 + dummyLocal.length]; + System.arraycopy(a.getHeaderId().getBytes(), 0, data, 0, 2); + System.arraycopy(a.getLocalFileDataLength().getBytes(), 0, data, 2, 2); + System.arraycopy(aLocal, 0, data, 4, aLocal.length); + System.arraycopy(dummy.getHeaderId().getBytes(), 0, data, + 4+aLocal.length, 2); + System.arraycopy(dummy.getLocalFileDataLength().getBytes(), 0, data, + 4+aLocal.length+2, 2); + System.arraycopy(dummyLocal, 0, data, + 4+aLocal.length+4, dummyLocal.length); + + } + + /** + * test parser. + */ + public void testParse() throws Exception { + ZipExtraField[] ze = ExtraFieldUtils.parse(data); + assertEquals("number of fields", 2, ze.length); + assert("type field 1", ze[0] instanceof AsiExtraField); + assertEquals("mode field 1", 040755, + ((AsiExtraField) ze[0]).getMode()); + assert("type field 2", ze[1] instanceof UnrecognizedExtraField); + assertEquals("data length field 2", 0, + ze[1].getLocalFileDataLength().getValue()); + + byte[] data2 = new byte[data.length-1]; + System.arraycopy(data, 0, data2, 0, data2.length); + try { + ExtraFieldUtils.parse(data2); + fail("data should be invalid"); + } catch (Exception e) { + assertEquals("message", + "data starting at "+(4+aLocal.length)+" is in unknown format", + e.getMessage()); + } + } + + /** + * Test merge methods + */ + public void testMerge() { + byte[] local = + ExtraFieldUtils.mergeLocalFileDataData(new ZipExtraField[] {a, dummy}); + assertEquals("local length", data.length, local.length); + for (int i=0; i. + */ + +package org.apache.tools.zip; + +import junit.framework.TestCase; + +/** + * JUnit 3 testcases for org.apache.tools.zip.ZipEntry. + * + * @author Stefan Bodewig + */ +public class ZipEntryTest extends TestCase { + + public ZipEntryTest(String name) { + super(name); + } + + /** + * test handling of extra fields + * + * @since 1.1 + */ + public void testExtraFields() { + AsiExtraField a = new AsiExtraField(); + a.setDirectory(true); + a.setMode(0755); + UnrecognizedExtraField u = new UnrecognizedExtraField(); + u.setHeaderId(new ZipShort(1)); + u.setLocalFileDataData(new byte[0]); + + ZipEntry ze = new ZipEntry("test/"); + ze.setExtraFields(new ZipExtraField[] {a, u}); + byte[] data1 = ze.getExtra(); + ZipExtraField[] result = ze.getExtraFields(); + assertEquals("first pass", 2, result.length); + assertSame(a, result[0]); + assertSame(u, result[1]); + + UnrecognizedExtraField u2 = new UnrecognizedExtraField(); + u2.setHeaderId(new ZipShort(1)); + u2.setLocalFileDataData(new byte[] {1}); + + ze.addExtraField(u2); + byte[] data2 = ze.getExtra(); + result = ze.getExtraFields(); + assertEquals("second pass", 2, result.length); + assertSame(a, result[0]); + assertSame(u2, result[1]); + assertEquals("length second pass", data1.length+1, data2.length); + + UnrecognizedExtraField u3 = new UnrecognizedExtraField(); + u3.setHeaderId(new ZipShort(2)); + u3.setLocalFileDataData(new byte[] {1}); + ze.addExtraField(u3); + result = ze.getExtraFields(); + assertEquals("third pass", 3, result.length); + + ze.removeExtraField(new ZipShort(1)); + byte[] data3 = ze.getExtra(); + result = ze.getExtraFields(); + assertEquals("fourth pass", 2, result.length); + assertSame(a, result[0]); + assertSame(u3, result[1]); + assertEquals("length fourth pass", data2.length, data3.length); + + try { + ze.removeExtraField(new ZipShort(1)); + fail("should be no such element"); + } catch (java.util.NoSuchElementException nse) { + } + } +} diff --git a/src/testcases/org/apache/tools/zip/ZipLongTest.java b/src/testcases/org/apache/tools/zip/ZipLongTest.java new file mode 100644 index 000000000..0c72db8a5 --- /dev/null +++ b/src/testcases/org/apache/tools/zip/ZipLongTest.java @@ -0,0 +1,119 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.zip; + +import junit.framework.TestCase; + +/** + * JUnit 3 testcases for org.apache.tools.zip.ZipLong. + * + * @author Stefan Bodewig + */ +public class ZipLongTest extends TestCase { + + public ZipLongTest(String name) { + super(name); + } + + /** + * Test conversion to bytes. + */ + public void testToBytes() { + ZipLong zl = new ZipLong(0x12345678); + byte[] result = zl.getBytes(); + assertEquals("length getBytes", 4, result.length); + assertEquals("first byte getBytes", 0x78, result[0]); + assertEquals("second byte getBytes", 0x56, result[1]); + assertEquals("third byte getBytes", 0x34, result[2]); + assertEquals("fourth byte getBytes", 0x12, result[3]); + } + + /** + * Test conversion from bytes. + */ + public void testFromBytes() { + byte[] val = new byte[] {0x78, 0x56, 0x34, 0x12}; + ZipLong zl = new ZipLong(val); + assertEquals("value from bytes", 0x12345678, zl.getValue()); + } + + /** + * Test the contract of the equals method. + */ + public void testEquals() { + ZipLong zl = new ZipLong(0x12345678); + ZipLong zl2 = new ZipLong(0x12345678); + ZipLong zl3 = new ZipLong(0x87654321); + + assert("reflexive", zl.equals(zl)); + + assert("works", zl.equals(zl2)); + assert("works, part two", !zl.equals(zl3)); + + assert("symmetric", zl2.equals(zl)); + + assert("null handling", !zl.equals(null)); + assert("non ZipLong handling", !zl.equals(new Integer(0x1234))); + } + + /** + * Test sign handling. + */ + public void testSign() { + ZipLong zl = new ZipLong(new byte[] {(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF}); + assertEquals(0x00000000FFFFFFFFl, zl.getValue()); + } + +} diff --git a/src/testcases/org/apache/tools/zip/ZipShortTest.java b/src/testcases/org/apache/tools/zip/ZipShortTest.java new file mode 100644 index 000000000..733f3f867 --- /dev/null +++ b/src/testcases/org/apache/tools/zip/ZipShortTest.java @@ -0,0 +1,117 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.zip; + +import junit.framework.TestCase; + +/** + * JUnit 3 testcases for org.apache.tools.zip.ZipShort. + * + * @author Stefan Bodewig + */ +public class ZipShortTest extends TestCase { + + public ZipShortTest(String name) { + super(name); + } + + /** + * Test conversion to bytes. + */ + public void testToBytes() { + ZipShort zs = new ZipShort(0x1234); + byte[] result = zs.getBytes(); + assertEquals("length getBytes", 2, result.length); + assertEquals("first byte getBytes", 0x34, result[0]); + assertEquals("second byte getBytes", 0x12, result[1]); + } + + /** + * Test conversion from bytes. + */ + public void testFromBytes() { + byte[] val = new byte[] {0x34, 0x12}; + ZipShort zs = new ZipShort(val); + assertEquals("value from bytes", 0x1234, zs.getValue()); + } + + /** + * Test the contract of the equals method. + */ + public void testEquals() { + ZipShort zs = new ZipShort(0x1234); + ZipShort zs2 = new ZipShort(0x1234); + ZipShort zs3 = new ZipShort(0x5678); + + assert("reflexive", zs.equals(zs)); + + assert("works", zs.equals(zs2)); + assert("works, part two", !zs.equals(zs3)); + + assert("symmetric", zs2.equals(zs)); + + assert("null handling", !zs.equals(null)); + assert("non ZipShort handling", !zs.equals(new Integer(0x1234))); + } + + /** + * Test sign handling. + */ + public void testSign() { + ZipShort zs = new ZipShort(new byte[] {(byte)0xFF, (byte)0xFF}); + assertEquals(0x0000FFFF, zs.getValue()); + } + +}