1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 package org.apache.commons.compress.archivers; 20 21 import static org.junit.Assert.*; 22 23 import java.io.ByteArrayInputStream; 24 import java.io.File; 25 import java.io.FileInputStream; 26 import java.io.FileOutputStream; 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.io.OutputStream; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.Enumeration; 33 import java.util.HashMap; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.zip.ZipEntry; 37 import java.util.zip.ZipException; 38 39 import org.apache.commons.compress.AbstractTestCase; 40 import org.apache.commons.compress.archivers.zip.Zip64Mode; 41 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; 42 import org.apache.commons.compress.archivers.zip.ZipArchiveEntryPredicate; 43 import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; 44 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; 45 import org.apache.commons.compress.archivers.zip.ZipFile; 46 import org.apache.commons.compress.archivers.zip.ZipMethod; 47 import org.apache.commons.compress.utils.IOUtils; 48 import org.apache.commons.compress.utils.InputStreamStatistics; 49 import org.apache.commons.compress.utils.SeekableInMemoryByteChannel; 50 import org.junit.Assert; 51 import org.junit.Test; 52 53 public final class ZipTestCase extends AbstractTestCase { 54 /** 55 * Archives 2 files and unarchives it again. If the file length of result 56 * and source is the same, it looks like the operations have worked 57 * @throws Exception 58 */ 59 @Test testZipArchiveCreation()60 public void testZipArchiveCreation() throws Exception { 61 // Archive 62 final File output = new File(dir, "bla.zip"); 63 final File file1 = getFile("test1.xml"); 64 final File file2 = getFile("test2.xml"); 65 66 final OutputStream out = new FileOutputStream(output); 67 ArchiveOutputStream os = null; 68 try { 69 os = new ArchiveStreamFactory() 70 .createArchiveOutputStream("zip", out); 71 os.putArchiveEntry(new ZipArchiveEntry("testdata/test1.xml")); 72 IOUtils.copy(new FileInputStream(file1), os); 73 os.closeArchiveEntry(); 74 75 os.putArchiveEntry(new ZipArchiveEntry("testdata/test2.xml")); 76 IOUtils.copy(new FileInputStream(file2), os); 77 os.closeArchiveEntry(); 78 } finally { 79 if (os != null) { 80 os.close(); 81 } 82 } 83 out.close(); 84 85 // Unarchive the same 86 final List<File> results = new ArrayList<>(); 87 88 final InputStream is = new FileInputStream(output); 89 ArchiveInputStream in = null; 90 try { 91 in = new ArchiveStreamFactory() 92 .createArchiveInputStream("zip", is); 93 94 ZipArchiveEntry entry = null; 95 while((entry = (ZipArchiveEntry)in.getNextEntry()) != null) { 96 final File outfile = new File(resultDir.getCanonicalPath() + "/result/" + entry.getName()); 97 outfile.getParentFile().mkdirs(); 98 try (OutputStream o = new FileOutputStream(outfile)) { 99 IOUtils.copy(in, o); 100 } 101 results.add(outfile); 102 } 103 } finally { 104 if (in != null) { 105 in.close(); 106 } 107 } 108 is.close(); 109 110 assertEquals(results.size(), 2); 111 File result = results.get(0); 112 assertEquals(file1.length(), result.length()); 113 result = results.get(1); 114 assertEquals(file2.length(), result.length()); 115 } 116 117 /** 118 * Archives 2 files and unarchives it again. If the file contents of result 119 * and source is the same, it looks like the operations have worked 120 * @throws Exception 121 */ 122 @Test testZipArchiveCreationInMemory()123 public void testZipArchiveCreationInMemory() throws Exception { 124 final File file1 = getFile("test1.xml"); 125 final File file2 = getFile("test2.xml"); 126 final byte[] file1Contents = new byte[(int) file1.length()]; 127 final byte[] file2Contents = new byte[(int) file2.length()]; 128 IOUtils.readFully(new FileInputStream(file1), file1Contents); 129 IOUtils.readFully(new FileInputStream(file2), file2Contents); 130 131 SeekableInMemoryByteChannel channel = new SeekableInMemoryByteChannel(); 132 try (ZipArchiveOutputStream os = new ZipArchiveOutputStream(channel)) { 133 os.putArchiveEntry(new ZipArchiveEntry("testdata/test1.xml")); 134 os.write(file1Contents); 135 os.closeArchiveEntry(); 136 137 os.putArchiveEntry(new ZipArchiveEntry("testdata/test2.xml")); 138 os.write(file2Contents); 139 os.closeArchiveEntry(); 140 } 141 142 // Unarchive the same 143 final List<byte[]> results = new ArrayList<>(); 144 145 try (ArchiveInputStream in = new ArchiveStreamFactory() 146 .createArchiveInputStream("zip", new ByteArrayInputStream(channel.array()))) { 147 148 ZipArchiveEntry entry; 149 while((entry = (ZipArchiveEntry)in.getNextEntry()) != null) { 150 byte[] result = new byte[(int) entry.getSize()]; 151 IOUtils.readFully(in, result); 152 results.add(result); 153 } 154 } 155 156 assertArrayEquals(results.get(0), file1Contents); 157 assertArrayEquals(results.get(1), file2Contents); 158 } 159 160 /** 161 * Simple unarchive test. Asserts nothing. 162 * @throws Exception 163 */ 164 @Test testZipUnarchive()165 public void testZipUnarchive() throws Exception { 166 final File input = getFile("bla.zip"); 167 try (final InputStream is = new FileInputStream(input); 168 final ArchiveInputStream in = new ArchiveStreamFactory().createArchiveInputStream("zip", is)) { 169 final ZipArchiveEntry entry = (ZipArchiveEntry) in.getNextEntry(); 170 try (final OutputStream out = new FileOutputStream(new File(dir, entry.getName()))) { 171 IOUtils.copy(in, out); 172 } 173 } 174 } 175 176 /** 177 * Test case for 178 * <a href="https://issues.apache.org/jira/browse/COMPRESS-208" 179 * >COMPRESS-208</a>. 180 */ 181 @Test testSkipsPK00Prefix()182 public void testSkipsPK00Prefix() throws Exception { 183 final File input = getFile("COMPRESS-208.zip"); 184 final ArrayList<String> al = new ArrayList<>(); 185 al.add("test1.xml"); 186 al.add("test2.xml"); 187 try (InputStream is = new FileInputStream(input)) { 188 checkArchiveContent(new ZipArchiveInputStream(is), al); 189 } 190 } 191 192 /** 193 * Test case for 194 * <a href="https://issues.apache.org/jira/browse/COMPRESS-93" 195 * >COMPRESS-93</a>. 196 */ 197 @Test testSupportedCompressionMethod()198 public void testSupportedCompressionMethod() throws IOException { 199 /* 200 ZipFile bla = new ZipFile(getFile("bla.zip")); 201 assertTrue(bla.canReadEntryData(bla.getEntry("test1.xml"))); 202 bla.close(); 203 */ 204 205 final ZipFile moby = new ZipFile(getFile("moby.zip")); 206 final ZipArchiveEntry entry = moby.getEntry("README"); 207 assertEquals("method", ZipMethod.TOKENIZATION.getCode(), entry.getMethod()); 208 assertFalse(moby.canReadEntryData(entry)); 209 moby.close(); 210 } 211 212 /** 213 * Test case for being able to skip an entry in an 214 * {@link ZipArchiveInputStream} even if the compression method of that 215 * entry is unsupported. 216 * 217 * @see <a href="https://issues.apache.org/jira/browse/COMPRESS-93" 218 * >COMPRESS-93</a> 219 */ 220 @Test testSkipEntryWithUnsupportedCompressionMethod()221 public void testSkipEntryWithUnsupportedCompressionMethod() 222 throws IOException { 223 try (ZipArchiveInputStream zip = new ZipArchiveInputStream(new FileInputStream(getFile("moby.zip")))) { 224 final ZipArchiveEntry entry = zip.getNextZipEntry(); 225 assertEquals("method", ZipMethod.TOKENIZATION.getCode(), entry.getMethod()); 226 assertEquals("README", entry.getName()); 227 assertFalse(zip.canReadEntryData(entry)); 228 try { 229 assertNull(zip.getNextZipEntry()); 230 } catch (final IOException e) { 231 e.printStackTrace(); 232 fail("COMPRESS-93: Unable to skip an unsupported zip entry"); 233 } 234 } 235 } 236 237 /** 238 * Checks if all entries from a nested archive can be read. 239 * The archive: OSX_ArchiveWithNestedArchive.zip contains: 240 * NestedArchiv.zip and test.xml3. 241 * 242 * The nested archive: NestedArchive.zip contains test1.xml and test2.xml 243 * 244 * @throws Exception 245 */ 246 @Test testListAllFilesWithNestedArchive()247 public void testListAllFilesWithNestedArchive() throws Exception { 248 final File input = getFile("OSX_ArchiveWithNestedArchive.zip"); 249 250 final List<String> results = new ArrayList<>(); 251 final List<ZipException> expectedExceptions = new ArrayList<>(); 252 253 final InputStream is = new FileInputStream(input); 254 ArchiveInputStream in = null; 255 try { 256 in = new ArchiveStreamFactory().createArchiveInputStream("zip", is); 257 258 ZipArchiveEntry entry = null; 259 while ((entry = (ZipArchiveEntry) in.getNextEntry()) != null) { 260 results.add(entry.getName()); 261 262 final ArchiveInputStream nestedIn = new ArchiveStreamFactory().createArchiveInputStream("zip", in); 263 try { 264 ZipArchiveEntry nestedEntry = null; 265 while ((nestedEntry = (ZipArchiveEntry) nestedIn.getNextEntry()) != null) { 266 results.add(nestedEntry.getName()); 267 } 268 } catch (ZipException ex) { 269 // expected since you cannot create a final ArchiveInputStream from test3.xml 270 expectedExceptions.add(ex); 271 } 272 // nested stream must not be closed here 273 } 274 } finally { 275 if (in != null) { 276 in.close(); 277 } 278 } 279 is.close(); 280 281 assertTrue(results.contains("NestedArchiv.zip")); 282 assertTrue(results.contains("test1.xml")); 283 assertTrue(results.contains("test2.xml")); 284 assertTrue(results.contains("test3.xml")); 285 assertEquals(1, expectedExceptions.size()); 286 } 287 288 @Test testDirectoryEntryFromFile()289 public void testDirectoryEntryFromFile() throws Exception { 290 final File[] tmp = createTempDirAndFile(); 291 File archive = null; 292 ZipArchiveOutputStream zos = null; 293 ZipFile zf = null; 294 try { 295 archive = File.createTempFile("test.", ".zip", tmp[0]); 296 archive.deleteOnExit(); 297 zos = new ZipArchiveOutputStream(archive); 298 final long beforeArchiveWrite = tmp[0].lastModified(); 299 final ZipArchiveEntry in = new ZipArchiveEntry(tmp[0], "foo"); 300 zos.putArchiveEntry(in); 301 zos.closeArchiveEntry(); 302 zos.close(); 303 zos = null; 304 zf = new ZipFile(archive); 305 final ZipArchiveEntry out = zf.getEntry("foo/"); 306 assertNotNull(out); 307 assertEquals("foo/", out.getName()); 308 assertEquals(0, out.getSize()); 309 // ZIP stores time with a granularity of 2 seconds 310 assertEquals(beforeArchiveWrite / 2000, 311 out.getLastModifiedDate().getTime() / 2000); 312 assertTrue(out.isDirectory()); 313 } finally { 314 ZipFile.closeQuietly(zf); 315 if (zos != null) { 316 zos.close(); 317 } 318 tryHardToDelete(archive); 319 tryHardToDelete(tmp[1]); 320 rmdir(tmp[0]); 321 } 322 } 323 324 @Test testExplicitDirectoryEntry()325 public void testExplicitDirectoryEntry() throws Exception { 326 final File[] tmp = createTempDirAndFile(); 327 File archive = null; 328 ZipArchiveOutputStream zos = null; 329 ZipFile zf = null; 330 try { 331 archive = File.createTempFile("test.", ".zip", tmp[0]); 332 archive.deleteOnExit(); 333 zos = new ZipArchiveOutputStream(archive); 334 final long beforeArchiveWrite = tmp[0].lastModified(); 335 final ZipArchiveEntry in = new ZipArchiveEntry("foo/"); 336 in.setTime(beforeArchiveWrite); 337 zos.putArchiveEntry(in); 338 zos.closeArchiveEntry(); 339 zos.close(); 340 zos = null; 341 zf = new ZipFile(archive); 342 final ZipArchiveEntry out = zf.getEntry("foo/"); 343 assertNotNull(out); 344 assertEquals("foo/", out.getName()); 345 assertEquals(0, out.getSize()); 346 assertEquals(beforeArchiveWrite / 2000, 347 out.getLastModifiedDate().getTime() / 2000); 348 assertTrue(out.isDirectory()); 349 } finally { 350 ZipFile.closeQuietly(zf); 351 if (zos != null) { 352 zos.close(); 353 } 354 tryHardToDelete(archive); 355 tryHardToDelete(tmp[1]); 356 rmdir(tmp[0]); 357 } 358 } 359 String first_payload = "ABBA"; 360 String second_payload = "AAAAAAAAAAAA"; 361 ZipArchiveEntryPredicate allFilesPredicate = new ZipArchiveEntryPredicate() { 362 @Override 363 public boolean test(final ZipArchiveEntry zipArchiveEntry) { 364 return true; 365 } 366 }; 367 368 @Test testCopyRawEntriesFromFile()369 public void testCopyRawEntriesFromFile() 370 throws IOException { 371 372 final File[] tmp = createTempDirAndFile(); 373 final File reference = createReferenceFile(tmp[0], Zip64Mode.Never, "expected."); 374 375 final File a1 = File.createTempFile("src1.", ".zip", tmp[0]); 376 try (final ZipArchiveOutputStream zos = new ZipArchiveOutputStream(a1)) { 377 zos.setUseZip64(Zip64Mode.Never); 378 createFirstEntry(zos).close(); 379 } 380 381 final File a2 = File.createTempFile("src2.", ".zip", tmp[0]); 382 try (final ZipArchiveOutputStream zos1 = new ZipArchiveOutputStream(a2)) { 383 zos1.setUseZip64(Zip64Mode.Never); 384 createSecondEntry(zos1).close(); 385 } 386 387 try (final ZipFile zf1 = new ZipFile(a1); final ZipFile zf2 = new ZipFile(a2)) { 388 final File fileResult = File.createTempFile("file-actual.", ".zip", tmp[0]); 389 try (final ZipArchiveOutputStream zos2 = new ZipArchiveOutputStream(fileResult)) { 390 zf1.copyRawEntries(zos2, allFilesPredicate); 391 zf2.copyRawEntries(zos2, allFilesPredicate); 392 } 393 // copyRawEntries does not add superfluous zip64 header like regular zip output stream 394 // does when using Zip64Mode.AsNeeded so all the source material has to be Zip64Mode.Never, 395 // if exact binary equality is to be achieved 396 assertSameFileContents(reference, fileResult); 397 } 398 } 399 400 @Test testCopyRawZip64EntryFromFile()401 public void testCopyRawZip64EntryFromFile() 402 throws IOException { 403 404 final File[] tmp = createTempDirAndFile(); 405 final File reference = File.createTempFile("z64reference.", ".zip", tmp[0]); 406 try (final ZipArchiveOutputStream zos1 = new ZipArchiveOutputStream(reference)) { 407 zos1.setUseZip64(Zip64Mode.Always); 408 createFirstEntry(zos1); 409 } 410 411 final File a1 = File.createTempFile("zip64src.", ".zip", tmp[0]); 412 try (final ZipArchiveOutputStream zos = new ZipArchiveOutputStream(a1)) { 413 zos.setUseZip64(Zip64Mode.Always); 414 createFirstEntry(zos).close(); 415 } 416 417 final File fileResult = File.createTempFile("file-actual.", ".zip", tmp[0]); 418 try (final ZipFile zf1 = new ZipFile(a1)) { 419 try (final ZipArchiveOutputStream zos2 = new ZipArchiveOutputStream(fileResult)) { 420 zos2.setUseZip64(Zip64Mode.Always); 421 zf1.copyRawEntries(zos2, allFilesPredicate); 422 } 423 assertSameFileContents(reference, fileResult); 424 } 425 } 426 427 @Test testUnixModeInAddRaw()428 public void testUnixModeInAddRaw() throws IOException { 429 430 final File[] tmp = createTempDirAndFile(); 431 432 final File a1 = File.createTempFile("unixModeBits.", ".zip", tmp[0]); 433 try (final ZipArchiveOutputStream zos = new ZipArchiveOutputStream(a1)) { 434 435 final ZipArchiveEntry archiveEntry = new ZipArchiveEntry("fred"); 436 archiveEntry.setUnixMode(0664); 437 archiveEntry.setMethod(ZipEntry.DEFLATED); 438 zos.addRawArchiveEntry(archiveEntry, new ByteArrayInputStream("fud".getBytes())); 439 } 440 441 try (final ZipFile zf1 = new ZipFile(a1)) { 442 final ZipArchiveEntry fred = zf1.getEntry("fred"); 443 assertEquals(0664, fred.getUnixMode()); 444 } 445 } 446 createReferenceFile(final File directory, final Zip64Mode zipMode, final String prefix)447 private File createReferenceFile(final File directory, final Zip64Mode zipMode, final String prefix) 448 throws IOException { 449 final File reference = File.createTempFile(prefix, ".zip", directory); 450 try (final ZipArchiveOutputStream zos = new ZipArchiveOutputStream(reference)) { 451 zos.setUseZip64(zipMode); 452 createFirstEntry(zos); 453 createSecondEntry(zos); 454 } 455 return reference; 456 } 457 createFirstEntry(final ZipArchiveOutputStream zos)458 private ZipArchiveOutputStream createFirstEntry(final ZipArchiveOutputStream zos) throws IOException { 459 createArchiveEntry(first_payload, zos, "file1.txt"); 460 return zos; 461 } 462 createSecondEntry(final ZipArchiveOutputStream zos)463 private ZipArchiveOutputStream createSecondEntry(final ZipArchiveOutputStream zos) throws IOException { 464 createArchiveEntry(second_payload, zos, "file2.txt"); 465 return zos; 466 } 467 468 assertSameFileContents(final File expectedFile, final File actualFile)469 private void assertSameFileContents(final File expectedFile, final File actualFile) throws IOException { 470 final int size = (int) Math.max(expectedFile.length(), actualFile.length()); 471 try (final ZipFile expected = new ZipFile(expectedFile); final ZipFile actual = new ZipFile(actualFile)) { 472 final byte[] expectedBuf = new byte[size]; 473 final byte[] actualBuf = new byte[size]; 474 475 final Enumeration<ZipArchiveEntry> actualInOrder = actual.getEntriesInPhysicalOrder(); 476 final Enumeration<ZipArchiveEntry> expectedInOrder = expected.getEntriesInPhysicalOrder(); 477 478 while (actualInOrder.hasMoreElements()) { 479 final ZipArchiveEntry actualElement = actualInOrder.nextElement(); 480 final ZipArchiveEntry expectedElement = expectedInOrder.nextElement(); 481 assertEquals(expectedElement.getName(), actualElement.getName()); 482 // Don't compare timestamps since they may vary; 483 // there's no support for stubbed out clock (TimeSource) in ZipArchiveOutputStream 484 assertEquals(expectedElement.getMethod(), actualElement.getMethod()); 485 assertEquals(expectedElement.getGeneralPurposeBit(), actualElement.getGeneralPurposeBit()); 486 assertEquals(expectedElement.getCrc(), actualElement.getCrc()); 487 assertEquals(expectedElement.getCompressedSize(), actualElement.getCompressedSize()); 488 assertEquals(expectedElement.getSize(), actualElement.getSize()); 489 assertEquals(expectedElement.getExternalAttributes(), actualElement.getExternalAttributes()); 490 assertEquals(expectedElement.getInternalAttributes(), actualElement.getInternalAttributes()); 491 492 final InputStream actualIs = actual.getInputStream(actualElement); 493 final InputStream expectedIs = expected.getInputStream(expectedElement); 494 IOUtils.readFully(expectedIs, expectedBuf); 495 IOUtils.readFully(actualIs, actualBuf); 496 expectedIs.close(); 497 actualIs.close(); 498 Assert.assertArrayEquals(expectedBuf, actualBuf); // Buffers are larger than payload. dont care 499 } 500 501 } 502 } 503 504 createArchiveEntry(final String payload, final ZipArchiveOutputStream zos, final String name)505 private void createArchiveEntry(final String payload, final ZipArchiveOutputStream zos, final String name) 506 throws IOException { 507 final ZipArchiveEntry in = new ZipArchiveEntry(name); 508 zos.putArchiveEntry(in); 509 510 zos.write(payload.getBytes()); 511 zos.closeArchiveEntry(); 512 } 513 514 @Test testFileEntryFromFile()515 public void testFileEntryFromFile() throws Exception { 516 final File[] tmp = createTempDirAndFile(); 517 File archive = null; 518 ZipArchiveOutputStream zos = null; 519 ZipFile zf = null; 520 FileInputStream fis = null; 521 try { 522 archive = File.createTempFile("test.", ".zip", tmp[0]); 523 archive.deleteOnExit(); 524 zos = new ZipArchiveOutputStream(archive); 525 final ZipArchiveEntry in = new ZipArchiveEntry(tmp[1], "foo"); 526 zos.putArchiveEntry(in); 527 final byte[] b = new byte[(int) tmp[1].length()]; 528 fis = new FileInputStream(tmp[1]); 529 while (fis.read(b) > 0) { 530 zos.write(b); 531 } 532 fis.close(); 533 fis = null; 534 zos.closeArchiveEntry(); 535 zos.close(); 536 zos = null; 537 zf = new ZipFile(archive); 538 final ZipArchiveEntry out = zf.getEntry("foo"); 539 assertNotNull(out); 540 assertEquals("foo", out.getName()); 541 assertEquals(tmp[1].length(), out.getSize()); 542 assertEquals(tmp[1].lastModified() / 2000, 543 out.getLastModifiedDate().getTime() / 2000); 544 assertFalse(out.isDirectory()); 545 } finally { 546 ZipFile.closeQuietly(zf); 547 if (zos != null) { 548 zos.close(); 549 } 550 tryHardToDelete(archive); 551 if (fis != null) { 552 fis.close(); 553 } 554 tryHardToDelete(tmp[1]); 555 rmdir(tmp[0]); 556 } 557 } 558 559 @Test testExplicitFileEntry()560 public void testExplicitFileEntry() throws Exception { 561 final File[] tmp = createTempDirAndFile(); 562 File archive = null; 563 ZipArchiveOutputStream zos = null; 564 ZipFile zf = null; 565 FileInputStream fis = null; 566 try { 567 archive = File.createTempFile("test.", ".zip", tmp[0]); 568 archive.deleteOnExit(); 569 zos = new ZipArchiveOutputStream(archive); 570 final ZipArchiveEntry in = new ZipArchiveEntry("foo"); 571 in.setTime(tmp[1].lastModified()); 572 in.setSize(tmp[1].length()); 573 zos.putArchiveEntry(in); 574 final byte[] b = new byte[(int) tmp[1].length()]; 575 fis = new FileInputStream(tmp[1]); 576 while (fis.read(b) > 0) { 577 zos.write(b); 578 } 579 fis.close(); 580 fis = null; 581 zos.closeArchiveEntry(); 582 zos.close(); 583 zos = null; 584 zf = new ZipFile(archive); 585 final ZipArchiveEntry out = zf.getEntry("foo"); 586 assertNotNull(out); 587 assertEquals("foo", out.getName()); 588 assertEquals(tmp[1].length(), out.getSize()); 589 assertEquals(tmp[1].lastModified() / 2000, 590 out.getLastModifiedDate().getTime() / 2000); 591 assertFalse(out.isDirectory()); 592 } finally { 593 ZipFile.closeQuietly(zf); 594 if (zos != null) { 595 zos.close(); 596 } 597 tryHardToDelete(archive); 598 if (fis != null) { 599 fis.close(); 600 } 601 tryHardToDelete(tmp[1]); 602 rmdir(tmp[0]); 603 } 604 } 605 606 @Test inputStreamStatisticsOfZipBombExcel()607 public void inputStreamStatisticsOfZipBombExcel() throws IOException, ArchiveException { 608 Map<String, List<Long>> expected = new HashMap<String, List<Long>>() {{ 609 put("[Content_Types].xml", Arrays.asList(8390036L, 8600L)); 610 put("xl/worksheets/sheet1.xml", Arrays.asList(1348L, 508L)); 611 }}; 612 testInputStreamStatistics("zipbomb.xlsx", expected); 613 } 614 615 @Test inputStreamStatisticsForImplodedEntry()616 public void inputStreamStatisticsForImplodedEntry() throws IOException, ArchiveException { 617 Map<String, List<Long>> expected = new HashMap<String, List<Long>>() {{ 618 put("LICENSE.TXT", Arrays.asList(11560L, 4131L)); 619 }}; 620 testInputStreamStatistics("imploding-8Kdict-3trees.zip", expected); 621 } 622 623 @Test inputStreamStatisticsForShrunkEntry()624 public void inputStreamStatisticsForShrunkEntry() throws IOException, ArchiveException { 625 Map<String, List<Long>> expected = new HashMap<String, List<Long>>() {{ 626 put("TEST1.XML", Arrays.asList(76L, 66L)); 627 put("TEST2.XML", Arrays.asList(81L, 76L)); 628 }}; 629 testInputStreamStatistics("SHRUNK.ZIP", expected); 630 } 631 632 @Test inputStreamStatisticsForStoredEntry()633 public void inputStreamStatisticsForStoredEntry() throws IOException, ArchiveException { 634 Map<String, List<Long>> expected = new HashMap<String, List<Long>>() {{ 635 put("test.txt", Arrays.asList(5L, 5L)); 636 }}; 637 testInputStreamStatistics("COMPRESS-264.zip", expected); 638 } 639 640 @Test inputStreamStatisticsForBzip2Entry()641 public void inputStreamStatisticsForBzip2Entry() throws IOException, ArchiveException { 642 Map<String, List<Long>> expected = new HashMap<String, List<Long>>() {{ 643 put("lots-of-as", Arrays.asList(42L, 39L)); 644 }}; 645 testInputStreamStatistics("bzip2-zip.zip", expected); 646 } 647 648 @Test inputStreamStatisticsForDeflate64Entry()649 public void inputStreamStatisticsForDeflate64Entry() throws IOException, ArchiveException { 650 Map<String, List<Long>> expected = new HashMap<String, List<Long>>() {{ 651 put("input2", Arrays.asList(3072L, 2111L)); 652 }}; 653 testInputStreamStatistics("COMPRESS-380/COMPRESS-380.zip", expected); 654 } 655 testInputStreamStatistics(String fileName, Map<String, List<Long>> expectedStatistics)656 private void testInputStreamStatistics(String fileName, Map<String, List<Long>> expectedStatistics) 657 throws IOException, ArchiveException { 658 final File input = getFile(fileName); 659 660 final Map<String,List<List<Long>>> actualStatistics = new HashMap<>(); 661 662 // stream access 663 try (final FileInputStream fis = new FileInputStream(input); 664 final ArchiveInputStream in = new ArchiveStreamFactory().createArchiveInputStream("zip", fis)) { 665 for (ArchiveEntry entry; (entry = in.getNextEntry()) != null; ) { 666 readStream(in, entry, actualStatistics); 667 } 668 } 669 670 // file access 671 try (final ZipFile zf = new ZipFile(input)) { 672 final Enumeration<ZipArchiveEntry> entries = zf.getEntries(); 673 while (entries.hasMoreElements()) { 674 final ZipArchiveEntry zae = entries.nextElement(); 675 try (InputStream in = zf.getInputStream(zae)) { 676 readStream(in, zae, actualStatistics); 677 } 678 } 679 } 680 681 // compare statistics of stream / file access 682 for (Map.Entry<String,List<List<Long>>> me : actualStatistics.entrySet()) { 683 assertEquals("Mismatch of stats for: " + me.getKey(), 684 me.getValue().get(0), me.getValue().get(1)); 685 } 686 687 for (Map.Entry<String, List<Long>> me : expectedStatistics.entrySet()) { 688 assertEquals("Mismatch of stats with expected value for: " + me.getKey(), 689 me.getValue(), actualStatistics.get(me.getKey()).get(0)); 690 } 691 } 692 readStream(final InputStream in, final ArchiveEntry entry, final Map<String,List<List<Long>>> map)693 private void readStream(final InputStream in, final ArchiveEntry entry, final Map<String,List<List<Long>>> map) throws IOException { 694 final byte[] buf = new byte[4096]; 695 final InputStreamStatistics stats = (InputStreamStatistics) in; 696 while (in.read(buf) != -1); 697 698 final String name = entry.getName(); 699 final List<List<Long>> l; 700 if (map.containsKey(name)) { 701 l = map.get(name); 702 } else { 703 map.put(name, l = new ArrayList<>()); 704 } 705 706 final long t = stats.getUncompressedCount(); 707 final long b = stats.getCompressedCount(); 708 l.add(Arrays.asList(t, b)); 709 } 710 } 711