Author: tdraier
Date: Fri Jul  6 15:01:35 2007
New Revision: 17906

URL: https://svndev.jahia.net/websvn/listing.php?sc=3D1&rev=3D17906&repname=
=3Djahia
Log:
replaced zip implementation by more encoding permissive one ( DOC-112 , DOC=
-111 , DOC-134 ) . If filenames does not appear to be utf8, switch to Cp437=
 (which seems to be winzip default) (backport 17352)

Added:
    trunk/core/src/java/org/jahia/utils/zip/DeflaterOutputStream.java
    trunk/core/src/java/org/jahia/utils/zip/InflaterInputStream.java
    trunk/core/src/java/org/jahia/utils/zip/ZipConstants.java
    trunk/core/src/java/org/jahia/utils/zip/ZipEntry.java
    trunk/core/src/java/org/jahia/utils/zip/ZipInputStream.java
    trunk/core/src/java/org/jahia/utils/zip/ZipOutputStream.java

Added: trunk/core/src/java/org/jahia/utils/zip/DeflaterOutputStream.java
URL: https://svndev.jahia.net/websvn/filedetails.php?path=3D/trunk/core/src=
/java/org/jahia/utils/zip/DeflaterOutputStream.java&rev=3D17906&repname=3Dj=
ahia
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/core/src/java/org/jahia/utils/zip/DeflaterOutputStream.java (adde=
d)
+++ trunk/core/src/java/org/jahia/utils/zip/DeflaterOutputStream.java Fri J=
ul  6 15:01:35 2007
@@ -0,0 +1,164 @@
+/*
+ * @(#)DeflaterOutputStream.java       1.34 06/04/05
+ *
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+package org.jahia.utils.zip;
+
+import java.io.FilterOutputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.util.zip.Deflater;
+
+/**
+ * This class implements an output stream filter for compressing data in
+ * the "deflate" compression format. It is also used as the basis for other
+ * types of compression filters, such as GZIPOutputStream.
+ *
+ * @see                Deflater
+ * @version    1.34, 01/12/04
+ * @author     David Connelly
+ */
+public
+class DeflaterOutputStream extends FilterOutputStream {
+    /**
+     * Compressor for this stream.
+     */
+    protected Deflater def;
+
+    /**
+     * Output buffer for writing compressed data.
+     */
+    protected byte[] buf;
+
+    /**
+     * Indicates that the stream has been closed.
+     */
+
+    private boolean closed =3D false;
+
+    /**
+     * Creates a new output stream with the specified compressor and
+     * buffer size.
+     * @param out the output stream
+     * @param def the compressor ("deflater")
+     * @param size the output buffer size
+     * @exception IllegalArgumentException if size is <=3D 0
+     */
+    public DeflaterOutputStream(OutputStream out, Deflater def, int size) {
+        super(out);
+        if (out =3D=3D null || def =3D=3D null) {
+            throw new NullPointerException();
+        } else if (size <=3D 0) {
+            throw new IllegalArgumentException("buffer size <=3D 0");
+        }
+        this.def =3D def;
+        buf =3D new byte[size];
+    }
+
+    /**
+     * Creates a new output stream with the specified compressor and
+     * a default buffer size.
+     * @param out the output stream
+     * @param def the compressor ("deflater")
+     */
+    public DeflaterOutputStream(OutputStream out, Deflater def) {
+       this(out, def, 512);
+    }
+
+    boolean usesDefaultDeflater =3D false;
+
+    /**
+     * Creates a new output stream with a default compressor and buffer si=
ze.
+     * @param out the output stream
+     */
+    public DeflaterOutputStream(OutputStream out) {
+       this(out, new Deflater());
+        usesDefaultDeflater =3D true;
+    }
+
+    /**
+     * Writes a byte to the compressed output stream. This method will
+     * block until the byte can be written.
+     * @param b the byte to be written
+     * @exception IOException if an I/O error has occurred
+     */
+    public void write(int b) throws IOException {
+        byte[] buf =3D new byte[1];
+       buf[0] =3D (byte)(b & 0xff);
+       write(buf, 0, 1);
+    }
+
+    /**
+     * Writes an array of bytes to the compressed output stream. This
+     * method will block until all the bytes are written.
+     * @param b the data to be written
+     * @param off the start offset of the data
+     * @param len the length of the data
+     * @exception IOException if an I/O error has occurred
+     */
+    public void write(byte[] b, int off, int len) throws IOException {
+       if (def.finished()) {
+           throw new IOException("write beyond end of stream");
+       }
+        if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
+           throw new IndexOutOfBoundsException();
+       } else if (len =3D=3D 0) {
+           return;
+       }
+       if (!def.finished()) {
+            // Deflate no more than stride bytes at a time.  This avoids
+            // excess copying in deflateBytes (see Deflater.c)
+            int stride =3D buf.length;
+            for (int i =3D 0; i < len; i+=3D stride) {
+                def.setInput(b, off + i, Math.min(stride, len - i));
+                while (!def.needsInput()) {
+                    deflate();
+                }
+           }
+       }
+    }
+
+    /**
+     * Finishes writing compressed data to the output stream without closi=
ng
+     * the underlying stream. Use this method when applying multiple filte=
rs
+     * in succession to the same output stream.
+     * @exception IOException if an I/O error has occurred
+     */
+    public void finish() throws IOException {
+       if (!def.finished()) {
+           def.finish();
+           while (!def.finished()) {
+               deflate();
+           }
+       }
+    }
+
+    /**
+     * Writes remaining compressed data to the output stream and closes the
+     * underlying stream.
+     * @exception IOException if an I/O error has occurred
+     */
+    public void close() throws IOException {
+        if (!closed) {
+            finish();
+            if (usesDefaultDeflater)
+                def.end();
+            out.close();
+            closed =3D true;
+        }
+    }
+
+    /**
+     * Writes next block of compressed data to the output stream.
+     * @throws IOException if an I/O error has occurred
+     */
+    protected void deflate() throws IOException {
+       int len =3D def.deflate(buf, 0, buf.length);
+       if (len > 0) {
+           out.write(buf, 0, len);
+       }
+    }
+}

