306
307 // The outstanding inputstreams that need to be closed,
308 // mapped to the inflater objects they use.
309 private final Map<InputStream, Inflater> streams = new WeakHashMap<>();
310
311 /**
312 * Returns an input stream for reading the contents of the specified
313 * zip file entry.
314 * <p>
315 * Closing this ZIP file will, in turn, close all input streams that
316 * have been returned by invocations of this method.
317 *
318 * @param entry the zip file entry
319 * @return the input stream for reading the contents of the specified
320 * zip file entry.
321 * @throws ZipException if a ZIP format error has occurred
322 * @throws IOException if an I/O error has occurred
323 * @throws IllegalStateException if the zip file has been closed
324 */
325 public InputStream getInputStream(ZipEntry entry) throws IOException {
326 Objects.requireNonNull(entry, "entry");
327 int pos = -1;
328 ZipFileInputStream in = null;
329 synchronized (this) {
330 ensureOpen();
331 if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
332 pos = zsrc.getEntryPos(zc.getBytesUTF8(entry.name), false);
333 } else {
334 pos = zsrc.getEntryPos(zc.getBytes(entry.name), false);
335 }
336 if (pos == -1) {
337 return null;
338 }
339 in = new ZipFileInputStream(zsrc.cen, pos);
340 switch (CENHOW(zsrc.cen, pos)) {
341 case STORED:
342 synchronized (streams) {
343 streams.put(in, null);
344 }
345 return in;
346 case DEFLATED:
347 // Inflater likes a bit of slack
348 // MORE: Compute good size for inflater stream:
349 long size = CENLEN(zsrc.cen, pos) + 2;
350 if (size > 65536) {
351 size = 8192;
352 }
353 if (size <= 0) {
354 size = 4096;
355 }
356 Inflater inf = getInflater();
357 InputStream is = new ZipFileInflaterInputStream(in, inf, (int)size);
358 synchronized (streams) {
359 streams.put(is, inf);
360 }
361 return is;
362 default:
363 throw new ZipException("invalid compression method");
364 }
365 }
366 }
367
368 private class ZipFileInflaterInputStream extends InflaterInputStream {
369 private volatile boolean closeRequested = false;
370 private boolean eof = false;
371 private final ZipFileInputStream zfin;
372
373 ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf,
374 int size) {
375 super(zfin, inf, size);
376 this.zfin = zfin;
640 if (zsrc == null) {
641 throw new IllegalStateException("The object is not initialized.");
642 }
643 }
644
645 private void ensureOpenOrZipException() throws IOException {
646 if (closeRequested) {
647 throw new ZipException("ZipFile closed");
648 }
649 }
650
651 /*
652 * Inner class implementing the input stream used to read a
653 * (possibly compressed) zip file entry.
654 */
655 private class ZipFileInputStream extends InputStream {
656 private volatile boolean closeRequested = false;
657 private long pos; // current position within entry data
658 protected long rem; // number of remaining bytes within entry
659 protected long size; // uncompressed size of this entry
660
661 ZipFileInputStream(byte[] cen, int cenpos) throws IOException {
662 rem = CENSIZ(cen, cenpos);
663 size = CENLEN(cen, cenpos);
664 pos = CENOFF(cen, cenpos);
665 // zip64
666 if (rem == ZIP64_MAGICVAL || size == ZIP64_MAGICVAL ||
667 pos == ZIP64_MAGICVAL) {
668 checkZIP64(cen, cenpos);
669 }
670 // negative for lazy initialization, see getDataOffset();
671 pos = - (pos + ZipFile.this.zsrc.locpos);
672 }
673
674 private void checkZIP64(byte[] cen, int cenpos) throws IOException {
675 int off = cenpos + CENHDR + CENNAM(cen, cenpos);
676 int end = off + CENEXT(cen, cenpos);
677 while (off + 4 < end) {
678 int tag = get16(cen, off);
679 int sz = get16(cen, off + 2);
680 off += 4;
681 if (off + sz > end) // invalid data
682 break;
683 if (tag == EXTID_ZIP64) {
684 if (size == ZIP64_MAGICVAL) {
685 if (sz < 8 || (off + 8) > end)
686 break;
687 size = get64(cen, off);
688 sz -= 8;
689 off += 8;
690 }
691 if (rem == ZIP64_MAGICVAL) {
713 * the CEN extra data size, we need to read the LOC to determine
714 * the entry data offset.
715 */
716 private long initDataOffset() throws IOException {
717 if (pos <= 0) {
718 byte[] loc = new byte[LOCHDR];
719 pos = -pos;
720 int len = ZipFile.this.zsrc.readFullyAt(loc, 0, loc.length, pos);
721 if (len != LOCHDR) {
722 throw new ZipException("ZipFile error reading zip file");
723 }
724 if (LOCSIG(loc) != LOCSIG) {
725 throw new ZipException("ZipFile invalid LOC header (bad signature)");
726 }
727 pos += LOCHDR + LOCNAM(loc) + LOCEXT(loc);
728 }
729 return pos;
730 }
731
732 public int read(byte b[], int off, int len) throws IOException {
733 synchronized (ZipFile.this) {
734 ensureOpenOrZipException();
735 initDataOffset();
736 if (rem == 0) {
737 return -1;
738 }
739 if (len > rem) {
740 len = (int) rem;
741 }
742 if (len <= 0) {
743 return 0;
744 }
745 len = ZipFile.this.zsrc.readAt(b, off, len, pos);
746 if (len > 0) {
747 pos += len;
748 rem -= len;
749 }
750 }
751 if (rem == 0) {
752 close();
1162
1163 // Iterate through the entries in the central directory
1164 int i = 0;
1165 int hsh = 0;
1166 int pos = 0;
1167 int limit = cen.length - ENDHDR;
1168 while (pos + CENHDR <= limit) {
1169 if (i >= total) {
1170 // This will only happen if the zip file has an incorrect
1171 // ENDTOT field, which usually means it contains more than
1172 // 65535 entries.
1173 initCEN(countCENHeaders(cen, limit));
1174 return;
1175 }
1176 if (CENSIG(cen, pos) != CENSIG)
1177 zerror("invalid CEN header (bad signature)");
1178 int method = CENHOW(cen, pos);
1179 int nlen = CENNAM(cen, pos);
1180 int elen = CENEXT(cen, pos);
1181 int clen = CENCOM(cen, pos);
1182 if ((CENFLG(cen, pos) & 1) != 0)
1183 zerror("invalid CEN header (encrypted entry)");
1184 if (method != STORED && method != DEFLATED)
1185 zerror("invalid CEN header (bad compression method: " + method + ")");
1186 if (pos + CENHDR + nlen > limit)
1187 zerror("invalid CEN header (bad header size)");
1188 // Record the CEN offset and the name hash in our hash cell.
1189 hash = hashN(cen, pos + CENHDR, nlen);
1190 hsh = (hash & 0x7fffffff) % tablelen;
1191 next = table[hsh];
1192 table[hsh] = idx;
1193 idx = addEntry(idx, hash, next, pos);
1194 // Adds name to metanames.
1195 if (isMetaName(cen, pos + CENHDR, nlen)) {
1196 metanames.add(pos);
1197 }
1198 // skip ext and comment
1199 pos += (CENHDR + nlen + elen + clen);
1200 i++;
1201 }
1202 total = i;
1203 if (pos + ENDHDR != cen.length) {
|
306
307 // The outstanding inputstreams that need to be closed,
308 // mapped to the inflater objects they use.
309 private final Map<InputStream, Inflater> streams = new WeakHashMap<>();
310
311 /**
312 * Returns an input stream for reading the contents of the specified
313 * zip file entry.
314 * <p>
315 * Closing this ZIP file will, in turn, close all input streams that
316 * have been returned by invocations of this method.
317 *
318 * @param entry the zip file entry
319 * @return the input stream for reading the contents of the specified
320 * zip file entry.
321 * @throws ZipException if a ZIP format error has occurred
322 * @throws IOException if an I/O error has occurred
323 * @throws IllegalStateException if the zip file has been closed
324 */
325 public InputStream getInputStream(ZipEntry entry) throws IOException {
326 return getInputStream(entry, null);
327 }
328
329 /**
330 * Returns an input stream for reading the contents of the specified
331 * zip file entry.
332 * <p>
333 * Closing this ZIP file will, in turn, close all input streams that
334 * have been returned by invocations of this method.
335 *
336 * @param entry the zip file entry
337 * @param zipCryption ZIP encrypt/decrypt engine. zip decryption will not
338 * work if this value set to null.
339 * @return the input stream for reading the contents of the specified
340 * zip file entry.
341 * @throws ZipException if a ZIP format error has occurred
342 * @throws IOException if an I/O error has occurred
343 * @throws IllegalStateException if the zip file has been closed
344 * @since 1.9
345 */
346 public InputStream getInputStream(ZipEntry entry, ZipCryption zipCryption)
347 throws IOException {
348 Objects.requireNonNull(entry, "entry");
349
350 if ((entry.flag & 1) == 1) {
351 Objects.requireNonNull(zipCryption, "Passphrase is required");
352 zipCryption.reset();
353 }
354
355 int pos = -1;
356 ZipFileInputStream in = null;
357 synchronized (this) {
358 ensureOpen();
359 if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
360 pos = zsrc.getEntryPos(zc.getBytesUTF8(entry.name), false);
361 } else {
362 pos = zsrc.getEntryPos(zc.getBytes(entry.name), false);
363 }
364 if (pos == -1) {
365 return null;
366 }
367 in = new ZipFileInputStream(zsrc.cen, pos, zipCryption);
368 switch (CENHOW(zsrc.cen, pos)) {
369 case STORED:
370 if((entry.flag & 1) == 1) {
371 byte[] encryptionHeader =
372 new byte[zipCryption.getEncryptionHeaderSize()];
373 in.readRaw(encryptionHeader, 0, encryptionHeader.length);
374 zipCryption.decryptBytes(encryptionHeader);
375
376 if (!zipCryption.isValid(entry, encryptionHeader)) {
377 throw new ZipException("possibly incorrect passphrase");
378 }
379 }
380 synchronized (streams) {
381 streams.put(in, null);
382 }
383 return in;
384 case DEFLATED:
385 // Inflater likes a bit of slack
386 // MORE: Compute good size for inflater stream:
387 long size = CENLEN(zsrc.cen, pos) + 2;
388 if (size > 65536) {
389 size = 8192;
390 }
391 if (size <= 0) {
392 size = 4096;
393 }
394 Inflater inf = getInflater();
395
396 if((entry.flag & 1) == 1) {
397 byte[] encryptionHeader =
398 new byte[zipCryption.getEncryptionHeaderSize()];
399 in.readRaw(encryptionHeader, 0, encryptionHeader.length);
400 zipCryption.decryptBytes(encryptionHeader);
401
402 if (!zipCryption.isValid(entry, encryptionHeader)) {
403 throw new ZipException("possibly incorrect passphrase");
404 }
405 }
406
407 InputStream is = new ZipFileInflaterInputStream(in, inf, (int)size);
408 synchronized (streams) {
409 streams.put(is, inf);
410 }
411 return is;
412 default:
413 throw new ZipException("invalid compression method");
414 }
415 }
416 }
417
418 private class ZipFileInflaterInputStream extends InflaterInputStream {
419 private volatile boolean closeRequested = false;
420 private boolean eof = false;
421 private final ZipFileInputStream zfin;
422
423 ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf,
424 int size) {
425 super(zfin, inf, size);
426 this.zfin = zfin;
690 if (zsrc == null) {
691 throw new IllegalStateException("The object is not initialized.");
692 }
693 }
694
695 private void ensureOpenOrZipException() throws IOException {
696 if (closeRequested) {
697 throw new ZipException("ZipFile closed");
698 }
699 }
700
701 /*
702 * Inner class implementing the input stream used to read a
703 * (possibly compressed) zip file entry.
704 */
705 private class ZipFileInputStream extends InputStream {
706 private volatile boolean closeRequested = false;
707 private long pos; // current position within entry data
708 protected long rem; // number of remaining bytes within entry
709 protected long size; // uncompressed size of this entry
710 private ZipCryption zipCryption; // ZIP encrypt/decrypt engine
711
712 ZipFileInputStream(byte[] cen, int cenpos, ZipCryption zipCryption)
713 throws IOException {
714 rem = CENSIZ(cen, cenpos);
715 size = CENLEN(cen, cenpos);
716 pos = CENOFF(cen, cenpos);
717 // zip64
718 if (rem == ZIP64_MAGICVAL || size == ZIP64_MAGICVAL ||
719 pos == ZIP64_MAGICVAL) {
720 checkZIP64(cen, cenpos);
721 }
722 // negative for lazy initialization, see getDataOffset();
723 pos = - (pos + ZipFile.this.zsrc.locpos);
724 this.zipCryption = zipCryption;
725 }
726
727 private void checkZIP64(byte[] cen, int cenpos) throws IOException {
728 int off = cenpos + CENHDR + CENNAM(cen, cenpos);
729 int end = off + CENEXT(cen, cenpos);
730 while (off + 4 < end) {
731 int tag = get16(cen, off);
732 int sz = get16(cen, off + 2);
733 off += 4;
734 if (off + sz > end) // invalid data
735 break;
736 if (tag == EXTID_ZIP64) {
737 if (size == ZIP64_MAGICVAL) {
738 if (sz < 8 || (off + 8) > end)
739 break;
740 size = get64(cen, off);
741 sz -= 8;
742 off += 8;
743 }
744 if (rem == ZIP64_MAGICVAL) {
766 * the CEN extra data size, we need to read the LOC to determine
767 * the entry data offset.
768 */
769 private long initDataOffset() throws IOException {
770 if (pos <= 0) {
771 byte[] loc = new byte[LOCHDR];
772 pos = -pos;
773 int len = ZipFile.this.zsrc.readFullyAt(loc, 0, loc.length, pos);
774 if (len != LOCHDR) {
775 throw new ZipException("ZipFile error reading zip file");
776 }
777 if (LOCSIG(loc) != LOCSIG) {
778 throw new ZipException("ZipFile invalid LOC header (bad signature)");
779 }
780 pos += LOCHDR + LOCNAM(loc) + LOCEXT(loc);
781 }
782 return pos;
783 }
784
785 public int read(byte b[], int off, int len) throws IOException {
786 len = readRaw(b, off, len);
787
788 if (zipCryption != null) {
789 zipCryption.decryptBytes(b, off, len);
790 }
791
792 return len;
793 }
794
795 public int readRaw(byte b[], int off, int len) throws IOException {
796 synchronized (ZipFile.this) {
797 ensureOpenOrZipException();
798 initDataOffset();
799 if (rem == 0) {
800 return -1;
801 }
802 if (len > rem) {
803 len = (int) rem;
804 }
805 if (len <= 0) {
806 return 0;
807 }
808 len = ZipFile.this.zsrc.readAt(b, off, len, pos);
809 if (len > 0) {
810 pos += len;
811 rem -= len;
812 }
813 }
814 if (rem == 0) {
815 close();
1225
1226 // Iterate through the entries in the central directory
1227 int i = 0;
1228 int hsh = 0;
1229 int pos = 0;
1230 int limit = cen.length - ENDHDR;
1231 while (pos + CENHDR <= limit) {
1232 if (i >= total) {
1233 // This will only happen if the zip file has an incorrect
1234 // ENDTOT field, which usually means it contains more than
1235 // 65535 entries.
1236 initCEN(countCENHeaders(cen, limit));
1237 return;
1238 }
1239 if (CENSIG(cen, pos) != CENSIG)
1240 zerror("invalid CEN header (bad signature)");
1241 int method = CENHOW(cen, pos);
1242 int nlen = CENNAM(cen, pos);
1243 int elen = CENEXT(cen, pos);
1244 int clen = CENCOM(cen, pos);
1245 if (method != STORED && method != DEFLATED)
1246 zerror("invalid CEN header (bad compression method: " + method + ")");
1247 if (pos + CENHDR + nlen > limit)
1248 zerror("invalid CEN header (bad header size)");
1249 // Record the CEN offset and the name hash in our hash cell.
1250 hash = hashN(cen, pos + CENHDR, nlen);
1251 hsh = (hash & 0x7fffffff) % tablelen;
1252 next = table[hsh];
1253 table[hsh] = idx;
1254 idx = addEntry(idx, hash, next, pos);
1255 // Adds name to metanames.
1256 if (isMetaName(cen, pos + CENHDR, nlen)) {
1257 metanames.add(pos);
1258 }
1259 // skip ext and comment
1260 pos += (CENHDR + nlen + elen + clen);
1261 i++;
1262 }
1263 total = i;
1264 if (pos + ENDHDR != cen.length) {
|