1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.compress.archivers.zip;
20
21 import static org.apache.commons.compress.archivers.zip.ZipConstants.DWORD;
22 import static org.apache.commons.compress.archivers.zip.ZipConstants.SHORT;
23 import static org.apache.commons.compress.archivers.zip.ZipConstants.WORD;
24 import static org.apache.commons.compress.archivers.zip.ZipConstants.ZIP64_MAGIC;
25
26 import java.io.ByteArrayInputStream;
27 import java.io.ByteArrayOutputStream;
28 import java.io.EOFException;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.PushbackInputStream;
32 import java.math.BigInteger;
33 import java.nio.ByteBuffer;
34 import java.nio.charset.StandardCharsets;
35 import java.util.Arrays;
36 import java.util.Objects;
37 import java.util.function.Function;
38 import java.util.zip.CRC32;
39 import java.util.zip.DataFormatException;
40 import java.util.zip.Inflater;
41 import java.util.zip.ZipEntry;
42 import java.util.zip.ZipException;
43
44 import org.apache.commons.compress.archivers.ArchiveEntry;
45 import org.apache.commons.compress.archivers.ArchiveInputStream;
46 import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
47 import org.apache.commons.compress.compressors.deflate64.Deflate64CompressorInputStream;
48 import org.apache.commons.compress.utils.ArchiveUtils;
49 import org.apache.commons.compress.utils.IOUtils;
50 import org.apache.commons.compress.utils.InputStreamStatistics;
51 import org.apache.commons.io.input.BoundedInputStream;
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 public class ZipArchiveInputStream extends ArchiveInputStream<ZipArchiveEntry> implements InputStreamStatistics {
75
76
77
78
79 private final class BoundCountInputStream extends BoundedInputStream {
80
81
82
83
84
85
86
87
88
89 BoundCountInputStream(final InputStream in, final long max) {
90 super(in, max);
91 }
92
93 private boolean atMaxLength() {
94 return getMaxCount() >= 0 && getCount() >= getMaxCount();
95 }
96
97 @Override
98 public int read() throws IOException {
99 if (atMaxLength()) {
100 return -1;
101 }
102 final int result = super.read();
103 if (result != -1) {
104 readCount(1);
105 }
106 return result;
107 }
108
109 @Override
110 public int read(final byte[] b, final int off, final int len) throws IOException {
111 if (len == 0) {
112 return 0;
113 }
114 if (atMaxLength()) {
115 return -1;
116 }
117 final long maxRead = getMaxCount() >= 0 ? Math.min(len, getMaxCount() - getCount()) : len;
118 return readCount(super.read(b, off, (int) maxRead));
119 }
120
121 private int readCount(final int bytesRead) {
122 if (bytesRead != -1) {
123 count(bytesRead);
124 current.bytesReadFromStream += bytesRead;
125 }
126 return bytesRead;
127 }
128
129 }
130
131
132
133
134 private static final class CurrentEntry {
135
136
137
138
139 private final ZipArchiveEntry entry = new ZipArchiveEntry();
140
141
142
143
144 private boolean hasDataDescriptor;
145
146
147
148
149 private boolean usesZip64;
150
151
152
153
154 private long bytesRead;
155
156
157
158
159
160
161
162
163 private long bytesReadFromStream;
164
165
166
167
168 private final CRC32 crc = new CRC32();
169
170
171
172
173 private InputStream inputStream;
174
175 @SuppressWarnings("unchecked")
176 private <T extends InputStream> T checkInputStream() {
177 return (T) Objects.requireNonNull(inputStream, "inputStream");
178 }
179 }
180
181 public static final int PREAMBLE_GARBAGE_MAX_SIZE = 4096;
182
183 private static final int LFH_LEN = 30;
184
185
186
187
188
189 private static final int CFH_LEN = 46;
190
191
192
193
194
195
196 private static final long TWO_EXP_32 = ZIP64_MAGIC + 1;
197
198 private static final String USE_ZIPFILE_INSTEAD_OF_STREAM_DISCLAIMER = " while reading a stored entry using data descriptor. Either the archive is broken"
199 + " or it can not be read using ZipArchiveInputStream and you must use ZipFile."
200 + " A common cause for this is a ZIP archive containing a ZIP archive."
201 + " See https://commons.apache.org/proper/commons-compress/zip.html#ZipArchiveInputStream_vs_ZipFile";
202
203 private static final byte[] LFH = ZipLong.LFH_SIG.getBytes();
204
205 private static final byte[] CFH = ZipLong.CFH_SIG.getBytes();
206
207 private static final byte[] DD = ZipLong.DD_SIG.getBytes();
208
209 private static final byte[] APK_SIGNING_BLOCK_MAGIC = { 'A', 'P', 'K', ' ', 'S', 'i', 'g', ' ', 'B', 'l', 'o', 'c', 'k', ' ', '4', '2', };
210
211 private static final BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);
212
213 private static boolean checksig(final byte[] expected, final byte[] signature) {
214 for (int i = 0; i < expected.length; i++) {
215 if (signature[i] != expected[i]) {
216 return false;
217 }
218 }
219 return true;
220 }
221
222
223
224
225
226
227
228
229 public static boolean matches(final byte[] signature, final int length) {
230 if (length < ZipArchiveOutputStream.LFH_SIG.length) {
231 return false;
232 }
233
234 return checksig(ZipArchiveOutputStream.LFH_SIG, signature)
235 || checksig(ZipArchiveOutputStream.EOCD_SIG, signature)
236 || checksig(ZipArchiveOutputStream.DD_SIG, signature)
237 || checksig(ZipLong.SINGLE_SEGMENT_SPLIT_MARKER.getBytes(), signature);
238 }
239
240
241 private final ZipEncoding zipEncoding;
242
243
244 private final boolean useUnicodeExtraFields;
245
246
247 private final Inflater inf = new Inflater(true);
248
249
250 private final ByteBuffer buf = ByteBuffer.allocate(ZipArchiveOutputStream.BUFFER_SIZE);
251
252
253 private CurrentEntry current;
254
255
256 private boolean closed;
257
258
259 private boolean hitCentralDirectory;
260
261
262
263
264 private ByteArrayInputStream lastStoredEntry;
265
266
267
268
269
270
271
272
273
274
275 private final boolean allowStoredEntriesWithDataDescriptor;
276
277
278 private long uncompressedCount;
279
280
281 private final boolean skipSplitSig;
282
283
284 private final byte[] lfhBuf = new byte[LFH_LEN];
285
286 private final byte[] skipBuf = new byte[1024];
287
288 private final byte[] shortBuf = new byte[SHORT];
289
290 private final byte[] wordBuf = new byte[WORD];
291
292 private final byte[] twoDwordBuf = new byte[2 * DWORD];
293
294 private int entriesRead;
295
296
297
298
299
300
301
302
303
304
305
306 public ZipArchiveInputStream(final InputStream inputStream) {
307 this(inputStream, StandardCharsets.UTF_8.name());
308 }
309
310
311
312
313
314
315
316
317 public ZipArchiveInputStream(final InputStream inputStream, final String encoding) {
318 this(inputStream, encoding, true);
319 }
320
321
322
323
324
325
326
327
328 public ZipArchiveInputStream(final InputStream inputStream, final String encoding, final boolean useUnicodeExtraFields) {
329 this(inputStream, encoding, useUnicodeExtraFields, false);
330 }
331
332
333
334
335
336
337
338
339
340
341 public ZipArchiveInputStream(final InputStream inputStream, final String encoding, final boolean useUnicodeExtraFields,
342 final boolean allowStoredEntriesWithDataDescriptor) {
343 this(inputStream, encoding, useUnicodeExtraFields, allowStoredEntriesWithDataDescriptor, false);
344 }
345
346
347
348
349
350
351
352
353
354
355
356
357 public ZipArchiveInputStream(final InputStream inputStream, final String encoding, final boolean useUnicodeExtraFields,
358 final boolean allowStoredEntriesWithDataDescriptor, final boolean skipSplitSig) {
359 super(inputStream, encoding);
360 this.in = new PushbackInputStream(inputStream, buf.capacity());
361 this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
362 this.useUnicodeExtraFields = useUnicodeExtraFields;
363 this.allowStoredEntriesWithDataDescriptor = allowStoredEntriesWithDataDescriptor;
364 this.skipSplitSig = skipSplitSig;
365
366 buf.limit(0);
367 }
368
369
370
371
372
373
374
375
376 private boolean bufferContainsSignature(final ByteArrayOutputStream bos, final int offset, final int lastRead, final int expectedDDLen) throws IOException {
377
378 boolean done = false;
379 for (int i = 0; !done && i < offset + lastRead - 4; i++) {
380 if (buf.array()[i] == LFH[0] && buf.array()[i + 1] == LFH[1]) {
381 int expectDDPos = i;
382 if (i >= expectedDDLen && buf.array()[i + 2] == LFH[2] && buf.array()[i + 3] == LFH[3]
383 || buf.array()[i + 2] == CFH[2] && buf.array()[i + 3] == CFH[3]) {
384
385 expectDDPos = i - expectedDDLen;
386 done = true;
387 } else if (buf.array()[i + 2] == DD[2] && buf.array()[i + 3] == DD[3]) {
388
389 done = true;
390 }
391 if (done) {
392
393
394
395
396 pushback(buf.array(), expectDDPos, offset + lastRead - expectDDPos);
397 bos.write(buf.array(), 0, expectDDPos);
398 readDataDescriptor();
399 }
400 }
401 }
402 return done;
403 }
404
405
406
407
408
409
410
411
412 private int cacheBytesRead(final ByteArrayOutputStream bos, int offset, final int lastRead, final int expectedDDLen) {
413 final int cacheable = offset + lastRead - expectedDDLen - 3;
414 if (cacheable > 0) {
415 bos.write(buf.array(), 0, cacheable);
416 System.arraycopy(buf.array(), cacheable, buf.array(), 0, expectedDDLen + 3);
417 offset = expectedDDLen + 3;
418 } else {
419 offset += lastRead;
420 }
421 return offset;
422 }
423
424
425
426
427
428
429
430
431
432 @Override
433 public boolean canReadEntryData(final ArchiveEntry ae) {
434 if (ae instanceof ZipArchiveEntry) {
435 final ZipArchiveEntry ze = (ZipArchiveEntry) ae;
436 return ZipUtil.canHandleEntryData(ze) && supportsDataDescriptorFor(ze) && supportsCompressedSizeFor(ze);
437 }
438 return false;
439 }
440
441 @Override
442 public void close() throws IOException {
443 if (!closed) {
444 closed = true;
445 try {
446 in.close();
447 } finally {
448 inf.end();
449 }
450 }
451 }
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467 private void closeEntry() throws IOException {
468 if (closed) {
469 throw new IOException("The stream is closed");
470 }
471 if (current == null) {
472 return;
473 }
474
475
476 if (currentEntryHasOutstandingBytes()) {
477 drainCurrentEntryData();
478 } else {
479
480 if (skip(Long.MAX_VALUE) < 0) {
481 throw new IllegalStateException("Can't read the remainder of the stream");
482 }
483
484 final long inB = current.entry.getMethod() == ZipArchiveOutputStream.DEFLATED ? getBytesInflated() : current.bytesRead;
485
486
487
488 final int diff = (int) (current.bytesReadFromStream - inB);
489
490
491 if (diff > 0) {
492 pushback(buf.array(), buf.limit() - diff, diff);
493 current.bytesReadFromStream -= diff;
494 }
495
496
497 if (currentEntryHasOutstandingBytes()) {
498 drainCurrentEntryData();
499 }
500 }
501
502 if (lastStoredEntry == null && current.hasDataDescriptor) {
503 readDataDescriptor();
504 }
505
506 inf.reset();
507 buf.clear().flip();
508 current = null;
509 lastStoredEntry = null;
510 }
511
512
513
514
515
516
517
518 private boolean currentEntryHasOutstandingBytes() {
519 return current.bytesReadFromStream <= current.entry.getCompressedSize() && !current.hasDataDescriptor;
520 }
521
522
523
524
525 private void drainCurrentEntryData() throws IOException {
526 long remaining = current.entry.getCompressedSize() - current.bytesReadFromStream;
527 while (remaining > 0) {
528 final long n = in.read(buf.array(), 0, (int) Math.min(buf.capacity(), remaining));
529 if (n < 0) {
530 throw new EOFException("Truncated ZIP entry: " + ArchiveUtils.sanitize(current.entry.getName()));
531 }
532 count(n);
533 remaining -= n;
534 }
535 }
536
537 private int fill() throws IOException {
538 if (closed) {
539 throw new IOException("The stream is closed");
540 }
541 final int length = in.read(buf.array());
542 if (length > 0) {
543 buf.limit(length);
544 count(buf.limit());
545 inf.setInput(buf.array(), 0, buf.limit());
546 }
547 return length;
548 }
549
550
551
552
553 private boolean findEocdRecord() throws IOException {
554 int currentByte = -1;
555 boolean skipReadCall = false;
556 while (skipReadCall || (currentByte = readOneByte()) > -1) {
557 skipReadCall = false;
558 if (!isFirstByteOfEocdSig(currentByte)) {
559 continue;
560 }
561 currentByte = readOneByte();
562 if (currentByte != ZipArchiveOutputStream.EOCD_SIG[1]) {
563 if (currentByte == -1) {
564 break;
565 }
566 skipReadCall = isFirstByteOfEocdSig(currentByte);
567 continue;
568 }
569 currentByte = readOneByte();
570 if (currentByte != ZipArchiveOutputStream.EOCD_SIG[2]) {
571 if (currentByte == -1) {
572 break;
573 }
574 skipReadCall = isFirstByteOfEocdSig(currentByte);
575 continue;
576 }
577 currentByte = readOneByte();
578 if (currentByte == -1) {
579 break;
580 }
581 if (currentByte == ZipArchiveOutputStream.EOCD_SIG[3]) {
582 return true;
583 }
584 skipReadCall = isFirstByteOfEocdSig(currentByte);
585 }
586 return false;
587 }
588
589
590
591
592
593
594
595
596
597
598
599
600 private long getBytesInflated() {
601 long inB = inf.getBytesRead();
602 if (current.bytesReadFromStream >= TWO_EXP_32) {
603 while (inB + TWO_EXP_32 <= current.bytesReadFromStream) {
604 inB += TWO_EXP_32;
605 }
606 }
607 return inB;
608 }
609
610
611
612
613 @SuppressWarnings("resource")
614 @Override
615 public long getCompressedCount() {
616 final int method = current.entry.getMethod();
617 if (method == ZipArchiveOutputStream.STORED) {
618 return current.bytesRead;
619 }
620 if (method == ZipArchiveOutputStream.DEFLATED) {
621 return getBytesInflated();
622 }
623 if (method == ZipMethod.UNSHRINKING.getCode() || method == ZipMethod.IMPLODING.getCode() || method == ZipMethod.ENHANCED_DEFLATED.getCode()
624 || method == ZipMethod.BZIP2.getCode()) {
625 return ((InputStreamStatistics) current.checkInputStream()).getCompressedCount();
626 }
627 return -1;
628 }
629
630 @Override
631 public ZipArchiveEntry getNextEntry() throws IOException {
632 return getNextZipEntry();
633 }
634
635
636
637
638
639
640
641
642 @Deprecated
643 public ZipArchiveEntry getNextZipEntry() throws IOException {
644 uncompressedCount = 0;
645
646 boolean firstEntry = true;
647 if (closed || hitCentralDirectory) {
648 return null;
649 }
650 if (current != null) {
651 closeEntry();
652 firstEntry = false;
653 }
654
655 final long currentHeaderOffset = getBytesRead();
656 try {
657 if (firstEntry) {
658
659
660
661
662 if (!readFirstLocalFileHeader()) {
663 hitCentralDirectory = true;
664 skipRemainderOfArchive();
665 return null;
666 }
667 } else {
668 readFully(lfhBuf);
669 }
670 } catch (final EOFException e) {
671 return null;
672 }
673
674 final ZipLong sig = new ZipLong(lfhBuf);
675 if (!sig.equals(ZipLong.LFH_SIG)) {
676 if (sig.equals(ZipLong.CFH_SIG) || sig.equals(ZipLong.AED_SIG) || isApkSigningBlock(lfhBuf)) {
677 hitCentralDirectory = true;
678 skipRemainderOfArchive();
679 return null;
680 }
681 throw new ZipException(String.format("Unexpected record signature: 0x%x", sig.getValue()));
682 }
683
684 int off = WORD;
685 current = new CurrentEntry();
686
687 final int versionMadeBy = ZipShort.getValue(lfhBuf, off);
688 off += SHORT;
689 current.entry.setPlatform(versionMadeBy >> ZipFile.BYTE_SHIFT & ZipFile.NIBLET_MASK);
690
691 final GeneralPurposeBit gpFlag = GeneralPurposeBit.parse(lfhBuf, off);
692 final boolean hasUTF8Flag = gpFlag.usesUTF8ForNames();
693 final ZipEncoding entryEncoding = hasUTF8Flag ? ZipEncodingHelper.ZIP_ENCODING_UTF_8 : zipEncoding;
694 current.hasDataDescriptor = gpFlag.usesDataDescriptor();
695 current.entry.setGeneralPurposeBit(gpFlag);
696
697 off += SHORT;
698
699 current.entry.setMethod(ZipShort.getValue(lfhBuf, off));
700 off += SHORT;
701
702 final long time = ZipUtil.dosToJavaTime(ZipLong.getValue(lfhBuf, off));
703 current.entry.setTime(time);
704 off += WORD;
705
706 ZipLong size = null, cSize = null;
707 if (!current.hasDataDescriptor) {
708 current.entry.setCrc(ZipLong.getValue(lfhBuf, off));
709 off += WORD;
710
711 cSize = new ZipLong(lfhBuf, off);
712 off += WORD;
713
714 size = new ZipLong(lfhBuf, off);
715 off += WORD;
716 } else {
717 off += 3 * WORD;
718 }
719
720 final int fileNameLen = ZipShort.getValue(lfhBuf, off);
721
722 off += SHORT;
723
724 final int extraLen = ZipShort.getValue(lfhBuf, off);
725 off += SHORT;
726
727 final byte[] fileName = readRange(fileNameLen);
728 current.entry.setName(entryEncoding.decode(fileName), fileName);
729 if (hasUTF8Flag) {
730 current.entry.setNameSource(ZipArchiveEntry.NameSource.NAME_WITH_EFS_FLAG);
731 }
732
733 final byte[] extraData = readRange(extraLen);
734 try {
735 current.entry.setExtra(extraData);
736 } catch (final RuntimeException ex) {
737 final ZipException z = new ZipException("Invalid extra data in entry " + current.entry.getName());
738 z.initCause(ex);
739 throw z;
740 }
741
742 if (!hasUTF8Flag && useUnicodeExtraFields) {
743 ZipUtil.setNameAndCommentFromExtraFields(current.entry, fileName, null);
744 }
745
746 processZip64Extra(size, cSize);
747
748 current.entry.setLocalHeaderOffset(currentHeaderOffset);
749 current.entry.setDataOffset(getBytesRead());
750 current.entry.setStreamContiguous(true);
751
752 final ZipMethod m = ZipMethod.getMethodByCode(current.entry.getMethod());
753 if (current.entry.getCompressedSize() != ArchiveEntry.SIZE_UNKNOWN) {
754 if (ZipUtil.canHandleEntryData(current.entry) && m != ZipMethod.STORED && m != ZipMethod.DEFLATED) {
755 final InputStream bis = new BoundCountInputStream(in, current.entry.getCompressedSize());
756 switch (m) {
757 case UNSHRINKING:
758 current.inputStream = new UnshrinkingInputStream(bis);
759 break;
760 case IMPLODING:
761 try {
762 current.inputStream = new ExplodingInputStream(current.entry.getGeneralPurposeBit().getSlidingDictionarySize(),
763 current.entry.getGeneralPurposeBit().getNumberOfShannonFanoTrees(), bis);
764 } catch (final IllegalArgumentException ex) {
765 throw new IOException("bad IMPLODE data", ex);
766 }
767 break;
768 case BZIP2:
769 current.inputStream = new BZip2CompressorInputStream(bis);
770 break;
771 case ENHANCED_DEFLATED:
772 current.inputStream = new Deflate64CompressorInputStream(bis);
773 break;
774 default:
775
776
777
778 break;
779 }
780 }
781 } else if (m == ZipMethod.ENHANCED_DEFLATED) {
782 current.inputStream = new Deflate64CompressorInputStream(in);
783 }
784
785 entriesRead++;
786 return current.entry;
787 }
788
789
790
791
792
793
794 @Override
795 public long getUncompressedCount() {
796 return uncompressedCount;
797 }
798
799
800
801
802
803
804
805
806
807
808
809
810 private boolean isApkSigningBlock(final byte[] suspectLocalFileHeader) throws IOException {
811
812 final BigInteger len = ZipEightByteInteger.getValue(suspectLocalFileHeader);
813
814
815 BigInteger toSkip = len.add(BigInteger.valueOf(DWORD - suspectLocalFileHeader.length - (long) APK_SIGNING_BLOCK_MAGIC.length));
816 final byte[] magic = new byte[APK_SIGNING_BLOCK_MAGIC.length];
817
818 try {
819 if (toSkip.signum() < 0) {
820
821 final int off = suspectLocalFileHeader.length + toSkip.intValue();
822
823 if (off < DWORD) {
824 return false;
825 }
826 final int bytesInBuffer = Math.abs(toSkip.intValue());
827 System.arraycopy(suspectLocalFileHeader, off, magic, 0, Math.min(bytesInBuffer, magic.length));
828 if (bytesInBuffer < magic.length) {
829 readFully(magic, bytesInBuffer);
830 }
831 } else {
832 while (toSkip.compareTo(LONG_MAX) > 0) {
833 realSkip(Long.MAX_VALUE);
834 toSkip = toSkip.add(LONG_MAX.negate());
835 }
836 realSkip(toSkip.longValue());
837 readFully(magic);
838 }
839 } catch (final EOFException ex) {
840
841 return false;
842 }
843 return Arrays.equals(magic, APK_SIGNING_BLOCK_MAGIC);
844 }
845
846 private boolean isFirstByteOfEocdSig(final int b) {
847 return b == ZipArchiveOutputStream.EOCD_SIG[0];
848 }
849
850
851
852
853 private void processZip64Extra(final ZipLong size, final ZipLong cSize) throws ZipException {
854 final ZipExtraField extra = current.entry.getExtraField(Zip64ExtendedInformationExtraField.HEADER_ID);
855 if (extra != null && !(extra instanceof Zip64ExtendedInformationExtraField)) {
856 throw new ZipException("archive contains unparseable zip64 extra field");
857 }
858 final Zip64ExtendedInformationExtraField z64 = (Zip64ExtendedInformationExtraField) extra;
859 current.usesZip64 = z64 != null;
860 if (!current.hasDataDescriptor) {
861 if (z64 != null
862 && (ZipLong.ZIP64_MAGIC.equals(cSize) || ZipLong.ZIP64_MAGIC.equals(size))) {
863 if (z64.getCompressedSize() == null || z64.getSize() == null) {
864
865 throw new ZipException("archive contains corrupted zip64 extra field");
866 }
867 long s = z64.getCompressedSize().getLongValue();
868 if (s < 0) {
869 throw new ZipException("broken archive, entry with negative compressed size");
870 }
871 current.entry.setCompressedSize(s);
872 s = z64.getSize().getLongValue();
873 if (s < 0) {
874 throw new ZipException("broken archive, entry with negative size");
875 }
876 current.entry.setSize(s);
877 } else if (cSize != null && size != null) {
878 if (cSize.getValue() < 0) {
879 throw new ZipException("broken archive, entry with negative compressed size");
880 }
881 current.entry.setCompressedSize(cSize.getValue());
882 if (size.getValue() < 0) {
883 throw new ZipException("broken archive, entry with negative size");
884 }
885 current.entry.setSize(size.getValue());
886 }
887 }
888 }
889
890 private void pushback(final byte[] buf, final int offset, final int length) throws IOException {
891 if (offset < 0) {
892
893 throw new IOException(String.format("Negative offset %,d into buffer", offset));
894 }
895 ((PushbackInputStream) in).unread(buf, offset, length);
896 pushedBackBytes(length);
897 }
898
899 @Override
900 public int read(final byte[] buffer, final int offset, final int length) throws IOException {
901 if (length == 0) {
902 return 0;
903 }
904 if (closed) {
905 throw new IOException("The stream is closed");
906 }
907
908 if (current == null) {
909 return -1;
910 }
911
912
913 if (offset > buffer.length || length < 0 || offset < 0 || buffer.length - offset < length) {
914 throw new ArrayIndexOutOfBoundsException();
915 }
916
917 ZipUtil.checkRequestedFeatures(current.entry);
918 if (!supportsDataDescriptorFor(current.entry)) {
919 throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.DATA_DESCRIPTOR, current.entry);
920 }
921 if (!supportsCompressedSizeFor(current.entry)) {
922 throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.UNKNOWN_COMPRESSED_SIZE, current.entry);
923 }
924
925 final int read;
926 if (current.entry.getMethod() == ZipArchiveOutputStream.STORED) {
927 read = readStored(buffer, offset, length);
928 } else if (current.entry.getMethod() == ZipArchiveOutputStream.DEFLATED) {
929 read = readDeflated(buffer, offset, length);
930 } else if (current.entry.getMethod() == ZipMethod.UNSHRINKING.getCode() || current.entry.getMethod() == ZipMethod.IMPLODING.getCode()
931 || current.entry.getMethod() == ZipMethod.ENHANCED_DEFLATED.getCode() || current.entry.getMethod() == ZipMethod.BZIP2.getCode()) {
932 read = current.inputStream.read(buffer, offset, length);
933 } else {
934 throw new UnsupportedZipFeatureException(ZipMethod.getMethodByCode(current.entry.getMethod()), current.entry);
935 }
936
937 if (read >= 0) {
938 current.crc.update(buffer, offset, read);
939 uncompressedCount += read;
940 }
941
942 return read;
943 }
944
945 private void readDataDescriptor() throws IOException {
946 readFully(wordBuf);
947 ZipLong val = new ZipLong(wordBuf);
948 if (ZipLong.DD_SIG.equals(val)) {
949
950 readFully(wordBuf);
951 val = new ZipLong(wordBuf);
952 }
953 current.entry.setCrc(val.getValue());
954
955
956
957
958
959
960
961
962
963
964
965
966 readFully(twoDwordBuf);
967 final ZipLong potentialSig = new ZipLong(twoDwordBuf, DWORD);
968 if (potentialSig.equals(ZipLong.CFH_SIG) || potentialSig.equals(ZipLong.LFH_SIG)) {
969 pushback(twoDwordBuf, DWORD, DWORD);
970 long size = ZipLong.getValue(twoDwordBuf);
971 if (size < 0) {
972 throw new ZipException("broken archive, entry with negative compressed size");
973 }
974 current.entry.setCompressedSize(size);
975 size = ZipLong.getValue(twoDwordBuf, WORD);
976 if (size < 0) {
977 throw new ZipException("broken archive, entry with negative size");
978 }
979 current.entry.setSize(size);
980 } else {
981 long size = ZipEightByteInteger.getLongValue(twoDwordBuf);
982 if (size < 0) {
983 throw new ZipException("broken archive, entry with negative compressed size");
984 }
985 current.entry.setCompressedSize(size);
986 size = ZipEightByteInteger.getLongValue(twoDwordBuf, DWORD);
987 if (size < 0) {
988 throw new ZipException("broken archive, entry with negative size");
989 }
990 current.entry.setSize(size);
991 }
992 }
993
994
995
996
997 private int readDeflated(final byte[] buffer, final int offset, final int length) throws IOException {
998 final int read = readFromInflater(buffer, offset, length);
999 if (read <= 0) {
1000 if (inf.finished()) {
1001 return -1;
1002 }
1003 if (inf.needsDictionary()) {
1004 throw new ZipException("This archive needs a preset dictionary" + " which is not supported by Commons" + " Compress.");
1005 }
1006 if (read == -1) {
1007 throw new IOException("Truncated ZIP file");
1008 }
1009 }
1010 return read;
1011 }
1012
1013
1014
1015
1016 private boolean readFirstLocalFileHeader() throws IOException {
1017
1018 final byte[] header = new byte[Math.min(LFH_LEN, ZipFile.MIN_EOCD_SIZE)];
1019 readFully(header);
1020 try {
1021 READ_LOOP: for (int i = 0; ; ) {
1022 for (int j = 0; i <= PREAMBLE_GARBAGE_MAX_SIZE - 4 && j <= header.length - 4; ++j, ++i) {
1023 final ZipLong sig = new ZipLong(header, j);
1024 if (sig.equals(ZipLong.LFH_SIG) ||
1025 sig.equals(ZipLong.SINGLE_SEGMENT_SPLIT_MARKER) ||
1026 sig.equals(ZipLong.DD_SIG)) {
1027
1028 System.arraycopy(header, j, header, 0, header.length - j);
1029 readFully(header, header.length - j);
1030 break READ_LOOP;
1031 }
1032 if (sig.equals(new ZipLong(ZipArchiveOutputStream.EOCD_SIG))) {
1033
1034 pushback(header, j, header.length - j);
1035 return false;
1036 }
1037 }
1038 if (i >= PREAMBLE_GARBAGE_MAX_SIZE - 4) {
1039 throw new ZipException("Cannot find zip signature within the first " + PREAMBLE_GARBAGE_MAX_SIZE + " bytes");
1040 }
1041 System.arraycopy(header, header.length - 3, header, 0, 3);
1042 readFully(header, 3);
1043 }
1044 System.arraycopy(header, 0, lfhBuf, 0, header.length);
1045 readFully(lfhBuf, header.length);
1046 } catch (final EOFException ex) {
1047 throw new ZipException("Cannot find zip signature within the file");
1048 }
1049 final ZipLong sig = new ZipLong(lfhBuf);
1050
1051 if (!skipSplitSig && sig.equals(ZipLong.DD_SIG)) {
1052 throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.SPLITTING);
1053 }
1054
1055
1056 if (sig.equals(ZipLong.SINGLE_SEGMENT_SPLIT_MARKER) || sig.equals(ZipLong.DD_SIG)) {
1057
1058 System.arraycopy(lfhBuf, 4, lfhBuf, 0, lfhBuf.length - 4);
1059 readFully(lfhBuf, lfhBuf.length - 4);
1060 }
1061 return true;
1062 }
1063
1064
1065
1066
1067 private int readFromInflater(final byte[] buffer, final int offset, final int length) throws IOException {
1068 int read = 0;
1069 do {
1070 if (inf.needsInput()) {
1071 final int l = fill();
1072 if (l > 0) {
1073 current.bytesReadFromStream += buf.limit();
1074 } else if (l == -1) {
1075 return -1;
1076 } else {
1077 break;
1078 }
1079 }
1080 try {
1081 read = inf.inflate(buffer, offset, length);
1082 } catch (final DataFormatException e) {
1083 throw (IOException) new ZipException(e.getMessage()).initCause(e);
1084 }
1085 } while (read == 0 && inf.needsInput());
1086 return read;
1087 }
1088
1089 private void readFully(final byte[] b) throws IOException {
1090 readFully(b, 0);
1091 }
1092
1093 private void readFully(final byte[] b, final int off) throws IOException {
1094 final int len = b.length - off;
1095 final int count = IOUtils.readFully(in, b, off, len);
1096 count(count);
1097 if (count < len) {
1098 throw new EOFException();
1099 }
1100 }
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124 private int readOneByte() throws IOException {
1125 final int b = in.read();
1126 if (b != -1) {
1127 count(1);
1128 }
1129 return b;
1130 }
1131
1132 private byte[] readRange(final int len) throws IOException {
1133 final byte[] ret = IOUtils.readRange(in, len);
1134 count(ret.length);
1135 if (ret.length < len) {
1136 throw new EOFException();
1137 }
1138 return ret;
1139 }
1140
1141
1142
1143
1144 private int readStored(final byte[] buffer, final int offset, final int length) throws IOException {
1145
1146 if (current.hasDataDescriptor) {
1147 if (lastStoredEntry == null) {
1148 readStoredEntry();
1149 }
1150 return lastStoredEntry.read(buffer, offset, length);
1151 }
1152
1153 final long csize = current.entry.getSize();
1154 if (current.bytesRead >= csize) {
1155 return -1;
1156 }
1157
1158 if (buf.position() >= buf.limit()) {
1159 buf.position(0);
1160 final int l = in.read(buf.array());
1161 if (l == -1) {
1162 buf.limit(0);
1163 throw new IOException("Truncated ZIP file");
1164 }
1165 buf.limit(l);
1166
1167 count(l);
1168 current.bytesReadFromStream += l;
1169 }
1170
1171 int toRead = Math.min(buf.remaining(), length);
1172 if (csize - current.bytesRead < toRead) {
1173
1174 toRead = (int) (csize - current.bytesRead);
1175 }
1176 buf.get(buffer, offset, toRead);
1177 current.bytesRead += toRead;
1178 return toRead;
1179 }
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195 private void readStoredEntry() throws IOException {
1196 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
1197 int off = 0;
1198 boolean done = false;
1199
1200
1201 final int ddLen = current.usesZip64 ? WORD + 2 * DWORD : 3 * WORD;
1202
1203 while (!done) {
1204 final int r = in.read(buf.array(), off, ZipArchiveOutputStream.BUFFER_SIZE - off);
1205 if (r <= 0) {
1206
1207
1208 throw new IOException("Truncated ZIP file");
1209 }
1210 if (r + off < 4) {
1211
1212 off += r;
1213 continue;
1214 }
1215
1216 done = bufferContainsSignature(bos, off, r, ddLen);
1217 if (!done) {
1218 off = cacheBytesRead(bos, off, r, ddLen);
1219 }
1220 }
1221 if (current.entry.getCompressedSize() != current.entry.getSize()) {
1222 throw new ZipException("compressed and uncompressed size don't match" + USE_ZIPFILE_INSTEAD_OF_STREAM_DISCLAIMER);
1223 }
1224 final byte[] b = bos.toByteArray();
1225 if (b.length != current.entry.getSize()) {
1226 throw new ZipException("actual and claimed size don't match" + USE_ZIPFILE_INSTEAD_OF_STREAM_DISCLAIMER);
1227 }
1228 lastStoredEntry = new ByteArrayInputStream(b);
1229 }
1230
1231
1232
1233
1234
1235
1236 private void realSkip(final long value) throws IOException {
1237 if (value >= 0) {
1238 long skipped = 0;
1239 while (skipped < value) {
1240 final long rem = value - skipped;
1241 final int x = in.read(skipBuf, 0, (int) (skipBuf.length > rem ? rem : skipBuf.length));
1242 if (x == -1) {
1243 return;
1244 }
1245 count(x);
1246 skipped += x;
1247 }
1248 return;
1249 }
1250 throw new IllegalArgumentException();
1251 }
1252
1253
1254
1255
1256
1257
1258
1259
1260 public ZipArchiveInputStream setExtraFieldSupport(final Function<ZipShort, ZipExtraField> extraFieldSupport) {
1261
1262 return this;
1263 }
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279 @Override
1280 public long skip(final long value) throws IOException {
1281 if (value >= 0) {
1282 long skipped = 0;
1283 while (skipped < value) {
1284 final long rem = value - skipped;
1285 final int x = read(skipBuf, 0, (int) (skipBuf.length > rem ? rem : skipBuf.length));
1286 if (x == -1) {
1287 return skipped;
1288 }
1289 skipped += x;
1290 }
1291 return skipped;
1292 }
1293 throw new IllegalArgumentException("Negative skip value");
1294 }
1295
1296
1297
1298
1299 private void skipRemainderOfArchive() throws IOException {
1300
1301
1302
1303 if (entriesRead > 0) {
1304 realSkip((long) entriesRead * CFH_LEN - LFH_LEN);
1305 }
1306 final boolean foundEocd = findEocdRecord();
1307 if (foundEocd) {
1308 realSkip((long) ZipFile.MIN_EOCD_SIZE - WORD - SHORT );
1309 readFully(shortBuf);
1310
1311 final int commentLen = ZipShort.getValue(shortBuf);
1312 if (commentLen >= 0) {
1313 realSkip(commentLen);
1314 return;
1315 }
1316 }
1317 throw new IOException("Truncated ZIP file");
1318 }
1319
1320
1321
1322
1323 private boolean supportsCompressedSizeFor(final ZipArchiveEntry entry) {
1324 return entry.getCompressedSize() != ArchiveEntry.SIZE_UNKNOWN || entry.getMethod() == ZipEntry.DEFLATED
1325 || entry.getMethod() == ZipMethod.ENHANCED_DEFLATED.getCode()
1326 || entry.getGeneralPurposeBit().usesDataDescriptor() && allowStoredEntriesWithDataDescriptor && entry.getMethod() == ZipEntry.STORED;
1327 }
1328
1329
1330
1331
1332
1333
1334
1335 private boolean supportsDataDescriptorFor(final ZipArchiveEntry entry) {
1336 return !entry.getGeneralPurposeBit().usesDataDescriptor() || allowStoredEntriesWithDataDescriptor && entry.getMethod() == ZipEntry.STORED
1337 || entry.getMethod() == ZipEntry.DEFLATED || entry.getMethod() == ZipMethod.ENHANCED_DEFLATED.getCode();
1338 }
1339 }