Added: trunk/core/src/java/org/jahia/utils/zip/InflaterInputStream.java
URL: https://svndev.jahia.net/websvn/filedetails.php?path=3D/trunk/core/src=
/java/org/jahia/utils/zip/InflaterInputStream.java&rev=3D17906&repname=3Dja=
hia
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/core/src/java/org/jahia/utils/zip/InflaterInputStream.java (added)
+++ trunk/core/src/java/org/jahia/utils/zip/InflaterInputStream.java Fri Ju=
l  6 15:01:35 2007
@@ -0,0 +1,222 @@
+/*
+ * @(#)InflaterInputStream.java        1.32 03/01/23
+ *
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+package org.jahia.utils.zip;
+
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.EOFException;
+import java.util.zip.Inflater;
+import java.util.zip.DataFormatException;
+import java.util.zip.ZipException;
+
+/**
+ * This class implements a stream filter for uncompressing data in the
+ * "deflate" compression format. It is also used as the basis for other
+ * decompression filters, such as GZIPInputStream.
+ *
+ * @see                Inflater
+ * @version    1.32, 01/23/03
+ * @author     David Connelly
+ */
+public
+class InflaterInputStream extends FilterInputStream {
+    /**
+     * Decompressor for this stream.
+     */
+    protected Inflater inf;
+
+    /**
+     * Input buffer for decompression.
+     */
+    protected byte[] buf;
+
+    /**
+     * Length of input buffer.
+     */
+    protected int len;
+
+    private boolean closed =3D false;
+    // this flag is set to true after EOF has reached
+    private boolean reachEOF =3D false;
+
+    /**
+     * Check to make sure that this stream has not been closed
+     */
+    private void ensureOpen() throws IOException {
+       if (closed) {
+           throw new IOException("Stream closed");
+        }
+    }
+
+
+    /**
+     * Creates a new input stream with the specified decompressor and
+     * buffer size.
+     * @param in the input stream
+     * @param inf the decompressor ("inflater")
+     * @param size the input buffer size
+     * @exception IllegalArgumentException if size is <=3D 0
+     */
+    public InflaterInputStream(InputStream in, Inflater inf, int size) {
+       super(in);
+        if (in =3D=3D null || inf =3D=3D null) {
+            throw new NullPointerException();
+        } else if (size <=3D 0) {
+            throw new IllegalArgumentException("buffer size <=3D 0");
+        }
+       this.inf =3D inf;
+       buf =3D new byte[size];
+    }
+
+    /**
+     * Creates a new input stream with the specified decompressor and a
+     * default buffer size.
+     * @param in the input stream
+     * @param inf the decompressor ("inflater")
+     */
+    public InflaterInputStream(InputStream in, Inflater inf) {
+       this(in, inf, 512);
+    }
+
+    boolean usesDefaultInflater =3D false;
+
+    /**
+     * Creates a new input stream with a default decompressor and buffer s=
ize.
+     * @param in the input stream
+     */
+    public InflaterInputStream(InputStream in) {
+       this(in, new Inflater());
+        usesDefaultInflater =3D true;
+    }
+
+    private byte[] singleByteBuf =3D new byte[1];
+
+    /**
+     * Reads a byte of uncompressed data. This method will block until
+     * enough input is available for decompression.
+     * @return the byte read, or -1 if end of compressed input is reached
+     * @exception IOException if an I/O error has occurred
+     */
+    public int read() throws IOException {
+       ensureOpen();
+       return read(singleByteBuf, 0, 1) =3D=3D -1 ? -1 : singleByteBuf[0] & 
0xff;
+    }
+
+    /**
+     * Reads uncompressed data into an array of bytes. This method will
+     * block until some input can be decompressed.
+     * @param b the buffer into which the data is read
+     * @param off the start offset of the data
+     * @param len the maximum number of bytes read
+     * @return the actual number of bytes read, or -1 if the end of the
+     *         compressed input is reached or a preset dictionary is needed
+     * @exception java.util.zip.ZipException if a ZIP format error has occ=
urred
+     * @exception IOException if an I/O error has occurred
+     */
+    public int read(byte[] b, int off, int len) throws IOException {
+       ensureOpen();
+        if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
+           throw new IndexOutOfBoundsException();
+       } else if (len =3D=3D 0) {
+           return 0;
+       }
+       try {
+           int n;
+           while ((n =3D inf.inflate(b, off, len)) =3D=3D 0) {
+               if (inf.finished() || inf.needsDictionary()) {
+                    reachEOF =3D true;
+                   return -1;
+               }
+               if (inf.needsInput()) {
+                   fill();
+               }
+           }
+           return n;
+       } catch (DataFormatException e) {
+           String s =3D e.getMessage();
+           throw new ZipException(s !=3D null ? s : "Invalid ZLIB data 
format");
+       }
+    }
+
+    /**
+     * Returns 0 after EOF has reached, otherwise always return 1.
+     * <p>
+     * Programs should not count on this method to return the actual number
+     * of bytes that could be read without blocking.
+     *
+     * @return     1 before EOF and 0 after EOF.
+     * @exception  IOException  if an I/O error occurs.
+     *
+     */
+    public int available() throws IOException {
+        ensureOpen();
+        if (reachEOF) {
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    private byte[] b =3D new byte[512];
+
+    /**
+     * Skips specified number of bytes of uncompressed data.
+     * @param n the number of bytes to skip
+     * @return the actual number of bytes skipped.
+     * @exception IOException if an I/O error has occurred
+     * @exception IllegalArgumentException if n < 0
+     */
+    public long skip(long n) throws IOException {
+        if (n < 0) {
+            throw new IllegalArgumentException("negative skip length");
+        }
+       ensureOpen();
+       int max =3D (int)Math.min(n, Integer.MAX_VALUE);
+       int total =3D 0;
+       while (total < max) {
+           int len =3D max - total;
+           if (len > b.length) {
+               len =3D b.length;
+           }
+           len =3D read(b, 0, len);
+           if (len =3D=3D -1) {
+                reachEOF =3D true;
+               break;
+           }
+           total +=3D len;
+       }
+       return total;
+    }
+
+    /**
+     * Closes the input stream.
+     * @exception IOException if an I/O error has occurred
+     */
+    public void close() throws IOException {
+        if (!closed) {
+            if (usesDefaultInflater)
+                inf.end();
+           in.close();
+            closed =3D true;
+        }
+    }
+
+    /**
+     * Fills input buffer with more data to decompress.
+     * @exception IOException if an I/O error has occurred
+     */
+    protected void fill() throws IOException {
+       ensureOpen();
+       len =3D in.read(buf, 0, buf.length);
+       if (len =3D=3D -1) {
+           throw new EOFException("Unexpected end of ZLIB input stream");
+       }
+       inf.setInput(buf, 0, len);
+    }
+}

Added: trunk/core/src/java/org/jahia/utils/zip/ZipConstants.java
URL: https://svndev.jahia.net/websvn/filedetails.php?path=3D/trunk/core/src=
/java/org/jahia/utils/zip/ZipConstants.java&rev=3D17906&repname=3Djahia
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/core/src/java/org/jahia/utils/zip/ZipConstants.java (added)
+++ trunk/core/src/java/org/jahia/utils/zip/ZipConstants.java Fri Jul  6 15=
:01:35 2007
@@ -0,0 +1,81 @@
+/*
+ * @(#)ZipConstants.java       1.17 03/01/23
+ *
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+package org.jahia.utils.zip;
+
+/*
+ * This interface defines the constants that are used by the classes
+ * which manipulate ZIP files.
+ *
+ * @version    1.17, 01/23/03
+ * @author     David Connelly
+ */
+interface ZipConstants {
+    /*
+     * Header signatures
+     */
+    static long LOCSIG =3D 0x04034b50L;        // "PK\003\004"
+    static long EXTSIG =3D 0x08074b50L;        // "PK\007\008"
+    static long CENSIG =3D 0x02014b50L;        // "PK\001\002"
+    static long ENDSIG =3D 0x06054b50L;        // "PK\005\006"
+
+    /*
+     * Header sizes in bytes (including signatures)
+     */
+    static final int LOCHDR =3D 30;    // LOC header size
+    static final int EXTHDR =3D 16;    // EXT header size
+    static final int CENHDR =3D 46;    // CEN header size
+    static final int ENDHDR =3D 22;    // END header size
+
+    /*
+     * Local file (LOC) header field offsets
+     */
+    static final int LOCVER =3D 4;     // version needed to extract
+    static final int LOCFLG =3D 6;     // general purpose bit flag
+    static final int LOCHOW =3D 8;     // compression method
+    static final int LOCTIM =3D 10;    // modification time
+    static final int LOCCRC =3D 14;    // uncompressed file crc-32 value
+    static final int LOCSIZ =3D 18;    // compressed size
+    static final int LOCLEN =3D 22;    // uncompressed size
+    static final int LOCNAM =3D 26;    // filename length
+    static final int LOCEXT =3D 28;    // extra field length
+
+    /*
+     * Extra local (EXT) header field offsets
+     */
+    static final int EXTCRC =3D 4;     // uncompressed file crc-32 value
+    static final int EXTSIZ =3D 8;     // compressed size
+    static final int EXTLEN =3D 12;    // uncompressed size
+
+    /*
+     * Central directory (CEN) header field offsets
+     */
+    static final int CENVEM =3D 4;     // version made by
+    static final int CENVER =3D 6;     // version needed to extract
+    static final int CENFLG =3D 8;     // encrypt, decrypt flags
+    static final int CENHOW =3D 10;    // compression method
+    static final int CENTIM =3D 12;    // modification time
+    static final int CENCRC =3D 16;    // uncompressed file crc-32 value
+    static final int CENSIZ =3D 20;    // compressed size
+    static final int CENLEN =3D 24;    // uncompressed size
+    static final int CENNAM =3D 28;    // filename length
+    static final int CENEXT =3D 30;    // extra field length
+    static final int CENCOM =3D 32;    // comment length
+    static final int CENDSK =3D 34;    // disk number start
+    static final int CENATT =3D 36;    // internal file attributes
+    static final int CENATX =3D 38;    // external file attributes
+    static final int CENOFF =3D 42;    // LOC header offset
+
+    /*
+     * End of central directory (END) header field offsets
+     */
+    static final int ENDSUB =3D 8;     // number of entries on this disk
+    static final int ENDTOT =3D 10;    // total number of entries
+    static final int ENDSIZ =3D 12;    // central directory size in bytes
+    static final int ENDOFF =3D 16;    // offset of first CEN header
+    static final int ENDCOM =3D 20;    // zip file comment length
+}

Added: trunk/core/src/java/org/jahia/utils/zip/ZipEntry.java
URL: https://svndev.jahia.net/websvn/filedetails.php?path=3D/trunk/core/src=
/java/org/jahia/utils/zip/ZipEntry.java&rev=3D17906&repname=3Djahia
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/core/src/java/org/jahia/utils/zip/ZipEntry.java (added)
+++ trunk/core/src/java/org/jahia/utils/zip/ZipEntry.java Fri Jul  6 15:01:=
35 2007
@@ -0,0 +1,331 @@
+/*
+ * @(#)ZipEntry.java   1.37 05/04/29
+ *
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+package org.jahia.utils.zip;
+
+import java.util.Date;
+
+/**
+ * This class is used to represent a ZIP file entry.
+ *
+ * @version    1.37, 04/29/05
+ * @author     David Connelly
+ */
+public
+class ZipEntry implements ZipConstants, Cloneable {
+    String name;       // entry name
+    long time =3D -1;  // modification time (in DOS time)
+    long crc =3D -1;   // crc-32 of entry data
+    long size =3D -1;  // uncompressed size of entry data
+    long csize =3D -1;         // compressed size of entry data
+    int method =3D -1; // compression method
+    byte[] extra;      // optional extra field data for entry
+    String comment;    // optional comment string for entry
+    // The following flags are used only by Zip{Input,Output}Stream
+    int flag;          // bit flags
+    int version;       // version needed to extract
+    long offset;       // offset of loc header
+
+    /**
+     * Compression method for uncompressed entries.
+     */
+    public static final int STORED =3D 0;
+
+    /**
+     * Compression method for compressed (deflated) entries.
+     */
+    public static final int DEFLATED =3D 8;
+
+//    static {
+//     /* Zip library is loaded from System.initializeSystemClass */
+//     initIDs();
+//    }
+//
+//    private static native void initIDs();
+
+    /**
+     * Creates a new zip entry with the specified name.
+     *
+     * @param name the entry name
+     * @exception NullPointerException if the entry name is null
+     * @exception IllegalArgumentException if the entry name is longer than
+     *           0xFFFF bytes
+     */
+    public ZipEntry(String name) {
+       if (name =3D=3D null) {
+           throw new NullPointerException();
+       }
+       if (name.length() > 0xFFFF) {
+           throw new IllegalArgumentException("entry name too long");
+       }
+       this.name =3D name;
+    }
+
+    /**
+     * Creates a new zip entry with fields taken from the specified
+     * zip entry.
+     * @param e a zip Entry object
+     */
+    public ZipEntry(ZipEntry e) {
+       name =3D e.name;
+       time =3D e.time;
+       crc =3D e.crc;
+       size =3D e.size;
+       csize =3D e.csize;
+       method =3D e.method;
+       extra =3D e.extra;
+       comment =3D e.comment;
+    }
+
+    /*
+     * Creates a new zip entry for the given name with fields initialized
+     * from the specified jzentry data.
+     */
+//    ZipEntry(String name, long jzentry) {
+//     this.name =3D name;
+//     initFields(jzentry);
+//    }
+
+//    private native void initFields(long jzentry);
+
+    /*
+     * Creates a new zip entry with fields initialized from the specified
+     * jzentry data.
+     */
+//    ZipEntry(long jzentry) {
+//     initFields(jzentry);
+//    }
+
+    /**
+     * Returns the name of the entry.
+     * @return the name of the entry
+     */
+    public String getName() {
+       return name;
+    }
+
+    /**
+     * Sets the modification time of the entry.
+     * @param time the entry modification time in number of milliseconds
+     *            since the epoch
+     * @see #getTime()
+     */
+    public void setTime(long time) {
+       this.time =3D javaToDosTime(time);
+    }
+
+    /**
+     * Returns the modification time of the entry, or -1 if not specified.
+     * @return the modification time of the entry, or -1 if not specified
+     * @see #setTime(long)
+     */
+    public long getTime() {
+       return time !=3D -1 ? dosToJavaTime(time) : -1;
+    }
+
+    /**
+     * Sets the uncompressed size of the entry data.
+     * @param size the uncompressed size in bytes
+     * @exception IllegalArgumentException if the specified size is less
+     *           than 0 or greater than 0xFFFFFFFF bytes
+     * @see #getSize()
+     */
+    public void setSize(long size) {
+       if (size < 0 || size > 0xFFFFFFFFL) {
+           throw new IllegalArgumentException("invalid entry size");
+       }
+       this.size =3D size;
+    }
+
+    /**
+     * Returns the uncompressed size of the entry data, or -1 if not known.
+     * @return the uncompressed size of the entry data, or -1 if not known
+     * @see #setSize(long)
+     */
+    public long getSize() {
+       return size;
+    }
+
+    /**
+     * Returns the size of the compressed entry data, or -1 if not known.
+     * In the case of a stored entry, the compressed size will be the same
+     * as the uncompressed size of the entry.
+     * @return the size of the compressed entry data, or -1 if not known
+     * @see #setCompressedSize(long)
+     */
+    public long getCompressedSize() {
+       return csize;
+    }
+
+    /**
+     * Sets the size of the compressed entry data.
+     * @param csize the compressed size to set to
+     * @see #getCompressedSize()
+     */
+    public void setCompressedSize(long csize) {
+       this.csize =3D csize;
+    }
+
+    /**
+     * Sets the CRC-32 checksum of the uncompressed entry data.
+     * @param crc the CRC-32 value
+     * @exception IllegalArgumentException if the specified CRC-32 value is
+     *           less than 0 or greater than 0xFFFFFFFF
+     * @see #setCrc(long)
+     */
+    public void setCrc(long crc) {
+       if (crc < 0 || crc > 0xFFFFFFFFL) {
+           throw new IllegalArgumentException("invalid entry crc-32");
+       }
+       this.crc =3D crc;
+    }
+
+    /**
+     * Returns the CRC-32 checksum of the uncompressed entry data, or -1 if
+     * not known.
+     * @return the CRC-32 checksum of the uncompressed entry data, or -1 if
+     * not known
+     * @see #getCrc()
+     */
+    public long getCrc() {
+       return crc;
+    }
+
+    /**
+     * Sets the compression method for the entry.
+     * @param method the compression method, either STORED or DEFLATED
+     * @exception IllegalArgumentException if the specified compression
+     *           method is invalid
+     * @see #getMethod()
+     */
+    public void setMethod(int method) {
+       if (method !=3D STORED && method !=3D DEFLATED) {
+           throw new IllegalArgumentException("invalid compression method");
+       }
+       this.method =3D method;
+    }
+
+    /**
+     * Returns the compression method of the entry, or -1 if not specified.
+     * @return the compression method of the entry, or -1 if not specified
+     * @see #setMethod(int)
+     */
+    public int getMethod() {
+       return method;
+    }
+
+    /**
+     * Sets the optional extra field data for the entry.
+     * @param extra the extra field data bytes
+     * @exception IllegalArgumentException if the length of the specified
+     *           extra field data is greater than 0xFFFF bytes
+     * @see #getExtra()
+     */
+    public void setExtra(byte[] extra) {
+       if (extra !=3D null && extra.length > 0xFFFF) {
+           throw new IllegalArgumentException("invalid extra field length");
+       }
+       this.extra =3D extra;
+    }
+
+    /**
+     * Returns the extra field data for the entry, or null if none.
+     * @return the extra field data for the entry, or null if none
+     * @see #setExtra(byte[])
+     */
+    public byte[] getExtra() {
+       return extra;
+    }
+
+    /**
+     * Sets the optional comment string for the entry.
+     * @param comment the comment string
+     * @exception IllegalArgumentException if the length of the specified
+     *           comment string is greater than 0xFFFF bytes
+     * @see #getComment()
+     */
+    public void setComment(String comment) {
+       if (comment !=3D null && comment.length() > 0xffff/3
+                    && ZipOutputStream.getUTF8Length(comment) > 0xffff) {
+           throw new IllegalArgumentException("invalid entry comment length");
+       }
+       this.comment =3D comment;
+    }
+
+    /**
+     * Returns the comment string for the entry, or null if none.
+     * @return the comment string for the entry, or null if none
+     * @see #setComment(String)
+     */
+    public String getComment() {
+       return comment;
+    }
+
+    /**
+     * Returns true if this is a directory entry. A directory entry is
+     * defined to be one whose name ends with a '/'.
+     * @return true if this is a directory entry
+     */
+    public boolean isDirectory() {
+       return name.endsWith("/");
+    }
+
+    /**
+     * Returns a string representation of the ZIP entry.
+     */
+    public String toString() {
+       return getName();
+    }
+
+    /*
+     * Converts DOS time to Java time (number of milliseconds since epoch).
+     */
+    private static long dosToJavaTime(long dtime) {
+       Date d =3D new Date((int)(((dtime >> 25) & 0x7f) + 80),
+                         (int)(((dtime >> 21) & 0x0f) - 1),
+                         (int)((dtime >> 16) & 0x1f),
+                         (int)((dtime >> 11) & 0x1f),
+                         (int)((dtime >> 5) & 0x3f),
+                         (int)((dtime << 1) & 0x3e));
+       return d.getTime();
+    }
+
+    /*
+     * Converts Java time to DOS time.
+     */
+    private static long javaToDosTime(long time) {
+       Date d =3D new Date(time);
+       int year =3D d.getYear() + 1900;
+       if (year < 1980) {
+           return (1 << 21) | (1 << 16);
+       }
+       return (year - 1980) << 25 | (d.getMonth() + 1) << 21 |
+               d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << =
5 |
+               d.getSeconds() >> 1;
+    }
+
+    /**
+     * Returns the hash code value for this entry.
+     */
+    public int hashCode() {
+       return name.hashCode();
+    }
+
+    /**
+     * Returns a copy of this entry.
+     */
+    public Object clone() {
+       try {
+           ZipEntry e =3D (ZipEntry)super.clone();
+           e.extra =3D (extra =3D=3D null ? null : (byte[])extra.clone());
+           return e;
+       } catch (CloneNotSupportedException e) {
+           // This should never happen, since we are Cloneable
+           throw new InternalError();
+       }
+    }
+}

Added: trunk/core/src/java/org/jahia/utils/zip/ZipInputStream.java
URL: https://svndev.jahia.net/websvn/filedetails.php?path=3D/trunk/core/src=
/java/org/jahia/utils/zip/ZipInputStream.java&rev=3D17906&repname=3Djahia
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/core/src/java/org/jahia/utils/zip/ZipInputStream.java (added)
+++ trunk/core/src/java/org/jahia/utils/zip/ZipInputStream.java Fri Jul  6 =
15:01:35 2007
@@ -0,0 +1,372 @@
+/*
+ * @(#)ZipInputStream.java     1.33 03/02/07
+ *
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+package org.jahia.utils.zip;
+
+import org.apache.commons.lang.ArrayUtils;
+
+import java.io.*;
+import java.util.zip.CRC32;
+import java.util.zip.Inflater;
+import java.util.zip.ZipException;
+
+/**
+ * This class implements an input stream filter for reading files in the
+ * ZIP file format. Includes support for both compressed and uncompressed
+ * entries.
+ *
+ * @author     David Connelly
+ * @version    1.33, 02/07/03
+ */
+public
+class ZipInputStream extends InflaterInputStream implements ZipConstants {
+    private ZipEntry entry;
+    private CRC32 crc =3D new CRC32();
+    private long remaining;
+    private byte[] tmpbuf =3D new byte[512];
+
+    private static final int STORED =3D ZipEntry.STORED;
+    private static final int DEFLATED =3D ZipEntry.DEFLATED;
+
+    private boolean closed =3D false;
+    // this flag is set to true after EOF has reached for
+    // one entry
+    private boolean entryEOF =3D false;
+
+    /**
+     * Check to make sure that this stream has not been closed
+     */
+    private void ensureOpen() throws IOException {
+       if (closed) {
+           throw new IOException("Stream closed");
+        }
+    }
+
+    /**
+     * Creates a new ZIP input stream.
+     * @param in the actual input stream
+     */
+    public ZipInputStream(InputStream in) {
+       super(new PushbackInputStream(in, 512), new Inflater(true), 512);
+        usesDefaultInflater =3D true;
+        if(in =3D=3D null) {
+            throw new NullPointerException("in is null");
+        }
+    }
+
+    /**
+     * Reads the next ZIP file entry and positions stream at the beginning
+     * of the entry data.
+     * @return the ZipEntry just read
+     * @exception ZipException if a ZIP file error has occurred
+     * @exception IOException if an I/O error has occurred
+     */
+    public ZipEntry getNextEntry() throws IOException {
+        ensureOpen();
+       if (entry !=3D null) {
+           closeEntry();
+       }
+       crc.reset();
+       inf.reset();
+       if ((entry =3D readLOC()) =3D=3D null) {
+           return null;
+       }
+       if (entry.method =3D=3D STORED) {
+           remaining =3D entry.size;
+       }
+        entryEOF =3D false;
+       return entry;
+    }
+
+    /**
+     * Closes the current ZIP entry and positions the stream for reading t=
he
+     * next entry.
+     * @exception java.util.zip.ZipException if a ZIP file error has occur=
red
+     * @exception IOException if an I/O error has occurred
+     */
+    public void closeEntry() throws IOException {
+        ensureOpen();
+       while (read(tmpbuf, 0, tmpbuf.length) !=3D -1) ;
+        entryEOF =3D true;
+    }
+
+    /**
+     * Returns 0 after EOF has reached for the current entry data,
+     * otherwise always return 1.
+     * <p>
+     * Programs should not count on this method to return the actual number
+     * of bytes that could be read without blocking.
+     *
+     * @return     1 before EOF and 0 after EOF has reached for current en=
try.
+     * @exception  IOException  if an I/O error occurs.
+     *
+     */
+    public int available() throws IOException {
+        ensureOpen();
+        if (entryEOF) {
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    /**
+     * Reads from the current ZIP entry into an array of bytes. Blocks unt=
il
+     * some input is available.
+     * @param b the buffer into which the data is read
+     * @param off the start offset of the data
+     * @param len the maximum number of bytes read
+     * @return the actual number of bytes read, or -1 if the end of the
+     *         entry is reached
+     * @exception java.util.zip.ZipException if a ZIP file error has occur=
red
+     * @exception IOException if an I/O error has occurred
+     */
+    public int read(byte[] b, int off, int len) throws IOException {
+        ensureOpen();
+        if (off < 0 || len < 0 || off > b.length - len) {
+           throw new IndexOutOfBoundsException();
+       } else if (len =3D=3D 0) {
+           return 0;
+       }
+
+       if (entry =3D=3D null) {
+           return -1;
+       }
+       switch (entry.method) {
+       case DEFLATED:
+           len =3D super.read(b, off, len);
+           if (len =3D=3D -1) {
+               readEnd(entry);
+                entryEOF =3D true;
+               entry =3D null;
+           } else {
+               crc.update(b, off, len);
+           }
+           return len;
+       case STORED:
+           if (remaining <=3D 0) {
+                entryEOF =3D true;
+               entry =3D null;
+               return -1;
+           }
+           if (len > remaining) {
+               len =3D (int)remaining;
+           }
+           len =3D in.read(b, off, len);
+           if (len =3D=3D -1) {
+               throw new ZipException("unexpected EOF");
+           }
+           crc.update(b, off, len);
+           remaining -=3D len;
+           return len;
+       default:
+           throw new InternalError("invalid compression method");
+       }
+    }
+
+    /**
+     * Skips specified number of bytes in the current ZIP entry.
+     * @param n the number of bytes to skip
+     * @return the actual number of bytes skipped
+     * @exception java.util.zip.ZipException if a ZIP file error has occur=
red
+     * @exception IOException if an I/O error has occurred
+     * @exception IllegalArgumentException if n < 0
+     */
+    public long skip(long n) throws IOException {
+        if (n < 0) {
+            throw new IllegalArgumentException("negative skip length");
+        }
+        ensureOpen();
+       int max =3D (int)Math.min(n, Integer.MAX_VALUE);
+       int total =3D 0;
+       while (total < max) {
+           int len =3D max - total;
+           if (len > tmpbuf.length) {
+               len =3D tmpbuf.length;
+           }
+           len =3D read(tmpbuf, 0, len);
+           if (len =3D=3D -1) {
+                entryEOF =3D true;
+               break;
+           }
+           total +=3D len;
+       }
+       return total;
+    }
+
+    /**
+     * Closes the ZIP input stream.
+     * @exception IOException if an I/O error has occurred
+     */
+    public void close() throws IOException {
+        if (!closed) {
+           super.close();
+            closed =3D true;
+        }
+    }
+
+    private byte[] b =3D new byte[256];
+
+    /*
+     * Reads local file (LOC) header for next entry.
+     */
+    private ZipEntry readLOC() throws IOException {
+       try {
+           readFully(tmpbuf, 0, LOCHDR);
+       } catch (EOFException e) {
+           return null;
+       }
+       if (get32(tmpbuf, 0) !=3D LOCSIG) {
+           return null;
+       }
+       // get the entry name and create the ZipEntry first
+       int len =3D get16(tmpbuf, LOCNAM);
+       if (len =3D=3D 0) {
+           throw new ZipException("missing entry name");
+       }
+        int blen =3D b.length;
+        if (len > blen) {
+            do
+                blen =3D blen * 2;
+            while (len > blen);
+            b =3D new byte[blen];
+        }
+       readFully(b, 0, len);
+       ZipEntry e =3D createZipEntry(getUTF8String(b, 0, len));
+       // now get the remaining fields for the entry
+       e.version =3D get16(tmpbuf, LOCVER);
+       e.flag =3D get16(tmpbuf, LOCFLG);
+       if ((e.flag & 1) =3D=3D 1) {
+           throw new ZipException("encrypted ZIP entry not supported");
+       }
+       e.method =3D get16(tmpbuf, LOCHOW);
+       e.time =3D get32(tmpbuf, LOCTIM);
+       if ((e.flag & 8) =3D=3D 8) {
+           /* EXT descriptor present */
+           if (e.method !=3D DEFLATED) {
+               throw new ZipException(
+                       "only DEFLATED entries can have EXT descriptor");
+           }
+       } else {
+           e.crc =3D get32(tmpbuf, LOCCRC);
+           e.csize =3D get32(tmpbuf, LOCSIZ);
+           e.size =3D get32(tmpbuf, LOCLEN);
+       }
+       len =3D get16(tmpbuf, LOCEXT);
+       if (len > 0) {
+           byte[] bb =3D new byte[len];
+           readFully(bb, 0, len);
+           e.extra =3D bb;
+       }
+       return e;
+    }
+
+    private boolean utfFailed =3D false;
+    private String encoding =3D "Cp437";
+
+    /*
+     * Fetches a UTF8-encoded String from the specified byte array.
+     */
+    private String getUTF8String(byte[] b, int off, int len) {
+        try {
+            if (!utfFailed) {
+                String r =3D new String(b,off,len,"UTF-8");
+                byte[] b2 =3D r.getBytes("UTF-8");
+                if (ArrayUtils.isEquals(b2, ArrayUtils.subarray(b,0,b2.len=
gth))) {
+                    return r;
+                } else {
+                    utfFailed =3D true;
+                }
+            }
+            return new String(b,off,len,encoding);
+        } catch (UnsupportedEncodingException e) {
+            return new String(b,off,len);
+        }
+    }
+
+    /**
+     * Creates a new <code>ZipEntry</code> object for the specified
+     * entry name.
+     *
+     * @param name the ZIP file entry name
+     * @return the ZipEntry just created
+     */
+    protected ZipEntry createZipEntry(String name) {
+       return new ZipEntry(name);
+    }
+
+    /*
+     * Reads end of deflated entry as well as EXT descriptor if present.
+     */
+    private void readEnd(ZipEntry e) throws IOException {
+       int n =3D inf.getRemaining();
+       if (n > 0) {
+           ((PushbackInputStream)in).unread(buf, len - n, n);
+       }
+       if ((e.flag & 8) =3D=3D 8) {
+           /* EXT descriptor present */
+           readFully(tmpbuf, 0, EXTHDR);
+           long sig =3D get32(tmpbuf, 0);
+            if (sig !=3D EXTSIG) { // no EXTSIG present
+                e.crc =3D sig;
+                e.csize =3D get32(tmpbuf, EXTSIZ - EXTCRC);
+                e.size =3D get32(tmpbuf, EXTLEN - EXTCRC);
+                ((PushbackInputStream)in).unread(
+                                           tmpbuf, EXTHDR - EXTCRC - 1, EX=
TCRC);
+            } else {
+                e.crc =3D get32(tmpbuf, EXTCRC);
+                e.csize =3D get32(tmpbuf, EXTSIZ);
+                e.size =3D get32(tmpbuf, EXTLEN);
+            }
+       }
+       if (e.size !=3D inf.getTotalOut()) {
+           throw new ZipException(
+               "invalid entry size (expected " + e.size + " but got " +
+               inf.getTotalOut() + " bytes)");
+       }
+       if (e.csize !=3D inf.getTotalIn()) {
+           throw new ZipException(
+               "invalid entry compressed size (expected " + e.csize +
+               " but got " + inf.getTotalIn() + " bytes)");
+       }
+       if (e.crc !=3D crc.getValue()) {
+           throw new ZipException(
+               "invalid entry CRC (expected 0x" + Long.toHexString(e.crc) +
+               " but got 0x" + Long.toHexString(crc.getValue()) + ")");
+       }
+    }
+
+    /*
+     * Reads bytes, blocking until all bytes are read.
+     */
+    private void readFully(byte[] b, int off, int len) throws IOException {
+       while (len > 0) {
+           int n =3D in.read(b, off, len);
+           if (n =3D=3D -1) {
+               throw new EOFException();
+           }
+           off +=3D n;
+           len -=3D n;
+       }
+    }
+
+    /*
+     * Fetches unsigned 16-bit value from byte array at specified offset.
+     * The bytes are assumed to be in Intel (little-endian) byte order.
+     */
+    private static final int get16(byte b[], int off) {
+       return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8);
+    }
+
+    /*
+     * Fetches unsigned 32-bit value from byte array at specified offset.
+     * The bytes are assumed to be in Intel (little-endian) byte order.
+     */
+    private static final long get32(byte b[], int off) {
+       return get16(b, off) | ((long)get16(b, off+2) << 16);
+    }
+}

Added: trunk/core/src/java/org/jahia/utils/zip/ZipOutputStream.java
URL: https://svndev.jahia.net/websvn/filedetails.php?path=3D/trunk/core/src=
/java/org/jahia/utils/zip/ZipOutputStream.java&rev=3D17906&repname=3Djahia
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/core/src/java/org/jahia/utils/zip/ZipOutputStream.java (added)
+++ trunk/core/src/java/org/jahia/utils/zip/ZipOutputStream.java Fri Jul  6=
 15:01:35 2007
@@ -0,0 +1,467 @@
+/*
+ * @(#)ZipOutputStream.java    1.27 03/02/07
+ *
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+package org.jahia.utils.zip;
+
+import java.io.OutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Vector;
+import java.util.Hashtable;
+import java.util.Enumeration;
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+import java.util.zip.ZipException;
+
+/**
+ * This class implements an output stream filter for writing files in the
+ * ZIP file format. Includes support for both compressed and uncompressed
+ * entries.
+ *
+ * @author     David Connelly
+ * @version    1.27, 02/07/03
+ */
+public
+class ZipOutputStream extends DeflaterOutputStream implements ZipConstants=
 {
+    private ZipEntry entry;
+    private Vector entries =3D new Vector();
+    private Hashtable names =3D new Hashtable();
+    private CRC32 crc =3D new CRC32();
+    private long written;
+    private long locoff =3D 0;
+    private String comment;
+    private int method =3D DEFLATED;
+    private boolean finished;
+
+    private boolean closed =3D false;
+
+    /**
+     * Check to make sure that this stream has not been closed
+     */
+    private void ensureOpen() throws IOException {
+       if (closed) {
+           throw new IOException("Stream closed");
+        }
+    }
+    /**
+     * Compression method for uncompressed (STORED) entries.
+     */
+    public static final int STORED =3D ZipEntry.STORED;
+
+    /**
+     * Compression method for compressed (DEFLATED) entries.
+     */
+    public static final int DEFLATED =3D ZipEntry.DEFLATED;
+
+    /**
+     * Creates a new ZIP output stream.
+     * @param out the actual output stream
+     */
+    public ZipOutputStream(OutputStream out) {
+       super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
+        usesDefaultDeflater =3D true;
+    }
+
+    /**
+     * Sets the ZIP file comment.
+     * @param comment the comment string
+     * @exception IllegalArgumentException if the length of the specified
+     *           ZIP file comment is greater than 0xFFFF bytes
+     */
+    public void setComment(String comment) {
+        if (comment !=3D null && comment.length() > 0xffff/3
+                                           && getUTF8Length(comment) > 0xf=
fff) {
+           throw new IllegalArgumentException("ZIP file comment too long.");
+       }
+       this.comment =3D comment;
+    }
+
+    /**
+     * Sets the default compression method for subsequent entries. This
+     * default will be used whenever the compression method is not specifi=
ed
+     * for an individual ZIP file entry, and is initially set to DEFLATED.
+     * @param method the default compression method
+     * @exception IllegalArgumentException if the specified compression me=
thod
+     *           is invalid
+     */
+    public void setMethod(int method) {
+       if (method !=3D DEFLATED && method !=3D STORED) {
+           throw new IllegalArgumentException("invalid compression method");
+       }
+       this.method =3D method;
+    }
+
+    /**
+     * Sets the compression level for subsequent entries which are DEFLATE=
D.
+     * The default setting is DEFAULT_COMPRESSION.
+     * @param level the compression level (0-9)
+     * @exception IllegalArgumentException if the compression level is inv=
alid
+     */
+    public void setLevel(int level) {
+       def.setLevel(level);
+    }
+
+    /**
+     * Begins writing a new ZIP file entry and positions the stream to the
+     * start of the entry data. Closes the current entry if still active.
+     * The default compression method will be used if no compression method
+     * was specified for the entry, and the current time will be used if
+     * the entry has no set modification time.
+     * @param e the ZIP entry to be written
+     * @exception ZipException if a ZIP format error has occurred
+     * @exception IOException if an I/O error has occurred
+     */
+    public void putNextEntry(ZipEntry e) throws IOException {
+       ensureOpen();
+       if (entry !=3D null) {
+           closeEntry();       // close previous entry
+       }
+       if (e.time =3D=3D -1) {
+           e.setTime(System.currentTimeMillis());
+       }
+       if (e.method =3D=3D -1) {
+           e.method =3D method;        // use default method
+       }
+       switch (e.method) {
+       case DEFLATED:
+           if (e.size =3D=3D -1 || e.csize =3D=3D -1 || e.crc =3D=3D -1) {
+               // store size, compressed size, and crc-32 in data descriptor
+               // immediately following the compressed entry data
+               e.flag =3D 8;
+           } else if (e.size !=3D -1 && e.csize !=3D -1 && e.crc !=3D -1) {
+               // store size, compressed size, and crc-32 in LOC header
+               e.flag =3D 0;
+           } else {
+               throw new ZipException(
+                   "DEFLATED entry missing size, compressed size, or crc-32");
+           }
+           e.version =3D 20;
+           break;
+       case STORED:
+           // compressed size, uncompressed size, and crc-32 must all be
+           // set for entries using STORED compression method
+           if (e.size =3D=3D -1) {
+               e.size =3D e.csize;
+           } else if (e.csize =3D=3D -1) {
+               e.csize =3D e.size;
+           } else if (e.size !=3D e.csize) {
+               throw new ZipException(
+                   "STORED entry where compressed !=3D uncompressed size");
+           }
+           if (e.size =3D=3D -1 || e.crc =3D=3D -1) {
+               throw new ZipException(
+                   "STORED entry missing size, compressed size, or crc-32");
+           }
+           e.version =3D 10;
+           e.flag =3D 0;
+           break;
+       default:
+           throw new ZipException("unsupported compression method");
+       }
+       e.offset =3D written;
+       if (names.put(e.name, e) !=3D null) {
+           throw new ZipException("duplicate entry: " + e.name);
+       }
+        writeLOC(e);
+       entries.addElement(e);
+       entry =3D e;
+    }
+
+    /**
+     * Closes the current ZIP entry and positions the stream for writing
+     * the next entry.
+     * @exception ZipException if a ZIP format error has occurred
+     * @exception IOException if an I/O error has occurred
+     */
+    public void closeEntry() throws IOException {
+       ensureOpen();
+       ZipEntry e =3D entry;
+       if (e !=3D null) {
+           switch (e.method) {
+           case DEFLATED:
+               def.finish();
+               while (!def.finished()) {
+                   deflate();
+               }
+               if ((e.flag & 8) =3D=3D 0) {
+                   // verify size, compressed size, and crc-32 settings
+                   if (e.size !=3D def.getTotalIn()) {
+                       throw new ZipException(
+                           "invalid entry size (expected " + e.size +
+                           " but got " + def.getTotalIn() + " bytes)");
+                   }
+                   if (e.csize !=3D def.getTotalOut()) {
+                       throw new ZipException(
+                           "invalid entry compressed size (expected " +
+                           e.csize + " but got " + def.getTotalOut() +
+                           " bytes)");
+                   }
+                   if (e.crc !=3D crc.getValue()) {
+                       throw new ZipException(
+                           "invalid entry CRC-32 (expected 0x" +
+                           Long.toHexString(e.crc) + " but got 0x" +
+                           Long.toHexString(crc.getValue()) + ")");
+                   }
+               } else {
+                   e.size =3D def.getTotalIn();
+                   e.csize =3D def.getTotalOut();
+                   e.crc =3D crc.getValue();
+                   writeEXT(e);
+               }
+               def.reset();
+               written +=3D e.csize;
+               break;
+           case STORED:
+               // we already know that both e.size and e.csize are the same
+               if (e.size !=3D written - locoff) {
+                   throw new ZipException(
+                       "invalid entry size (expected " + e.size +
+                       " but got " + (written - locoff) + " bytes)");
+               }
+               if (e.crc !=3D crc.getValue()) {
+                   throw new ZipException(
+                        "invalid entry crc-32 (expected 0x" +
+                        Long.toHexString(e.crc) + " but got 0x" +
+                        Long.toHexString(crc.getValue()) + ")");
+               }
+               break;
+           default:
+               throw new InternalError("invalid compression method");
+           }
+           crc.reset();
+           entry =3D null;
+       }
+    }
+
+    /**
+     * Writes an array of bytes to the current ZIP entry data. This method
+     * will block until all the bytes are written.
+     * @param b the data to be written
+     * @param off the start offset in the data
+     * @param len the number of bytes that are written
+     * @exception ZipException if a ZIP file error has occurred
+     * @exception IOException if an I/O error has occurred
+     */
+    public synchronized void write(byte[] b, int off, int len)
+       throws IOException
+    {
+       ensureOpen();
+        if (off < 0 || len < 0 || off > b.length - len) {
+           throw new IndexOutOfBoundsException();
+       } else if (len =3D=3D 0) {
+           return;
+       }
+
+       if (entry =3D=3D null) {
+           throw new ZipException("no current ZIP entry");
+       }
+       switch (entry.method) {
+       case DEFLATED:
+           super.write(b, off, len);
+           break;
+       case STORED:
+           written +=3D len;
+           if (written - locoff > entry.size) {
+               throw new ZipException(
+                   "attempt to write past end of STORED entry");
+           }
+           out.write(b, off, len);
+           break;
+       default:
+           throw new InternalError("invalid compression method");
+       }
+       crc.update(b, off, len);
+    }
+
+    /**
+     * Finishes writing the contents of the ZIP output stream without clos=
ing
+     * the underlying stream. Use this method when applying multiple filte=
rs
+     * in succession to the same output stream.
+     * @exception ZipException if a ZIP file error has occurred
+     * @exception IOException if an I/O exception has occurred
+     */
+    public void finish() throws IOException {
+       ensureOpen();
+       if (finished) {
+           return;
+       }
+       if (entry !=3D null) {
+           closeEntry();
+       }
+       if (entries.size() < 1) {
+           throw new ZipException("ZIP file must have at least one entry");
+       }
+       // write central directory
+       long off =3D written;
+       Enumeration e =3D entries.elements();
+       while (e.hasMoreElements()) {
+           writeCEN((ZipEntry)e.nextElement());
+       }
+       writeEND(off, written - off);
+       finished =3D true;
+    }
+
+    /**
+     * Closes the ZIP output stream as well as the stream being filtered.
+     * @exception ZipException if a ZIP file error has occurred
+     * @exception IOException if an I/O error has occurred
+     */
+    public void close() throws IOException {
+        if (!closed) {
+            super.close();
+            closed =3D true;
+        }
+    }
+
+    /*
+     * Writes local file (LOC) header for specified entry.
+     */
+    private void writeLOC(ZipEntry e) throws IOException {
+       writeInt(LOCSIG);           // LOC header signature
+       writeShort(e.version);      // version needed to extract
+       writeShort(e.flag);         // general purpose bit flag
+       writeShort(e.method);       // compression method
+       writeInt(e.time);           // last modification time
+       if ((e.flag & 8) =3D=3D 8) {
+           // store size, uncompressed size, and crc-32 in data descriptor
+           // immediately following compressed entry data
+           writeInt(0);
+           writeInt(0);
+           writeInt(0);
+       } else {
+           writeInt(e.crc);        // crc-32
+           writeInt(e.csize);      // compressed size
+           writeInt(e.size);       // uncompressed size
+       }
+       byte[] nameBytes =3D getUTF8Bytes(e.name);
+       writeShort(nameBytes.length);
+       writeShort(e.extra !=3D null ? e.extra.length : 0);
+       writeBytes(nameBytes, 0, nameBytes.length);
+       if (e.extra !=3D null) {
+           writeBytes(e.extra, 0, e.extra.length);
+       }
+       locoff =3D written;
+    }
+
+    /*
+     * Writes extra data descriptor (EXT) for specified entry.
+     */
+    private void writeEXT(ZipEntry e) throws IOException {
+       writeInt(EXTSIG);           // EXT header signature
+       writeInt(e.crc);            // crc-32
+       writeInt(e.csize);          // compressed size
+       writeInt(e.size);           // uncompressed size
+    }
+
+    /*
+     * Write central directory (CEN) header for specified entry.
+     * REMIND: add support for file attributes
+     */
+    private void writeCEN(ZipEntry e) throws IOException {
+       writeInt(CENSIG);           // CEN header signature
+       writeShort(e.version);      // version made by
+       writeShort(e.version);      // version needed to extract
+       writeShort(e.flag);         // general purpose bit flag
+       writeShort(e.method);       // compression method
+       writeInt(e.time);           // last modification time
+       writeInt(e.crc);            // crc-32
+       writeInt(e.csize);          // compressed size
+       writeInt(e.size);           // uncompressed size
+       byte[] nameBytes =3D getUTF8Bytes(e.name);
+       writeShort(nameBytes.length);
+       writeShort(e.extra !=3D null ? e.extra.length : 0);
+       byte[] commentBytes;
+       if (e.comment !=3D null) {
+           commentBytes =3D getUTF8Bytes(e.comment);
+           writeShort(commentBytes.length);
+       } else {
+           commentBytes =3D null;
+           writeShort(0);
+       }
+       writeShort(0);              // starting disk number
+       writeShort(0);              // internal file attributes (unused)
+       writeInt(0);                // external file attributes (unused)
+       writeInt(e.offset);         // relative offset of local header
+       writeBytes(nameBytes, 0, nameBytes.length);
+       if (e.extra !=3D null) {
+           writeBytes(e.extra, 0, e.extra.length);
+       }
+       if (commentBytes !=3D null) {
+           writeBytes(commentBytes, 0, commentBytes.length);
+       }
+    }
+
+    /*
+     * Writes end of central directory (END) header.
+     */
+    private void writeEND(long off, long len) throws IOException {
+       writeInt(ENDSIG);           // END record signature
+       writeShort(0);              // number of this disk
+       writeShort(0);              // central directory start disk
+       writeShort(entries.size()); // number of directory entries on disk
+       writeShort(entries.size()); // total number of directory entries
+       writeInt(len);              // length of central directory
+       writeInt(off);              // offset of central directory
+       if (comment !=3D null) {            // zip file comment
+           byte[] b =3D getUTF8Bytes(comment);
+           writeShort(b.length);
+           writeBytes(b, 0, b.length);
+       } else {
+           writeShort(0);
+       }
+    }
+
+    /*
+     * Writes a 16-bit short to the output stream in little-endian byte or=
der.
+     */
+    private void writeShort(int v) throws IOException {
+       OutputStream out =3D this.out;
+       out.write((v >>> 0) & 0xff);
+       out.write((v >>> 8) & 0xff);
+       written +=3D 2;
+    }
+
+    /*
+     * Writes a 32-bit int to the output stream in little-endian byte orde=
r.
+     */
+    private void writeInt(long v) throws IOException {
+       OutputStream out =3D this.out;
+       out.write((int)((v >>>  0) & 0xff));
+       out.write((int)((v >>>  8) & 0xff));
+       out.write((int)((v >>> 16) & 0xff));
+       out.write((int)((v >>> 24) & 0xff));
+       written +=3D 4;
+    }
+
+    /*
+     * Writes an array of bytes to the output stream.
+     */
+    private void writeBytes(byte[] b, int off, int len) throws IOException=
 {
+       super.out.write(b, off, len);
+       written +=3D len;
+    }
+
+    static private String encoding =3D "UTF-8";
+    /*
+     * Returns the length of String's UTF8 encoding.
+     */
+    static int getUTF8Length(String s) {
+        return getUTF8Bytes(s).length;
+    }
+
+    /*
+     * Returns an array of bytes representing the UTF8 encoding
+     * of the specified String.
+     */
+    private static byte[] getUTF8Bytes(String s) {
+        try {
+            return s.getBytes(encoding);
+        } catch (UnsupportedEncodingException e) {
+            return s.getBytes();
+        }
+    }
+}

_______________________________________________
cvs_list mailing list
[email protected]
http://lists.jahia.org/cgi-bin/mailman/listinfo/cvs_list

Reply via email to