1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/emul/mini/src/main/java/java/util/zip/ZipInputStream.java Wed Jan 30 14:00:17 2013 +0100
1.3 @@ -0,0 +1,456 @@
1.4 +/*
1.5 + * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved.
1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1.7 + *
1.8 + * This code is free software; you can redistribute it and/or modify it
1.9 + * under the terms of the GNU General Public License version 2 only, as
1.10 + * published by the Free Software Foundation. Oracle designates this
1.11 + * particular file as subject to the "Classpath" exception as provided
1.12 + * by Oracle in the LICENSE file that accompanied this code.
1.13 + *
1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1.17 + * version 2 for more details (a copy is included in the LICENSE file that
1.18 + * accompanied this code).
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License version
1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1.23 + *
1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
1.25 + * or visit www.oracle.com if you need additional information or have any
1.26 + * questions.
1.27 + */
1.28 +
1.29 +package java.util.zip;
1.30 +
1.31 +import java.io.InputStream;
1.32 +import java.io.IOException;
1.33 +import java.io.EOFException;
1.34 +import java.io.PushbackInputStream;
1.35 +import java.nio.charset.Charset;
1.36 +import java.nio.charset.StandardCharsets;
1.37 +import static java.util.zip.ZipConstants64.*;
1.38 +
1.39 +/**
1.40 + * This class implements an input stream filter for reading files in the
1.41 + * ZIP file format. Includes support for both compressed and uncompressed
1.42 + * entries.
1.43 + *
1.44 + * @author David Connelly
1.45 + */
1.46 +public
1.47 +class ZipInputStream extends InflaterInputStream implements ZipConstants {
1.48 + private ZipEntry entry;
1.49 + private int flag;
1.50 + private CRC32 crc = new CRC32();
1.51 + private long remaining;
1.52 + private byte[] tmpbuf = new byte[512];
1.53 +
1.54 + private static final int STORED = ZipEntry.STORED;
1.55 + private static final int DEFLATED = ZipEntry.DEFLATED;
1.56 +
1.57 + private boolean closed = false;
1.58 + // this flag is set to true after EOF has reached for
1.59 + // one entry
1.60 + private boolean entryEOF = false;
1.61 +
1.62 + private ZipCoder zc;
1.63 +
1.64 + /**
1.65 + * Check to make sure that this stream has not been closed
1.66 + */
1.67 + private void ensureOpen() throws IOException {
1.68 + if (closed) {
1.69 + throw new IOException("Stream closed");
1.70 + }
1.71 + }
1.72 +
1.73 + /**
1.74 + * Creates a new ZIP input stream.
1.75 + *
1.76 + * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
1.77 + * decode the entry names.
1.78 + *
1.79 + * @param in the actual input stream
1.80 + */
1.81 + public ZipInputStream(InputStream in) {
1.82 + this(in, StandardCharsets.UTF_8);
1.83 + }
1.84 +
1.85 + /**
1.86 + * Creates a new ZIP input stream.
1.87 + *
1.88 + * @param in the actual input stream
1.89 + *
1.90 + * @param charset
1.91 + * The {@linkplain java.nio.charset.Charset charset} to be
1.92 + * used to decode the ZIP entry name (ignored if the
1.93 + * <a href="package-summary.html#lang_encoding"> language
1.94 + * encoding bit</a> of the ZIP entry's general purpose bit
1.95 + * flag is set).
1.96 + *
1.97 + * @since 1.7
1.98 + */
1.99 + public ZipInputStream(InputStream in, Charset charset) {
1.100 + super(new PushbackInputStream(in, 512), new Inflater(true), 512);
1.101 + usesDefaultInflater = true;
1.102 + if(in == null) {
1.103 + throw new NullPointerException("in is null");
1.104 + }
1.105 + if (charset == null)
1.106 + throw new NullPointerException("charset is null");
1.107 + this.zc = ZipCoder.get(charset);
1.108 + }
1.109 +
1.110 + /**
1.111 + * Reads the next ZIP file entry and positions the stream at the
1.112 + * beginning of the entry data.
1.113 + * @return the next ZIP file entry, or null if there are no more entries
1.114 + * @exception ZipException if a ZIP file error has occurred
1.115 + * @exception IOException if an I/O error has occurred
1.116 + */
1.117 + public ZipEntry getNextEntry() throws IOException {
1.118 + ensureOpen();
1.119 + if (entry != null) {
1.120 + closeEntry();
1.121 + }
1.122 + crc.reset();
1.123 + inf.reset();
1.124 + if ((entry = readLOC()) == null) {
1.125 + return null;
1.126 + }
1.127 + if (entry.method == STORED) {
1.128 + remaining = entry.size;
1.129 + }
1.130 + entryEOF = false;
1.131 + return entry;
1.132 + }
1.133 +
1.134 + /**
1.135 + * Closes the current ZIP entry and positions the stream for reading the
1.136 + * next entry.
1.137 + * @exception ZipException if a ZIP file error has occurred
1.138 + * @exception IOException if an I/O error has occurred
1.139 + */
1.140 + public void closeEntry() throws IOException {
1.141 + ensureOpen();
1.142 + while (read(tmpbuf, 0, tmpbuf.length) != -1) ;
1.143 + entryEOF = true;
1.144 + }
1.145 +
1.146 + /**
1.147 + * Returns 0 after EOF has reached for the current entry data,
1.148 + * otherwise always return 1.
1.149 + * <p>
1.150 + * Programs should not count on this method to return the actual number
1.151 + * of bytes that could be read without blocking.
1.152 + *
1.153 + * @return 1 before EOF and 0 after EOF has reached for current entry.
1.154 + * @exception IOException if an I/O error occurs.
1.155 + *
1.156 + */
1.157 + public int available() throws IOException {
1.158 + ensureOpen();
1.159 + if (entryEOF) {
1.160 + return 0;
1.161 + } else {
1.162 + return 1;
1.163 + }
1.164 + }
1.165 +
1.166 + /**
1.167 + * Reads from the current ZIP entry into an array of bytes.
1.168 + * If <code>len</code> is not zero, the method
1.169 + * blocks until some input is available; otherwise, no
1.170 + * bytes are read and <code>0</code> is returned.
1.171 + * @param b the buffer into which the data is read
1.172 + * @param off the start offset in the destination array <code>b</code>
1.173 + * @param len the maximum number of bytes read
1.174 + * @return the actual number of bytes read, or -1 if the end of the
1.175 + * entry is reached
1.176 + * @exception NullPointerException if <code>b</code> is <code>null</code>.
1.177 + * @exception IndexOutOfBoundsException if <code>off</code> is negative,
1.178 + * <code>len</code> is negative, or <code>len</code> is greater than
1.179 + * <code>b.length - off</code>
1.180 + * @exception ZipException if a ZIP file error has occurred
1.181 + * @exception IOException if an I/O error has occurred
1.182 + */
1.183 + public int read(byte[] b, int off, int len) throws IOException {
1.184 + ensureOpen();
1.185 + if (off < 0 || len < 0 || off > b.length - len) {
1.186 + throw new IndexOutOfBoundsException();
1.187 + } else if (len == 0) {
1.188 + return 0;
1.189 + }
1.190 +
1.191 + if (entry == null) {
1.192 + return -1;
1.193 + }
1.194 + switch (entry.method) {
1.195 + case DEFLATED:
1.196 + len = super.read(b, off, len);
1.197 + if (len == -1) {
1.198 + readEnd(entry);
1.199 + entryEOF = true;
1.200 + entry = null;
1.201 + } else {
1.202 + crc.update(b, off, len);
1.203 + }
1.204 + return len;
1.205 + case STORED:
1.206 + if (remaining <= 0) {
1.207 + entryEOF = true;
1.208 + entry = null;
1.209 + return -1;
1.210 + }
1.211 + if (len > remaining) {
1.212 + len = (int)remaining;
1.213 + }
1.214 + len = in.read(b, off, len);
1.215 + if (len == -1) {
1.216 + throw new ZipException("unexpected EOF");
1.217 + }
1.218 + crc.update(b, off, len);
1.219 + remaining -= len;
1.220 + if (remaining == 0 && entry.crc != crc.getValue()) {
1.221 + throw new ZipException(
1.222 + "invalid entry CRC (expected 0x" + Long.toHexString(entry.crc) +
1.223 + " but got 0x" + Long.toHexString(crc.getValue()) + ")");
1.224 + }
1.225 + return len;
1.226 + default:
1.227 + throw new ZipException("invalid compression method");
1.228 + }
1.229 + }
1.230 +
1.231 + /**
1.232 + * Skips specified number of bytes in the current ZIP entry.
1.233 + * @param n the number of bytes to skip
1.234 + * @return the actual number of bytes skipped
1.235 + * @exception ZipException if a ZIP file error has occurred
1.236 + * @exception IOException if an I/O error has occurred
1.237 + * @exception IllegalArgumentException if n < 0
1.238 + */
1.239 + public long skip(long n) throws IOException {
1.240 + if (n < 0) {
1.241 + throw new IllegalArgumentException("negative skip length");
1.242 + }
1.243 + ensureOpen();
1.244 + int max = (int)Math.min(n, Integer.MAX_VALUE);
1.245 + int total = 0;
1.246 + while (total < max) {
1.247 + int len = max - total;
1.248 + if (len > tmpbuf.length) {
1.249 + len = tmpbuf.length;
1.250 + }
1.251 + len = read(tmpbuf, 0, len);
1.252 + if (len == -1) {
1.253 + entryEOF = true;
1.254 + break;
1.255 + }
1.256 + total += len;
1.257 + }
1.258 + return total;
1.259 + }
1.260 +
1.261 + /**
1.262 + * Closes this input stream and releases any system resources associated
1.263 + * with the stream.
1.264 + * @exception IOException if an I/O error has occurred
1.265 + */
1.266 + public void close() throws IOException {
1.267 + if (!closed) {
1.268 + super.close();
1.269 + closed = true;
1.270 + }
1.271 + }
1.272 +
1.273 + private byte[] b = new byte[256];
1.274 +
1.275 + /*
1.276 + * Reads local file (LOC) header for next entry.
1.277 + */
1.278 + private ZipEntry readLOC() throws IOException {
1.279 + try {
1.280 + readFully(tmpbuf, 0, LOCHDR);
1.281 + } catch (EOFException e) {
1.282 + return null;
1.283 + }
1.284 + if (get32(tmpbuf, 0) != LOCSIG) {
1.285 + return null;
1.286 + }
1.287 + // get flag first, we need check EFS.
1.288 + flag = get16(tmpbuf, LOCFLG);
1.289 + // get the entry name and create the ZipEntry first
1.290 + int len = get16(tmpbuf, LOCNAM);
1.291 + int blen = b.length;
1.292 + if (len > blen) {
1.293 + do
1.294 + blen = blen * 2;
1.295 + while (len > blen);
1.296 + b = new byte[blen];
1.297 + }
1.298 + readFully(b, 0, len);
1.299 + // Force to use UTF-8 if the EFS bit is ON, even the cs is NOT UTF-8
1.300 + ZipEntry e = createZipEntry(((flag & EFS) != 0)
1.301 + ? zc.toStringUTF8(b, len)
1.302 + : zc.toString(b, len));
1.303 + // now get the remaining fields for the entry
1.304 + if ((flag & 1) == 1) {
1.305 + throw new ZipException("encrypted ZIP entry not supported");
1.306 + }
1.307 + e.method = get16(tmpbuf, LOCHOW);
1.308 + e.time = get32(tmpbuf, LOCTIM);
1.309 + if ((flag & 8) == 8) {
1.310 + /* "Data Descriptor" present */
1.311 + if (e.method != DEFLATED) {
1.312 + throw new ZipException(
1.313 + "only DEFLATED entries can have EXT descriptor");
1.314 + }
1.315 + } else {
1.316 + e.crc = get32(tmpbuf, LOCCRC);
1.317 + e.csize = get32(tmpbuf, LOCSIZ);
1.318 + e.size = get32(tmpbuf, LOCLEN);
1.319 + }
1.320 + len = get16(tmpbuf, LOCEXT);
1.321 + if (len > 0) {
1.322 + byte[] bb = new byte[len];
1.323 + readFully(bb, 0, len);
1.324 + e.setExtra(bb);
1.325 + // extra fields are in "HeaderID(2)DataSize(2)Data... format
1.326 + if (e.csize == ZIP64_MAGICVAL || e.size == ZIP64_MAGICVAL) {
1.327 + int off = 0;
1.328 + while (off + 4 < len) {
1.329 + int sz = get16(bb, off + 2);
1.330 + if (get16(bb, off) == ZIP64_EXTID) {
1.331 + off += 4;
1.332 + // LOC extra zip64 entry MUST include BOTH original and
1.333 + // compressed file size fields
1.334 + if (sz < 16 || (off + sz) > len ) {
1.335 + // Invalid zip64 extra fields, simply skip. Even it's
1.336 + // rare, it's possible the entry size happens to be
1.337 + // the magic value and it "accidnetly" has some bytes
1.338 + // in extra match the id.
1.339 + return e;
1.340 + }
1.341 + e.size = get64(bb, off);
1.342 + e.csize = get64(bb, off + 8);
1.343 + break;
1.344 + }
1.345 + off += (sz + 4);
1.346 + }
1.347 + }
1.348 + }
1.349 + return e;
1.350 + }
1.351 +
1.352 + /**
1.353 + * Creates a new <code>ZipEntry</code> object for the specified
1.354 + * entry name.
1.355 + *
1.356 + * @param name the ZIP file entry name
1.357 + * @return the ZipEntry just created
1.358 + */
1.359 + protected ZipEntry createZipEntry(String name) {
1.360 + return new ZipEntry(name);
1.361 + }
1.362 +
1.363 + /*
1.364 + * Reads end of deflated entry as well as EXT descriptor if present.
1.365 + */
1.366 + private void readEnd(ZipEntry e) throws IOException {
1.367 + int n = inf.getRemaining();
1.368 + if (n > 0) {
1.369 + ((PushbackInputStream)in).unread(buf, len - n, n);
1.370 + }
1.371 + if ((flag & 8) == 8) {
1.372 + /* "Data Descriptor" present */
1.373 + if (inf.getBytesWritten() > ZIP64_MAGICVAL ||
1.374 + inf.getBytesRead() > ZIP64_MAGICVAL) {
1.375 + // ZIP64 format
1.376 + readFully(tmpbuf, 0, ZIP64_EXTHDR);
1.377 + long sig = get32(tmpbuf, 0);
1.378 + if (sig != EXTSIG) { // no EXTSIG present
1.379 + e.crc = sig;
1.380 + e.csize = get64(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC);
1.381 + e.size = get64(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC);
1.382 + ((PushbackInputStream)in).unread(
1.383 + tmpbuf, ZIP64_EXTHDR - ZIP64_EXTCRC - 1, ZIP64_EXTCRC);
1.384 + } else {
1.385 + e.crc = get32(tmpbuf, ZIP64_EXTCRC);
1.386 + e.csize = get64(tmpbuf, ZIP64_EXTSIZ);
1.387 + e.size = get64(tmpbuf, ZIP64_EXTLEN);
1.388 + }
1.389 + } else {
1.390 + readFully(tmpbuf, 0, EXTHDR);
1.391 + long sig = get32(tmpbuf, 0);
1.392 + if (sig != EXTSIG) { // no EXTSIG present
1.393 + e.crc = sig;
1.394 + e.csize = get32(tmpbuf, EXTSIZ - EXTCRC);
1.395 + e.size = get32(tmpbuf, EXTLEN - EXTCRC);
1.396 + ((PushbackInputStream)in).unread(
1.397 + tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC);
1.398 + } else {
1.399 + e.crc = get32(tmpbuf, EXTCRC);
1.400 + e.csize = get32(tmpbuf, EXTSIZ);
1.401 + e.size = get32(tmpbuf, EXTLEN);
1.402 + }
1.403 + }
1.404 + }
1.405 + if (e.size != inf.getBytesWritten()) {
1.406 + throw new ZipException(
1.407 + "invalid entry size (expected " + e.size +
1.408 + " but got " + inf.getBytesWritten() + " bytes)");
1.409 + }
1.410 + if (e.csize != inf.getBytesRead()) {
1.411 + throw new ZipException(
1.412 + "invalid entry compressed size (expected " + e.csize +
1.413 + " but got " + inf.getBytesRead() + " bytes)");
1.414 + }
1.415 + if (e.crc != crc.getValue()) {
1.416 + throw new ZipException(
1.417 + "invalid entry CRC (expected 0x" + Long.toHexString(e.crc) +
1.418 + " but got 0x" + Long.toHexString(crc.getValue()) + ")");
1.419 + }
1.420 + }
1.421 +
1.422 + /*
1.423 + * Reads bytes, blocking until all bytes are read.
1.424 + */
1.425 + private void readFully(byte[] b, int off, int len) throws IOException {
1.426 + while (len > 0) {
1.427 + int n = in.read(b, off, len);
1.428 + if (n == -1) {
1.429 + throw new EOFException();
1.430 + }
1.431 + off += n;
1.432 + len -= n;
1.433 + }
1.434 + }
1.435 +
1.436 + /*
1.437 + * Fetches unsigned 16-bit value from byte array at specified offset.
1.438 + * The bytes are assumed to be in Intel (little-endian) byte order.
1.439 + */
1.440 + private static final int get16(byte b[], int off) {
1.441 + return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8);
1.442 + }
1.443 +
1.444 + /*
1.445 + * Fetches unsigned 32-bit value from byte array at specified offset.
1.446 + * The bytes are assumed to be in Intel (little-endian) byte order.
1.447 + */
1.448 + private static final long get32(byte b[], int off) {
1.449 + return (get16(b, off) | ((long)get16(b, off+2) << 16)) & 0xffffffffL;
1.450 + }
1.451 +
1.452 + /*
1.453 + * Fetches signed 64-bit value from byte array at specified offset.
1.454 + * The bytes are assumed to be in Intel (little-endian) byte order.
1.455 + */
1.456 + private static final long get64(byte b[], int off) {
1.457 + return get32(b, off) | (get32(b, off+4) << 32);
1.458 + }
1.459 +}