| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.commons.compress.archivers; |
| |
| import static org.junit.Assert.*; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.List; |
| import java.util.zip.ZipEntry; |
| |
| import org.apache.commons.compress.AbstractTestCase; |
| import org.apache.commons.compress.archivers.zip.Zip64Mode; |
| import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; |
| import org.apache.commons.compress.archivers.zip.ZipArchiveEntryPredicate; |
| import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; |
| import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; |
| import org.apache.commons.compress.archivers.zip.ZipFile; |
| import org.apache.commons.compress.archivers.zip.ZipMethod; |
| import org.apache.commons.compress.utils.IOUtils; |
| import org.junit.Assert; |
| import org.junit.Test; |
| |
| public final class ZipTestCase extends AbstractTestCase { |
| /** |
| * Archives 2 files and unarchives it again. If the file length of result |
| * and source is the same, it looks like the operations have worked |
| * @throws Exception |
| */ |
| @Test |
| public void testZipArchiveCreation() throws Exception { |
| // Archive |
| final File output = new File(dir, "bla.zip"); |
| final File file1 = getFile("test1.xml"); |
| final File file2 = getFile("test2.xml"); |
| |
| final OutputStream out = new FileOutputStream(output); |
| ArchiveOutputStream os = null; |
| try { |
| os = new ArchiveStreamFactory() |
| .createArchiveOutputStream("zip", out); |
| os.putArchiveEntry(new ZipArchiveEntry("testdata/test1.xml")); |
| IOUtils.copy(new FileInputStream(file1), os); |
| os.closeArchiveEntry(); |
| |
| os.putArchiveEntry(new ZipArchiveEntry("testdata/test2.xml")); |
| IOUtils.copy(new FileInputStream(file2), os); |
| os.closeArchiveEntry(); |
| } finally { |
| if (os != null) { |
| os.close(); |
| } |
| } |
| out.close(); |
| |
| // Unarchive the same |
| final List<File> results = new ArrayList<>(); |
| |
| final InputStream is = new FileInputStream(output); |
| ArchiveInputStream in = null; |
| try { |
| in = new ArchiveStreamFactory() |
| .createArchiveInputStream("zip", is); |
| |
| ZipArchiveEntry entry = null; |
| while((entry = (ZipArchiveEntry)in.getNextEntry()) != null) { |
| final File outfile = new File(resultDir.getCanonicalPath() + "/result/" + entry.getName()); |
| outfile.getParentFile().mkdirs(); |
| try (OutputStream o = new FileOutputStream(outfile)) { |
| IOUtils.copy(in, o); |
| } |
| results.add(outfile); |
| } |
| } finally { |
| if (in != null) { |
| in.close(); |
| } |
| } |
| is.close(); |
| |
| assertEquals(results.size(), 2); |
| File result = results.get(0); |
| assertEquals(file1.length(), result.length()); |
| result = results.get(1); |
| assertEquals(file2.length(), result.length()); |
| } |
| |
| /** |
| * Simple unarchive test. Asserts nothing. |
| * @throws Exception |
| */ |
| @Test |
| public void testZipUnarchive() throws Exception { |
| final File input = getFile("bla.zip"); |
| final InputStream is = new FileInputStream(input); |
| final ArchiveInputStream in = new ArchiveStreamFactory().createArchiveInputStream("zip", is); |
| final ZipArchiveEntry entry = (ZipArchiveEntry)in.getNextEntry(); |
| final OutputStream out = new FileOutputStream(new File(dir, entry.getName())); |
| IOUtils.copy(in, out); |
| out.close(); |
| in.close(); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/COMPRESS-208" |
| * >COMPRESS-208</a>. |
| */ |
| @Test |
| public void testSkipsPK00Prefix() throws Exception { |
| final File input = getFile("COMPRESS-208.zip"); |
| final ArrayList<String> al = new ArrayList<>(); |
| al.add("test1.xml"); |
| al.add("test2.xml"); |
| try (InputStream is = new FileInputStream(input)) { |
| checkArchiveContent(new ZipArchiveInputStream(is), al); |
| } |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/COMPRESS-93" |
| * >COMPRESS-93</a>. |
| */ |
| @Test |
| public void testSupportedCompressionMethod() throws IOException { |
| /* |
| ZipFile bla = new ZipFile(getFile("bla.zip")); |
| assertTrue(bla.canReadEntryData(bla.getEntry("test1.xml"))); |
| bla.close(); |
| */ |
| |
| final ZipFile moby = new ZipFile(getFile("moby.zip")); |
| final ZipArchiveEntry entry = moby.getEntry("README"); |
| assertEquals("method", ZipMethod.TOKENIZATION.getCode(), entry.getMethod()); |
| assertFalse(moby.canReadEntryData(entry)); |
| moby.close(); |
| } |
| |
| /** |
| * Test case for being able to skip an entry in an |
| * {@link ZipArchiveInputStream} even if the compression method of that |
| * entry is unsupported. |
| * |
| * @see <a href="https://issues.apache.org/jira/browse/COMPRESS-93" |
| * >COMPRESS-93</a> |
| */ |
| @Test |
| public void testSkipEntryWithUnsupportedCompressionMethod() |
| throws IOException { |
| try (ZipArchiveInputStream zip = new ZipArchiveInputStream(new FileInputStream(getFile("moby.zip")))) { |
| final ZipArchiveEntry entry = zip.getNextZipEntry(); |
| assertEquals("method", ZipMethod.TOKENIZATION.getCode(), entry.getMethod()); |
| assertEquals("README", entry.getName()); |
| assertFalse(zip.canReadEntryData(entry)); |
| try { |
| assertNull(zip.getNextZipEntry()); |
| } catch (final IOException e) { |
| e.printStackTrace(); |
| fail("COMPRESS-93: Unable to skip an unsupported zip entry"); |
| } |
| } |
| } |
| |
| /** |
| * Checks if all entries from a nested archive can be read. |
| * The archive: OSX_ArchiveWithNestedArchive.zip contains: |
| * NestedArchiv.zip and test.xml3. |
| * |
| * The nested archive: NestedArchive.zip contains test1.xml and test2.xml |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testListAllFilesWithNestedArchive() throws Exception { |
| final File input = getFile("OSX_ArchiveWithNestedArchive.zip"); |
| |
| final List<String> results = new ArrayList<>(); |
| |
| final InputStream is = new FileInputStream(input); |
| ArchiveInputStream in = null; |
| try { |
| in = new ArchiveStreamFactory().createArchiveInputStream("zip", is); |
| |
| ZipArchiveEntry entry = null; |
| while((entry = (ZipArchiveEntry)in.getNextEntry()) != null) { |
| results.add(entry.getName()); |
| |
| final ArchiveInputStream nestedIn = new ArchiveStreamFactory().createArchiveInputStream("zip", in); |
| ZipArchiveEntry nestedEntry = null; |
| while((nestedEntry = (ZipArchiveEntry)nestedIn.getNextEntry()) != null) { |
| results.add(nestedEntry.getName()); |
| } |
| // nested stream must not be closed here |
| } |
| } finally { |
| if (in != null) { |
| in.close(); |
| } |
| } |
| is.close(); |
| |
| results.contains("NestedArchiv.zip"); |
| results.contains("test1.xml"); |
| results.contains("test2.xml"); |
| results.contains("test3.xml"); |
| } |
| |
| @Test |
| public void testDirectoryEntryFromFile() throws Exception { |
| final File[] tmp = createTempDirAndFile(); |
| File archive = null; |
| ZipArchiveOutputStream zos = null; |
| ZipFile zf = null; |
| try { |
| archive = File.createTempFile("test.", ".zip", tmp[0]); |
| archive.deleteOnExit(); |
| zos = new ZipArchiveOutputStream(archive); |
| final long beforeArchiveWrite = tmp[0].lastModified(); |
| final ZipArchiveEntry in = new ZipArchiveEntry(tmp[0], "foo"); |
| zos.putArchiveEntry(in); |
| zos.closeArchiveEntry(); |
| zos.close(); |
| zos = null; |
| zf = new ZipFile(archive); |
| final ZipArchiveEntry out = zf.getEntry("foo/"); |
| assertNotNull(out); |
| assertEquals("foo/", out.getName()); |
| assertEquals(0, out.getSize()); |
| // ZIP stores time with a granularity of 2 seconds |
| assertEquals(beforeArchiveWrite / 2000, |
| out.getLastModifiedDate().getTime() / 2000); |
| assertTrue(out.isDirectory()); |
| } finally { |
| ZipFile.closeQuietly(zf); |
| if (zos != null) { |
| zos.close(); |
| } |
| tryHardToDelete(archive); |
| tryHardToDelete(tmp[1]); |
| rmdir(tmp[0]); |
| } |
| } |
| |
| @Test |
| public void testExplicitDirectoryEntry() throws Exception { |
| final File[] tmp = createTempDirAndFile(); |
| File archive = null; |
| ZipArchiveOutputStream zos = null; |
| ZipFile zf = null; |
| try { |
| archive = File.createTempFile("test.", ".zip", tmp[0]); |
| archive.deleteOnExit(); |
| zos = new ZipArchiveOutputStream(archive); |
| final long beforeArchiveWrite = tmp[0].lastModified(); |
| final ZipArchiveEntry in = new ZipArchiveEntry("foo/"); |
| in.setTime(beforeArchiveWrite); |
| zos.putArchiveEntry(in); |
| zos.closeArchiveEntry(); |
| zos.close(); |
| zos = null; |
| zf = new ZipFile(archive); |
| final ZipArchiveEntry out = zf.getEntry("foo/"); |
| assertNotNull(out); |
| assertEquals("foo/", out.getName()); |
| assertEquals(0, out.getSize()); |
| assertEquals(beforeArchiveWrite / 2000, |
| out.getLastModifiedDate().getTime() / 2000); |
| assertTrue(out.isDirectory()); |
| } finally { |
| ZipFile.closeQuietly(zf); |
| if (zos != null) { |
| zos.close(); |
| } |
| tryHardToDelete(archive); |
| tryHardToDelete(tmp[1]); |
| rmdir(tmp[0]); |
| } |
| } |
| String first_payload = "ABBA"; |
| String second_payload = "AAAAAAAAAAAA"; |
| ZipArchiveEntryPredicate allFilesPredicate = new ZipArchiveEntryPredicate() { |
| @Override |
| public boolean test(final ZipArchiveEntry zipArchiveEntry) { |
| return true; |
| } |
| }; |
| |
| @Test |
| public void testCopyRawEntriesFromFile() |
| throws IOException { |
| |
| final File[] tmp = createTempDirAndFile(); |
| final File reference = createReferenceFile(tmp[0], Zip64Mode.Never, "expected."); |
| |
| final File a1 = File.createTempFile("src1.", ".zip", tmp[0]); |
| final ZipArchiveOutputStream zos = new ZipArchiveOutputStream(a1); |
| zos.setUseZip64(Zip64Mode.Never); |
| createFirstEntry(zos).close(); |
| |
| final File a2 = File.createTempFile("src2.", ".zip", tmp[0]); |
| final ZipArchiveOutputStream zos1 = new ZipArchiveOutputStream(a2); |
| zos1.setUseZip64(Zip64Mode.Never); |
| createSecondEntry(zos1).close(); |
| |
| final ZipFile zf1 = new ZipFile(a1); |
| final ZipFile zf2 = new ZipFile(a2); |
| final File fileResult = File.createTempFile("file-actual.", ".zip", tmp[0]); |
| final ZipArchiveOutputStream zos2 = new ZipArchiveOutputStream(fileResult); |
| zf1.copyRawEntries(zos2, allFilesPredicate); |
| zf2.copyRawEntries(zos2, allFilesPredicate); |
| zos2.close(); |
| // copyRawEntries does not add superfluous zip64 header like regular zip output stream |
| // does when using Zip64Mode.AsNeeded so all the source material has to be Zip64Mode.Never, |
| // if exact binary equality is to be achieved |
| assertSameFileContents(reference, fileResult); |
| zf1.close(); |
| zf2.close(); |
| } |
| |
| @Test |
| public void testCopyRawZip64EntryFromFile() |
| throws IOException { |
| |
| final File[] tmp = createTempDirAndFile(); |
| final File reference = File.createTempFile("z64reference.", ".zip", tmp[0]); |
| final ZipArchiveOutputStream zos1 = new ZipArchiveOutputStream(reference); |
| zos1.setUseZip64(Zip64Mode.Always); |
| createFirstEntry(zos1); |
| zos1.close(); |
| |
| final File a1 = File.createTempFile("zip64src.", ".zip", tmp[0]); |
| final ZipArchiveOutputStream zos = new ZipArchiveOutputStream(a1); |
| zos.setUseZip64(Zip64Mode.Always); |
| createFirstEntry(zos).close(); |
| |
| final ZipFile zf1 = new ZipFile(a1); |
| final File fileResult = File.createTempFile("file-actual.", ".zip", tmp[0]); |
| final ZipArchiveOutputStream zos2 = new ZipArchiveOutputStream(fileResult); |
| zos2.setUseZip64(Zip64Mode.Always); |
| zf1.copyRawEntries(zos2, allFilesPredicate); |
| zos2.close(); |
| assertSameFileContents(reference, fileResult); |
| zf1.close(); |
| } |
| |
| @Test |
| public void testUnixModeInAddRaw() throws IOException { |
| |
| final File[] tmp = createTempDirAndFile(); |
| |
| final File a1 = File.createTempFile("unixModeBits.", ".zip", tmp[0]); |
| final ZipArchiveOutputStream zos = new ZipArchiveOutputStream(a1); |
| |
| final ZipArchiveEntry archiveEntry = new ZipArchiveEntry("fred"); |
| archiveEntry.setUnixMode(0664); |
| archiveEntry.setMethod(ZipEntry.DEFLATED); |
| zos.addRawArchiveEntry(archiveEntry, new ByteArrayInputStream("fud".getBytes())); |
| zos.close(); |
| |
| final ZipFile zf1 = new ZipFile(a1); |
| final ZipArchiveEntry fred = zf1.getEntry("fred"); |
| assertEquals(0664, fred.getUnixMode()); |
| zf1.close(); |
| } |
| |
| private File createReferenceFile(final File directory, final Zip64Mode zipMode, final String prefix) throws IOException { |
| final File reference = File.createTempFile(prefix, ".zip", directory); |
| final ZipArchiveOutputStream zos = new ZipArchiveOutputStream(reference); |
| zos.setUseZip64(zipMode); |
| createFirstEntry(zos); |
| createSecondEntry(zos); |
| zos.close(); |
| return reference; |
| } |
| |
| private ZipArchiveOutputStream createFirstEntry(final ZipArchiveOutputStream zos) throws IOException { |
| createArchiveEntry(first_payload, zos, "file1.txt"); |
| return zos; |
| } |
| |
| private ZipArchiveOutputStream createSecondEntry(final ZipArchiveOutputStream zos) throws IOException { |
| createArchiveEntry(second_payload, zos, "file2.txt"); |
| return zos; |
| } |
| |
| |
| private void assertSameFileContents(final File expectedFile, final File actualFile) throws IOException { |
| final int size = (int) Math.max(expectedFile.length(), actualFile.length()); |
| final ZipFile expected = new ZipFile(expectedFile); |
| final ZipFile actual = new ZipFile(actualFile); |
| final byte[] expectedBuf = new byte[size]; |
| final byte[] actualBuf = new byte[size]; |
| |
| final Enumeration<ZipArchiveEntry> actualInOrder = actual.getEntriesInPhysicalOrder(); |
| final Enumeration<ZipArchiveEntry> expectedInOrder = expected.getEntriesInPhysicalOrder(); |
| |
| while (actualInOrder.hasMoreElements()){ |
| final ZipArchiveEntry actualElement = actualInOrder.nextElement(); |
| final ZipArchiveEntry expectedElement = expectedInOrder.nextElement(); |
| assertEquals( expectedElement.getName(), actualElement.getName()); |
| // Don't compare timestamps since they may vary; |
| // there's no support for stubbed out clock (TimeSource) in ZipArchiveOutputStream |
| assertEquals( expectedElement.getMethod(), actualElement.getMethod()); |
| assertEquals( expectedElement.getGeneralPurposeBit(), actualElement.getGeneralPurposeBit()); |
| assertEquals( expectedElement.getCrc(), actualElement.getCrc()); |
| assertEquals( expectedElement.getCompressedSize(), actualElement.getCompressedSize()); |
| assertEquals( expectedElement.getSize(), actualElement.getSize()); |
| assertEquals( expectedElement.getExternalAttributes(), actualElement.getExternalAttributes()); |
| assertEquals( expectedElement.getInternalAttributes(), actualElement.getInternalAttributes()); |
| |
| final InputStream actualIs = actual.getInputStream(actualElement); |
| final InputStream expectedIs = expected.getInputStream(expectedElement); |
| IOUtils.readFully(expectedIs, expectedBuf); |
| IOUtils.readFully(actualIs, actualBuf); |
| expectedIs.close(); |
| actualIs.close(); |
| Assert.assertArrayEquals(expectedBuf, actualBuf); // Buffers are larger than payload. dont care |
| } |
| |
| expected.close(); |
| actual.close(); |
| } |
| |
| |
| private void createArchiveEntry(final String payload, final ZipArchiveOutputStream zos, final String name) |
| throws IOException { |
| final ZipArchiveEntry in = new ZipArchiveEntry(name); |
| zos.putArchiveEntry(in); |
| |
| zos.write(payload.getBytes()); |
| zos.closeArchiveEntry(); |
| } |
| |
| @Test |
| public void testFileEntryFromFile() throws Exception { |
| final File[] tmp = createTempDirAndFile(); |
| File archive = null; |
| ZipArchiveOutputStream zos = null; |
| ZipFile zf = null; |
| FileInputStream fis = null; |
| try { |
| archive = File.createTempFile("test.", ".zip", tmp[0]); |
| archive.deleteOnExit(); |
| zos = new ZipArchiveOutputStream(archive); |
| final ZipArchiveEntry in = new ZipArchiveEntry(tmp[1], "foo"); |
| zos.putArchiveEntry(in); |
| final byte[] b = new byte[(int) tmp[1].length()]; |
| fis = new FileInputStream(tmp[1]); |
| while (fis.read(b) > 0) { |
| zos.write(b); |
| } |
| fis.close(); |
| fis = null; |
| zos.closeArchiveEntry(); |
| zos.close(); |
| zos = null; |
| zf = new ZipFile(archive); |
| final ZipArchiveEntry out = zf.getEntry("foo"); |
| assertNotNull(out); |
| assertEquals("foo", out.getName()); |
| assertEquals(tmp[1].length(), out.getSize()); |
| assertEquals(tmp[1].lastModified() / 2000, |
| out.getLastModifiedDate().getTime() / 2000); |
| assertFalse(out.isDirectory()); |
| } finally { |
| ZipFile.closeQuietly(zf); |
| if (zos != null) { |
| zos.close(); |
| } |
| tryHardToDelete(archive); |
| if (fis != null) { |
| fis.close(); |
| } |
| tryHardToDelete(tmp[1]); |
| rmdir(tmp[0]); |
| } |
| } |
| |
| @Test |
| public void testExplicitFileEntry() throws Exception { |
| final File[] tmp = createTempDirAndFile(); |
| File archive = null; |
| ZipArchiveOutputStream zos = null; |
| ZipFile zf = null; |
| FileInputStream fis = null; |
| try { |
| archive = File.createTempFile("test.", ".zip", tmp[0]); |
| archive.deleteOnExit(); |
| zos = new ZipArchiveOutputStream(archive); |
| final ZipArchiveEntry in = new ZipArchiveEntry("foo"); |
| in.setTime(tmp[1].lastModified()); |
| in.setSize(tmp[1].length()); |
| zos.putArchiveEntry(in); |
| final byte[] b = new byte[(int) tmp[1].length()]; |
| fis = new FileInputStream(tmp[1]); |
| while (fis.read(b) > 0) { |
| zos.write(b); |
| } |
| fis.close(); |
| fis = null; |
| zos.closeArchiveEntry(); |
| zos.close(); |
| zos = null; |
| zf = new ZipFile(archive); |
| final ZipArchiveEntry out = zf.getEntry("foo"); |
| assertNotNull(out); |
| assertEquals("foo", out.getName()); |
| assertEquals(tmp[1].length(), out.getSize()); |
| assertEquals(tmp[1].lastModified() / 2000, |
| out.getLastModifiedDate().getTime() / 2000); |
| assertFalse(out.isDirectory()); |
| } finally { |
| ZipFile.closeQuietly(zf); |
| if (zos != null) { |
| zos.close(); |
| } |
| tryHardToDelete(archive); |
| if (fis != null) { |
| fis.close(); |
| } |
| tryHardToDelete(tmp[1]); |
| rmdir(tmp[0]); |
| } |
| } |
| } |