提交 812584de 编写于 作者: S sherman

4244499: ZipEntry() does not convert filenames from Unicode to platform

4532049: IllegalArgumentException in ZipInputStream while reading unicode file
5030283: Incorrect implementation of UTF-8 in zip package
4700978: ZipFile can't treat Japanese name in a zipfile properly
4980042: Cannot use Surrogates in zip file metadata like filenames
4820807: java.util.zip.ZipInputStream cannot extract files with Chinese chars in name
Summary: Add new constructors for zip classes to support non-UTF-8 encoded names/comments in ZIP file
Reviewed-by: alanb, martin
上级 cbfb10d1
......@@ -29,7 +29,6 @@ FILES_c = \
Deflater.c \
Inflater.c \
ZipFile.c \
ZipEntry.c \
zadler32.c \
zcrc32.c \
deflate.c \
......
......@@ -50,15 +50,17 @@ SUNWprivate_1.1 {
Java_java_util_zip_Inflater_initIDs;
Java_java_util_zip_Inflater_reset;
Java_java_util_zip_Inflater_setDictionary;
Java_java_util_zip_ZipEntry_initFields;
Java_java_util_zip_ZipEntry_initIDs;
Java_java_util_zip_ZipFile_close;
Java_java_util_zip_ZipFile_freeEntry;
Java_java_util_zip_ZipFile_getCSize;
Java_java_util_zip_ZipFile_getEntry;
Java_java_util_zip_ZipFile_getMethod;
Java_java_util_zip_ZipFile_getEntryBytes;
Java_java_util_zip_ZipFile_getEntryCrc;
Java_java_util_zip_ZipFile_getEntryCSize;
Java_java_util_zip_ZipFile_getEntryFlag;
Java_java_util_zip_ZipFile_getEntryMethod;
Java_java_util_zip_ZipFile_getEntrySize;
Java_java_util_zip_ZipFile_getEntryTime;
Java_java_util_zip_ZipFile_getNextEntry;
Java_java_util_zip_ZipFile_getSize;
Java_java_util_zip_ZipFile_getZipMessage;
Java_java_util_zip_ZipFile_getTotal;
Java_java_util_zip_ZipFile_initIDs;
......
......@@ -20,12 +20,14 @@ text: .text%Java_java_util_zip_ZipFile_initIDs;
text: .text%Java_java_util_zip_ZipFile_open;
text: .text%Java_java_util_zip_ZipFile_getTotal;
text: .text%Java_java_util_zip_ZipFile_getEntry;
text: .text%Java_java_util_zip_ZipEntry_initIDs;
text: .text%Java_java_util_zip_ZipEntry_initFields;
text: .text%Java_java_util_zip_ZipFile_freeEntry;
text: .text%Java_java_util_zip_ZipFile_getCSize;
text: .text%Java_java_util_zip_ZipFile_getSize;
text: .text%Java_java_util_zip_ZipFile_getMethod;
text: .text%Java_java_util_zip_ZipFile_getEntryTime;
text: .text%Java_java_util_zip_ZipFile_getEntryCrc;
text: .text%Java_java_util_zip_ZipFile_getEntryCSize;
text: .text%Java_java_util_zip_ZipFile_getEntrySize;
text: .text%Java_java_util_zip_ZipFile_getEntryFlag;
text: .text%Java_java_util_zip_ZipFile_getEntryMethod;
text: .text%Java_java_util_zip_ZipFile_getEntryBytes;
text: .text%Java_java_util_zip_Inflater_initIDs;
text: .text%Java_java_util_zip_Inflater_init;
text: .text%inflateInit2_;
......
......@@ -19,12 +19,14 @@ text: .text%Java_java_util_zip_ZipFile_initIDs;
text: .text%Java_java_util_zip_ZipFile_open;
text: .text%Java_java_util_zip_ZipFile_getTotal;
text: .text%Java_java_util_zip_ZipFile_getEntry;
text: .text%Java_java_util_zip_ZipEntry_initIDs;
text: .text%Java_java_util_zip_ZipEntry_initFields;
text: .text%Java_java_util_zip_ZipFile_freeEntry;
text: .text%Java_java_util_zip_ZipFile_getCSize;
text: .text%Java_java_util_zip_ZipFile_getSize;
text: .text%Java_java_util_zip_ZipFile_getMethod;
text: .text%Java_java_util_zip_ZipFile_getEntryTime;
text: .text%Java_java_util_zip_ZipFile_getEntryCrc;
text: .text%Java_java_util_zip_ZipFile_getEntryCSize;
text: .text%Java_java_util_zip_ZipFile_getEntrySize;
text: .text%Java_java_util_zip_ZipFile_getEntryFlag;
text: .text%Java_java_util_zip_ZipFile_getEntryMethod;
text: .text%Java_java_util_zip_ZipFile_getEntryBytes;
text: .text%Java_java_util_zip_Inflater_initIDs;
text: .text%Java_java_util_zip_Inflater_init;
text: .text%inflateInit2_;
......
......@@ -20,12 +20,14 @@ text: .text%Java_java_util_zip_ZipFile_initIDs;
text: .text%Java_java_util_zip_ZipFile_open;
text: .text%Java_java_util_zip_ZipFile_getTotal;
text: .text%Java_java_util_zip_ZipFile_getEntry;
text: .text%Java_java_util_zip_ZipEntry_initIDs;
text: .text%Java_java_util_zip_ZipEntry_initFields;
text: .text%Java_java_util_zip_ZipFile_freeEntry;
text: .text%Java_java_util_zip_ZipFile_getCSize;
text: .text%Java_java_util_zip_ZipFile_getSize;
text: .text%Java_java_util_zip_ZipFile_getMethod;
text: .text%Java_java_util_zip_ZipFile_getEntryTime;
text: .text%Java_java_util_zip_ZipFile_getEntryCrc;
text: .text%Java_java_util_zip_ZipFile_getEntryCSize;
text: .text%Java_java_util_zip_ZipFile_getEntrySize;
text: .text%Java_java_util_zip_ZipFile_getEntryFlag;
text: .text%Java_java_util_zip_ZipFile_getEntryMethod;
text: .text%Java_java_util_zip_ZipFile_getEntryBytes;
text: .text%Java_java_util_zip_Inflater_initIDs;
text: .text%Java_java_util_zip_Inflater_init;
text: .text%inflateInit2_;
......
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package java.util.zip;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.util.Arrays;
/**
* Utility class for zipfile name and comment decoding and encoding
*/
final class ZipCoder {
String toString(byte[] ba, int length) {
CharsetDecoder cd = decoder().reset();
int len = (int)(length * cd.maxCharsPerByte());
char[] ca = new char[len];
if (len == 0)
return new String(ca);
ByteBuffer bb = ByteBuffer.wrap(ba, 0, length);
CharBuffer cb = CharBuffer.wrap(ca);
CoderResult cr = cd.decode(bb, cb, true);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
cr = cd.flush(cb);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
return new String(ca, 0, cb.position());
}
String toString(byte[] ba) {
return toString(ba, ba.length);
}
byte[] getBytes(String s) {
CharsetEncoder ce = encoder().reset();
char[] ca = s.toCharArray();
int len = (int)(ca.length * ce.maxBytesPerChar());
byte[] ba = new byte[len];
if (len == 0)
return ba;
ByteBuffer bb = ByteBuffer.wrap(ba);
CharBuffer cb = CharBuffer.wrap(ca);
CoderResult cr = ce.encode(cb, bb, true);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
cr = ce.flush(bb);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
if (bb.position() == ba.length) // defensive copy?
return ba;
else
return Arrays.copyOf(ba, bb.position());
}
// assume invoked only if "this" is not utf8
byte[] getBytesUTF8(String s) {
if (isutf8)
return getBytes(s);
if (utf8 == null)
utf8 = new ZipCoder(Charset.forName("UTF-8"));
return utf8.getBytes(s);
}
String toStringUTF8(byte[] ba, int len) {
if (isutf8)
return toString(ba, len);
if (utf8 == null)
utf8 = new ZipCoder(Charset.forName("UTF-8"));
return utf8.toString(ba, len);
}
boolean isUTF8() {
return isutf8;
}
private Charset cs;
private CharsetDecoder dec;
private CharsetEncoder enc;
private boolean isutf8;
private ZipCoder utf8;
private ZipCoder(Charset cs) {
this.cs = cs;
this.isutf8 = cs.name().equals("UTF-8");
}
static ZipCoder get(Charset charset) {
return new ZipCoder(charset);
}
private CharsetDecoder decoder() {
if (dec == null) {
dec = cs.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
}
return dec;
}
private CharsetEncoder encoder() {
if (enc == null) {
enc = cs.newEncoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
}
return enc;
}
}
......@@ -73,5 +73,12 @@ class ZipConstants64 {
static final int ZIP64_EXTSIZ = 8; // compressed size, 8-byte
static final int ZIP64_EXTLEN = 16; // uncompressed size, 8-byte
/*
* Language encoding flag EFS
*/
static final int EFS = 0x800; // If this bit is set the filename and
// comment fields for this file must be
// encoded using UTF-8.
private ZipConstants64() {}
}
......@@ -40,6 +40,7 @@ class ZipEntry implements ZipConstants, Cloneable {
long size = -1; // uncompressed size of entry data
long csize = -1; // compressed size of entry data
int method = -1; // compression method
int flag = 0; // general purpose flag
byte[] extra; // optional extra field data for entry
String comment; // optional comment string for entry
......@@ -53,13 +54,6 @@ class ZipEntry implements ZipConstants, Cloneable {
*/
public static final int DEFLATED = 8;
static {
/* Zip library is loaded from System.initializeSystemClass */
initIDs();
}
private static native void initIDs();
/**
* Creates a new zip entry with the specified name.
*
......@@ -90,28 +84,15 @@ class ZipEntry implements ZipConstants, Cloneable {
size = e.size;
csize = e.csize;
method = e.method;
flag = e.flag;
extra = e.extra;
comment = e.comment;
}
/*
* Creates a new zip entry for the given name with fields initialized
* from the specified jzentry data.
* Creates a new un-initialized zip entry
*/
ZipEntry(String name, long jzentry) {
this.name = 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);
}
ZipEntry() {}
/**
* Returns the name of the entry.
......@@ -258,16 +239,16 @@ class ZipEntry implements ZipConstants, Cloneable {
/**
* Sets the optional comment string for the entry.
*
* <p>ZIP entry comments have maximum length of 0xffff. If the length of the
* specified comment string is greater than 0xFFFF bytes after encoding, only
* the first 0xFFFF bytes are output to the ZIP file 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 != null && comment.length() > 0xffff/3
&& ZipOutputStream.getUTF8Length(comment) > 0xffff) {
throw new IllegalArgumentException("invalid entry comment length");
}
this.comment = comment;
}
......
......@@ -29,9 +29,11 @@ import java.io.InputStream;
import java.io.IOException;
import java.io.EOFException;
import java.io.File;
import java.nio.charset.Charset;
import java.util.Vector;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import static java.util.zip.ZipConstants64.*;
/**
* This class is used to read entries from a zip file.
......@@ -76,16 +78,19 @@ class ZipFile implements ZipConstants {
/**
* Opens a zip file for reading.
*
* <p>First, if there is a security
* manager, its <code>checkRead</code> method
* is called with the <code>name</code> argument
* as its argument to ensure the read is allowed.
* <p>First, if there is a security manager, its <code>checkRead</code>
* method is called with the <code>name</code> argument as its argument
* to ensure the read is allowed.
*
* <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
* decode the entry names and comments.
*
* @param name the name of the zip file
* @throws ZipException if a ZIP format error has occurred
* @throws IOException if an I/O error has occurred
* @throws SecurityException if a security manager exists and its
* <code>checkRead</code> method doesn't allow read access to the file.
*
* @see SecurityManager#checkRead(java.lang.String)
*/
public ZipFile(String name) throws IOException {
......@@ -101,6 +106,9 @@ class ZipFile implements ZipConstants {
* method is called with the <code>name</code> argument as its argument to
* ensure the read is allowed.
*
* <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
* decode the entry names and comments
*
* @param file the ZIP file to be opened for reading
* @param mode the mode in which the file is to be opened
* @throws ZipException if a ZIP format error has occurred
......@@ -115,6 +123,59 @@ class ZipFile implements ZipConstants {
* @since 1.3
*/
public ZipFile(File file, int mode) throws IOException {
this(file, mode, Charset.forName("UTF-8"));
}
/**
* Opens a ZIP file for reading given the specified File object.
*
* <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
* decode the entry names and comments.
*
* @param file the ZIP file to be opened for reading
* @throws ZipException if a ZIP format error has occurred
* @throws IOException if an I/O error has occurred
*/
public ZipFile(File file) throws ZipException, IOException {
this(file, OPEN_READ);
}
private ZipCoder zc;
/**
* Opens a new <code>ZipFile</code> to read from the specified
* <code>File</code> object in the specified mode. The mode argument
* must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
*
* <p>First, if there is a security manager, its <code>checkRead</code>
* method is called with the <code>name</code> argument as its argument to
* ensure the read is allowed.
*
* @param file the ZIP file to be opened for reading
* @param mode the mode in which the file is to be opened
* @param charset
* the {@link java.nio.charset.Charset {@code charset}} to
* be used to decode the ZIP entry name and comment that are not
* encoded by using UTF-8 encoding (indicated by entry's general
* purpose flag).
*
* @throws ZipException if a ZIP format error has occurred
* @throws IOException if an I/O error has occurred
*
* @throws SecurityException
* if a security manager exists and its <code>checkRead</code>
* method doesn't allow read access to the file,or its
* <code>checkDelete</code> method doesn't allow deleting the
* file when the <tt>OPEN_DELETE</tt> flag is set
*
* @throws IllegalArgumentException if the <tt>mode</tt> argument is invalid
*
* @see SecurityManager#checkRead(java.lang.String)
*
* @since 1.7
*/
public ZipFile(File file, int mode, Charset charset) throws IOException
{
if (((mode & OPEN_READ) == 0) ||
((mode & ~(OPEN_READ | OPEN_DELETE)) != 0)) {
throw new IllegalArgumentException("Illegal mode: 0x"+
......@@ -128,24 +189,61 @@ class ZipFile implements ZipConstants {
sm.checkDelete(name);
}
}
if (charset == null)
throw new NullPointerException("charset is null");
this.zc = ZipCoder.get(charset);
jzfile = open(name, mode, file.lastModified());
this.name = name;
this.total = getTotal(jzfile);
}
private static native long open(String name, int mode, long lastModified);
private static native int getTotal(long jzfile);
/**
* Opens a zip file for reading.
*
* <p>First, if there is a security manager, its <code>checkRead</code>
* method is called with the <code>name</code> argument as its argument
* to ensure the read is allowed.
*
* @param name the name of the zip file
* @param charset
* the {@link java.nio.charset.Charset {@code charset}} to
* be used to decode the ZIP entry name and comment that are not
* encoded by using UTF-8 encoding (indicated by entry's general
* purpose flag).
*
* @throws ZipException if a ZIP format error has occurred
* @throws IOException if an I/O error has occurred
* @throws SecurityException
* if a security manager exists and its <code>checkRead</code>
* method doesn't allow read access to the file
*
* @see SecurityManager#checkRead(java.lang.String)
*
* @since 1.7
*/
public ZipFile(String name, Charset charset) throws IOException
{
this(new File(name), OPEN_READ, charset);
}
/**
* Opens a ZIP file for reading given the specified File object.
* @param file the ZIP file to be opened for reading
* @throws ZipException if a ZIP error has occurred
* @param charset
* The {@link java.nio.charset.Charset {@code charset}} to be
* used to decode the ZIP entry name and comment (ignored if
* the <a href="package-summary.html#lang_encoding"> language
* encoding bit</a> of the ZIP entry's general purpose bit
* flag is set).
*
* @throws ZipException if a ZIP format error has occurred
* @throws IOException if an I/O error has occurred
*
* @since 1.7
*/
public ZipFile(File file) throws ZipException, IOException {
this(file, OPEN_READ);
public ZipFile(File file, Charset charset) throws IOException
{
this(file, OPEN_READ, charset);
}
/**
......@@ -163,9 +261,9 @@ class ZipFile implements ZipConstants {
long jzentry = 0;
synchronized (this) {
ensureOpen();
jzentry = getEntry(jzfile, name, true);
jzentry = getEntry(jzfile, zc.getBytes(name), true);
if (jzentry != 0) {
ZipEntry ze = new ZipEntry(name, jzentry);
ZipEntry ze = getZipEntry(name, jzentry);
freeEntry(jzfile, jzentry);
return ze;
}
......@@ -173,7 +271,7 @@ class ZipFile implements ZipConstants {
return null;
}
private static native long getEntry(long jzfile, String name,
private static native long getEntry(long jzfile, byte[] name,
boolean addSlash);
// freeEntry releases the C jzentry struct.
......@@ -194,36 +292,30 @@ class ZipFile implements ZipConstants {
* @throws IllegalStateException if the zip file has been closed
*/
public InputStream getInputStream(ZipEntry entry) throws IOException {
return getInputStream(entry.name);
}
/**
* Returns an input stream for reading the contents of the specified
* entry, or null if the entry was not found.
*/
private InputStream getInputStream(String name) throws IOException {
if (name == null) {
throw new NullPointerException("name");
if (entry == null) {
throw new NullPointerException("entry");
}
long jzentry = 0;
ZipFileInputStream in = null;
synchronized (this) {
ensureOpen();
jzentry = getEntry(jzfile, name, false);
if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false);
} else {
jzentry = getEntry(jzfile, zc.getBytes(entry.name), false);
}
if (jzentry == 0) {
return null;
}
in = new ZipFileInputStream(jzentry);
}
final ZipFileInputStream zfin = in;
switch (getMethod(jzentry)) {
switch (getEntryMethod(jzentry)) {
case STORED:
return zfin;
case DEFLATED:
// MORE: Compute good size for inflater stream:
long size = getSize(jzentry) + 2; // Inflater likes a bit of slack
long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack
if (size > 65536) size = 8192;
if (size <= 0) size = 4096;
return new InflaterInputStream(zfin, getInflater(), (int)size) {
......@@ -267,8 +359,6 @@ class ZipFile implements ZipConstants {
}
}
private static native int getMethod(long jzentry);
/*
* Gets an inflater from the list of available inflaters or allocates
* a new one.
......@@ -343,7 +433,7 @@ class ZipFile implements ZipConstants {
",\n message = " + message
);
}
ZipEntry ze = new ZipEntry(jzentry);
ZipEntry ze = getZipEntry(null, jzentry);
freeEntry(jzfile, jzentry);
return ze;
}
......@@ -351,6 +441,38 @@ class ZipFile implements ZipConstants {
};
}
private ZipEntry getZipEntry(String name, long jzentry) {
ZipEntry e = new ZipEntry();
e.flag = getEntryFlag(jzentry); // get the flag first
if (name != null) {
e.name = name;
} else {
byte[] bname = getEntryBytes(jzentry, JZENTRY_NAME);
if (!zc.isUTF8() && (e.flag & EFS) != 0) {
e.name = zc.toStringUTF8(bname, bname.length);
} else {
e.name = zc.toString(bname, bname.length);
}
}
e.time = getEntryTime(jzentry);
e.crc = getEntryCrc(jzentry);
e.size = getEntrySize(jzentry);
e. csize = getEntryCSize(jzentry);
e.method = getEntryMethod(jzentry);
e.extra = getEntryBytes(jzentry, JZENTRY_EXTRA);
byte[] bcomm = getEntryBytes(jzentry, JZENTRY_COMMENT);
if (bcomm == null) {
e.comment = null;
} else {
if (!zc.isUTF8() && (e.flag & EFS) != 0) {
e.comment = zc.toStringUTF8(bcomm, bcomm.length);
} else {
e.comment = zc.toString(bcomm, bcomm.length);
}
}
return e;
}
private static native long getNextEntry(long jzfile, int i);
/**
......@@ -443,8 +565,8 @@ class ZipFile implements ZipConstants {
ZipFileInputStream(long jzentry) {
pos = 0;
rem = getCSize(jzentry);
size = getSize(jzentry);
rem = getEntryCSize(jzentry);
size = getEntrySize(jzentry);
this.jzentry = jzentry;
}
......@@ -514,13 +636,25 @@ class ZipFile implements ZipConstants {
}
private static native long open(String name, int mode, long lastModified)
throws IOException;
private static native int getTotal(long jzfile);
private static native int read(long jzfile, long jzentry,
long pos, byte[] b, int off, int len);
private static native long getCSize(long jzentry);
// access to the native zentry object
private static native long getEntryTime(long jzentry);
private static native long getEntryCrc(long jzentry);
private static native long getEntryCSize(long jzentry);
private static native long getEntrySize(long jzentry);
private static native int getEntryMethod(long jzentry);
private static native int getEntryFlag(long jzentry);
private static native long getSize(long jzentry);
private static final int JZENTRY_NAME = 0;
private static final int JZENTRY_EXTRA = 1;
private static final int JZENTRY_COMMENT = 2;
private static native byte[] getEntryBytes(long jzentry, int type);
// Temporary add on for bug troubleshooting
private static native String getZipMessage(long jzfile);
}
......@@ -29,6 +29,7 @@ import java.io.InputStream;
import java.io.IOException;
import java.io.EOFException;
import java.io.PushbackInputStream;
import java.nio.charset.Charset;
import static java.util.zip.ZipConstants64.*;
/**
......@@ -54,6 +55,8 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants {
// one entry
private boolean entryEOF = false;
private ZipCoder zc;
/**
* Check to make sure that this stream has not been closed
*/
......@@ -65,14 +68,39 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants {
/**
* Creates a new ZIP input stream.
*
* <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
* decode the entry names.
*
* @param in the actual input stream
*/
public ZipInputStream(InputStream in) {
this(in, Charset.forName("UTF-8"));
}
/**
* Creates a new ZIP input stream.
*
* @param in the actual input stream
*
* @param charset
* The {@link java.nio.charset.Charset {@code charset}} to be
* used to decode the ZIP entry name (ignored if the
* <a href="package-summary.html#lang_encoding"> language
* encoding bit</a> of the ZIP entry's general purpose bit
* flag is set).
*
* @since 1.7
*/
public ZipInputStream(InputStream in, Charset charset) {
super(new PushbackInputStream(in, 512), new Inflater(true), 512);
usesDefaultInflater = true;
if(in == null) {
throw new NullPointerException("in is null");
}
if (charset == null)
throw new NullPointerException("charset is null");
this.zc = ZipCoder.get(charset);
}
/**
......@@ -141,8 +169,8 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants {
* @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 NullPointerException If <code>b</code> is <code>null</code>.
* @exception IndexOutOfBoundsException If <code>off</code> is negative,
* @exception NullPointerException if <code>b</code> is <code>null</code>.
* @exception IndexOutOfBoundsException if <code>off</code> is negative,
* <code>len</code> is negative, or <code>len</code> is greater than
* <code>b.length - off</code>
* @exception ZipException if a ZIP file error has occurred
......@@ -252,6 +280,8 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants {
if (get32(tmpbuf, 0) != LOCSIG) {
return null;
}
// get flag first, we need check EFS.
flag = get16(tmpbuf, LOCFLG);
// get the entry name and create the ZipEntry first
int len = get16(tmpbuf, LOCNAM);
int blen = b.length;
......@@ -262,9 +292,11 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants {
b = new byte[blen];
}
readFully(b, 0, len);
ZipEntry e = createZipEntry(getUTF8String(b, 0, len));
// Force to use UTF-8 if the EFS bit is ON, even the cs is NOT UTF-8
ZipEntry e = createZipEntry(((flag & EFS) != 0)
? zc.toStringUTF8(b, len)
: zc.toString(b, len));
// now get the remaining fields for the entry
flag = get16(tmpbuf, LOCFLG);
if ((flag & 1) == 1) {
throw new ZipException("encrypted ZIP entry not supported");
}
......@@ -313,71 +345,6 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants {
return e;
}
/*
* Fetches a UTF8-encoded String from the specified byte array.
*/
private static String getUTF8String(byte[] b, int off, int len) {
// First, count the number of characters in the sequence
int count = 0;
int max = off + len;
int i = off;
while (i < max) {
int c = b[i++] & 0xff;
switch (c >> 4) {
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
// 0xxxxxxx
count++;
break;
case 12: case 13:
// 110xxxxx 10xxxxxx
if ((b[i++] & 0xc0) != 0x80) {
throw new IllegalArgumentException();
}
count++;
break;
case 14:
// 1110xxxx 10xxxxxx 10xxxxxx
if (((b[i++] & 0xc0) != 0x80) ||
((b[i++] & 0xc0) != 0x80)) {
throw new IllegalArgumentException();
}
count++;
break;
default:
// 10xxxxxx, 1111xxxx
throw new IllegalArgumentException();
}
}
if (i != max) {
throw new IllegalArgumentException();
}
// Now decode the characters...
char[] cs = new char[count];
i = 0;
while (off < max) {
int c = b[off++] & 0xff;
switch (c >> 4) {
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
// 0xxxxxxx
cs[i++] = (char)c;
break;
case 12: case 13:
// 110xxxxx 10xxxxxx
cs[i++] = (char)(((c & 0x1f) << 6) | (b[off++] & 0x3f));
break;
case 14:
// 1110xxxx 10xxxxxx 10xxxxxx
int t = (b[off++] & 0x3f) << 6;
cs[i++] = (char)(((c & 0x0f) << 12) | t | (b[off++] & 0x3f));
break;
default:
// 10xxxxxx, 1111xxxx
throw new IllegalArgumentException();
}
}
return new String(cs, 0, count);
}
/**
* Creates a new <code>ZipEntry</code> object for the specified
* entry name.
......
......@@ -27,6 +27,7 @@ package java.util.zip;
import java.io.OutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Vector;
import java.util.HashSet;
import static java.util.zip.ZipConstants64.*;
......@@ -44,19 +45,9 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
private static class XEntry {
public final ZipEntry entry;
public final long offset;
public final int flag;
public XEntry(ZipEntry entry, long offset) {
this.entry = entry;
this.offset = offset;
this.flag = (entry.method == DEFLATED &&
(entry.size == -1 ||
entry.csize == -1 ||
entry.crc == -1))
// store size, compressed size, and crc-32 in data descriptor
// immediately following the compressed entry data
? 8
// store size, compressed size, and crc-32 in LOC header
: 0;
}
}
......@@ -66,12 +57,14 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
private CRC32 crc = new CRC32();
private long written = 0;
private long locoff = 0;
private String comment;
private byte[] comment;
private int method = DEFLATED;
private boolean finished;
private boolean closed = false;
private final ZipCoder zc;
private static int version(ZipEntry e) throws ZipException {
switch (e.method) {
case DEFLATED: return 20;
......@@ -100,10 +93,31 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
/**
* Creates a new ZIP output stream.
*
* <p>The UTF-8 {@link java.nio.charset.Charset charset} is used
* to encode the entry names and comments.
*
* @param out the actual output stream
*/
public ZipOutputStream(OutputStream out) {
this(out, Charset.forName("UTF-8"));
}
/**
* Creates a new ZIP output stream.
*
* @param out the actual output stream
*
* @param charset the {@link java.nio.charset.Charset </code>charset<code>}
* to be used to encode the entry names and comments
*
* @since 1.7
*/
public ZipOutputStream(OutputStream out, Charset charset) {
super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
if (charset == null)
throw new NullPointerException("charset is null");
this.zc = ZipCoder.get(charset);
usesDefaultDeflater = true;
}
......@@ -114,11 +128,11 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
* ZIP file comment is greater than 0xFFFF bytes
*/
public void setComment(String comment) {
if (comment != null && comment.length() > 0xffff/3
&& getUTF8Length(comment) > 0xffff) {
if (comment != null) {
this.comment = zc.getBytes(comment);
if (this.comment.length > 0xffff)
throw new IllegalArgumentException("ZIP file comment too long.");
}
this.comment = comment;
}
/**
......@@ -167,8 +181,15 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
if (e.method == -1) {
e.method = method; // use default method
}
// store size, compressed size, and crc-32 in LOC header
e.flag = 0;
switch (e.method) {
case DEFLATED:
// store size, compressed size, and crc-32 in data descriptor
// immediately following the compressed entry data
if (e.size == -1 || e.csize == -1 || e.crc == -1)
e.flag = 8;
break;
case STORED:
// compressed size, uncompressed size, and crc-32 must all be
......@@ -192,6 +213,8 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
if (! names.add(e.name)) {
throw new ZipException("duplicate entry: " + e.name);
}
if (zc.isUTF8())
e.flag |= EFS;
current = new XEntry(e, written);
xentries.add(current);
writeLOC(current);
......@@ -213,7 +236,7 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
while (!def.finished()) {
deflate();
}
if ((current.flag & 8) == 0) {
if ((e.flag & 8) == 0) {
// verify size, compressed size, and crc-32 settings
if (e.size != def.getBytesRead()) {
throw new ZipException(
......@@ -343,7 +366,7 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
*/
private void writeLOC(XEntry xentry) throws IOException {
ZipEntry e = xentry.entry;
int flag = xentry.flag;
int flag = e.flag;
int elen = (e.extra != null) ? e.extra.length : 0;
boolean hasZip64 = false;
......@@ -380,7 +403,7 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
writeInt(e.size); // uncompressed size
}
}
byte[] nameBytes = getUTF8Bytes(e.name);
byte[] nameBytes = zc.getBytes(e.name);
writeShort(nameBytes.length);
writeShort(elen);
writeBytes(nameBytes, 0, nameBytes.length);
......@@ -417,7 +440,7 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
*/
private void writeCEN(XEntry xentry) throws IOException {
ZipEntry e = xentry.entry;
int flag = xentry.flag;
int flag = e.flag;
int version = version(e);
long csize = e.csize;
......@@ -454,7 +477,7 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
writeInt(e.crc); // crc-32
writeInt(csize); // compressed size
writeInt(size); // uncompressed size
byte[] nameBytes = getUTF8Bytes(e.name);
byte[] nameBytes = zc.getBytes(e.name);
writeShort(nameBytes.length);
if (hasZip64) {
// + headid(2) + datasize(2)
......@@ -464,8 +487,8 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
}
byte[] commentBytes;
if (e.comment != null) {
commentBytes = getUTF8Bytes(e.comment);
writeShort(commentBytes.length);
commentBytes = zc.getBytes(e.comment);
writeShort(Math.min(commentBytes.length, 0xffff));
} else {
commentBytes = null;
writeShort(0);
......@@ -489,7 +512,7 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
writeBytes(e.extra, 0, e.extra.length);
}
if (commentBytes != null) {
writeBytes(commentBytes, 0, commentBytes.length);
writeBytes(commentBytes, 0, Math.min(commentBytes.length, 0xffff));
}
}
......@@ -541,9 +564,8 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
writeInt(xlen); // length of central directory
writeInt(xoff); // offset of central directory
if (comment != null) { // zip file comment
byte[] b = getUTF8Bytes(comment);
writeShort(b.length);
writeBytes(b, 0, b.length);
writeShort(comment.length);
writeBytes(comment, 0, comment.length);
} else {
writeShort(0);
}
......@@ -594,60 +616,4 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
super.out.write(b, off, len);
written += len;
}
/*
* Returns the length of String's UTF8 encoding.
*/
static int getUTF8Length(String s) {
int count = 0;
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (ch <= 0x7f) {
count++;
} else if (ch <= 0x7ff) {
count += 2;
} else {
count += 3;
}
}
return count;
}
/*
* Returns an array of bytes representing the UTF8 encoding
* of the specified String.
*/
private static byte[] getUTF8Bytes(String s) {
char[] c = s.toCharArray();
int len = c.length;
// Count the number of encoded bytes...
int count = 0;
for (int i = 0; i < len; i++) {
int ch = c[i];
if (ch <= 0x7f) {
count++;
} else if (ch <= 0x7ff) {
count += 2;
} else {
count += 3;
}
}
// Now return the encoded bytes...
byte[] b = new byte[count];
int off = 0;
for (int i = 0; i < len; i++) {
int ch = c[i];
if (ch <= 0x7f) {
b[off++] = (byte)ch;
} else if (ch <= 0x7ff) {
b[off++] = (byte)((ch >> 6) | 0xc0);
b[off++] = (byte)((ch & 0x3f) | 0x80);
} else {
b[off++] = (byte)((ch >> 12) | 0xe0);
b[off++] = (byte)(((ch >> 6) & 0x3f) | 0x80);
b[off++] = (byte)((ch & 0x3f) | 0x80);
}
}
return b;
}
}
......@@ -52,6 +52,11 @@ input streams.
<a href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">
PKWARE ZIP File Format Specification</a>. The ZIP64(tm) format extensions
are used to overcome the size limitations of the original ZIP format.
<p>
<a name="lang_encoding">
<li>APPENDIX D of <a href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">
PKWARE ZIP File Format Specification</a> - Language Encoding Flag (EFS) to
encode ZIP entry filename and comment fields using UTF-8.
<p>
<li><a href="http://www.isi.edu/in-notes/rfc1950.txt">
ZLIB Compressed Data Format Specification version 3.3</a>
......
/*
* Copyright 1998 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/*
* Native method support for java.util.zip.ZipEntry
*/
#include <stdio.h>
#include <stdlib.h>
#include "jlong.h"
#include "jvm.h"
#include "jni.h"
#include "jni_util.h"
#include "zip_util.h"
#include "java_util_zip_ZipEntry.h"
#define DEFLATED 8
#define STORED 0
static jfieldID nameID;
static jfieldID timeID;
static jfieldID crcID;
static jfieldID sizeID;
static jfieldID csizeID;
static jfieldID methodID;
static jfieldID extraID;
static jfieldID commentID;
JNIEXPORT void JNICALL
Java_java_util_zip_ZipEntry_initIDs(JNIEnv *env, jclass cls)
{
nameID = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");
timeID = (*env)->GetFieldID(env, cls, "time", "J");
crcID = (*env)->GetFieldID(env, cls, "crc", "J");
sizeID = (*env)->GetFieldID(env, cls, "size", "J");
csizeID = (*env)->GetFieldID(env, cls, "csize", "J");
methodID = (*env)->GetFieldID(env, cls, "method", "I");
extraID = (*env)->GetFieldID(env, cls, "extra", "[B");
commentID = (*env)->GetFieldID(env, cls, "comment", "Ljava/lang/String;");
}
JNIEXPORT void JNICALL
Java_java_util_zip_ZipEntry_initFields(JNIEnv *env, jobject obj, jlong zentry)
{
jzentry *ze = jlong_to_ptr(zentry);
jstring name = (*env)->GetObjectField(env, obj, nameID);
if (name == 0) {
name = (*env)->NewStringUTF(env, ze->name);
if (name == 0) {
return;
}
(*env)->SetObjectField(env, obj, nameID, name);
}
(*env)->SetLongField(env, obj, timeID, (jlong)ze->time & 0xffffffffUL);
(*env)->SetLongField(env, obj, crcID, (jlong)ze->crc & 0xffffffffUL);
(*env)->SetLongField(env, obj, sizeID, (jlong)ze->size);
if (ze->csize == 0) {
(*env)->SetLongField(env, obj, csizeID, (jlong)ze->size);
(*env)->SetIntField(env, obj, methodID, STORED);
} else {
(*env)->SetLongField(env, obj, csizeID, (jlong)ze->csize);
(*env)->SetIntField(env, obj, methodID, DEFLATED);
}
if (ze->extra != 0) {
unsigned char *bp = (unsigned char *)&ze->extra[0];
jsize len = (bp[0] | (bp[1] << 8));
jbyteArray extra = (*env)->NewByteArray(env, len);
if (extra == 0) {
return;
}
(*env)->SetByteArrayRegion(env, extra, 0, len, &ze->extra[2]);
(*env)->SetObjectField(env, obj, extraID, extra);
}
if (ze->comment != 0) {
jstring comment = (*env)->NewStringUTF(env, ze->comment);
if (comment == 0) {
return;
}
(*env)->SetObjectField(env, obj, commentID, comment);
}
}
......@@ -141,12 +141,11 @@ Java_java_util_zip_ZipFile_close(JNIEnv *env, jclass cls, jlong zfile)
JNIEXPORT jlong JNICALL
Java_java_util_zip_ZipFile_getEntry(JNIEnv *env, jclass cls, jlong zfile,
jstring name, jboolean addSlash)
jbyteArray name, jboolean addSlash)
{
#define MAXNAME 1024
jzfile *zip = jlong_to_ptr(zfile);
jsize slen = (*env)->GetStringLength(env, name);
jsize ulen = (*env)->GetStringUTFLength(env, name);
jsize ulen = (*env)->GetArrayLength(env, name);
char buf[MAXNAME+2], *path;
jzentry *ze;
......@@ -159,7 +158,7 @@ Java_java_util_zip_ZipFile_getEntry(JNIEnv *env, jclass cls, jlong zfile,
} else {
path = buf;
}
(*env)->GetStringUTFRegion(env, name, 0, slen, path);
(*env)->GetByteArrayRegion(env, name, 0, ulen, (jbyte *)path);
path[ulen] = '\0';
if (addSlash == JNI_FALSE) {
ze = ZIP_GetEntry(zip, path, 0);
......@@ -186,34 +185,87 @@ Java_java_util_zip_ZipFile_getNextEntry(JNIEnv *env, jclass cls, jlong zfile,
jint n)
{
jzentry *ze = ZIP_GetNextEntry(jlong_to_ptr(zfile), n);
return ptr_to_jlong(ze);
}
JNIEXPORT jint JNICALL
Java_java_util_zip_ZipFile_getMethod(JNIEnv *env, jclass cls, jlong zentry)
Java_java_util_zip_ZipFile_getEntryMethod(JNIEnv *env, jclass cls, jlong zentry)
{
jzentry *ze = jlong_to_ptr(zentry);
return ze->csize != 0 ? DEFLATED : STORED;
}
JNIEXPORT jlong JNICALL
Java_java_util_zip_ZipFile_getCSize(JNIEnv *env, jclass cls, jlong zentry)
JNIEXPORT jint JNICALL
Java_java_util_zip_ZipFile_getEntryFlag(JNIEnv *env, jclass cls, jlong zentry)
{
jzentry *ze = jlong_to_ptr(zentry);
return ze->flag;
}
JNIEXPORT jlong JNICALL
Java_java_util_zip_ZipFile_getEntryCSize(JNIEnv *env, jclass cls, jlong zentry)
{
jzentry *ze = jlong_to_ptr(zentry);
return ze->csize != 0 ? ze->csize : ze->size;
}
JNIEXPORT jlong JNICALL
Java_java_util_zip_ZipFile_getSize(JNIEnv *env, jclass cls, jlong zentry)
Java_java_util_zip_ZipFile_getEntrySize(JNIEnv *env, jclass cls, jlong zentry)
{
jzentry *ze = jlong_to_ptr(zentry);
return ze->size;
}
JNIEXPORT jlong JNICALL
Java_java_util_zip_ZipFile_getEntryTime(JNIEnv *env, jclass cls, jlong zentry)
{
jzentry *ze = jlong_to_ptr(zentry);
return (jlong)ze->time & 0xffffffffUL;
}
JNIEXPORT jlong JNICALL
Java_java_util_zip_ZipFile_getEntryCrc(JNIEnv *env, jclass cls, jlong zentry)
{
jzentry *ze = jlong_to_ptr(zentry);
return (jlong)ze->crc & 0xffffffffUL;
}
JNIEXPORT jbyteArray JNICALL
Java_java_util_zip_ZipFile_getEntryBytes(JNIEnv *env, jclass cls, jlong zentry, jint type)
{
jzentry *ze = jlong_to_ptr(zentry);
int len = 0;
jbyteArray jba = NULL;
switch (type) {
case java_util_zip_ZipFile_JZENTRY_NAME:
if (ze->name != 0) {
len = (int)strlen(ze->name);
if (len == 0 || (jba = (*env)->NewByteArray(env, len)) == NULL)
break;
(*env)->SetByteArrayRegion(env, jba, 0, len, (jbyte *)ze->name);
}
break;
case java_util_zip_ZipFile_JZENTRY_EXTRA:
if (ze->extra != 0) {
unsigned char *bp = (unsigned char *)&ze->extra[0];
len = (bp[0] | (bp[1] << 8));
if (len <= 0 || (jba = (*env)->NewByteArray(env, len)) == NULL)
break;
(*env)->SetByteArrayRegion(env, jba, 0, len, &ze->extra[2]);
}
break;
case java_util_zip_ZipFile_JZENTRY_COMMENT:
if (ze->comment != 0) {
len = (int)strlen(ze->comment);
if (len == 0 || (jba = (*env)->NewByteArray(env, len)) == NULL)
break;
(*env)->SetByteArrayRegion(env, jba, 0, len, (jbyte*)ze->comment);
}
break;
}
return jba;
}
JNIEXPORT jint JNICALL
Java_java_util_zip_ZipFile_read(JNIEnv *env, jclass cls, jlong zfile,
jlong zentry, jlong pos, jbyteArray bytes,
......
......@@ -512,7 +512,6 @@ readCEN(jzfile *zip, jint knownTotal)
/* Clear previous zip error */
zip->msg = NULL;
/* Get position of END header */
if ((endpos = findEND(zip, endbuf)) == -1)
return -1; /* no END header or system error */
......@@ -520,7 +519,6 @@ readCEN(jzfile *zip, jint knownTotal)
if (endpos == 0) return 0; /* only END header present */
freeCEN(zip);
/* Get position and length of central directory */
cenlen = ENDSIZ(endbuf);
cenoff = ENDOFF(endbuf);
......@@ -935,6 +933,7 @@ newEntry(jzfile *zip, jzcell *zc, AccessHint accessHint)
ze->crc = CENCRC(cen);
locoff = CENOFF(cen);
ze->pos = -(zip->locpos + locoff);
ze->flag = CENFLG(cen);
if ((ze->name = malloc(nlen + 1)) == NULL) goto Catch;
memcpy(ze->name, cen + CENHDR, nlen);
......
......@@ -168,6 +168,7 @@ typedef struct jzentry { /* Zip file entry */
char *comment; /* optional zip file comment */
jbyte *extra; /* optional extra data */
jlong pos; /* position of LOC header or entry data */
jint flag; /* general purpose flag */
} jzentry;
/*
......
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/**
* @test
* @bug 4244499 4532049 4700978 4820807 4980042
* @summary Test ZipInputStream, ZipOutputStream and ZipFile with non-UTF8 encoding
*/
import java.io.*;
import java.nio.charset.*;
import java.util.*;
import java.util.zip.*;
public class ZipCoding {
public static void main(String[] args) throws Exception {
test("MS932",
"\u4e00\u4e01", "\uff67\uff68\uff69\uff6a\uff6b\uff6c");
test("ibm437",
"\u00e4\u00fc", "German Umlaut \u00fc in comment");
test("utf-8",
"\u4e00\u4e01", "\uff67\uff68\uff69\uff6a\uff6b\uff6c");
test("utf-8",
"\u00e4\u00fc", "German Umlaut \u00fc in comment");
test("utf-8",
"Surrogate\ud801\udc01", "Surrogates \ud800\udc00 in comment");
}
static void testZipInputStream(InputStream is, Charset cs,
String name, String comment, byte[] bb)
throws Exception
{
ZipInputStream zis = new ZipInputStream(is, cs);
ZipEntry e = zis.getNextEntry();
if (e == null || ! name.equals(e.getName()))
throw new RuntimeException("ZipIS name doesn't match!");
byte[] bBuf = new byte[bb.length << 1];
int n = zis.read(bBuf, 0, bBuf.length);
if (n != bb.length ||
!Arrays.equals(bb, Arrays.copyOf(bBuf, n))) {
throw new RuntimeException("ZipIS content doesn't match!");
}
zis.close();
}
static void testZipFile(File f, Charset cs,
String name, String comment, byte[] bb)
throws Exception
{
ZipFile zf = new ZipFile(f, cs);
Enumeration<? extends ZipEntry> zes = zf.entries();
ZipEntry e = (ZipEntry)zes.nextElement();
if (! name.equals(e.getName()) ||
! comment.equals(e.getComment()))
throw new RuntimeException("ZipFile: name/comment doesn't match!");
InputStream is = zf.getInputStream(e);
if (is == null)
throw new RuntimeException("ZipFile: getIS failed!");
byte[] bBuf = new byte[bb.length << 1];
int n = 0;
int nn =0;
while ((nn = is.read(bBuf, n, bBuf.length-n)) != -1) {
n += nn;
}
if (n != bb.length ||
!Arrays.equals(bb, Arrays.copyOf(bBuf, n))) {
throw new RuntimeException("ZipFile content doesn't match!");
}
zf.close();
}
static void test(String csn, String name, String comment)
throws Exception
{
byte[] bb = "This is the conent of the zipfile".getBytes("ISO-8859-1");
Charset cs = Charset.forName(csn);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos, cs);
ZipEntry e = new ZipEntry(name);
e.setComment(comment);
zos.putNextEntry(e);
zos.write(bb, 0, bb.length);
zos.closeEntry();
zos.close();
ByteArrayInputStream bis = new ByteArrayInputStream(baos.toByteArray());
testZipInputStream(bis, cs, name, comment, bb);
if ("utf-8".equals(csn)) {
// EFS should be set
bis.reset();
testZipInputStream(bis, Charset.forName("MS932"), name, comment, bb);
}
File f = new File(new File(System.getProperty("test.dir", ".")),
"zfcoding.zip");
FileOutputStream fos = new FileOutputStream(f);
baos.writeTo(fos);
fos.close();
testZipFile(f, cs, name, comment, bb);
if ("utf-8".equals(csn)) {
testZipFile(f, Charset.forName("MS932"), name, comment, bb);
}
f.delete();
}
}
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册