ZipArchiveInputStream.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one
  3.  * or more contributor license agreements.  See the NOTICE file
  4.  * distributed with this work for additional information
  5.  * regarding copyright ownership.  The ASF licenses this file
  6.  * to you under the Apache License, Version 2.0 (the
  7.  * "License"); you may not use this file except in compliance
  8.  * with the License.  You may obtain a copy of the License at
  9.  *
  10.  * http://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing,
  13.  * software distributed under the License is distributed on an
  14.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15.  * KIND, either express or implied.  See the License for the
  16.  * specific language governing permissions and limitations
  17.  * under the License.
  18.  */
  19. package org.apache.commons.compress.archivers.zip;

  20. import static org.apache.commons.compress.archivers.zip.ZipConstants.DWORD;
  21. import static org.apache.commons.compress.archivers.zip.ZipConstants.SHORT;
  22. import static org.apache.commons.compress.archivers.zip.ZipConstants.WORD;
  23. import static org.apache.commons.compress.archivers.zip.ZipConstants.ZIP64_MAGIC;

  24. import java.io.ByteArrayInputStream;
  25. import java.io.ByteArrayOutputStream;
  26. import java.io.EOFException;
  27. import java.io.IOException;
  28. import java.io.InputStream;
  29. import java.io.PushbackInputStream;
  30. import java.math.BigInteger;
  31. import java.nio.ByteBuffer;
  32. import java.nio.charset.StandardCharsets;
  33. import java.util.Arrays;
  34. import java.util.Objects;
  35. import java.util.function.Function;
  36. import java.util.zip.CRC32;
  37. import java.util.zip.DataFormatException;
  38. import java.util.zip.Inflater;
  39. import java.util.zip.ZipEntry;
  40. import java.util.zip.ZipException;

  41. import org.apache.commons.compress.archivers.ArchiveEntry;
  42. import org.apache.commons.compress.archivers.ArchiveInputStream;
  43. import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
  44. import org.apache.commons.compress.compressors.deflate64.Deflate64CompressorInputStream;
  45. import org.apache.commons.compress.utils.ArchiveUtils;
  46. import org.apache.commons.compress.utils.IOUtils;
  47. import org.apache.commons.compress.utils.InputStreamStatistics;
  48. import org.apache.commons.io.input.BoundedInputStream;

  49. /**
  50.  * Implements an input stream that can read Zip archives.
  51.  * <p>
  52.  * As of Apache Commons Compress it transparently supports Zip64 extensions and thus individual entries and archives larger than 4 GB or with more than 65,536
  53.  * entries.
  54.  * </p>
  55.  * <p>
  56.  * The {@link ZipFile} class is preferred when reading from files as {@link ZipArchiveInputStream} is limited by not being able to read the central directory
  57.  * header before returning entries. In particular {@link ZipArchiveInputStream}
  58.  * </p>
  59.  * <ul>
  60.  * <li>may return entries that are not part of the central directory at all and shouldn't be considered part of the archive.</li>
  61.  * <li>may return several entries with the same name.</li>
  62.  * <li>will not return internal or external attributes.</li>
  63.  * <li>may return incomplete extra field data.</li>
  64.  * <li>may return unknown sizes and CRC values for entries until the next entry has been reached if the archive uses the data descriptor feature.</li>
  65.  * </ul>
  66.  *
  67.  * @see ZipFile
  68.  * @NotThreadSafe
  69.  */
  70. public class ZipArchiveInputStream extends ArchiveInputStream<ZipArchiveEntry> implements InputStreamStatistics {

  71.     /**
  72.      * Input stream adapted from commons-io.
  73.      */
  74.     private final class BoundCountInputStream extends BoundedInputStream {

  75.         // TODO Consider how to do this from a final class, an IO class, or basically without the current side-effect implementation.

  76.         /**
  77.          * Creates a new {@code BoundedInputStream} that wraps the given input stream and limits it to a certain size.
  78.          *
  79.          * @param in   The wrapped input stream
  80.          * @param max The maximum number of bytes to return
  81.          */
  82.         BoundCountInputStream(final InputStream in, final long max) {
  83.             super(in, max);
  84.         }

  85.         private boolean atMaxLength() {
  86.             return getMaxCount() >= 0 && getCount() >= getMaxCount();
  87.         }

  88.         @Override
  89.         public int read() throws IOException {
  90.             if (atMaxLength()) {
  91.                 return -1;
  92.             }
  93.             final int result = super.read();
  94.             if (result != -1) {
  95.                 readCount(1);
  96.             }
  97.             return result;
  98.         }

  99.         @Override
  100.         public int read(final byte[] b, final int off, final int len) throws IOException {
  101.             if (len == 0) {
  102.                 return 0;
  103.             }
  104.             if (atMaxLength()) {
  105.                 return -1;
  106.             }
  107.             final long maxRead = getMaxCount() >= 0 ? Math.min(len, getMaxCount() - getCount()) : len;
  108.             return readCount(super.read(b, off, (int) maxRead));
  109.         }

  110.         private int readCount(final int bytesRead) {
  111.             if (bytesRead != -1) {
  112.                 count(bytesRead);
  113.                 current.bytesReadFromStream += bytesRead;
  114.             }
  115.             return bytesRead;
  116.         }

  117.     }

  118.     /**
  119.      * Structure collecting information for the entry that is currently being read.
  120.      */
  121.     private static final class CurrentEntry {

  122.         /**
  123.          * Current ZIP entry.
  124.          */
  125.         private final ZipArchiveEntry entry = new ZipArchiveEntry();

  126.         /**
  127.          * Does the entry use a data descriptor?
  128.          */
  129.         private boolean hasDataDescriptor;

  130.         /**
  131.          * Does the entry have a ZIP64 extended information extra field.
  132.          */
  133.         private boolean usesZip64;

  134.         /**
  135.          * Number of bytes of entry content read by the client if the entry is STORED.
  136.          */
  137.         private long bytesRead;

  138.         /**
  139.          * Number of bytes of entry content read from the stream.
  140.          * <p>
  141.          * This may be more than the actual entry's length as some stuff gets buffered up and needs to be pushed back when the end of the entry has been
  142.          * reached.
  143.          * </p>
  144.          */
  145.         private long bytesReadFromStream;

  146.         /**
  147.          * The checksum calculated as the current entry is read.
  148.          */
  149.         private final CRC32 crc = new CRC32();

  150.         /**
  151.          * The input stream decompressing the data for shrunk and imploded entries.
  152.          */
  153.         private InputStream inputStream;

  154.         @SuppressWarnings("unchecked") // Caller beware
  155.         private <T extends InputStream> T checkInputStream() {
  156.             return (T) Objects.requireNonNull(inputStream, "inputStream");
  157.         }
  158.     }

  159.     public static final int PREAMBLE_GARBAGE_MAX_SIZE = 4096;

  160.     private static final int LFH_LEN = 30;

  161.     /*
  162.      * local file header signature WORD version needed to extract SHORT general purpose bit flag SHORT compression method SHORT last mod file time SHORT last
  163.      * mod file date SHORT crc-32 WORD compressed size WORD uncompressed size WORD file name length SHORT extra field length SHORT
  164.      */
  165.     private static final int CFH_LEN = 46;

  166.     /*
  167.      * central file header signature WORD version made by SHORT version needed to extract SHORT general purpose bit flag SHORT compression method SHORT last mod
  168.      * file time SHORT last mod file date SHORT crc-32 WORD compressed size WORD uncompressed size WORD file name length SHORT extra field length SHORT file
  169.      * comment length SHORT disk number start SHORT internal file attributes SHORT external file attributes WORD relative offset of local header WORD
  170.      */
  171.     private static final long TWO_EXP_32 = ZIP64_MAGIC + 1;

  172.     private static final String USE_ZIPFILE_INSTEAD_OF_STREAM_DISCLAIMER = " while reading a stored entry using data descriptor. Either the archive is broken"
  173.             + " or it can not be read using ZipArchiveInputStream and you must use ZipFile."
  174.             + " A common cause for this is a ZIP archive containing a ZIP archive."
  175.             + " See https://commons.apache.org/proper/commons-compress/zip.html#ZipArchiveInputStream_vs_ZipFile";

  176.     private static final byte[] LFH = ZipLong.LFH_SIG.getBytes();

  177.     private static final byte[] CFH = ZipLong.CFH_SIG.getBytes();

  178.     private static final byte[] DD = ZipLong.DD_SIG.getBytes();

  179.     private static final byte[] APK_SIGNING_BLOCK_MAGIC = { 'A', 'P', 'K', ' ', 'S', 'i', 'g', ' ', 'B', 'l', 'o', 'c', 'k', ' ', '4', '2', };

  180.     private static final BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);

  181.     private static boolean checksig(final byte[] expected, final byte[] signature) {
  182.         for (int i = 0; i < expected.length; i++) {
  183.             if (signature[i] != expected[i]) {
  184.                 return false;
  185.             }
  186.         }
  187.         return true;
  188.     }

  189.     /**
  190.      * Checks if the signature matches what is expected for a ZIP file. Does not currently handle self-extracting ZIPs which may have arbitrary leading content.
  191.      *
  192.      * @param signature the bytes to check
  193.      * @param length    the number of bytes to check
  194.      * @return true, if this stream is a ZIP archive stream, false otherwise
  195.      */
  196.     public static boolean matches(final byte[] signature, final int length) {
  197.         if (length < ZipArchiveOutputStream.LFH_SIG.length) {
  198.             return false;
  199.         }

  200.         return checksig(ZipArchiveOutputStream.LFH_SIG, signature) // normal file
  201.                 || checksig(ZipArchiveOutputStream.EOCD_SIG, signature) // empty zip
  202.                 || checksig(ZipArchiveOutputStream.DD_SIG, signature) // split zip
  203.                 || checksig(ZipLong.SINGLE_SEGMENT_SPLIT_MARKER.getBytes(), signature);
  204.     }

  205.     /** The ZIP encoding to use for file names and the file comment. */
  206.     private final ZipEncoding zipEncoding;

  207.     /** Whether to look for and use Unicode extra fields. */
  208.     private final boolean useUnicodeExtraFields;

  209.     /** Inflater used for all deflated entries. */
  210.     private final Inflater inf = new Inflater(true);

  211.     /** Buffer used to read from the wrapped stream. */
  212.     private final ByteBuffer buf = ByteBuffer.allocate(ZipArchiveOutputStream.BUFFER_SIZE);

  213.     /** The entry that is currently being read. */
  214.     private CurrentEntry current;

  215.     /** Whether the stream has been closed. */
  216.     private boolean closed;

  217.     /** Whether the stream has reached the central directory - and thus found all entries. */
  218.     private boolean hitCentralDirectory;

  219.     /**
  220.      * When reading a stored entry that uses the data descriptor this stream has to read the full entry and caches it. This is the cache.
  221.      */
  222.     private ByteArrayInputStream lastStoredEntry;

  223.     /**
  224.      * Whether the stream will try to read STORED entries that use a data descriptor. Setting it to true means we will not stop reading an entry with the
  225.      * compressed size, instead we will stop reading an entry when a data descriptor is met (by finding the Data Descriptor Signature). This will completely
  226.      * break down in some cases - like JARs in WARs.
  227.      * <p>
  228.      * See also : https://issues.apache.org/jira/projects/COMPRESS/issues/COMPRESS-555
  229.      * https://github.com/apache/commons-compress/pull/137#issuecomment-690835644
  230.      * </p>
  231.      */
  232.     private final boolean allowStoredEntriesWithDataDescriptor;

  233.     /** Count decompressed bytes for current entry */
  234.     private long uncompressedCount;

  235.     /** Whether the stream will try to skip the ZIP split signature(08074B50) at the beginning **/
  236.     private final boolean skipSplitSig;

  237.     /** Cached buffers - must only be used locally in the class (COMPRESS-172 - reduce garbage collection). */
  238.     private final byte[] lfhBuf = new byte[LFH_LEN];

  239.     private final byte[] skipBuf = new byte[1024];

  240.     private final byte[] shortBuf = new byte[SHORT];

  241.     private final byte[] wordBuf = new byte[WORD];

  242.     private final byte[] twoDwordBuf = new byte[2 * DWORD];

  243.     private int entriesRead;

  244.     /**
  245.      * The factory for extra fields or null.
  246.      */
  247.     // private Function<ZipShort, ZipExtraField> extraFieldSupport;

  248.     /**
  249.      * Constructs an instance using UTF-8 encoding
  250.      *
  251.      * @param inputStream the stream to wrap
  252.      */
  253.     public ZipArchiveInputStream(final InputStream inputStream) {
  254.         this(inputStream, StandardCharsets.UTF_8.name());
  255.     }

  256.     /**
  257.      * Constructs an instance using the specified encoding
  258.      *
  259.      * @param inputStream the stream to wrap
  260.      * @param encoding    the encoding to use for file names, use null for the platform's default encoding
  261.      * @since 1.5
  262.      */
  263.     public ZipArchiveInputStream(final InputStream inputStream, final String encoding) {
  264.         this(inputStream, encoding, true);
  265.     }

  266.     /**
  267.      * Constructs an instance using the specified encoding
  268.      *
  269.      * @param inputStream           the stream to wrap
  270.      * @param encoding              the encoding to use for file names, use null for the platform's default encoding
  271.      * @param useUnicodeExtraFields whether to use InfoZIP Unicode Extra Fields (if present) to set the file names.
  272.      */
  273.     public ZipArchiveInputStream(final InputStream inputStream, final String encoding, final boolean useUnicodeExtraFields) {
  274.         this(inputStream, encoding, useUnicodeExtraFields, false);
  275.     }

  276.     /**
  277.      * Constructs an instance using the specified encoding
  278.      *
  279.      * @param inputStream                          the stream to wrap
  280.      * @param encoding                             the encoding to use for file names, use null for the platform's default encoding
  281.      * @param useUnicodeExtraFields                whether to use InfoZIP Unicode Extra Fields (if present) to set the file names.
  282.      * @param allowStoredEntriesWithDataDescriptor whether the stream will try to read STORED entries that use a data descriptor
  283.      * @since 1.1
  284.      */
  285.     public ZipArchiveInputStream(final InputStream inputStream, final String encoding, final boolean useUnicodeExtraFields,
  286.             final boolean allowStoredEntriesWithDataDescriptor) {
  287.         this(inputStream, encoding, useUnicodeExtraFields, allowStoredEntriesWithDataDescriptor, false);
  288.     }

  289.     /**
  290.      * Constructs an instance using the specified encoding
  291.      *
  292.      * @param inputStream                          the stream to wrap
  293.      * @param encoding                             the encoding to use for file names, use null for the platform's default encoding
  294.      * @param useUnicodeExtraFields                whether to use InfoZIP Unicode Extra Fields (if present) to set the file names.
  295.      * @param allowStoredEntriesWithDataDescriptor whether the stream will try to read STORED entries that use a data descriptor
  296.      * @param skipSplitSig                         Whether the stream will try to skip the zip split signature(08074B50) at the beginning. You will need to set
  297.      *                                             this to true if you want to read a split archive.
  298.      * @since 1.20
  299.      */
  300.     public ZipArchiveInputStream(final InputStream inputStream, final String encoding, final boolean useUnicodeExtraFields,
  301.             final boolean allowStoredEntriesWithDataDescriptor, final boolean skipSplitSig) {
  302.         super(inputStream, encoding);
  303.         this.in = new PushbackInputStream(inputStream, buf.capacity());
  304.         this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
  305.         this.useUnicodeExtraFields = useUnicodeExtraFields;
  306.         this.allowStoredEntriesWithDataDescriptor = allowStoredEntriesWithDataDescriptor;
  307.         this.skipSplitSig = skipSplitSig;
  308.         // haven't read anything so far
  309.         buf.limit(0);
  310.     }

  311.     /**
  312.      * Checks whether the current buffer contains the signature of a &quot;data descriptor&quot;, &quot;local file header&quot; or &quot;central directory
  313.      * entry&quot;.
  314.      * <p>
  315.      * If it contains such a signature, reads the data descriptor and positions the stream right after the data descriptor.
  316.      * </p>
  317.      */
  318.     private boolean bufferContainsSignature(final ByteArrayOutputStream bos, final int offset, final int lastRead, final int expectedDDLen) throws IOException {

  319.         boolean done = false;
  320.         for (int i = 0; !done && i < offset + lastRead - 4; i++) {
  321.             if (buf.array()[i] == LFH[0] && buf.array()[i + 1] == LFH[1]) {
  322.                 int expectDDPos = i;
  323.                 if (i >= expectedDDLen && buf.array()[i + 2] == LFH[2] && buf.array()[i + 3] == LFH[3]
  324.                         || buf.array()[i + 2] == CFH[2] && buf.array()[i + 3] == CFH[3]) {
  325.                     // found an LFH or CFH:
  326.                     expectDDPos = i - expectedDDLen;
  327.                     done = true;
  328.                 } else if (buf.array()[i + 2] == DD[2] && buf.array()[i + 3] == DD[3]) {
  329.                     // found DD:
  330.                     done = true;
  331.                 }
  332.                 if (done) {
  333.                     // * push back bytes read in excess as well as the data
  334.                     // descriptor
  335.                     // * copy the remaining bytes to cache
  336.                     // * read data descriptor
  337.                     pushback(buf.array(), expectDDPos, offset + lastRead - expectDDPos);
  338.                     bos.write(buf.array(), 0, expectDDPos);
  339.                     readDataDescriptor();
  340.                 }
  341.             }
  342.         }
  343.         return done;
  344.     }

  345.     /**
  346.      * If the last read bytes could hold a data descriptor and an incomplete signature then save the last bytes to the front of the buffer and cache everything
  347.      * in front of the potential data descriptor into the given ByteArrayOutputStream.
  348.      * <p>
  349.      * Data descriptor plus incomplete signature (3 bytes in the worst case) can be 20 bytes max.
  350.      * </p>
  351.      */
  352.     private int cacheBytesRead(final ByteArrayOutputStream bos, int offset, final int lastRead, final int expectedDDLen) {
  353.         final int cacheable = offset + lastRead - expectedDDLen - 3;
  354.         if (cacheable > 0) {
  355.             bos.write(buf.array(), 0, cacheable);
  356.             System.arraycopy(buf.array(), cacheable, buf.array(), 0, expectedDDLen + 3);
  357.             offset = expectedDDLen + 3;
  358.         } else {
  359.             offset += lastRead;
  360.         }
  361.         return offset;
  362.     }

  363.     /**
  364.      * Whether this class is able to read the given entry.
  365.      * <p>
  366.      * May return false if it is set up to use encryption or a compression method that hasn't been implemented yet.
  367.      * </p>
  368.      *
  369.      * @since 1.1
  370.      */
  371.     @Override
  372.     public boolean canReadEntryData(final ArchiveEntry ae) {
  373.         if (ae instanceof ZipArchiveEntry) {
  374.             final ZipArchiveEntry ze = (ZipArchiveEntry) ae;
  375.             return ZipUtil.canHandleEntryData(ze) && supportsDataDescriptorFor(ze) && supportsCompressedSizeFor(ze);
  376.         }
  377.         return false;
  378.     }

  379.     @Override
  380.     public void close() throws IOException {
  381.         if (!closed) {
  382.             closed = true;
  383.             try {
  384.                 in.close();
  385.             } finally {
  386.                 inf.end();
  387.             }
  388.         }
  389.     }

  390.     /**
  391.      * Closes the current ZIP archive entry and positions the underlying stream to the beginning of the next entry. All per-entry variables and data structures
  392.      * are cleared.
  393.      * <p>
  394.      * If the compressed size of this entry is included in the entry header, then any outstanding bytes are simply skipped from the underlying stream without
  395.      * uncompressing them. This allows an entry to be safely closed even if the compression method is unsupported.
  396.      * </p>
  397.      * <p>
  398.      * In case we don't know the compressed size of this entry or have already buffered too much data from the underlying stream to support uncompression, then
  399.      * the uncompression process is completed and the end position of the stream is adjusted based on the result of that process.
  400.      * </p>
  401.      *
  402.      * @throws IOException if an error occurs
  403.      */
  404.     private void closeEntry() throws IOException {
  405.         if (closed) {
  406.             throw new IOException("The stream is closed");
  407.         }
  408.         if (current == null) {
  409.             return;
  410.         }

  411.         // Ensure all entry bytes are read
  412.         if (currentEntryHasOutstandingBytes()) {
  413.             drainCurrentEntryData();
  414.         } else {
  415.             // this is guaranteed to exhaust the stream
  416.             if (skip(Long.MAX_VALUE) < 0) {
  417.                 throw new IllegalStateException("Can't read the remainder of the stream");
  418.             }

  419.             final long inB = current.entry.getMethod() == ZipArchiveOutputStream.DEFLATED ? getBytesInflated() : current.bytesRead;

  420.             // this is at most a single read() operation and can't
  421.             // exceed the range of int
  422.             final int diff = (int) (current.bytesReadFromStream - inB);

  423.             // Pushback any required bytes
  424.             if (diff > 0) {
  425.                 pushback(buf.array(), buf.limit() - diff, diff);
  426.                 current.bytesReadFromStream -= diff;
  427.             }

  428.             // Drain remainder of entry if not all data bytes were required
  429.             if (currentEntryHasOutstandingBytes()) {
  430.                 drainCurrentEntryData();
  431.             }
  432.         }

  433.         if (lastStoredEntry == null && current.hasDataDescriptor) {
  434.             readDataDescriptor();
  435.         }

  436.         inf.reset();
  437.         buf.clear().flip();
  438.         current = null;
  439.         lastStoredEntry = null;
  440.     }

  441.     /**
  442.      * If the compressed size of the current entry is included in the entry header and there are any outstanding bytes in the underlying stream, then this
  443.      * returns true.
  444.      *
  445.      * @return true, if current entry is determined to have outstanding bytes, false otherwise
  446.      */
  447.     private boolean currentEntryHasOutstandingBytes() {
  448.         return current.bytesReadFromStream <= current.entry.getCompressedSize() && !current.hasDataDescriptor;
  449.     }

  450.     /**
  451.      * Read all data of the current entry from the underlying stream that hasn't been read, yet.
  452.      */
  453.     private void drainCurrentEntryData() throws IOException {
  454.         long remaining = current.entry.getCompressedSize() - current.bytesReadFromStream;
  455.         while (remaining > 0) {
  456.             final long n = in.read(buf.array(), 0, (int) Math.min(buf.capacity(), remaining));
  457.             if (n < 0) {
  458.                 throw new EOFException("Truncated ZIP entry: " + ArchiveUtils.sanitize(current.entry.getName()));
  459.             }
  460.             count(n);
  461.             remaining -= n;
  462.         }
  463.     }

  464.     private int fill() throws IOException {
  465.         if (closed) {
  466.             throw new IOException("The stream is closed");
  467.         }
  468.         final int length = in.read(buf.array());
  469.         if (length > 0) {
  470.             buf.limit(length);
  471.             count(buf.limit());
  472.             inf.setInput(buf.array(), 0, buf.limit());
  473.         }
  474.         return length;
  475.     }

  476.     /**
  477.      * Reads forward until the signature of the &quot;End of central directory&quot; record is found.
  478.      */
  479.     private boolean findEocdRecord() throws IOException {
  480.         int currentByte = -1;
  481.         boolean skipReadCall = false;
  482.         while (skipReadCall || (currentByte = readOneByte()) > -1) {
  483.             skipReadCall = false;
  484.             if (!isFirstByteOfEocdSig(currentByte)) {
  485.                 continue;
  486.             }
  487.             currentByte = readOneByte();
  488.             if (currentByte != ZipArchiveOutputStream.EOCD_SIG[1]) {
  489.                 if (currentByte == -1) {
  490.                     break;
  491.                 }
  492.                 skipReadCall = isFirstByteOfEocdSig(currentByte);
  493.                 continue;
  494.             }
  495.             currentByte = readOneByte();
  496.             if (currentByte != ZipArchiveOutputStream.EOCD_SIG[2]) {
  497.                 if (currentByte == -1) {
  498.                     break;
  499.                 }
  500.                 skipReadCall = isFirstByteOfEocdSig(currentByte);
  501.                 continue;
  502.             }
  503.             currentByte = readOneByte();
  504.             if (currentByte == -1) {
  505.                 break;
  506.             }
  507.             if (currentByte == ZipArchiveOutputStream.EOCD_SIG[3]) {
  508.                 return true;
  509.             }
  510.             skipReadCall = isFirstByteOfEocdSig(currentByte);
  511.         }
  512.         return false;
  513.     }

  514.     /**
  515.      * Gets the number of bytes Inflater has actually processed.
  516.      * <p>
  517.      * for Java &lt; Java7 the getBytes* methods in Inflater/Deflater seem to return unsigned ints rather than longs that start over with 0 at 2^32.
  518.      * </p>
  519.      * <p>
  520.      * The stream knows how many bytes it has read, but not how many the Inflater actually consumed - it should be between the total number of bytes read for
  521.      * the entry and the total number minus the last read operation. Here we just try to make the value close enough to the bytes we've read by assuming the
  522.      * number of bytes consumed must be smaller than (or equal to) the number of bytes read but not smaller by more than 2^32.
  523.      * </p>
  524.      */
  525.     private long getBytesInflated() {
  526.         long inB = inf.getBytesRead();
  527.         if (current.bytesReadFromStream >= TWO_EXP_32) {
  528.             while (inB + TWO_EXP_32 <= current.bytesReadFromStream) {
  529.                 inB += TWO_EXP_32;
  530.             }
  531.         }
  532.         return inB;
  533.     }

  534.     /**
  535.      * @since 1.17
  536.      */
  537.     @SuppressWarnings("resource") // checkInputStream() does not allocate.
  538.     @Override
  539.     public long getCompressedCount() {
  540.         final int method = current.entry.getMethod();
  541.         if (method == ZipArchiveOutputStream.STORED) {
  542.             return current.bytesRead;
  543.         }
  544.         if (method == ZipArchiveOutputStream.DEFLATED) {
  545.             return getBytesInflated();
  546.         }
  547.         if (method == ZipMethod.UNSHRINKING.getCode() || method == ZipMethod.IMPLODING.getCode() || method == ZipMethod.ENHANCED_DEFLATED.getCode()
  548.                 || method == ZipMethod.BZIP2.getCode()) {
  549.             return ((InputStreamStatistics) current.checkInputStream()).getCompressedCount();
  550.         }
  551.         return -1;
  552.     }

  553.     @Override
  554.     public ZipArchiveEntry getNextEntry() throws IOException {
  555.         return getNextZipEntry();
  556.     }

  557.     /**
  558.      * Gets the next entry.
  559.      *
  560.      * @return the next entry.
  561.      * @throws IOException if an I/O error occurs.
  562.      * @deprecated Use {@link #getNextEntry()}.
  563.      */
  564.     @Deprecated
  565.     public ZipArchiveEntry getNextZipEntry() throws IOException {
  566.         uncompressedCount = 0;

  567.         boolean firstEntry = true;
  568.         if (closed || hitCentralDirectory) {
  569.             return null;
  570.         }
  571.         if (current != null) {
  572.             closeEntry();
  573.             firstEntry = false;
  574.         }

  575.         final long currentHeaderOffset = getBytesRead();
  576.         try {
  577.             if (firstEntry) {
  578.                 // split archives have a special signature before the
  579.                 // first local file header - look for it and fail with
  580.                 // the appropriate error message if this is a split
  581.                 // archive.
  582.                 if (!readFirstLocalFileHeader()) {
  583.                     hitCentralDirectory = true;
  584.                     skipRemainderOfArchive();
  585.                     return null;
  586.                 }
  587.             } else {
  588.                 readFully(lfhBuf);
  589.             }
  590.         } catch (final EOFException e) { // NOSONAR
  591.             return null;
  592.         }

  593.         final ZipLong sig = new ZipLong(lfhBuf);
  594.         if (!sig.equals(ZipLong.LFH_SIG)) {
  595.             if (sig.equals(ZipLong.CFH_SIG) || sig.equals(ZipLong.AED_SIG) || isApkSigningBlock(lfhBuf)) {
  596.                 hitCentralDirectory = true;
  597.                 skipRemainderOfArchive();
  598.                 return null;
  599.             }
  600.             throw new ZipException(String.format("Unexpected record signature: 0x%x", sig.getValue()));
  601.         }

  602.         int off = WORD;
  603.         current = new CurrentEntry();

  604.         final int versionMadeBy = ZipShort.getValue(lfhBuf, off);
  605.         off += SHORT;
  606.         current.entry.setPlatform(versionMadeBy >> ZipFile.BYTE_SHIFT & ZipFile.NIBLET_MASK);

  607.         final GeneralPurposeBit gpFlag = GeneralPurposeBit.parse(lfhBuf, off);
  608.         final boolean hasUTF8Flag = gpFlag.usesUTF8ForNames();
  609.         final ZipEncoding entryEncoding = hasUTF8Flag ? ZipEncodingHelper.ZIP_ENCODING_UTF_8 : zipEncoding;
  610.         current.hasDataDescriptor = gpFlag.usesDataDescriptor();
  611.         current.entry.setGeneralPurposeBit(gpFlag);

  612.         off += SHORT;

  613.         current.entry.setMethod(ZipShort.getValue(lfhBuf, off));
  614.         off += SHORT;

  615.         final long time = ZipUtil.dosToJavaTime(ZipLong.getValue(lfhBuf, off));
  616.         current.entry.setTime(time);
  617.         off += WORD;

  618.         ZipLong size = null, cSize = null;
  619.         if (!current.hasDataDescriptor) {
  620.             current.entry.setCrc(ZipLong.getValue(lfhBuf, off));
  621.             off += WORD;

  622.             cSize = new ZipLong(lfhBuf, off);
  623.             off += WORD;

  624.             size = new ZipLong(lfhBuf, off);
  625.             off += WORD;
  626.         } else {
  627.             off += 3 * WORD;
  628.         }

  629.         final int fileNameLen = ZipShort.getValue(lfhBuf, off);

  630.         off += SHORT;

  631.         final int extraLen = ZipShort.getValue(lfhBuf, off);
  632.         off += SHORT; // NOSONAR - assignment as documentation

  633.         final byte[] fileName = readRange(fileNameLen);
  634.         current.entry.setName(entryEncoding.decode(fileName), fileName);
  635.         if (hasUTF8Flag) {
  636.             current.entry.setNameSource(ZipArchiveEntry.NameSource.NAME_WITH_EFS_FLAG);
  637.         }

  638.         final byte[] extraData = readRange(extraLen);
  639.         try {
  640.             current.entry.setExtra(extraData);
  641.         } catch (final RuntimeException ex) {
  642.             final ZipException z = new ZipException("Invalid extra data in entry " + current.entry.getName());
  643.             z.initCause(ex);
  644.             throw z;
  645.         }

  646.         if (!hasUTF8Flag && useUnicodeExtraFields) {
  647.             ZipUtil.setNameAndCommentFromExtraFields(current.entry, fileName, null);
  648.         }

  649.         processZip64Extra(size, cSize);

  650.         current.entry.setLocalHeaderOffset(currentHeaderOffset);
  651.         current.entry.setDataOffset(getBytesRead());
  652.         current.entry.setStreamContiguous(true);

  653.         final ZipMethod m = ZipMethod.getMethodByCode(current.entry.getMethod());
  654.         if (current.entry.getCompressedSize() != ArchiveEntry.SIZE_UNKNOWN) {
  655.             if (ZipUtil.canHandleEntryData(current.entry) && m != ZipMethod.STORED && m != ZipMethod.DEFLATED) {
  656.                 final InputStream bis = new BoundCountInputStream(in, current.entry.getCompressedSize());
  657.                 switch (m) {
  658.                 case UNSHRINKING:
  659.                     current.inputStream = new UnshrinkingInputStream(bis);
  660.                     break;
  661.                 case IMPLODING:
  662.                     try {
  663.                         current.inputStream = new ExplodingInputStream(current.entry.getGeneralPurposeBit().getSlidingDictionarySize(),
  664.                                 current.entry.getGeneralPurposeBit().getNumberOfShannonFanoTrees(), bis);
  665.                     } catch (final IllegalArgumentException ex) {
  666.                         throw new IOException("bad IMPLODE data", ex);
  667.                     }
  668.                     break;
  669.                 case BZIP2:
  670.                     current.inputStream = new BZip2CompressorInputStream(bis);
  671.                     break;
  672.                 case ENHANCED_DEFLATED:
  673.                     current.inputStream = new Deflate64CompressorInputStream(bis);
  674.                     break;
  675.                 default:
  676.                     // we should never get here as all supported methods have been covered
  677.                     // will cause an error when read is invoked, don't throw an exception here so people can
  678.                     // skip unsupported entries
  679.                     break;
  680.                 }
  681.             }
  682.         } else if (m == ZipMethod.ENHANCED_DEFLATED) {
  683.             current.inputStream = new Deflate64CompressorInputStream(in);
  684.         }

  685.         entriesRead++;
  686.         return current.entry;
  687.     }

  688.     /**
  689.      * Gets the uncompressed count.
  690.      *
  691.      * @since 1.17
  692.      */
  693.     @Override
  694.     public long getUncompressedCount() {
  695.         return uncompressedCount;
  696.     }

  697.     /**
  698.      * Checks whether this might be an APK Signing Block.
  699.      * <p>
  700.      * Unfortunately the APK signing block does not start with some kind of signature, it rather ends with one. It starts with a length, so what we do is parse
  701.      * the suspect length, skip ahead far enough, look for the signature and if we've found it, return true.
  702.      * </p>
  703.      *
  704.      * @param suspectLocalFileHeader the bytes read from the underlying stream in the expectation that they would hold the local file header of the next entry.
  705.      * @return true if this looks like an APK signing block
  706.      * @see <a href="https://source.android.com/security/apksigning/v2">https://source.android.com/security/apksigning/v2</a>
  707.      */
  708.     private boolean isApkSigningBlock(final byte[] suspectLocalFileHeader) throws IOException {
  709.         // length of block excluding the size field itself
  710.         final BigInteger len = ZipEightByteInteger.getValue(suspectLocalFileHeader);
  711.         // LFH has already been read and all but the first eight bytes contain (part of) the APK signing block,
  712.         // also subtract 16 bytes in order to position us at the magic string
  713.         BigInteger toSkip = len.add(BigInteger.valueOf(DWORD - suspectLocalFileHeader.length - (long) APK_SIGNING_BLOCK_MAGIC.length));
  714.         final byte[] magic = new byte[APK_SIGNING_BLOCK_MAGIC.length];

  715.         try {
  716.             if (toSkip.signum() < 0) {
  717.                 // suspectLocalFileHeader contains the start of suspect magic string
  718.                 final int off = suspectLocalFileHeader.length + toSkip.intValue();
  719.                 // length was shorter than magic length
  720.                 if (off < DWORD) {
  721.                     return false;
  722.                 }
  723.                 final int bytesInBuffer = Math.abs(toSkip.intValue());
  724.                 System.arraycopy(suspectLocalFileHeader, off, magic, 0, Math.min(bytesInBuffer, magic.length));
  725.                 if (bytesInBuffer < magic.length) {
  726.                     readFully(magic, bytesInBuffer);
  727.                 }
  728.             } else {
  729.                 while (toSkip.compareTo(LONG_MAX) > 0) {
  730.                     realSkip(Long.MAX_VALUE);
  731.                     toSkip = toSkip.add(LONG_MAX.negate());
  732.                 }
  733.                 realSkip(toSkip.longValue());
  734.                 readFully(magic);
  735.             }
  736.         } catch (final EOFException ex) { // NOSONAR
  737.             // length was invalid
  738.             return false;
  739.         }
  740.         return Arrays.equals(magic, APK_SIGNING_BLOCK_MAGIC);
  741.     }

  742.     private boolean isFirstByteOfEocdSig(final int b) {
  743.         return b == ZipArchiveOutputStream.EOCD_SIG[0];
  744.     }

  745.     /**
  746.      * Records whether a Zip64 extra is present and sets the size information from it if sizes are 0xFFFFFFFF and the entry doesn't use a data descriptor.
  747.      */
  748.     private void processZip64Extra(final ZipLong size, final ZipLong cSize) throws ZipException {
  749.         final ZipExtraField extra = current.entry.getExtraField(Zip64ExtendedInformationExtraField.HEADER_ID);
  750.         if (extra != null && !(extra instanceof Zip64ExtendedInformationExtraField)) {
  751.             throw new ZipException("archive contains unparseable zip64 extra field");
  752.         }
  753.         final Zip64ExtendedInformationExtraField z64 = (Zip64ExtendedInformationExtraField) extra;
  754.         current.usesZip64 = z64 != null;
  755.         if (!current.hasDataDescriptor) {
  756.             if (z64 != null // same as current.usesZip64 but avoids NPE warning
  757.                     && (ZipLong.ZIP64_MAGIC.equals(cSize) || ZipLong.ZIP64_MAGIC.equals(size))) {
  758.                 if (z64.getCompressedSize() == null || z64.getSize() == null) {
  759.                     // avoid NPE if it's a corrupted ZIP archive
  760.                     throw new ZipException("archive contains corrupted zip64 extra field");
  761.                 }
  762.                 long s = z64.getCompressedSize().getLongValue();
  763.                 if (s < 0) {
  764.                     throw new ZipException("broken archive, entry with negative compressed size");
  765.                 }
  766.                 current.entry.setCompressedSize(s);
  767.                 s = z64.getSize().getLongValue();
  768.                 if (s < 0) {
  769.                     throw new ZipException("broken archive, entry with negative size");
  770.                 }
  771.                 current.entry.setSize(s);
  772.             } else if (cSize != null && size != null) {
  773.                 if (cSize.getValue() < 0) {
  774.                     throw new ZipException("broken archive, entry with negative compressed size");
  775.                 }
  776.                 current.entry.setCompressedSize(cSize.getValue());
  777.                 if (size.getValue() < 0) {
  778.                     throw new ZipException("broken archive, entry with negative size");
  779.                 }
  780.                 current.entry.setSize(size.getValue());
  781.             }
  782.         }
  783.     }

  784.     private void pushback(final byte[] buf, final int offset, final int length) throws IOException {
  785.         if (offset < 0) {
  786.             // Instead of ArrayIndexOutOfBoundsException
  787.             throw new IOException(String.format("Negative offset %,d into buffer", offset));
  788.         }
  789.         ((PushbackInputStream) in).unread(buf, offset, length);
  790.         pushedBackBytes(length);
  791.     }

  792.     @Override
  793.     public int read(final byte[] buffer, final int offset, final int length) throws IOException {
  794.         if (length == 0) {
  795.             return 0;
  796.         }
  797.         if (closed) {
  798.             throw new IOException("The stream is closed");
  799.         }

  800.         if (current == null) {
  801.             return -1;
  802.         }

  803.         // avoid int overflow, check null buffer
  804.         if (offset > buffer.length || length < 0 || offset < 0 || buffer.length - offset < length) {
  805.             throw new ArrayIndexOutOfBoundsException();
  806.         }

  807.         ZipUtil.checkRequestedFeatures(current.entry);
  808.         if (!supportsDataDescriptorFor(current.entry)) {
  809.             throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.DATA_DESCRIPTOR, current.entry);
  810.         }
  811.         if (!supportsCompressedSizeFor(current.entry)) {
  812.             throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.UNKNOWN_COMPRESSED_SIZE, current.entry);
  813.         }

  814.         final int read;
  815.         if (current.entry.getMethod() == ZipArchiveOutputStream.STORED) {
  816.             read = readStored(buffer, offset, length);
  817.         } else if (current.entry.getMethod() == ZipArchiveOutputStream.DEFLATED) {
  818.             read = readDeflated(buffer, offset, length);
  819.         } else if (current.entry.getMethod() == ZipMethod.UNSHRINKING.getCode() || current.entry.getMethod() == ZipMethod.IMPLODING.getCode()
  820.                 || current.entry.getMethod() == ZipMethod.ENHANCED_DEFLATED.getCode() || current.entry.getMethod() == ZipMethod.BZIP2.getCode()) {
  821.             read = current.inputStream.read(buffer, offset, length);
  822.         } else {
  823.             throw new UnsupportedZipFeatureException(ZipMethod.getMethodByCode(current.entry.getMethod()), current.entry);
  824.         }

  825.         if (read >= 0) {
  826.             current.crc.update(buffer, offset, read);
  827.             uncompressedCount += read;
  828.         }

  829.         return read;
  830.     }

  831.     private void readDataDescriptor() throws IOException {
  832.         readFully(wordBuf);
  833.         ZipLong val = new ZipLong(wordBuf);
  834.         if (ZipLong.DD_SIG.equals(val)) {
  835.             // data descriptor with signature, skip sig
  836.             readFully(wordBuf);
  837.             val = new ZipLong(wordBuf);
  838.         }
  839.         current.entry.setCrc(val.getValue());

  840.         // if there is a ZIP64 extra field, sizes are eight bytes
  841.         // each, otherwise four bytes each. Unfortunately some
  842.         // implementations - namely Java7 - use eight bytes without
  843.         // using a ZIP64 extra field -
  844.         // https://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588

  845.         // just read 16 bytes and check whether bytes nine to twelve
  846.         // look like one of the signatures of what could follow a data
  847.         // descriptor (ignoring archive decryption headers for now).
  848.         // If so, push back eight bytes and assume sizes are four
  849.         // bytes, otherwise sizes are eight bytes each.
  850.         readFully(twoDwordBuf);
  851.         final ZipLong potentialSig = new ZipLong(twoDwordBuf, DWORD);
  852.         if (potentialSig.equals(ZipLong.CFH_SIG) || potentialSig.equals(ZipLong.LFH_SIG)) {
  853.             pushback(twoDwordBuf, DWORD, DWORD);
  854.             long size = ZipLong.getValue(twoDwordBuf);
  855.             if (size < 0) {
  856.                 throw new ZipException("broken archive, entry with negative compressed size");
  857.             }
  858.             current.entry.setCompressedSize(size);
  859.             size = ZipLong.getValue(twoDwordBuf, WORD);
  860.             if (size < 0) {
  861.                 throw new ZipException("broken archive, entry with negative size");
  862.             }
  863.             current.entry.setSize(size);
  864.         } else {
  865.             long size = ZipEightByteInteger.getLongValue(twoDwordBuf);
  866.             if (size < 0) {
  867.                 throw new ZipException("broken archive, entry with negative compressed size");
  868.             }
  869.             current.entry.setCompressedSize(size);
  870.             size = ZipEightByteInteger.getLongValue(twoDwordBuf, DWORD);
  871.             if (size < 0) {
  872.                 throw new ZipException("broken archive, entry with negative size");
  873.             }
  874.             current.entry.setSize(size);
  875.         }
  876.     }

  877.     /**
  878.      * Implements read for DEFLATED entries.
  879.      */
  880.     private int readDeflated(final byte[] buffer, final int offset, final int length) throws IOException {
  881.         final int read = readFromInflater(buffer, offset, length);
  882.         if (read <= 0) {
  883.             if (inf.finished()) {
  884.                 return -1;
  885.             }
  886.             if (inf.needsDictionary()) {
  887.                 throw new ZipException("This archive needs a preset dictionary" + " which is not supported by Commons" + " Compress.");
  888.             }
  889.             if (read == -1) {
  890.                 throw new IOException("Truncated ZIP file");
  891.             }
  892.         }
  893.         return read;
  894.     }

  895.     /**
  896.      * Fills the given array with the first local file header and deals with splitting/spanning markers that may prefix the first LFH.
  897.      */
  898.     private boolean readFirstLocalFileHeader() throws IOException {
  899.         // for empty archive, we may get only EOCD size:
  900.         final byte[] header = new byte[Math.min(LFH_LEN, ZipFile.MIN_EOCD_SIZE)];
  901.         readFully(header);
  902.         try {
  903.             READ_LOOP: for (int i = 0; ; ) {
  904.                 for (int j = 0; i <= PREAMBLE_GARBAGE_MAX_SIZE - 4 && j <= header.length - 4; ++j, ++i) {
  905.                     final ZipLong sig = new ZipLong(header, j);
  906.                     if (sig.equals(ZipLong.LFH_SIG) ||
  907.                         sig.equals(ZipLong.SINGLE_SEGMENT_SPLIT_MARKER) ||
  908.                         sig.equals(ZipLong.DD_SIG)) {
  909.                         // regular archive containing at least one entry:
  910.                         System.arraycopy(header, j, header, 0, header.length - j);
  911.                         readFully(header, header.length - j);
  912.                         break READ_LOOP;
  913.                     }
  914.                     if (sig.equals(new ZipLong(ZipArchiveOutputStream.EOCD_SIG))) {
  915.                         // empty archive:
  916.                         pushback(header, j, header.length - j);
  917.                         return false;
  918.                     }
  919.                 }
  920.                 if (i >= PREAMBLE_GARBAGE_MAX_SIZE - 4) {
  921.                     throw new ZipException("Cannot find zip signature within the first " + PREAMBLE_GARBAGE_MAX_SIZE + " bytes");
  922.                 }
  923.                 System.arraycopy(header, header.length - 3, header, 0, 3);
  924.                 readFully(header, 3);
  925.             }
  926.             System.arraycopy(header, 0, lfhBuf, 0, header.length);
  927.             readFully(lfhBuf, header.length);
  928.         } catch (final EOFException ex) {
  929.             throw new ZipException("Cannot find zip signature within the file");
  930.         }
  931.         final ZipLong sig = new ZipLong(lfhBuf);

  932.         if (!skipSplitSig && sig.equals(ZipLong.DD_SIG)) {
  933.             throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.SPLITTING);
  934.         }

  935.         // the split ZIP signature(08074B50) should only be skipped when the skipSplitSig is set
  936.         if (sig.equals(ZipLong.SINGLE_SEGMENT_SPLIT_MARKER) || sig.equals(ZipLong.DD_SIG)) {
  937.             // Just skip over the marker.
  938.             System.arraycopy(lfhBuf, 4, lfhBuf, 0, lfhBuf.length - 4);
  939.             readFully(lfhBuf, lfhBuf.length - 4);
  940.         }
  941.         return true;
  942.     }

  943.     /**
  944.      * Potentially reads more bytes to fill the inflater's buffer and reads from it.
  945.      */
  946.     private int readFromInflater(final byte[] buffer, final int offset, final int length) throws IOException {
  947.         int read = 0;
  948.         do {
  949.             if (inf.needsInput()) {
  950.                 final int l = fill();
  951.                 if (l > 0) {
  952.                     current.bytesReadFromStream += buf.limit();
  953.                 } else if (l == -1) {
  954.                     return -1;
  955.                 } else {
  956.                     break;
  957.                 }
  958.             }
  959.             try {
  960.                 read = inf.inflate(buffer, offset, length);
  961.             } catch (final DataFormatException e) {
  962.                 throw (IOException) new ZipException(e.getMessage()).initCause(e);
  963.             }
  964.         } while (read == 0 && inf.needsInput());
  965.         return read;
  966.     }

  967.     private void readFully(final byte[] b) throws IOException {
  968.         readFully(b, 0);
  969.     }

  970.     private void readFully(final byte[] b, final int off) throws IOException {
  971.         final int len = b.length - off;
  972.         final int count = IOUtils.readFully(in, b, off, len);
  973.         count(count);
  974.         if (count < len) {
  975.             throw new EOFException();
  976.         }
  977.     }

  978.     // End of Central Directory Record
  979.     // end of central dir signature WORD
  980.     // number of this disk SHORT
  981.     // number of the disk with the
  982.     // start of the central directory SHORT
  983.     // total number of entries in the
  984.     // central directory on this disk SHORT
  985.     // total number of entries in
  986.     // the central directory SHORT
  987.     // size of the central directory WORD
  988.     // offset of start of central
  989.     // directory with respect to
  990.     // the starting disk number WORD
  991.     // .ZIP file comment length SHORT
  992.     // .ZIP file comment up to 64KB
  993.     //

  994.     /**
  995.      * Reads bytes by reading from the underlying stream rather than the (potentially inflating) archive stream - which {@link #read} would do.
  996.      *
  997.      * Also updates bytes-read counter.
  998.      */
  999.     private int readOneByte() throws IOException {
  1000.         final int b = in.read();
  1001.         if (b != -1) {
  1002.             count(1);
  1003.         }
  1004.         return b;
  1005.     }

  1006.     private byte[] readRange(final int len) throws IOException {
  1007.         final byte[] ret = IOUtils.readRange(in, len);
  1008.         count(ret.length);
  1009.         if (ret.length < len) {
  1010.             throw new EOFException();
  1011.         }
  1012.         return ret;
  1013.     }

  1014.     /**
  1015.      * Implements read for STORED entries.
  1016.      */
  1017.     private int readStored(final byte[] buffer, final int offset, final int length) throws IOException {

  1018.         if (current.hasDataDescriptor) {
  1019.             if (lastStoredEntry == null) {
  1020.                 readStoredEntry();
  1021.             }
  1022.             return lastStoredEntry.read(buffer, offset, length);
  1023.         }

  1024.         final long csize = current.entry.getSize();
  1025.         if (current.bytesRead >= csize) {
  1026.             return -1;
  1027.         }

  1028.         if (buf.position() >= buf.limit()) {
  1029.             buf.position(0);
  1030.             final int l = in.read(buf.array());
  1031.             if (l == -1) {
  1032.                 buf.limit(0);
  1033.                 throw new IOException("Truncated ZIP file");
  1034.             }
  1035.             buf.limit(l);

  1036.             count(l);
  1037.             current.bytesReadFromStream += l;
  1038.         }

  1039.         int toRead = Math.min(buf.remaining(), length);
  1040.         if (csize - current.bytesRead < toRead) {
  1041.             // if it is smaller than toRead then it fits into an int
  1042.             toRead = (int) (csize - current.bytesRead);
  1043.         }
  1044.         buf.get(buffer, offset, toRead);
  1045.         current.bytesRead += toRead;
  1046.         return toRead;
  1047.     }

  1048.     /**
  1049.      * Caches a stored entry that uses the data descriptor.
  1050.      * <ul>
  1051.      * <li>Reads a stored entry until the signature of a local file header, central directory header or data descriptor has been found.</li>
  1052.      * <li>Stores all entry data in lastStoredEntry.
  1053.      * </p>
  1054.      * <li>Rewinds the stream to position at the data descriptor.</li>
  1055.      * <li>reads the data descriptor</li>
  1056.      * </ul>
  1057.      * <p>
  1058.      * After calling this method the entry should know its size, the entry's data is cached and the stream is positioned at the next local file or central
  1059.      * directory header.
  1060.      * </p>
  1061.      */
  1062.     private void readStoredEntry() throws IOException {
  1063.         final ByteArrayOutputStream bos = new ByteArrayOutputStream();
  1064.         int off = 0;
  1065.         boolean done = false;

  1066.         // length of DD without signature
  1067.         final int ddLen = current.usesZip64 ? WORD + 2 * DWORD : 3 * WORD;

  1068.         while (!done) {
  1069.             final int r = in.read(buf.array(), off, ZipArchiveOutputStream.BUFFER_SIZE - off);
  1070.             if (r <= 0) {
  1071.                 // read the whole archive without ever finding a
  1072.                 // central directory
  1073.                 throw new IOException("Truncated ZIP file");
  1074.             }
  1075.             if (r + off < 4) {
  1076.                 // buffer too small to check for a signature, loop
  1077.                 off += r;
  1078.                 continue;
  1079.             }

  1080.             done = bufferContainsSignature(bos, off, r, ddLen);
  1081.             if (!done) {
  1082.                 off = cacheBytesRead(bos, off, r, ddLen);
  1083.             }
  1084.         }
  1085.         if (current.entry.getCompressedSize() != current.entry.getSize()) {
  1086.             throw new ZipException("compressed and uncompressed size don't match" + USE_ZIPFILE_INSTEAD_OF_STREAM_DISCLAIMER);
  1087.         }
  1088.         final byte[] b = bos.toByteArray();
  1089.         if (b.length != current.entry.getSize()) {
  1090.             throw new ZipException("actual and claimed size don't match" + USE_ZIPFILE_INSTEAD_OF_STREAM_DISCLAIMER);
  1091.         }
  1092.         lastStoredEntry = new ByteArrayInputStream(b);
  1093.     }

  1094.     /**
  1095.      * Skips bytes by reading from the underlying stream rather than the (potentially inflating) archive stream - which {@link #skip} would do.
  1096.      *
  1097.      * Also updates bytes-read counter.
  1098.      */
  1099.     private void realSkip(final long value) throws IOException {
  1100.         if (value >= 0) {
  1101.             long skipped = 0;
  1102.             while (skipped < value) {
  1103.                 final long rem = value - skipped;
  1104.                 final int x = in.read(skipBuf, 0, (int) (skipBuf.length > rem ? rem : skipBuf.length));
  1105.                 if (x == -1) {
  1106.                     return;
  1107.                 }
  1108.                 count(x);
  1109.                 skipped += x;
  1110.             }
  1111.             return;
  1112.         }
  1113.         throw new IllegalArgumentException();
  1114.     }

  1115.     /**
  1116.      * Currently unused.
  1117.      *
  1118.      * Sets the custom extra fields factory.
  1119.      * @param extraFieldSupport the lookup function based on extra field header id.
  1120.      * @return the archive.
  1121.      */
  1122.     public ZipArchiveInputStream setExtraFieldSupport(final Function<ZipShort, ZipExtraField> extraFieldSupport) {
  1123.         // this.extraFieldSupport = extraFieldSupport;
  1124.         return this;
  1125.     }

  1126.     /**
  1127.      * Skips over and discards value bytes of data from this input stream.
  1128.      * <p>
  1129.      * This implementation may end up skipping over some smaller number of bytes, possibly 0, if and only if it reaches the end of the underlying stream.
  1130.      * </p>
  1131.      * <p>
  1132.      * The actual number of bytes skipped is returned.
  1133.      * </p>
  1134.      *
  1135.      * @param value the number of bytes to be skipped.
  1136.      * @return the actual number of bytes skipped.
  1137.      * @throws IOException              - if an I/O error occurs.
  1138.      * @throws IllegalArgumentException - if value is negative.
  1139.      */
  1140.     @Override
  1141.     public long skip(final long value) throws IOException {
  1142.         if (value >= 0) {
  1143.             long skipped = 0;
  1144.             while (skipped < value) {
  1145.                 final long rem = value - skipped;
  1146.                 final int x = read(skipBuf, 0, (int) (skipBuf.length > rem ? rem : skipBuf.length));
  1147.                 if (x == -1) {
  1148.                     return skipped;
  1149.                 }
  1150.                 skipped += x;
  1151.             }
  1152.             return skipped;
  1153.         }
  1154.         throw new IllegalArgumentException("Negative skip value");
  1155.     }

  1156.     /**
  1157.      * Reads the stream until it find the "End of central directory record" and consumes it as well.
  1158.      */
  1159.     private void skipRemainderOfArchive() throws IOException {
  1160.         // skip over central directory. One LFH has been read too much
  1161.         // already. The calculation discounts file names and extra
  1162.         // data, so it will be too short.
  1163.         if (entriesRead > 0) {
  1164.             realSkip((long) entriesRead * CFH_LEN - LFH_LEN);
  1165.         }
  1166.         final boolean foundEocd = findEocdRecord();
  1167.         if (foundEocd) {
  1168.             realSkip((long) ZipFile.MIN_EOCD_SIZE - WORD /* signature */ - SHORT /* comment len */);
  1169.             readFully(shortBuf);
  1170.             // file comment
  1171.             final int commentLen = ZipShort.getValue(shortBuf);
  1172.             if (commentLen >= 0) {
  1173.                 realSkip(commentLen);
  1174.                 return;
  1175.             }
  1176.         }
  1177.         throw new IOException("Truncated ZIP file");
  1178.     }

  1179.     /**
  1180.      * Whether the compressed size for the entry is either known or not required by the compression method being used.
  1181.      */
  1182.     private boolean supportsCompressedSizeFor(final ZipArchiveEntry entry) {
  1183.         return entry.getCompressedSize() != ArchiveEntry.SIZE_UNKNOWN || entry.getMethod() == ZipEntry.DEFLATED
  1184.                 || entry.getMethod() == ZipMethod.ENHANCED_DEFLATED.getCode()
  1185.                 || entry.getGeneralPurposeBit().usesDataDescriptor() && allowStoredEntriesWithDataDescriptor && entry.getMethod() == ZipEntry.STORED;
  1186.     }

  1187.     /**
  1188.      * Whether this entry requires a data descriptor this library can work with.
  1189.      *
  1190.      * @return true if allowStoredEntriesWithDataDescriptor is true, the entry doesn't require any data descriptor or the method is DEFLATED or
  1191.      *         ENHANCED_DEFLATED.
  1192.      */
  1193.     private boolean supportsDataDescriptorFor(final ZipArchiveEntry entry) {
  1194.         return !entry.getGeneralPurposeBit().usesDataDescriptor() || allowStoredEntriesWithDataDescriptor && entry.getMethod() == ZipEntry.STORED
  1195.                 || entry.getMethod() == ZipEntry.DEFLATED || entry.getMethod() == ZipMethod.ENHANCED_DEFLATED.getCode();
  1196.     }
  1197. }