blob: dc6278b4f14f16b1facfd3333d4156273907991d [file] [log] [blame]
Torsten Curdtca165392008-07-10 10:17:44 +00001/*
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 */
19package org.apache.commons.compress.archivers.zip;
20
Stefan Bodewig03e94a42010-03-19 15:08:23 +000021import java.io.ByteArrayInputStream;
22import java.io.ByteArrayOutputStream;
Stefan Bodewig008ca942009-03-26 22:29:59 +000023import java.io.EOFException;
Torsten Curdtca165392008-07-10 10:17:44 +000024import java.io.IOException;
25import java.io.InputStream;
Stefan Bodewig008ca942009-03-26 22:29:59 +000026import java.io.PushbackInputStream;
27import java.util.zip.CRC32;
28import java.util.zip.DataFormatException;
29import java.util.zip.Inflater;
Gary D. Gregory04173632012-04-01 13:26:55 +000030import java.util.zip.ZipEntry;
Stefan Bodewig008ca942009-03-26 22:29:59 +000031import java.util.zip.ZipException;
Torsten Curdtca165392008-07-10 10:17:44 +000032
33import org.apache.commons.compress.archivers.ArchiveEntry;
34import org.apache.commons.compress.archivers.ArchiveInputStream;
35
Stefan Bodewig6c5f04b2011-07-25 20:14:59 +000036import static org.apache.commons.compress.archivers.zip.ZipConstants.DWORD;
Stefan Bodewige53e88a2011-07-25 13:28:31 +000037import static org.apache.commons.compress.archivers.zip.ZipConstants.SHORT;
38import static org.apache.commons.compress.archivers.zip.ZipConstants.WORD;
Stefan Bodewigcadc5272011-08-03 14:33:44 +000039import static org.apache.commons.compress.archivers.zip.ZipConstants.ZIP64_MAGIC;
Stefan Bodewige53e88a2011-07-25 13:28:31 +000040
Sebastian Bazley99870ef2009-03-28 00:04:36 +000041/**
Sebastian Bazleyf7f6b182009-03-31 10:36:25 +000042 * Implements an input stream that can read Zip archives.
Stefan Bodewig794c20f2011-08-11 14:08:54 +000043 *
44 * <p>Note that {@link ZipArchiveEntry#getSize()} may return -1 if the
45 * DEFLATE algorithm is used, as the size information is not available
46 * from the header.</p>
47 *
48 * <p>The {@link ZipFile} class is preferred when reading from files.</p>
49 *
50 * <p>As of Apache Commons Compress it transparently supports Zip64
51 * extensions and thus individual entries and archives larger than 4
52 * GB or with more than 65536 entries.</p>
53 *
Sebastian Bazleyf7f6b182009-03-31 10:36:25 +000054 * @see ZipFile
Sebastian Bazley99870ef2009-03-28 00:04:36 +000055 * @NotThreadSafe
56 */
Torsten Curdtca165392008-07-10 10:17:44 +000057public class ZipArchiveInputStream extends ArchiveInputStream {
58
Stefan Bodewig008ca942009-03-26 22:29:59 +000059 /**
60 * The zip encoding to use for filenames and the file comment.
61 */
62 private final ZipEncoding zipEncoding;
63
64 /**
65 * Whether to look for and use Unicode extra fields.
66 */
Stefan Bodewigf84dd362009-04-28 08:31:15 +000067 private final boolean useUnicodeExtraFields;
Stefan Bodewig008ca942009-03-26 22:29:59 +000068
Stefan Bodewig04e132b2011-08-03 13:08:33 +000069 /**
70 * Wrapped stream, will always be a PushbackInputStream.
71 */
Stefan Bodewig008ca942009-03-26 22:29:59 +000072 private final InputStream in;
73
Stefan Bodewig04e132b2011-08-03 13:08:33 +000074 /**
75 * Inflater used for all deflated entries.
76 */
Stefan Bodewig008ca942009-03-26 22:29:59 +000077 private final Inflater inf = new Inflater(true);
Stefan Bodewig04e132b2011-08-03 13:08:33 +000078
79 /**
80 * Calculates checkusms for all entries.
81 */
Stefan Bodewig008ca942009-03-26 22:29:59 +000082 private final CRC32 crc = new CRC32();
83
Stefan Bodewig04e132b2011-08-03 13:08:33 +000084 /**
85 * Buffer used to read from the wrapped stream.
86 */
87 private final Buffer buf = new Buffer();
88 /**
89 * The entry that is currently being read.
90 */
91 private CurrentEntry current = null;
92 /**
93 * Whether the stream has been closed.
94 */
Stefan Bodewig008ca942009-03-26 22:29:59 +000095 private boolean closed = false;
Stefan Bodewig04e132b2011-08-03 13:08:33 +000096 /**
97 * Whether the stream has reached the central directory - and thus
98 * found all entries.
99 */
Stefan Bodewig008ca942009-03-26 22:29:59 +0000100 private boolean hitCentralDirectory = false;
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000101 /**
102 * When reading a stored entry that uses the data descriptor this
103 * stream has to read the full entry and caches it. This is the
104 * cache.
105 */
Stefan Bodewig03e94a42010-03-19 15:08:23 +0000106 private ByteArrayInputStream lastStoredEntry = null;
107
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000108 /**
109 * Whether the stream will try to read STORED entries that use a
110 * data descriptor.
111 */
Stefan Bodewig03e94a42010-03-19 15:08:23 +0000112 private boolean allowStoredEntriesWithDataDescriptor = false;
Stefan Bodewig008ca942009-03-26 22:29:59 +0000113
114 private static final int LFH_LEN = 30;
115 /*
Stefan Bodewigb86d8a62013-07-17 14:37:22 +0000116 local file header signature WORD
117 version needed to extract SHORT
118 general purpose bit flag SHORT
119 compression method SHORT
120 last mod file time SHORT
121 last mod file date SHORT
122 crc-32 WORD
123 compressed size WORD
124 uncompressed size WORD
125 file name length SHORT
126 extra field length SHORT
Stefan Bodewig008ca942009-03-26 22:29:59 +0000127 */
Torsten Curdtca165392008-07-10 10:17:44 +0000128
Stefan Bodewig7181d542013-01-22 12:45:24 +0000129 private static final int CFH_LEN = 46;
130 /*
Stefan Bodewigb86d8a62013-07-17 14:37:22 +0000131 central file header signature WORD
132 version made by SHORT
133 version needed to extract SHORT
134 general purpose bit flag SHORT
135 compression method SHORT
136 last mod file time SHORT
137 last mod file date SHORT
138 crc-32 WORD
139 compressed size WORD
140 uncompressed size WORD
141 file name length SHORT
142 extra field length SHORT
143 file comment length SHORT
144 disk number start SHORT
145 internal file attributes SHORT
146 external file attributes WORD
147 relative offset of local header WORD
Stefan Bodewig7181d542013-01-22 12:45:24 +0000148 */
149
Stefan Bodewigcadc5272011-08-03 14:33:44 +0000150 private static final long TWO_EXP_32 = ZIP64_MAGIC + 1;
151
Sebastian Bazleya6072ce2013-01-07 22:54:12 +0000152 // cached buffers - must only be used locally in the class (COMPRESS-172 - reduce garbage collection)
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000153 private final byte[] LFH_BUF = new byte[LFH_LEN];
154 private final byte[] SKIP_BUF = new byte[1024];
Stefan Bodewig7181d542013-01-22 12:45:24 +0000155 private final byte[] SHORT_BUF = new byte[SHORT];
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000156 private final byte[] WORD_BUF = new byte[WORD];
157 private final byte[] TWO_DWORD_BUF = new byte[2 * DWORD];
158
Stefan Bodewig7181d542013-01-22 12:45:24 +0000159 private int entriesRead = 0;
160
Stefan Bodewig743d7c52009-02-05 11:01:35 +0000161 public ZipArchiveInputStream(InputStream inputStream) {
Stefan Bodewig2ab43b02012-07-07 19:34:11 +0000162 this(inputStream, ZipEncodingHelper.UTF8);
163 }
164
165 /**
166 * @param encoding the encoding to use for file names, use null
167 * for the platform's default encoding
168 * @since 1.5
169 */
170 public ZipArchiveInputStream(InputStream inputStream, String encoding) {
171 this(inputStream, encoding, true);
Stefan Bodewig008ca942009-03-26 22:29:59 +0000172 }
173
174 /**
175 * @param encoding the encoding to use for file names, use null
176 * for the platform's default encoding
177 * @param useUnicodeExtraFields whether to use InfoZIP Unicode
178 * Extra Fields (if present) to set the file names.
179 */
180 public ZipArchiveInputStream(InputStream inputStream,
181 String encoding,
182 boolean useUnicodeExtraFields) {
Stefan Bodewig03e94a42010-03-19 15:08:23 +0000183 this(inputStream, encoding, useUnicodeExtraFields, false);
184 }
185
186 /**
187 * @param encoding the encoding to use for file names, use null
188 * for the platform's default encoding
189 * @param useUnicodeExtraFields whether to use InfoZIP Unicode
190 * Extra Fields (if present) to set the file names.
191 * @param allowStoredEntriesWithDataDescriptor whether the stream
192 * will try to read STORED entries that use a data descriptor
Gary D. Gregory2bd0dd42012-04-01 13:02:39 +0000193 * @since 1.1
Stefan Bodewig03e94a42010-03-19 15:08:23 +0000194 */
195 public ZipArchiveInputStream(InputStream inputStream,
196 String encoding,
197 boolean useUnicodeExtraFields,
198 boolean allowStoredEntriesWithDataDescriptor) {
Stefan Bodewig008ca942009-03-26 22:29:59 +0000199 zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
200 this.useUnicodeExtraFields = useUnicodeExtraFields;
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000201 in = new PushbackInputStream(inputStream, buf.buf.length);
Stefan Bodewig03e94a42010-03-19 15:08:23 +0000202 this.allowStoredEntriesWithDataDescriptor =
203 allowStoredEntriesWithDataDescriptor;
Stefan Bodewig743d7c52009-02-05 11:01:35 +0000204 }
Torsten Curdtca165392008-07-10 10:17:44 +0000205
Stefan Bodewiga7049ab2009-02-11 07:44:00 +0000206 public ZipArchiveEntry getNextZipEntry() throws IOException {
Stefan Bodewig92d8c572013-01-01 10:51:42 +0000207 boolean firstEntry = true;
Stefan Bodewig008ca942009-03-26 22:29:59 +0000208 if (closed || hitCentralDirectory) {
Stefan Bodewigfa8fea72009-02-06 08:49:49 +0000209 return null;
210 }
Stefan Bodewig008ca942009-03-26 22:29:59 +0000211 if (current != null) {
212 closeEntry();
Stefan Bodewig92d8c572013-01-01 10:51:42 +0000213 firstEntry = false;
Stefan Bodewig008ca942009-03-26 22:29:59 +0000214 }
Stefan Bodewig92d8c572013-01-01 10:51:42 +0000215
Stefan Bodewig008ca942009-03-26 22:29:59 +0000216 try {
Stefan Bodewig92d8c572013-01-01 10:51:42 +0000217 if (firstEntry) {
218 // split archives have a special signature before the
219 // first local file header - look for it and fail with
220 // the appropriate error message if this is a split
221 // archive.
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000222 readFirstLocalFileHeader(LFH_BUF);
Stefan Bodewig92d8c572013-01-01 10:51:42 +0000223 } else {
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000224 readFully(LFH_BUF);
Stefan Bodewig92d8c572013-01-01 10:51:42 +0000225 }
Stefan Bodewig008ca942009-03-26 22:29:59 +0000226 } catch (EOFException e) {
227 return null;
228 }
Sebastian Bazley2f69e632013-01-22 17:00:33 +0000229
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000230 ZipLong sig = new ZipLong(LFH_BUF);
Stefan Bodewig7181d542013-01-22 12:45:24 +0000231 if (sig.equals(ZipLong.CFH_SIG) || sig.equals(ZipLong.AED_SIG)) {
Stefan Bodewig008ca942009-03-26 22:29:59 +0000232 hitCentralDirectory = true;
Stefan Bodewig7181d542013-01-22 12:45:24 +0000233 skipRemainderOfArchive();
Stefan Bodewig008ca942009-03-26 22:29:59 +0000234 }
235 if (!sig.equals(ZipLong.LFH_SIG)) {
236 return null;
237 }
238
239 int off = WORD;
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000240 current = new CurrentEntry();
Stefan Bodewig008ca942009-03-26 22:29:59 +0000241
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000242 int versionMadeBy = ZipShort.getValue(LFH_BUF, off);
Stefan Bodewig008ca942009-03-26 22:29:59 +0000243 off += SHORT;
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000244 current.entry.setPlatform((versionMadeBy >> ZipFile.BYTE_SHIFT)
245 & ZipFile.NIBLET_MASK);
Stefan Bodewig008ca942009-03-26 22:29:59 +0000246
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000247 final GeneralPurposeBit gpFlag = GeneralPurposeBit.parse(LFH_BUF, off);
Stefan Bodewig4d68dda2010-02-19 09:56:37 +0000248 final boolean hasUTF8Flag = gpFlag.usesUTF8ForNames();
Stefan Bodewig008ca942009-03-26 22:29:59 +0000249 final ZipEncoding entryEncoding =
Stefan Bodewig2b209932010-02-19 08:41:23 +0000250 hasUTF8Flag ? ZipEncodingHelper.UTF8_ZIP_ENCODING : zipEncoding;
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000251 current.hasDataDescriptor = gpFlag.usesDataDescriptor();
252 current.entry.setGeneralPurposeBit(gpFlag);
Stefan Bodewig008ca942009-03-26 22:29:59 +0000253
254 off += SHORT;
255
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000256 current.entry.setMethod(ZipShort.getValue(LFH_BUF, off));
Stefan Bodewig008ca942009-03-26 22:29:59 +0000257 off += SHORT;
258
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000259 long time = ZipUtil.dosToJavaTime(ZipLong.getValue(LFH_BUF, off));
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000260 current.entry.setTime(time);
Stefan Bodewig008ca942009-03-26 22:29:59 +0000261 off += WORD;
262
Stefan Bodewig6c5f04b2011-07-25 20:14:59 +0000263 ZipLong size = null, cSize = null;
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000264 if (!current.hasDataDescriptor) {
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000265 current.entry.setCrc(ZipLong.getValue(LFH_BUF, off));
Stefan Bodewig008ca942009-03-26 22:29:59 +0000266 off += WORD;
267
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000268 cSize = new ZipLong(LFH_BUF, off);
Stefan Bodewig008ca942009-03-26 22:29:59 +0000269 off += WORD;
270
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000271 size = new ZipLong(LFH_BUF, off);
Stefan Bodewig008ca942009-03-26 22:29:59 +0000272 off += WORD;
273 } else {
274 off += 3 * WORD;
275 }
276
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000277 int fileNameLen = ZipShort.getValue(LFH_BUF, off);
Stefan Bodewig008ca942009-03-26 22:29:59 +0000278
279 off += SHORT;
280
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000281 int extraLen = ZipShort.getValue(LFH_BUF, off);
Stefan Bodewig008ca942009-03-26 22:29:59 +0000282 off += SHORT;
283
284 byte[] fileName = new byte[fileNameLen];
285 readFully(fileName);
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000286 current.entry.setName(entryEncoding.decode(fileName), fileName);
Stefan Bodewig008ca942009-03-26 22:29:59 +0000287
288 byte[] extraData = new byte[extraLen];
289 readFully(extraData);
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000290 current.entry.setExtra(extraData);
Stefan Bodewig008ca942009-03-26 22:29:59 +0000291
Stefan Bodewig2b209932010-02-19 08:41:23 +0000292 if (!hasUTF8Flag && useUnicodeExtraFields) {
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000293 ZipUtil.setNameAndCommentFromExtraFields(current.entry, fileName,
294 null);
Stefan Bodewigf84dd362009-04-28 08:31:15 +0000295 }
Stefan Bodewig3879e472011-07-26 04:38:35 +0000296
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000297 processZip64Extra(size, cSize);
Stefan Bodewig7181d542013-01-22 12:45:24 +0000298 entriesRead++;
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000299 return current.entry;
300 }
301
302 /**
Stefan Bodewig92d8c572013-01-01 10:51:42 +0000303 * Fills the given array with the first local file header and
304 * deals with splitting/spanning markers that may prefix the first
305 * LFH.
306 */
307 private void readFirstLocalFileHeader(byte[] lfh) throws IOException {
308 readFully(lfh);
309 ZipLong sig = new ZipLong(lfh);
310 if (sig.equals(ZipLong.DD_SIG)) {
Sebastian Bazley2f69e632013-01-22 17:00:33 +0000311 throw new
Stefan Bodewig92d8c572013-01-01 10:51:42 +0000312 UnsupportedZipFeatureException(UnsupportedZipFeatureException
313 .Feature.SPLITTING);
314 }
Stefan Bodewige10ce2c2013-01-01 10:57:23 +0000315 if (sig.equals(ZipLong.SINGLE_SEGMENT_SPLIT_MARKER)) {
316 // The archive is not really split as only one segment was
317 // needed in the end. Just skip over the marker.
318 byte[] missedLfhBytes = new byte[4];
319 readFully(missedLfhBytes);
320 System.arraycopy(lfh, 4, lfh, 0, LFH_LEN - 4);
321 System.arraycopy(missedLfhBytes, 0, lfh, LFH_LEN - 4, 4);
322 }
Stefan Bodewig92d8c572013-01-01 10:51:42 +0000323 }
324
325 /**
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000326 * Records whether a Zip64 extra is present and sets the size
327 * information from it if sizes are 0xFFFFFFFF and the entry
328 * doesn't use a data descriptor.
329 */
330 private void processZip64Extra(ZipLong size, ZipLong cSize) {
331 Zip64ExtendedInformationExtraField z64 =
Stefan Bodewig5031a972011-07-26 05:31:16 +0000332 (Zip64ExtendedInformationExtraField)
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000333 current.entry.getExtraField(Zip64ExtendedInformationExtraField
334 .HEADER_ID);
335 current.usesZip64 = z64 != null;
336 if (!current.hasDataDescriptor) {
Sebastian Bazleydf8b3b22013-05-14 00:19:04 +0000337 if (z64 != null // same as current.usesZip64 but avoids NPE warning
338 && (cSize.equals(ZipLong.ZIP64_MAGIC)
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000339 || size.equals(ZipLong.ZIP64_MAGIC))
Stefan Bodewig6c5f04b2011-07-25 20:14:59 +0000340 ) {
Sebastian Bazleydf8b3b22013-05-14 00:19:04 +0000341 current.entry.setCompressedSize(z64.getCompressedSize()
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000342 .getLongValue());
343 current.entry.setSize(z64.getSize().getLongValue());
Stefan Bodewig6c5f04b2011-07-25 20:14:59 +0000344 } else {
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000345 current.entry.setCompressedSize(cSize.getValue());
346 current.entry.setSize(size.getValue());
Stefan Bodewig6c5f04b2011-07-25 20:14:59 +0000347 }
348 }
Stefan Bodewiga7049ab2009-02-11 07:44:00 +0000349 }
350
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000351 @Override
Stefan Bodewiga7049ab2009-02-11 07:44:00 +0000352 public ArchiveEntry getNextEntry() throws IOException {
353 return getNextZipEntry();
Torsten Curdtca165392008-07-10 10:17:44 +0000354 }
355
Stefan Bodewigc7e51ed2010-02-19 10:55:27 +0000356 /**
357 * Whether this class is able to read the given entry.
358 *
359 * <p>May return false if it is set up to use encryption or a
360 * compression method that hasn't been implemented yet.</p>
Gary D. Gregory2bd0dd42012-04-01 13:02:39 +0000361 * @since 1.1
Stefan Bodewigc7e51ed2010-02-19 10:55:27 +0000362 */
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000363 @Override
Stefan Bodewige0692ec2010-02-24 15:35:47 +0000364 public boolean canReadEntryData(ArchiveEntry ae) {
Stefan Bodewiga33505b2010-02-19 12:23:27 +0000365 if (ae instanceof ZipArchiveEntry) {
Stefan Bodewig02e2be62010-03-17 15:55:00 +0000366 ZipArchiveEntry ze = (ZipArchiveEntry) ae;
367 return ZipUtil.canHandleEntryData(ze)
368 && supportsDataDescriptorFor(ze);
369
Stefan Bodewiga33505b2010-02-19 12:23:27 +0000370 }
Stefan Bodewig734e6f72010-02-19 12:33:28 +0000371 return false;
Stefan Bodewigc7e51ed2010-02-19 10:55:27 +0000372 }
373
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000374 @Override
Stefan Bodewig008ca942009-03-26 22:29:59 +0000375 public int read(byte[] buffer, int start, int length) throws IOException {
376 if (closed) {
377 throw new IOException("The stream is closed");
378 }
379 if (inf.finished() || current == null) {
380 return -1;
381 }
382
383 // avoid int overflow, check null buffer
384 if (start <= buffer.length && length >= 0 && start >= 0
385 && buffer.length - start >= length) {
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000386 ZipUtil.checkRequestedFeatures(current.entry);
387 if (!supportsDataDescriptorFor(current.entry)) {
Stefan Bodewig02e2be62010-03-17 15:55:00 +0000388 throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException
389 .Feature
390 .DATA_DESCRIPTOR,
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000391 current.entry);
Stefan Bodewig02e2be62010-03-17 15:55:00 +0000392 }
Jukka Zitting7b125a32009-12-13 18:31:55 +0000393
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000394 if (current.entry.getMethod() == ZipArchiveOutputStream.STORED) {
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000395 return readStored(buffer, start, length);
396 }
397 return readDeflated(buffer, start, length);
398 }
399 throw new ArrayIndexOutOfBoundsException();
400 }
401
402 /**
403 * Implementation of read for STORED entries.
404 */
405 private int readStored(byte[] buffer, int start, int length)
406 throws IOException {
Stefan Bodewig03e94a42010-03-19 15:08:23 +0000407
Stefan Bodewig9dd152d2011-08-11 14:22:37 +0000408 if (current.hasDataDescriptor) {
409 if (lastStoredEntry == null) {
410 readStoredEntry();
411 }
412 return lastStoredEntry.read(buffer, start, length);
413 }
414
415 long csize = current.entry.getSize();
416 if (current.bytesRead >= csize) {
417 return -1;
418 }
419
420 if (buf.offsetInBuffer >= buf.lengthOfLastRead) {
421 buf.offsetInBuffer = 0;
422 if ((buf.lengthOfLastRead = in.read(buf.buf)) == -1) {
423 return -1;
424 }
425 count(buf.lengthOfLastRead);
426 current.bytesReadFromStream += buf.lengthOfLastRead;
427 }
428
Stefan Bodewigd9b55962013-02-20 19:22:39 +0000429 int availableBytesInBuffer = buf.lengthOfLastRead - buf.offsetInBuffer;
430 int toRead = Math.min(availableBytesInBuffer, length);
Stefan Bodewig9dd152d2011-08-11 14:22:37 +0000431 if ((csize - current.bytesRead) < toRead) {
432 // if it is smaller than toRead then it fits into an int
433 toRead = (int) (csize - current.bytesRead);
434 }
435 System.arraycopy(buf.buf, buf.offsetInBuffer, buffer, start, toRead);
436 buf.offsetInBuffer += toRead;
437 current.bytesRead += toRead;
438 crc.update(buffer, start, toRead);
439 return toRead;
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000440 }
Jukka Zitting7b125a32009-12-13 18:31:55 +0000441
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000442 /**
443 * Implementation of read for DEFLATED entries.
444 */
445 private int readDeflated(byte[] buffer, int start, int length)
446 throws IOException {
Stefan Bodewige2cf4122012-12-28 06:53:21 +0000447 int read = readFromInflater(buffer, start, length);
Stefan Bodewig9dd152d2011-08-11 14:22:37 +0000448 if (read == 0) {
Stefan Bodewigb4a985f2012-12-28 17:29:53 +0000449 if (inf.finished()) {
Stefan Bodewig9dd152d2011-08-11 14:22:37 +0000450 return -1;
Stefan Bodewigb4a985f2012-12-28 17:29:53 +0000451 } else if (inf.needsDictionary()) {
452 throw new ZipException("This archive needs a preset dictionary"
453 + " which is not supported by Commons"
454 + " Compress.");
Stefan Bodewig9dd152d2011-08-11 14:22:37 +0000455 } else if (buf.lengthOfLastRead == -1) {
456 throw new IOException("Truncated ZIP file");
Stefan Bodewig008ca942009-03-26 22:29:59 +0000457 }
Stefan Bodewig9dd152d2011-08-11 14:22:37 +0000458 }
459 crc.update(buffer, start, read);
460 return read;
Stefan Bodewig008ca942009-03-26 22:29:59 +0000461 }
462
Stefan Bodewige2cf4122012-12-28 06:53:21 +0000463 /**
464 * Potentially reads more bytes to fill the inflater's buffer and
465 * reads from it.
466 */
467 private int readFromInflater(byte[] buffer, int start, int length)
468 throws IOException {
469 int read = 0;
470 do {
471 if (inf.needsInput()) {
472 fill();
473 if (buf.lengthOfLastRead > 0) {
474 current.bytesReadFromStream += buf.lengthOfLastRead;
475 } else {
476 break;
477 }
478 }
479 try {
480 read = inf.inflate(buffer, start, length);
481 } catch (DataFormatException e) {
482 throw new ZipException(e.getMessage());
483 }
484 } while (read == 0 && inf.needsInput());
485 return read;
486 }
487
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000488 @Override
Stefan Bodewig008ca942009-03-26 22:29:59 +0000489 public void close() throws IOException {
490 if (!closed) {
491 closed = true;
492 in.close();
Stefan Bodewigd48f1062011-08-03 13:36:01 +0000493 inf.end();
Stefan Bodewig008ca942009-03-26 22:29:59 +0000494 }
Torsten Curdtca165392008-07-10 10:17:44 +0000495 }
Stefan Bodewig3f9bcc62009-02-10 14:20:05 +0000496
Stefan Bodewig7a76c472011-07-20 14:55:50 +0000497 /**
498 * Skips over and discards value bytes of data from this input
499 * stream.
500 *
501 * <p>This implementation may end up skipping over some smaller
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000502 * number of bytes, possibly 0, if and only if it reaches the end
Stefan Bodewig7a76c472011-07-20 14:55:50 +0000503 * of the underlying stream.</p>
504 *
505 * <p>The actual number of bytes skipped is returned.</p>
506 *
507 * @param value the number of bytes to be skipped.
508 * @return the actual number of bytes skipped.
509 * @throws IOException - if an I/O error occurs.
510 * @throws IllegalArgumentException - if value is negative.
511 */
Stefan Bodewige53e88a2011-07-25 13:28:31 +0000512 @Override
Stefan Bodewig008ca942009-03-26 22:29:59 +0000513 public long skip(long value) throws IOException {
514 if (value >= 0) {
515 long skipped = 0;
Stefan Bodewig7a76c472011-07-20 14:55:50 +0000516 while (skipped < value) {
Stefan Bodewig008ca942009-03-26 22:29:59 +0000517 long rem = value - skipped;
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000518 int x = read(SKIP_BUF, 0,
519 (int) (SKIP_BUF.length > rem ? rem
520 : SKIP_BUF.length));
Stefan Bodewig008ca942009-03-26 22:29:59 +0000521 if (x == -1) {
522 return skipped;
523 }
524 skipped += x;
525 }
526 return skipped;
527 }
528 throw new IllegalArgumentException();
529 }
Stefan Bodewig3f9bcc62009-02-10 14:20:05 +0000530
Sebastian Bazley6209f812010-05-10 17:36:40 +0000531 /**
532 * Checks if the signature matches what is expected for a zip file.
533 * Does not currently handle self-extracting zips which may have arbitrary
534 * leading content.
Sebastian Bazley2f69e632013-01-22 17:00:33 +0000535 *
Sebastian Bazley6209f812010-05-10 17:36:40 +0000536 * @param signature
537 * the bytes to check
538 * @param length
539 * the number of bytes to check
540 * @return true, if this stream is a zip archive stream, false otherwise
Sebastian Bazleyfa526cb2009-03-30 00:39:05 +0000541 */
Stefan Bodewigeadbe112009-02-26 09:31:43 +0000542 public static boolean matches(byte[] signature, int length) {
543 if (length < ZipArchiveOutputStream.LFH_SIG.length) {
Stefan Bodewigdff90012009-02-06 08:59:14 +0000544 return false;
545 }
546
Sebastian Bazleyfa526cb2009-03-30 00:39:05 +0000547 return checksig(signature, ZipArchiveOutputStream.LFH_SIG) // normal file
Stefan Bodewig0d6defe2013-01-01 11:05:48 +0000548 || checksig(signature, ZipArchiveOutputStream.EOCD_SIG) // empty zip
549 || checksig(signature, ZipArchiveOutputStream.DD_SIG) // split zip
550 || checksig(signature,
551 ZipLong.SINGLE_SEGMENT_SPLIT_MARKER.getBytes());
Sebastian Bazleyfa526cb2009-03-30 00:39:05 +0000552 }
553
554 private static boolean checksig(byte[] signature, byte[] expected){
555 for (int i = 0; i < expected.length; i++) {
556 if (signature[i] != expected[i]) {
Stefan Bodewigeadbe112009-02-26 09:31:43 +0000557 return false;
558 }
Stefan Bodewigfa8fea72009-02-06 08:49:49 +0000559 }
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000560 return true;
Torsten Curdtca165392008-07-10 10:17:44 +0000561 }
Stefan Bodewig008ca942009-03-26 22:29:59 +0000562
Jukka Zittingb6886eb2009-12-13 19:44:37 +0000563 /**
564 * Closes the current ZIP archive entry and positions the underlying
565 * stream to the beginning of the next entry. All per-entry variables
566 * and data structures are cleared.
567 * <p>
568 * If the compressed size of this entry is included in the entry header,
569 * then any outstanding bytes are simply skipped from the underlying
570 * stream without uncompressing them. This allows an entry to be safely
571 * closed even if the compression method is unsupported.
572 * <p>
573 * In case we don't know the compressed size of this entry or have
574 * already buffered too much data from the underlying stream to support
575 * uncompression, then the uncompression process is completed and the
576 * end position of the stream is adjusted based on the result of that
577 * process.
578 *
579 * @throws IOException if an error occurs
580 */
Stefan Bodewig008ca942009-03-26 22:29:59 +0000581 private void closeEntry() throws IOException {
582 if (closed) {
583 throw new IOException("The stream is closed");
584 }
585 if (current == null) {
586 return;
587 }
Stefan Bodewig008ca942009-03-26 22:29:59 +0000588
Jukka Zittingb6886eb2009-12-13 19:44:37 +0000589 // Ensure all entry bytes are read
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000590 if (current.bytesReadFromStream <= current.entry.getCompressedSize()
Stefan Bodewig9dd152d2011-08-11 14:22:37 +0000591 && !current.hasDataDescriptor) {
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000592 drainCurrentEntryData();
Jukka Zittingb6886eb2009-12-13 19:44:37 +0000593 } else {
594 skip(Long.MAX_VALUE);
595
Sebastian Bazley2f69e632013-01-22 17:00:33 +0000596 long inB =
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000597 current.entry.getMethod() == ZipArchiveOutputStream.DEFLATED
598 ? getBytesInflated() : current.bytesRead;
Stefan Bodewig797d74f2011-07-21 03:49:35 +0000599
600 // this is at most a single read() operation and can't
601 // exceed the range of int
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000602 int diff = (int) (current.bytesReadFromStream - inB);
Jukka Zittingb6886eb2009-12-13 19:44:37 +0000603
604 // Pushback any required bytes
Stefan Bodewig797d74f2011-07-21 03:49:35 +0000605 if (diff > 0) {
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000606 pushback(buf.buf, buf.lengthOfLastRead - diff, diff);
Jukka Zittingb6886eb2009-12-13 19:44:37 +0000607 }
Stefan Bodewig008ca942009-03-26 22:29:59 +0000608 }
609
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000610 if (lastStoredEntry == null && current.hasDataDescriptor) {
Stefan Bodewig1154e7b2010-03-17 15:47:57 +0000611 readDataDescriptor();
Stefan Bodewig008ca942009-03-26 22:29:59 +0000612 }
613
614 inf.reset();
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000615 buf.reset();
Stefan Bodewig008ca942009-03-26 22:29:59 +0000616 crc.reset();
617 current = null;
Stefan Bodewig03e94a42010-03-19 15:08:23 +0000618 lastStoredEntry = null;
Stefan Bodewig008ca942009-03-26 22:29:59 +0000619 }
620
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000621 /**
622 * Read all data of the current entry from the underlying stream
623 * that hasn't been read, yet.
624 */
625 private void drainCurrentEntryData() throws IOException {
Stefan Bodewig9dd152d2011-08-11 14:22:37 +0000626 long remaining = current.entry.getCompressedSize()
627 - current.bytesReadFromStream;
628 while (remaining > 0) {
629 long n = in.read(buf.buf, 0, (int) Math.min(buf.buf.length,
630 remaining));
631 if (n < 0) {
632 throw new EOFException(
633 "Truncated ZIP entry: " + current.entry.getName());
634 } else {
635 count(n);
636 remaining -= n;
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000637 }
Stefan Bodewig9dd152d2011-08-11 14:22:37 +0000638 }
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000639 }
640
641 /**
642 * Get the number of bytes Inflater has actually processed.
643 *
644 * <p>for Java &lt; Java7 the getBytes* methods in
645 * Inflater/Deflater seem to return unsigned ints rather than
646 * longs that start over with 0 at 2^32.</p>
647 *
648 * <p>The stream knows how many bytes it has read, but not how
649 * many the Inflater actually consumed - it should be between the
650 * total number of bytes read for the entry and the total number
651 * minus the last read operation. Here we just try to make the
652 * value close enough to the bytes we've read by assuming the
653 * number of bytes consumed must be smaller than (or equal to) the
654 * number of bytes read but not smaller by more than 2^32.</p>
655 */
656 private long getBytesInflated() {
657 long inB = inf.getBytesRead();
658 if (current.bytesReadFromStream >= TWO_EXP_32) {
659 while (inB + TWO_EXP_32 <= current.bytesReadFromStream) {
660 inB += TWO_EXP_32;
661 }
662 }
663 return inB;
664 }
665
Stefan Bodewig008ca942009-03-26 22:29:59 +0000666 private void fill() throws IOException {
667 if (closed) {
668 throw new IOException("The stream is closed");
669 }
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000670 if ((buf.lengthOfLastRead = in.read(buf.buf)) > 0) {
671 count(buf.lengthOfLastRead);
672 inf.setInput(buf.buf, 0, buf.lengthOfLastRead);
Stefan Bodewig008ca942009-03-26 22:29:59 +0000673 }
674 }
675
676 private void readFully(byte[] b) throws IOException {
677 int count = 0, x = 0;
678 while (count != b.length) {
679 count += x = in.read(b, count, b.length - count);
680 if (x == -1) {
681 throw new EOFException();
682 }
Stefan Bodewiga1015f32010-02-18 16:11:13 +0000683 count(x);
Stefan Bodewig008ca942009-03-26 22:29:59 +0000684 }
685 }
Stefan Bodewig1154e7b2010-03-17 15:47:57 +0000686
687 private void readDataDescriptor() throws IOException {
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000688 readFully(WORD_BUF);
689 ZipLong val = new ZipLong(WORD_BUF);
Stefan Bodewig1154e7b2010-03-17 15:47:57 +0000690 if (ZipLong.DD_SIG.equals(val)) {
691 // data descriptor with signature, skip sig
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000692 readFully(WORD_BUF);
693 val = new ZipLong(WORD_BUF);
Stefan Bodewig1154e7b2010-03-17 15:47:57 +0000694 }
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000695 current.entry.setCrc(val.getValue());
Stefan Bodewig1e708a02011-08-03 09:55:53 +0000696
697 // if there is a ZIP64 extra field, sizes are eight bytes
698 // each, otherwise four bytes each. Unfortunately some
699 // implementations - namely Java7 - use eight bytes without
700 // using a ZIP64 extra field -
701 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588
702
703 // just read 16 bytes and check whether bytes nine to twelve
704 // look like one of the signatures of what could follow a data
705 // descriptor (ignoring archive decryption headers for now).
706 // If so, push back eight bytes and assume sizes are four
707 // bytes, otherwise sizes are eight bytes each.
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000708 readFully(TWO_DWORD_BUF);
709 ZipLong potentialSig = new ZipLong(TWO_DWORD_BUF, DWORD);
Stefan Bodewig1e708a02011-08-03 09:55:53 +0000710 if (potentialSig.equals(ZipLong.CFH_SIG)
711 || potentialSig.equals(ZipLong.LFH_SIG)) {
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000712 pushback(TWO_DWORD_BUF, DWORD, DWORD);
713 current.entry.setCompressedSize(ZipLong.getValue(TWO_DWORD_BUF));
714 current.entry.setSize(ZipLong.getValue(TWO_DWORD_BUF, WORD));
Stefan Bodewig6c5f04b2011-07-25 20:14:59 +0000715 } else {
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000716 current.entry
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000717 .setCompressedSize(ZipEightByteInteger
718 .getLongValue(TWO_DWORD_BUF));
719 current.entry.setSize(ZipEightByteInteger
720 .getLongValue(TWO_DWORD_BUF, DWORD));
Stefan Bodewig6c5f04b2011-07-25 20:14:59 +0000721 }
Stefan Bodewig1154e7b2010-03-17 15:47:57 +0000722 }
723
Stefan Bodewig02e2be62010-03-17 15:55:00 +0000724 /**
725 * Whether this entry requires a data descriptor this library can work with.
726 *
Stefan Bodewig03e94a42010-03-19 15:08:23 +0000727 * @return true if allowStoredEntriesWithDataDescriptor is true,
728 * the entry doesn't require any data descriptor or the method is
729 * DEFLATED.
Stefan Bodewig02e2be62010-03-17 15:55:00 +0000730 */
Stefan Bodewig03e94a42010-03-19 15:08:23 +0000731 private boolean supportsDataDescriptorFor(ZipArchiveEntry entry) {
732 return allowStoredEntriesWithDataDescriptor ||
733 !entry.getGeneralPurposeBit().usesDataDescriptor()
Gary D. Gregory04173632012-04-01 13:26:55 +0000734 || entry.getMethod() == ZipEntry.DEFLATED;
Stefan Bodewig02e2be62010-03-17 15:55:00 +0000735 }
736
Stefan Bodewig03e94a42010-03-19 15:08:23 +0000737 /**
738 * Caches a stored entry that uses the data descriptor.
739 *
740 * <ul>
741 * <li>Reads a stored entry until the signature of a local file
742 * header, central directory header or data descriptor has been
743 * found.</li>
744 * <li>Stores all entry data in lastStoredEntry.</p>
745 * <li>Rewinds the stream to position at the data
746 * descriptor.</li>
747 * <li>reads the data descriptor</li>
748 * </ul>
749 *
750 * <p>After calling this method the entry should know its size,
751 * the entry's data is cached and the stream is positioned at the
752 * next local file or central directory header.</p>
753 */
754 private void readStoredEntry() throws IOException {
755 ByteArrayOutputStream bos = new ByteArrayOutputStream();
Stefan Bodewig03e94a42010-03-19 15:08:23 +0000756 int off = 0;
757 boolean done = false;
758
Stefan Bodewig6c5f04b2011-07-25 20:14:59 +0000759 // length of DD without signature
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000760 int ddLen = current.usesZip64 ? WORD + 2 * DWORD : 3 * WORD;
Stefan Bodewig6c5f04b2011-07-25 20:14:59 +0000761
Stefan Bodewig03e94a42010-03-19 15:08:23 +0000762 while (!done) {
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000763 int r = in.read(buf.buf, off,
764 ZipArchiveOutputStream.BUFFER_SIZE - off);
Stefan Bodewig03e94a42010-03-19 15:08:23 +0000765 if (r <= 0) {
766 // read the whole archive without ever finding a
767 // central directory
768 throw new IOException("Truncated ZIP file");
769 }
770 if (r + off < 4) {
771 // buf is too small to check for a signature, loop
772 off += r;
773 continue;
774 }
775
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000776 done = bufferContainsSignature(bos, off, r, ddLen);
777 if (!done) {
778 off = cacheBytesRead(bos, off, r, ddLen);
779 }
780 }
781
782 byte[] b = bos.toByteArray();
783 lastStoredEntry = new ByteArrayInputStream(b);
784 }
785
786 private static final byte[] LFH = ZipLong.LFH_SIG.getBytes();
787 private static final byte[] CFH = ZipLong.CFH_SIG.getBytes();
788 private static final byte[] DD = ZipLong.DD_SIG.getBytes();
789
790 /**
791 * Checks whether the current buffer contains the signature of a
792 * &quot;data decsriptor&quot;, &quot;local file header&quot; or
793 * &quot;central directory entry&quot;.
794 *
795 * <p>If it contains such a signature, reads the data descriptor
796 * and positions the stream right after the data descriptor.</p>
797 */
798 private boolean bufferContainsSignature(ByteArrayOutputStream bos,
Stefan Bodewig9dd152d2011-08-11 14:22:37 +0000799 int offset, int lastRead,
800 int expectedDDLen)
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000801 throws IOException {
802 boolean done = false;
Stefan Bodewig9dd152d2011-08-11 14:22:37 +0000803 int readTooMuch = 0;
804 for (int i = 0; !done && i < lastRead - 4; i++) {
805 if (buf.buf[i] == LFH[0] && buf.buf[i + 1] == LFH[1]) {
806 if ((buf.buf[i + 2] == LFH[2] && buf.buf[i + 3] == LFH[3])
807 || (buf.buf[i] == CFH[2] && buf.buf[i + 3] == CFH[3])) {
808 // found a LFH or CFH:
809 readTooMuch = offset + lastRead - i - expectedDDLen;
810 done = true;
811 }
812 else if (buf.buf[i + 2] == DD[2] && buf.buf[i + 3] == DD[3]) {
813 // found DD:
814 readTooMuch = offset + lastRead - i;
815 done = true;
816 }
817 if (done) {
818 // * push back bytes read in excess as well as the data
819 // descriptor
820 // * copy the remaining bytes to cache
821 // * read data descriptor
822 pushback(buf.buf, offset + lastRead - readTooMuch,
823 readTooMuch);
824 bos.write(buf.buf, 0, i);
825 readDataDescriptor();
Stefan Bodewig03e94a42010-03-19 15:08:23 +0000826 }
827 }
Stefan Bodewig9dd152d2011-08-11 14:22:37 +0000828 }
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000829 return done;
830 }
Stefan Bodewig03e94a42010-03-19 15:08:23 +0000831
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000832 /**
833 * If the last read bytes could hold a data descriptor and an
834 * incomplete signature then save the last bytes to the front of
835 * the buffer and cache everything in front of the potential data
836 * descriptor into the given ByteArrayOutputStream.
837 *
838 * <p>Data descriptor plus incomplete signature (3 bytes in the
839 * worst case) can be 20 bytes max.</p>
840 */
841 private int cacheBytesRead(ByteArrayOutputStream bos, int offset,
Sebastian Bazley3b3f4cf2011-08-21 13:53:03 +0000842 int lastRead, int expecteDDLen) {
Stefan Bodewig794c20f2011-08-11 14:08:54 +0000843 final int cacheable = offset + lastRead - expecteDDLen - 3;
844 if (cacheable > 0) {
845 bos.write(buf.buf, 0, cacheable);
846 System.arraycopy(buf.buf, cacheable, buf.buf, 0,
847 expecteDDLen + 3);
848 offset = expecteDDLen + 3;
849 } else {
850 offset += lastRead;
851 }
852 return offset;
Stefan Bodewig03e94a42010-03-19 15:08:23 +0000853 }
Stefan Bodewig1e708a02011-08-03 09:55:53 +0000854
855 private void pushback(byte[] buf, int offset, int length)
856 throws IOException {
857 ((PushbackInputStream) in).unread(buf, offset, length);
858 pushedBackBytes(length);
859 }
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000860
Stefan Bodewig7181d542013-01-22 12:45:24 +0000861 // End of Central Directory Record
Stefan Bodewigb86d8a62013-07-17 14:37:22 +0000862 // end of central dir signature WORD
863 // number of this disk SHORT
Stefan Bodewig7181d542013-01-22 12:45:24 +0000864 // number of the disk with the
Stefan Bodewigb86d8a62013-07-17 14:37:22 +0000865 // start of the central directory SHORT
Stefan Bodewig7181d542013-01-22 12:45:24 +0000866 // total number of entries in the
Stefan Bodewigb86d8a62013-07-17 14:37:22 +0000867 // central directory on this disk SHORT
Stefan Bodewig7181d542013-01-22 12:45:24 +0000868 // total number of entries in
Stefan Bodewigb86d8a62013-07-17 14:37:22 +0000869 // the central directory SHORT
870 // size of the central directory WORD
Stefan Bodewig7181d542013-01-22 12:45:24 +0000871 // offset of start of central
872 // directory with respect to
Stefan Bodewigb86d8a62013-07-17 14:37:22 +0000873 // the starting disk number WORD
874 // .ZIP file comment length SHORT
875 // .ZIP file comment up to 64KB
Stefan Bodewig7181d542013-01-22 12:45:24 +0000876 //
877
878 /**
879 * Reads the stream until it find the "End of central directory
880 * record" and consumes it as well.
881 */
882 private void skipRemainderOfArchive() throws IOException {
883 // skip over central directory. One LFH has been read too much
884 // already. The calculation discounts file names and extra
885 // data so it will be too short.
886 realSkip(entriesRead * CFH_LEN - LFH_LEN);
887 findEocdRecord();
888 realSkip(ZipFile.MIN_EOCD_SIZE
889 - WORD /* signature */ - SHORT /* comment len */);
890 readFully(SHORT_BUF);
891 // file comment
892 realSkip(ZipShort.getValue(SHORT_BUF));
893 }
894
895 /**
896 * Reads forward until the signature of the &quot;End of central
897 * directory&quot; recod is found.
898 */
899 private void findEocdRecord() throws IOException {
900 int currentByte = -1;
901 boolean skipReadCall = false;
902 while (skipReadCall || (currentByte = readOneByte()) > -1) {
903 skipReadCall = false;
904 if (!isFirstByteOfEocdSig(currentByte)) {
905 continue;
906 }
907 currentByte = readOneByte();
908 if (currentByte != ZipArchiveOutputStream.EOCD_SIG[1]) {
909 if (currentByte == -1) {
910 break;
911 }
912 skipReadCall = isFirstByteOfEocdSig(currentByte);
913 continue;
914 }
915 currentByte = readOneByte();
916 if (currentByte != ZipArchiveOutputStream.EOCD_SIG[2]) {
917 if (currentByte == -1) {
918 break;
919 }
920 skipReadCall = isFirstByteOfEocdSig(currentByte);
921 continue;
922 }
923 currentByte = readOneByte();
924 if (currentByte == -1
925 || currentByte == ZipArchiveOutputStream.EOCD_SIG[3]) {
926 break;
927 }
928 skipReadCall = isFirstByteOfEocdSig(currentByte);
929 }
930 }
931
932 /**
933 * Skips bytes by reading from the underlying stream rather than
934 * the (potentially inflating) archive stream - which {@link
935 * #skip} would do.
936 *
937 * Also updates bytes-read counter.
938 */
939 private void realSkip(long value) throws IOException {
940 if (value >= 0) {
941 long skipped = 0;
942 while (skipped < value) {
943 long rem = value - skipped;
944 int x = in.read(SKIP_BUF, 0,
945 (int) (SKIP_BUF.length > rem ? rem
946 : SKIP_BUF.length));
947 if (x == -1) {
948 return;
949 }
950 count(x);
951 skipped += x;
952 }
953 return;
954 }
955 throw new IllegalArgumentException();
956 }
957
958 /**
959 * Reads bytes by reading from the underlying stream rather than
960 * the (potentially inflating) archive stream - which {@link
961 * #read} would do.
962 *
963 * Also updates bytes-read counter.
964 */
965 private int readOneByte() throws IOException {
966 int b = in.read();
967 if (b != -1) {
968 count(1);
969 }
970 return b;
971 }
972
973 private boolean isFirstByteOfEocdSig(int b) {
974 return b == ZipArchiveOutputStream.EOCD_SIG[0];
975 }
976
Stefan Bodewig04e132b2011-08-03 13:08:33 +0000977 /**
978 * Structure collecting information for the entry that is
979 * currently being read.
980 */
981 private static final class CurrentEntry {
982 /**
983 * Current ZIP entry.
984 */
985 private final ZipArchiveEntry entry = new ZipArchiveEntry();
986 /**
987 * Does the entry use a data descriptor?
988 */
989 private boolean hasDataDescriptor;
990 /**
991 * Does the entry have a ZIP64 extended information extra field.
992 */
993 private boolean usesZip64;
994 /**
995 * Number of bytes of entry content read by the client if the
996 * entry is STORED.
997 */
998 private long bytesRead;
999 /**
1000 * Number of bytes of entry content read so from the stream.
1001 *
1002 * <p>This may be more than the actual entry's length as some
1003 * stuff gets buffered up and needs to be pushed back when the
1004 * end of the entry has been reached.</p>
1005 */
1006 private long bytesReadFromStream;
1007 }
1008
1009 /**
1010 * Contains a temporary buffer used to read from the wrapped
1011 * stream together with some information needed for internal
1012 * housekeeping.
1013 */
1014 private static final class Buffer {
1015 /**
1016 * Buffer used as temporary buffer when reading from the stream.
1017 */
1018 private final byte[] buf = new byte[ZipArchiveOutputStream.BUFFER_SIZE];
1019 /**
1020 * {@link #buf buf} may contain data the client hasnt read, yet,
1021 * this is the first byte that hasn't been read so far.
1022 */
1023 private int offsetInBuffer = 0;
1024 /**
1025 * Number of bytes read from the wrapped stream into {@link #buf
1026 * buf} with the last read operation.
1027 */
1028 private int lengthOfLastRead = 0;
1029 /**
1030 * Reset internal housekeeping.
1031 */
1032 private void reset() {
1033 offsetInBuffer = lengthOfLastRead = 0;
1034 }
1035 }
Torsten Curdtca165392008-07-10 10:17:44 +00001036}