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 getMaxLength() >= 0 && getCount() >= getMaxLength();
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 = getMaxLength() >= 0 ? Math.min(len, getMaxLength() - 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 private Function<ZipShort, ZipExtraField> extraFieldSupport;
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 skip(Long.MAX_VALUE);
481
482 final long inB = current.entry.getMethod() == ZipArchiveOutputStream.DEFLATED ? getBytesInflated() : current.bytesRead;
483
484
485
486 final int diff = (int) (current.bytesReadFromStream - inB);
487
488
489 if (diff > 0) {
490 pushback(buf.array(), buf.limit() - diff, diff);
491 current.bytesReadFromStream -= diff;
492 }
493
494
495 if (currentEntryHasOutstandingBytes()) {
496 drainCurrentEntryData();
497 }
498 }
499
500 if (lastStoredEntry == null && current.hasDataDescriptor) {
501 readDataDescriptor();
502 }
503
504 inf.reset();
505 buf.clear().flip();
506 current = null;
507 lastStoredEntry = null;
508 }
509
510
511
512
513
514
515
516 private boolean currentEntryHasOutstandingBytes() {
517 return current.bytesReadFromStream <= current.entry.getCompressedSize() && !current.hasDataDescriptor;
518 }
519
520
521
522
523 private void drainCurrentEntryData() throws IOException {
524 long remaining = current.entry.getCompressedSize() - current.bytesReadFromStream;
525 while (remaining > 0) {
526 final long n = in.read(buf.array(), 0, (int) Math.min(buf.capacity(), remaining));
527 if (n < 0) {
528 throw new EOFException("Truncated ZIP entry: " + ArchiveUtils.sanitize(current.entry.getName()));
529 }
530 count(n);
531 remaining -= n;
532 }
533 }
534
535 private int fill() throws IOException {
536 if (closed) {
537 throw new IOException("The stream is closed");
538 }
539 final int length = in.read(buf.array());
540 if (length > 0) {
541 buf.limit(length);
542 count(buf.limit());
543 inf.setInput(buf.array(), 0, buf.limit());
544 }
545 return length;
546 }
547
548
549
550
551 private boolean findEocdRecord() throws IOException {
552 int currentByte = -1;
553 boolean skipReadCall = false;
554 while (skipReadCall || (currentByte = readOneByte()) > -1) {
555 skipReadCall = false;
556 if (!isFirstByteOfEocdSig(currentByte)) {
557 continue;
558 }
559 currentByte = readOneByte();
560 if (currentByte != ZipArchiveOutputStream.EOCD_SIG[1]) {
561 if (currentByte == -1) {
562 break;
563 }
564 skipReadCall = isFirstByteOfEocdSig(currentByte);
565 continue;
566 }
567 currentByte = readOneByte();
568 if (currentByte != ZipArchiveOutputStream.EOCD_SIG[2]) {
569 if (currentByte == -1) {
570 break;
571 }
572 skipReadCall = isFirstByteOfEocdSig(currentByte);
573 continue;
574 }
575 currentByte = readOneByte();
576 if (currentByte == -1) {
577 break;
578 }
579 if (currentByte == ZipArchiveOutputStream.EOCD_SIG[3]) {
580 return true;
581 }
582 skipReadCall = isFirstByteOfEocdSig(currentByte);
583 }
584 return false;
585 }
586
587
588
589
590
591
592
593
594
595
596
597
598 private long getBytesInflated() {
599 long inB = inf.getBytesRead();
600 if (current.bytesReadFromStream >= TWO_EXP_32) {
601 while (inB + TWO_EXP_32 <= current.bytesReadFromStream) {
602 inB += TWO_EXP_32;
603 }
604 }
605 return inB;
606 }
607
608
609
610
611 @SuppressWarnings("resource")
612 @Override
613 public long getCompressedCount() {
614 final int method = current.entry.getMethod();
615 if (method == ZipArchiveOutputStream.STORED) {
616 return current.bytesRead;
617 }
618 if (method == ZipArchiveOutputStream.DEFLATED) {
619 return getBytesInflated();
620 }
621 if (method == ZipMethod.UNSHRINKING.getCode() || method == ZipMethod.IMPLODING.getCode() || method == ZipMethod.ENHANCED_DEFLATED.getCode()
622 || method == ZipMethod.BZIP2.getCode()) {
623 return ((InputStreamStatistics) current.checkInputStream()).getCompressedCount();
624 }
625 return -1;
626 }
627
628 @Override
629 public ZipArchiveEntry getNextEntry() throws IOException {
630 return getNextZipEntry();
631 }
632
633
634
635
636
637
638
639
640 @Deprecated
641 public ZipArchiveEntry getNextZipEntry() throws IOException {
642 uncompressedCount = 0;
643
644 boolean firstEntry = true;
645 if (closed || hitCentralDirectory) {
646 return null;
647 }
648 if (current != null) {
649 closeEntry();
650 firstEntry = false;
651 }
652
653 final long currentHeaderOffset = getBytesRead();
654 try {
655 if (firstEntry) {
656
657
658
659
660 if (!readFirstLocalFileHeader()) {
661 hitCentralDirectory = true;
662 skipRemainderOfArchive(true);
663 return null;
664 }
665 } else {
666 readFully(lfhBuf);
667 }
668 } catch (final EOFException e) {
669 return null;
670 }
671
672 final ZipLong sig = new ZipLong(lfhBuf);
673 if (!sig.equals(ZipLong.LFH_SIG)) {
674 if (sig.equals(ZipLong.CFH_SIG) || sig.equals(ZipLong.AED_SIG) || isApkSigningBlock(lfhBuf)) {
675 hitCentralDirectory = true;
676 skipRemainderOfArchive(false);
677 return null;
678 }
679 throw new ZipException(String.format("Unexpected record signature: 0x%x", sig.getValue()));
680 }
681
682 int off = WORD;
683 current = new CurrentEntry();
684
685 final int versionMadeBy = ZipShort.getValue(lfhBuf, off);
686 off += SHORT;
687 current.entry.setPlatform(versionMadeBy >> ZipFile.BYTE_SHIFT & ZipFile.NIBLET_MASK);
688
689 final GeneralPurposeBit gpFlag = GeneralPurposeBit.parse(lfhBuf, off);
690 final boolean hasUTF8Flag = gpFlag.usesUTF8ForNames();
691 final ZipEncoding entryEncoding = hasUTF8Flag ? ZipEncodingHelper.ZIP_ENCODING_UTF_8 : zipEncoding;
692 current.hasDataDescriptor = gpFlag.usesDataDescriptor();
693 current.entry.setGeneralPurposeBit(gpFlag);
694
695 off += SHORT;
696
697 current.entry.setMethod(ZipShort.getValue(lfhBuf, off));
698 off += SHORT;
699
700 final long time = ZipUtil.dosToJavaTime(ZipLong.getValue(lfhBuf, off));
701 current.entry.setTime(time);
702 off += WORD;
703
704 ZipLong size = null, cSize = null;
705 if (!current.hasDataDescriptor) {
706 current.entry.setCrc(ZipLong.getValue(lfhBuf, off));
707 off += WORD;
708
709 cSize = new ZipLong(lfhBuf, off);
710 off += WORD;
711
712 size = new ZipLong(lfhBuf, off);
713 off += WORD;
714 } else {
715 off += 3 * WORD;
716 }
717
718 final int fileNameLen = ZipShort.getValue(lfhBuf, off);
719
720 off += SHORT;
721
722 final int extraLen = ZipShort.getValue(lfhBuf, off);
723 off += SHORT;
724
725 final byte[] fileName = readRange(fileNameLen);
726 current.entry.setName(entryEncoding.decode(fileName), fileName);
727 if (hasUTF8Flag) {
728 current.entry.setNameSource(ZipArchiveEntry.NameSource.NAME_WITH_EFS_FLAG);
729 }
730
731 final byte[] extraData = readRange(extraLen);
732 try {
733 current.entry.setExtra(extraData);
734 } catch (final RuntimeException ex) {
735 final ZipException z = new ZipException("Invalid extra data in entry " + current.entry.getName());
736 z.initCause(ex);
737 throw z;
738 }
739
740 if (!hasUTF8Flag && useUnicodeExtraFields) {
741 ZipUtil.setNameAndCommentFromExtraFields(current.entry, fileName, null);
742 }
743
744 processZip64Extra(size, cSize);
745
746 current.entry.setLocalHeaderOffset(currentHeaderOffset);
747 current.entry.setDataOffset(getBytesRead());
748 current.entry.setStreamContiguous(true);
749
750 final ZipMethod m = ZipMethod.getMethodByCode(current.entry.getMethod());
751 if (current.entry.getCompressedSize() != ArchiveEntry.SIZE_UNKNOWN) {
752 if (ZipUtil.canHandleEntryData(current.entry) && m != ZipMethod.STORED && m != ZipMethod.DEFLATED) {
753 final InputStream bis = new BoundCountInputStream(in, current.entry.getCompressedSize());
754 switch (m) {
755 case UNSHRINKING:
756 current.inputStream = new UnshrinkingInputStream(bis);
757 break;
758 case IMPLODING:
759 try {
760 current.inputStream = new ExplodingInputStream(current.entry.getGeneralPurposeBit().getSlidingDictionarySize(),
761 current.entry.getGeneralPurposeBit().getNumberOfShannonFanoTrees(), bis);
762 } catch (final IllegalArgumentException ex) {
763 throw new IOException("bad IMPLODE data", ex);
764 }
765 break;
766 case BZIP2:
767 current.inputStream = new BZip2CompressorInputStream(bis);
768 break;
769 case ENHANCED_DEFLATED:
770 current.inputStream = new Deflate64CompressorInputStream(bis);
771 break;
772 default:
773
774
775
776 break;
777 }
778 }
779 } else if (m == ZipMethod.ENHANCED_DEFLATED) {
780 current.inputStream = new Deflate64CompressorInputStream(in);
781 }
782
783 entriesRead++;
784 return current.entry;
785 }
786
787
788
789
790
791
792 @Override
793 public long getUncompressedCount() {
794 return uncompressedCount;
795 }
796
797
798
799
800
801
802
803
804
805
806
807
808 private boolean isApkSigningBlock(final byte[] suspectLocalFileHeader) throws IOException {
809
810 final BigInteger len = ZipEightByteInteger.getValue(suspectLocalFileHeader);
811
812
813 BigInteger toSkip = len.add(BigInteger.valueOf(DWORD - suspectLocalFileHeader.length - (long) APK_SIGNING_BLOCK_MAGIC.length));
814 final byte[] magic = new byte[APK_SIGNING_BLOCK_MAGIC.length];
815
816 try {
817 if (toSkip.signum() < 0) {
818
819 final int off = suspectLocalFileHeader.length + toSkip.intValue();
820
821 if (off < DWORD) {
822 return false;
823 }
824 final int bytesInBuffer = Math.abs(toSkip.intValue());
825 System.arraycopy(suspectLocalFileHeader, off, magic, 0, Math.min(bytesInBuffer, magic.length));
826 if (bytesInBuffer < magic.length) {
827 readFully(magic, bytesInBuffer);
828 }
829 } else {
830 while (toSkip.compareTo(LONG_MAX) > 0) {
831 realSkip(Long.MAX_VALUE);
832 toSkip = toSkip.add(LONG_MAX.negate());
833 }
834 realSkip(toSkip.longValue());
835 readFully(magic);
836 }
837 } catch (final EOFException ex) {
838
839 return false;
840 }
841 return Arrays.equals(magic, APK_SIGNING_BLOCK_MAGIC);
842 }
843
844 private boolean isFirstByteOfEocdSig(final int b) {
845 return b == ZipArchiveOutputStream.EOCD_SIG[0];
846 }
847
848
849
850
851 private void processZip64Extra(final ZipLong size, final ZipLong cSize) throws ZipException {
852 final ZipExtraField extra = current.entry.getExtraField(Zip64ExtendedInformationExtraField.HEADER_ID);
853 if (extra != null && !(extra instanceof Zip64ExtendedInformationExtraField)) {
854 throw new ZipException("archive contains unparseable zip64 extra field");
855 }
856 final Zip64ExtendedInformationExtraField z64 = (Zip64ExtendedInformationExtraField) extra;
857 current.usesZip64 = z64 != null;
858 if (!current.hasDataDescriptor) {
859 if (z64 != null
860 && (ZipLong.ZIP64_MAGIC.equals(cSize) || ZipLong.ZIP64_MAGIC.equals(size))) {
861 if (z64.getCompressedSize() == null || z64.getSize() == null) {
862
863 throw new ZipException("archive contains corrupted zip64 extra field");
864 }
865 long s = z64.getCompressedSize().getLongValue();
866 if (s < 0) {
867 throw new ZipException("broken archive, entry with negative compressed size");
868 }
869 current.entry.setCompressedSize(s);
870 s = z64.getSize().getLongValue();
871 if (s < 0) {
872 throw new ZipException("broken archive, entry with negative size");
873 }
874 current.entry.setSize(s);
875 } else if (cSize != null && size != null) {
876 if (cSize.getValue() < 0) {
877 throw new ZipException("broken archive, entry with negative compressed size");
878 }
879 current.entry.setCompressedSize(cSize.getValue());
880 if (size.getValue() < 0) {
881 throw new ZipException("broken archive, entry with negative size");
882 }
883 current.entry.setSize(size.getValue());
884 }
885 }
886 }
887
888 private void pushback(final byte[] buf, final int offset, final int length) throws IOException {
889 if (offset < 0) {
890
891 throw new IOException(String.format("Negative offset %,d into buffer", offset));
892 }
893 ((PushbackInputStream) in).unread(buf, offset, length);
894 pushedBackBytes(length);
895 }
896
897 @Override
898 public int read(final byte[] buffer, final int offset, final int length) throws IOException {
899 if (length == 0) {
900 return 0;
901 }
902 if (closed) {
903 throw new IOException("The stream is closed");
904 }
905
906 if (current == null) {
907 return -1;
908 }
909
910
911 if (offset > buffer.length || length < 0 || offset < 0 || buffer.length - offset < length) {
912 throw new ArrayIndexOutOfBoundsException();
913 }
914
915 ZipUtil.checkRequestedFeatures(current.entry);
916 if (!supportsDataDescriptorFor(current.entry)) {
917 throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.DATA_DESCRIPTOR, current.entry);
918 }
919 if (!supportsCompressedSizeFor(current.entry)) {
920 throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.UNKNOWN_COMPRESSED_SIZE, current.entry);
921 }
922
923 final int read;
924 if (current.entry.getMethod() == ZipArchiveOutputStream.STORED) {
925 read = readStored(buffer, offset, length);
926 } else if (current.entry.getMethod() == ZipArchiveOutputStream.DEFLATED) {
927 read = readDeflated(buffer, offset, length);
928 } else if (current.entry.getMethod() == ZipMethod.UNSHRINKING.getCode() || current.entry.getMethod() == ZipMethod.IMPLODING.getCode()
929 || current.entry.getMethod() == ZipMethod.ENHANCED_DEFLATED.getCode() || current.entry.getMethod() == ZipMethod.BZIP2.getCode()) {
930 read = current.inputStream.read(buffer, offset, length);
931 } else {
932 throw new UnsupportedZipFeatureException(ZipMethod.getMethodByCode(current.entry.getMethod()), current.entry);
933 }
934
935 if (read >= 0) {
936 current.crc.update(buffer, offset, read);
937 uncompressedCount += read;
938 }
939
940 return read;
941 }
942
943 private void readDataDescriptor() throws IOException {
944 readFully(wordBuf);
945 ZipLong val = new ZipLong(wordBuf);
946 if (ZipLong.DD_SIG.equals(val)) {
947
948 readFully(wordBuf);
949 val = new ZipLong(wordBuf);
950 }
951 current.entry.setCrc(val.getValue());
952
953
954
955
956
957
958
959
960
961
962
963
964 readFully(twoDwordBuf);
965 final ZipLong potentialSig = new ZipLong(twoDwordBuf, DWORD);
966 if (potentialSig.equals(ZipLong.CFH_SIG) || potentialSig.equals(ZipLong.LFH_SIG)) {
967 pushback(twoDwordBuf, DWORD, DWORD);
968 long size = ZipLong.getValue(twoDwordBuf);
969 if (size < 0) {
970 throw new ZipException("broken archive, entry with negative compressed size");
971 }
972 current.entry.setCompressedSize(size);
973 size = ZipLong.getValue(twoDwordBuf, WORD);
974 if (size < 0) {
975 throw new ZipException("broken archive, entry with negative size");
976 }
977 current.entry.setSize(size);
978 } else {
979 long size = ZipEightByteInteger.getLongValue(twoDwordBuf);
980 if (size < 0) {
981 throw new ZipException("broken archive, entry with negative compressed size");
982 }
983 current.entry.setCompressedSize(size);
984 size = ZipEightByteInteger.getLongValue(twoDwordBuf, DWORD);
985 if (size < 0) {
986 throw new ZipException("broken archive, entry with negative size");
987 }
988 current.entry.setSize(size);
989 }
990 }
991
992
993
994
995 private int readDeflated(final byte[] buffer, final int offset, final int length) throws IOException {
996 final int read = readFromInflater(buffer, offset, length);
997 if (read <= 0) {
998 if (inf.finished()) {
999 return -1;
1000 }
1001 if (inf.needsDictionary()) {
1002 throw new ZipException("This archive needs a preset dictionary" + " which is not supported by Commons" + " Compress.");
1003 }
1004 if (read == -1) {
1005 throw new IOException("Truncated ZIP file");
1006 }
1007 }
1008 return read;
1009 }
1010
1011
1012
1013
1014 private boolean readFirstLocalFileHeader() throws IOException {
1015
1016 final byte[] header = new byte[Math.min(LFH_LEN, ZipFile.MIN_EOCD_SIZE)];
1017 readFully(header);
1018 try {
1019 READ_LOOP: for (int i = 0; ; ) {
1020 for (int j = 0; i <= PREAMBLE_GARBAGE_MAX_SIZE - 4 && j <= header.length - 4; ++j, ++i) {
1021 final ZipLong sig = new ZipLong(header, j);
1022 if (
1023 sig.equals(ZipLong.LFH_SIG) ||
1024 sig.equals(ZipLong.SINGLE_SEGMENT_SPLIT_MARKER) ||
1025 sig.equals(ZipLong.DD_SIG)) {
1026
1027 System.arraycopy(header, j, header, 0, header.length - j);
1028 readFully(header, header.length - j);
1029 break READ_LOOP;
1030 }
1031 if (
1032 sig.equals(new ZipLong(ZipArchiveOutputStream.EOCD_SIG))
1033 ) {
1034
1035 pushback(header, j, header.length - j);
1036 return false;
1037 }
1038 }
1039 if (i >= PREAMBLE_GARBAGE_MAX_SIZE - 4) {
1040 throw new ZipException("Cannot find zip signature within the first " + PREAMBLE_GARBAGE_MAX_SIZE + " bytes");
1041 }
1042 System.arraycopy(header, header.length - 3, header, 0, 3);
1043 readFully(header, 3);
1044 }
1045 System.arraycopy(header, 0, lfhBuf, 0, header.length);
1046 readFully(lfhBuf, header.length);
1047 } catch (final EOFException ex) {
1048 throw new ZipException("Cannot find zip signature within the file");
1049 }
1050 final ZipLong sig = new ZipLong(lfhBuf);
1051
1052 if (!skipSplitSig && sig.equals(ZipLong.DD_SIG)) {
1053 throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.SPLITTING);
1054 }
1055
1056
1057 if (sig.equals(ZipLong.SINGLE_SEGMENT_SPLIT_MARKER) || sig.equals(ZipLong.DD_SIG)) {
1058
1059 System.arraycopy(lfhBuf, 4, lfhBuf, 0, lfhBuf.length - 4);
1060 readFully(lfhBuf, lfhBuf.length - 4);
1061 }
1062 return true;
1063 }
1064
1065
1066
1067
1068 private int readFromInflater(final byte[] buffer, final int offset, final int length) throws IOException {
1069 int read = 0;
1070 do {
1071 if (inf.needsInput()) {
1072 final int l = fill();
1073 if (l > 0) {
1074 current.bytesReadFromStream += buf.limit();
1075 } else if (l == -1) {
1076 return -1;
1077 } else {
1078 break;
1079 }
1080 }
1081 try {
1082 read = inf.inflate(buffer, offset, length);
1083 } catch (final DataFormatException e) {
1084 throw (IOException) new ZipException(e.getMessage()).initCause(e);
1085 }
1086 } while (read == 0 && inf.needsInput());
1087 return read;
1088 }
1089
1090 private void readFully(final byte[] b) throws IOException {
1091 readFully(b, 0);
1092 }
1093
1094 private void readFully(final byte[] b, final int off) throws IOException {
1095 final int len = b.length - off;
1096 final int count = IOUtils.readFully(in, b, off, len);
1097 count(count);
1098 if (count < len) {
1099 throw new EOFException();
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
1125 private int readOneByte() throws IOException {
1126 final int b = in.read();
1127 if (b != -1) {
1128 count(1);
1129 }
1130 return b;
1131 }
1132
1133 private byte[] readRange(final int len) throws IOException {
1134 final byte[] ret = IOUtils.readRange(in, len);
1135 count(ret.length);
1136 if (ret.length < len) {
1137 throw new EOFException();
1138 }
1139 return ret;
1140 }
1141
1142
1143
1144
1145 private int readStored(final byte[] buffer, final int offset, final int length) throws IOException {
1146
1147 if (current.hasDataDescriptor) {
1148 if (lastStoredEntry == null) {
1149 readStoredEntry();
1150 }
1151 return lastStoredEntry.read(buffer, offset, length);
1152 }
1153
1154 final long csize = current.entry.getSize();
1155 if (current.bytesRead >= csize) {
1156 return -1;
1157 }
1158
1159 if (buf.position() >= buf.limit()) {
1160 buf.position(0);
1161 final int l = in.read(buf.array());
1162 if (l == -1) {
1163 buf.limit(0);
1164 throw new IOException("Truncated ZIP file");
1165 }
1166 buf.limit(l);
1167
1168 count(l);
1169 current.bytesReadFromStream += l;
1170 }
1171
1172 int toRead = Math.min(buf.remaining(), length);
1173 if (csize - current.bytesRead < toRead) {
1174
1175 toRead = (int) (csize - current.bytesRead);
1176 }
1177 buf.get(buffer, offset, toRead);
1178 current.bytesRead += toRead;
1179 return toRead;
1180 }
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196 private void readStoredEntry() throws IOException {
1197 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
1198 int off = 0;
1199 boolean done = false;
1200
1201
1202 final int ddLen = current.usesZip64 ? WORD + 2 * DWORD : 3 * WORD;
1203
1204 while (!done) {
1205 final int r = in.read(buf.array(), off, ZipArchiveOutputStream.BUFFER_SIZE - off);
1206 if (r <= 0) {
1207
1208
1209 throw new IOException("Truncated ZIP file");
1210 }
1211 if (r + off < 4) {
1212
1213 off += r;
1214 continue;
1215 }
1216
1217 done = bufferContainsSignature(bos, off, r, ddLen);
1218 if (!done) {
1219 off = cacheBytesRead(bos, off, r, ddLen);
1220 }
1221 }
1222 if (current.entry.getCompressedSize() != current.entry.getSize()) {
1223 throw new ZipException("compressed and uncompressed size don't match" + USE_ZIPFILE_INSTEAD_OF_STREAM_DISCLAIMER);
1224 }
1225 final byte[] b = bos.toByteArray();
1226 if (b.length != current.entry.getSize()) {
1227 throw new ZipException("actual and claimed size don't match" + USE_ZIPFILE_INSTEAD_OF_STREAM_DISCLAIMER);
1228 }
1229 lastStoredEntry = new ByteArrayInputStream(b);
1230 }
1231
1232
1233
1234
1235
1236
1237 private void realSkip(final long value) throws IOException {
1238 if (value >= 0) {
1239 long skipped = 0;
1240 while (skipped < value) {
1241 final long rem = value - skipped;
1242 final int x = in.read(skipBuf, 0, (int) (skipBuf.length > rem ? rem : skipBuf.length));
1243 if (x == -1) {
1244 return;
1245 }
1246 count(x);
1247 skipped += x;
1248 }
1249 return;
1250 }
1251 throw new IllegalArgumentException();
1252 }
1253
1254
1255
1256
1257
1258
1259 public ZipArchiveInputStream setExtraFieldSupport(final Function<ZipShort, ZipExtraField> extraFieldSupport) {
1260 this.extraFieldSupport = extraFieldSupport;
1261 return this;
1262 }
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278 @Override
1279 public long skip(final long value) throws IOException {
1280 if (value >= 0) {
1281 long skipped = 0;
1282 while (skipped < value) {
1283 final long rem = value - skipped;
1284 final int x = read(skipBuf, 0, (int) (skipBuf.length > rem ? rem : skipBuf.length));
1285 if (x == -1) {
1286 return skipped;
1287 }
1288 skipped += x;
1289 }
1290 return skipped;
1291 }
1292 throw new IllegalArgumentException();
1293 }
1294
1295
1296
1297
1298 private void skipRemainderOfArchive(final boolean read) throws IOException {
1299
1300
1301
1302 if (entriesRead > 0) {
1303 realSkip((long) entriesRead * CFH_LEN - LFH_LEN);
1304 }
1305 final boolean foundEocd = findEocdRecord();
1306 if (foundEocd) {
1307 realSkip((long) ZipFile.MIN_EOCD_SIZE - WORD - SHORT );
1308 readFully(shortBuf);
1309
1310 final int commentLen = ZipShort.getValue(shortBuf);
1311 if (commentLen >= 0) {
1312 realSkip(commentLen);
1313 return;
1314 }
1315 }
1316 throw new IOException("Truncated ZIP file");
1317 }
1318
1319
1320
1321
1322 private boolean supportsCompressedSizeFor(final ZipArchiveEntry entry) {
1323 return entry.getCompressedSize() != ArchiveEntry.SIZE_UNKNOWN || entry.getMethod() == ZipEntry.DEFLATED
1324 || entry.getMethod() == ZipMethod.ENHANCED_DEFLATED.getCode()
1325 || entry.getGeneralPurposeBit().usesDataDescriptor() && allowStoredEntriesWithDataDescriptor && entry.getMethod() == ZipEntry.STORED;
1326 }
1327
1328
1329
1330
1331
1332
1333
1334 private boolean supportsDataDescriptorFor(final ZipArchiveEntry entry) {
1335 return !entry.getGeneralPurposeBit().usesDataDescriptor() || allowStoredEntriesWithDataDescriptor && entry.getMethod() == ZipEntry.STORED
1336 || entry.getMethod() == ZipEntry.DEFLATED || entry.getMethod() == ZipMethod.ENHANCED_DEFLATED.getCode();
1337 }
1338 }