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 instance of ZipCryption
338 * @return the input stream for reading the contents of the specified
339 * zip file entry.
340 * @throws ZipException if a ZIP format error has occurred
341 * @throws IOException if an I/O error has occurred
342 * @throws IllegalStateException if the zip file has been closed
343 */
344 public InputStream getInputStream(ZipEntry entry, ZipCryption zipCryption)
345 throws IOException {
346 Objects.requireNonNull(entry, "entry");
347
348 if ((entry.flag & 1) == 1) {
349 Objects.requireNonNull(entry, "Passphrase is required");
350 zipCryption.reset();
351 }
352
353 int pos = -1;
354 ZipFileInputStream in = null;
355 synchronized (this) {
356 ensureOpen();
357 if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
358 pos = zsrc.getEntryPos(zc.getBytesUTF8(entry.name), false);
359 } else {
360 pos = zsrc.getEntryPos(zc.getBytes(entry.name), false);
361 }
362 if (pos == -1) {
363 return null;
364 }
365 in = new ZipFileInputStream(zsrc.cen, pos, zipCryption);
366 switch (CENHOW(zsrc.cen, pos)) {
367 case STORED:
368 if((entry.flag & 1) == 1) {
369 byte[] encryptionHeader =
370 new byte[zipCryption.getEncryptionHeaderSize()];
371 in.readRaw(encryptionHeader, 0, encryptionHeader.length);
372 zipCryption.decryptBytes(encryptionHeader);
373
374 if (!zipCryption.isValid(entry, encryptionHeader)) {
375 throw new ZipException("possibly incorrect passphrase");
376 }
377 }
378 synchronized (streams) {
379 streams.put(in, null);
380 }
381 return in;
382 case DEFLATED:
383 // Inflater likes a bit of slack
384 // MORE: Compute good size for inflater stream:
385 long size = CENLEN(zsrc.cen, pos) + 2;
386 if (size > 65536) {
387 size = 8192;
388 }
389 if (size <= 0) {
390 size = 4096;
391 }
392 Inflater inf = getInflater();
393
394 if((entry.flag & 1) == 1) {
395 byte[] encryptionHeader =
396 new byte[zipCryption.getEncryptionHeaderSize()];
397 in.readRaw(encryptionHeader, 0, encryptionHeader.length);
398 zipCryption.decryptBytes(encryptionHeader);
399
400 if (!zipCryption.isValid(entry, encryptionHeader)) {
401 throw new ZipException("possibly incorrect passphrase");
402 }
403 }
404
405 InputStream is = new ZipFileInflaterInputStream(in, inf, (int)size);
406 synchronized (streams) {
407 streams.put(is, inf);
408 }
409 return is;
410 default:
411 throw new ZipException("invalid compression method");
412 }
413 }
414 }
415
416 private class ZipFileInflaterInputStream extends InflaterInputStream {
417 private volatile boolean closeRequested = false;
418 private boolean eof = false;
419 private final ZipFileInputStream zfin;
420
421 ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf,
422 int size) {
423 super(zfin, inf, size);
424 this.zfin = zfin;
688 if (zsrc == null) {
689 throw new IllegalStateException("The object is not initialized.");
690 }
691 }
692
693 private void ensureOpenOrZipException() throws IOException {
694 if (closeRequested) {
695 throw new ZipException("ZipFile closed");
696 }
697 }
698
699 /*
700 * Inner class implementing the input stream used to read a
701 * (possibly compressed) zip file entry.
702 */
703 private class ZipFileInputStream extends InputStream {
704 private volatile boolean closeRequested = false;
705 private long pos; // current position within entry data
706 protected long rem; // number of remaining bytes within entry
707 protected long size; // uncompressed size of this entry
708 private ZipCryption zipCryption; // ZIP encrypt/decrypt engine
709
710 ZipFileInputStream(byte[] cen, int cenpos, ZipCryption zipCryption)
711 throws IOException {
712 rem = CENSIZ(cen, cenpos);
713 size = CENLEN(cen, cenpos);
714 pos = CENOFF(cen, cenpos);
715 // zip64
716 if (rem == ZIP64_MAGICVAL || size == ZIP64_MAGICVAL ||
717 pos == ZIP64_MAGICVAL) {
718 checkZIP64(cen, cenpos);
719 }
720 // negative for lazy initialization, see getDataOffset();
721 pos = - (pos + ZipFile.this.zsrc.locpos);
722 this.zipCryption = zipCryption;
723 }
724
725 private void checkZIP64(byte[] cen, int cenpos) throws IOException {
726 int off = cenpos + CENHDR + CENNAM(cen, cenpos);
727 int end = off + CENEXT(cen, cenpos);
728 while (off + 4 < end) {
729 int tag = get16(cen, off);
730 int sz = get16(cen, off + 2);
731 off += 4;
732 if (off + sz > end) // invalid data
733 break;
734 if (tag == EXTID_ZIP64) {
735 if (size == ZIP64_MAGICVAL) {
736 if (sz < 8 || (off + 8) > end)
737 break;
738 size = get64(cen, off);
739 sz -= 8;
740 off += 8;
741 }
742 if (rem == ZIP64_MAGICVAL) {
764 * the CEN extra data size, we need to read the LOC to determine
765 * the entry data offset.
766 */
767 private long initDataOffset() throws IOException {
768 if (pos <= 0) {
769 byte[] loc = new byte[LOCHDR];
770 pos = -pos;
771 int len = ZipFile.this.zsrc.readFullyAt(loc, 0, loc.length, pos);
772 if (len != LOCHDR) {
773 throw new ZipException("ZipFile error reading zip file");
774 }
775 if (LOCSIG(loc) != LOCSIG) {
776 throw new ZipException("ZipFile invalid LOC header (bad signature)");
777 }
778 pos += LOCHDR + LOCNAM(loc) + LOCEXT(loc);
779 }
780 return pos;
781 }
782
783 public int read(byte b[], int off, int len) throws IOException {
784 len = readRaw(b, off, len);
785
786 if (zipCryption != null) {
787 zipCryption.decryptBytes(b, off, len);
788 }
789
790 return len;
791 }
792
793 public int readRaw(byte b[], int off, int len) throws IOException {
794 synchronized (ZipFile.this) {
795 ensureOpenOrZipException();
796 initDataOffset();
797 if (rem == 0) {
798 return -1;
799 }
800 if (len > rem) {
801 len = (int) rem;
802 }
803 if (len <= 0) {
804 return 0;
805 }
806 len = ZipFile.this.zsrc.readAt(b, off, len, pos);
807 if (len > 0) {
808 pos += len;
809 rem -= len;
810 }
811 }
812 if (rem == 0) {
813 close();
1223
1224 // Iterate through the entries in the central directory
1225 int i = 0;
1226 int hsh = 0;
1227 int pos = 0;
1228 int limit = cen.length - ENDHDR;
1229 while (pos + CENHDR <= limit) {
1230 if (i >= total) {
1231 // This will only happen if the zip file has an incorrect
1232 // ENDTOT field, which usually means it contains more than
1233 // 65535 entries.
1234 initCEN(countCENHeaders(cen, limit));
1235 return;
1236 }
1237 if (CENSIG(cen, pos) != CENSIG)
1238 zerror("invalid CEN header (bad signature)");
1239 int method = CENHOW(cen, pos);
1240 int nlen = CENNAM(cen, pos);
1241 int elen = CENEXT(cen, pos);
1242 int clen = CENCOM(cen, pos);
1243 if (method != STORED && method != DEFLATED)
1244 zerror("invalid CEN header (bad compression method: " + method + ")");
1245 if (pos + CENHDR + nlen > limit)
1246 zerror("invalid CEN header (bad header size)");
1247 // Record the CEN offset and the name hash in our hash cell.
1248 hash = hashN(cen, pos + CENHDR, nlen);
1249 hsh = (hash & 0x7fffffff) % tablelen;
1250 next = table[hsh];
1251 table[hsh] = idx;
1252 idx = addEntry(idx, hash, next, pos);
1253 // Adds name to metanames.
1254 if (isMetaName(cen, pos + CENHDR, nlen)) {
1255 metanames.add(pos);
1256 }
1257 // skip ext and comment
1258 pos += (CENHDR + nlen + elen + clen);
1259 i++;
1260 }
1261 total = i;
1262 if (pos + ENDHDR != cen.length) {
|