View Javadoc
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.Enumeration;
32  import java.util.List;
33  import java.util.zip.ZipEntry;
34  
35  import org.apache.commons.compress.AbstractTestCase;
36  import org.apache.commons.compress.archivers.zip.Zip64Mode;
37  import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
38  import org.apache.commons.compress.archivers.zip.ZipArchiveEntryPredicate;
39  import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; 	
40  import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; 	
41  import org.apache.commons.compress.archivers.zip.ZipFile; 	
42  import org.apache.commons.compress.archivers.zip.ZipMethod;
43  import org.apache.commons.compress.utils.IOUtils;
44  import org.junit.Assert;
45  import org.junit.Test;
46  
47  public final class ZipTestCase extends AbstractTestCase {
48      /**
49       * Archives 2 files and unarchives it again. If the file length of result
50       * and source is the same, it looks like the operations have worked
51       * @throws Exception
52       */
53      @Test
54      public void testZipArchiveCreation() throws Exception {
55          // Archive
56          final File output = new File(dir, "bla.zip");
57          final File file1 = getFile("test1.xml");
58          final File file2 = getFile("test2.xml");
59  
60          final OutputStream out = new FileOutputStream(output);
61          ArchiveOutputStream os = null;
62          try {
63              os = new ArchiveStreamFactory()
64                  .createArchiveOutputStream("zip", out);
65              os.putArchiveEntry(new ZipArchiveEntry("testdata/test1.xml"));
66              IOUtils.copy(new FileInputStream(file1), os);
67              os.closeArchiveEntry();
68  
69              os.putArchiveEntry(new ZipArchiveEntry("testdata/test2.xml"));
70              IOUtils.copy(new FileInputStream(file2), os);
71              os.closeArchiveEntry();
72          } finally {
73              if (os != null) {
74                  os.close();
75              }
76          }
77          out.close();
78  
79          // Unarchive the same
80          final List<File> results = new ArrayList<File>();
81  
82          final InputStream is = new FileInputStream(output);
83          ArchiveInputStream in = null;
84          try {
85              in = new ArchiveStreamFactory()
86                  .createArchiveInputStream("zip", is);
87  
88              ZipArchiveEntry entry = null;
89              while((entry = (ZipArchiveEntry)in.getNextEntry()) != null) {
90                  final File outfile = new File(resultDir.getCanonicalPath() + "/result/" + entry.getName());
91                  outfile.getParentFile().mkdirs();
92                  final OutputStream o = new FileOutputStream(outfile);
93                  try {
94                      IOUtils.copy(in, o);
95                  } finally {
96                      o.close();
97                  }
98                  results.add(outfile);
99              }
100         } finally {
101             if (in != null) {
102                 in.close();
103             }
104         }
105         is.close();
106 
107         assertEquals(results.size(), 2);
108         File result = results.get(0);
109         assertEquals(file1.length(), result.length());
110         result = results.get(1);
111         assertEquals(file2.length(), result.length());
112     }
113 
114     /**
115      * Simple unarchive test. Asserts nothing.
116      * @throws Exception
117      */
118     @Test
119     public void testZipUnarchive() throws Exception {
120         final File input = getFile("bla.zip");
121         final InputStream is = new FileInputStream(input);
122         final ArchiveInputStream in = new ArchiveStreamFactory().createArchiveInputStream("zip", is);
123         final ZipArchiveEntry entry = (ZipArchiveEntry)in.getNextEntry();
124         final OutputStream out = new FileOutputStream(new File(dir, entry.getName()));
125         IOUtils.copy(in, out);
126         out.close();
127         in.close();
128     }
129 
130     /**
131      * Test case for 
132      * <a href="https://issues.apache.org/jira/browse/COMPRESS-208"
133      * >COMPRESS-208</a>.
134      */
135     @Test
136     public void testSkipsPK00Prefix() throws Exception {
137         final File input = getFile("COMPRESS-208.zip");
138         final InputStream is = new FileInputStream(input);
139         final ArrayList<String> al = new ArrayList<String>();
140         al.add("test1.xml");
141         al.add("test2.xml");
142         try {
143             checkArchiveContent(new ZipArchiveInputStream(is), al);
144         } finally {
145             is.close();
146         }
147     }
148 
149     /**
150      * Test case for
151      * <a href="https://issues.apache.org/jira/browse/COMPRESS-93"
152      * >COMPRESS-93</a>.
153      */
154     @Test
155     public void testSupportedCompressionMethod() throws IOException {
156         /*
157         ZipFile bla = new ZipFile(getFile("bla.zip"));
158         assertTrue(bla.canReadEntryData(bla.getEntry("test1.xml")));
159         bla.close();
160         */
161         
162         final ZipFile moby = new ZipFile(getFile("moby.zip"));
163         final ZipArchiveEntry entry = moby.getEntry("README");
164         assertEquals("method", ZipMethod.TOKENIZATION.getCode(), entry.getMethod());
165         assertFalse(moby.canReadEntryData(entry));
166         moby.close();
167     }
168 
169     /**
170      * Test case for being able to skip an entry in an 
171      * {@link ZipArchiveInputStream} even if the compression method of that
172      * entry is unsupported.
173      *
174      * @see <a href="https://issues.apache.org/jira/browse/COMPRESS-93"
175      *        >COMPRESS-93</a>
176      */
177     @Test
178     public void testSkipEntryWithUnsupportedCompressionMethod()
179             throws IOException {
180         final ZipArchiveInputStream zip =
181             new ZipArchiveInputStream(new FileInputStream(getFile("moby.zip")));
182         try {
183             final ZipArchiveEntry entry = zip.getNextZipEntry();
184             assertEquals("method", ZipMethod.TOKENIZATION.getCode(), entry.getMethod());
185             assertEquals("README", entry.getName());
186             assertFalse(zip.canReadEntryData(entry));
187             try {
188                 assertNull(zip.getNextZipEntry());
189             } catch (final IOException e) {
190                 e.printStackTrace();
191                 fail("COMPRESS-93: Unable to skip an unsupported zip entry");
192             }
193         } finally {
194             zip.close();
195         }
196     }
197 
198     /**
199      * Checks if all entries from a nested archive can be read.
200      * The archive: OSX_ArchiveWithNestedArchive.zip contains:
201      * NestedArchiv.zip and test.xml3.
202      * 
203      * The nested archive:  NestedArchive.zip contains test1.xml and test2.xml
204      * 
205      * @throws Exception
206      */
207     @Test
208     public void testListAllFilesWithNestedArchive() throws Exception {
209         final File input = getFile("OSX_ArchiveWithNestedArchive.zip");
210 
211         final List<String> results = new ArrayList<String>();
212 
213         final InputStream is = new FileInputStream(input);
214         ArchiveInputStream in = null;
215         try {
216             in = new ArchiveStreamFactory().createArchiveInputStream("zip", is);
217 
218             ZipArchiveEntry entry = null;
219             while((entry = (ZipArchiveEntry)in.getNextEntry()) != null) {
220                 results.add(entry.getName());
221 
222                 final ArchiveInputStream nestedIn = new ArchiveStreamFactory().createArchiveInputStream("zip", in);
223                 ZipArchiveEntry nestedEntry = null;
224                 while((nestedEntry = (ZipArchiveEntry)nestedIn.getNextEntry()) != null) {
225                     results.add(nestedEntry.getName());
226                 }
227                // nested stream must not be closed here
228             }
229         } finally {
230             if (in != null) {
231                 in.close();
232             }
233         }
234         is.close();
235 
236         results.contains("NestedArchiv.zip");
237         results.contains("test1.xml");
238         results.contains("test2.xml");
239         results.contains("test3.xml");
240     }
241 
242     @Test
243     public void testDirectoryEntryFromFile() throws Exception {
244         final File[] tmp = createTempDirAndFile();
245         File archive = null;
246         ZipArchiveOutputStream zos = null;
247         ZipFile zf = null;
248         try {
249             archive = File.createTempFile("test.", ".zip", tmp[0]);
250             archive.deleteOnExit();
251             zos = new ZipArchiveOutputStream(archive);
252             final long beforeArchiveWrite = tmp[0].lastModified();
253             final ZipArchiveEntry in = new ZipArchiveEntry(tmp[0], "foo");
254             zos.putArchiveEntry(in);
255             zos.closeArchiveEntry();
256             zos.close();
257             zos = null;
258             zf = new ZipFile(archive);
259             final ZipArchiveEntry out = zf.getEntry("foo/");
260             assertNotNull(out);
261             assertEquals("foo/", out.getName());
262             assertEquals(0, out.getSize());
263             // ZIP stores time with a granularity of 2 seconds
264             assertEquals(beforeArchiveWrite / 2000,
265                          out.getLastModifiedDate().getTime() / 2000);
266             assertTrue(out.isDirectory());
267         } finally {
268             ZipFile.closeQuietly(zf);
269             if (zos != null) {
270                 zos.close();
271             }
272             tryHardToDelete(archive);
273             tryHardToDelete(tmp[1]);
274             rmdir(tmp[0]);
275         }
276     }
277 
278     @Test
279     public void testExplicitDirectoryEntry() throws Exception {
280         final File[] tmp = createTempDirAndFile();
281         File archive = null;
282         ZipArchiveOutputStream zos = null;
283         ZipFile zf = null;
284         try {
285             archive = File.createTempFile("test.", ".zip", tmp[0]);
286             archive.deleteOnExit();
287             zos = new ZipArchiveOutputStream(archive);
288             final long beforeArchiveWrite = tmp[0].lastModified();
289             final ZipArchiveEntry in = new ZipArchiveEntry("foo/");
290             in.setTime(beforeArchiveWrite);
291             zos.putArchiveEntry(in);
292             zos.closeArchiveEntry();
293             zos.close();
294             zos = null;
295             zf = new ZipFile(archive);
296             final ZipArchiveEntry out = zf.getEntry("foo/");
297             assertNotNull(out);
298             assertEquals("foo/", out.getName());
299             assertEquals(0, out.getSize());
300             assertEquals(beforeArchiveWrite / 2000,
301                          out.getLastModifiedDate().getTime() / 2000);
302             assertTrue(out.isDirectory());
303         } finally {
304             ZipFile.closeQuietly(zf);
305             if (zos != null) {
306                 zos.close();
307             }
308             tryHardToDelete(archive);
309             tryHardToDelete(tmp[1]);
310             rmdir(tmp[0]);
311         }
312     }
313     String first_payload = "ABBA";
314     String second_payload = "AAAAAAAAAAAA";
315     ZipArchiveEntryPredicate allFilesPredicate = new ZipArchiveEntryPredicate() {
316         @Override
317         public boolean test(final ZipArchiveEntry zipArchiveEntry) {
318             return true;
319         }
320     };
321 
322     @Test
323     public void testCopyRawEntriesFromFile()
324             throws IOException {
325 
326         final File[] tmp = createTempDirAndFile();
327         final File reference = createReferenceFile(tmp[0], Zip64Mode.Never, "expected.");
328 
329         final File a1 = File.createTempFile("src1.", ".zip", tmp[0]);
330         final ZipArchiveOutputStream zos = new ZipArchiveOutputStream(a1);
331         zos.setUseZip64(Zip64Mode.Never);
332         createFirstEntry(zos).close();
333 
334         final File a2 = File.createTempFile("src2.", ".zip", tmp[0]);
335         final ZipArchiveOutputStream zos1 = new ZipArchiveOutputStream(a2);
336         zos1.setUseZip64(Zip64Mode.Never);
337         createSecondEntry(zos1).close();
338 
339         final ZipFile zf1 = new ZipFile(a1);
340         final ZipFile zf2 = new ZipFile(a2);
341         final File fileResult = File.createTempFile("file-actual.", ".zip", tmp[0]);
342         final ZipArchiveOutputStream zos2 = new ZipArchiveOutputStream(fileResult);
343         zf1.copyRawEntries(zos2, allFilesPredicate);
344         zf2.copyRawEntries(zos2, allFilesPredicate);
345         zos2.close();
346         // copyRawEntries does not add superfluous zip64 header like regular zip output stream
347         // does when using Zip64Mode.AsNeeded so all the source material has to be Zip64Mode.Never,
348         // if exact binary equality is to be achieved
349         assertSameFileContents(reference, fileResult);
350         zf1.close();
351         zf2.close();
352     }
353 
354     @Test
355     public void testCopyRawZip64EntryFromFile()
356             throws IOException {
357 
358         final File[] tmp = createTempDirAndFile();
359         final File reference = File.createTempFile("z64reference.", ".zip", tmp[0]);
360         final ZipArchiveOutputStream zos1 = new ZipArchiveOutputStream(reference);
361         zos1.setUseZip64(Zip64Mode.Always);
362         createFirstEntry(zos1);
363         zos1.close();
364 
365         final File a1 = File.createTempFile("zip64src.", ".zip", tmp[0]);
366         final ZipArchiveOutputStream zos = new ZipArchiveOutputStream(a1);
367         zos.setUseZip64(Zip64Mode.Always);
368         createFirstEntry(zos).close();
369 
370         final ZipFile zf1 = new ZipFile(a1);
371         final File fileResult = File.createTempFile("file-actual.", ".zip", tmp[0]);
372         final ZipArchiveOutputStream zos2 = new ZipArchiveOutputStream(fileResult);
373         zos2.setUseZip64(Zip64Mode.Always);
374         zf1.copyRawEntries(zos2, allFilesPredicate);
375         zos2.close();
376         assertSameFileContents(reference, fileResult);
377         zf1.close();
378     }
379 
380     @Test
381     public void testUnixModeInAddRaw() throws IOException {
382 
383         final File[] tmp = createTempDirAndFile();
384 
385         final File a1 = File.createTempFile("unixModeBits.", ".zip", tmp[0]);
386         final ZipArchiveOutputStream zos = new ZipArchiveOutputStream(a1);
387 
388         final ZipArchiveEntry archiveEntry = new ZipArchiveEntry("fred");
389         archiveEntry.setUnixMode(0664);
390         archiveEntry.setMethod(ZipEntry.DEFLATED);
391         zos.addRawArchiveEntry(archiveEntry, new ByteArrayInputStream("fud".getBytes()));
392         zos.close();
393 
394         final ZipFile zf1 = new ZipFile(a1);
395         final ZipArchiveEntry fred = zf1.getEntry("fred");
396         assertEquals(0664, fred.getUnixMode());
397         zf1.close();
398     }
399 
400     private File createReferenceFile(final File directory, final Zip64Mode zipMode, final String prefix) throws IOException {
401         final File reference = File.createTempFile(prefix, ".zip", directory);
402         final ZipArchiveOutputStream zos = new ZipArchiveOutputStream(reference);
403         zos.setUseZip64(zipMode);
404         createFirstEntry(zos);
405         createSecondEntry(zos);
406         zos.close();
407         return reference;
408     }
409 
410     private ZipArchiveOutputStream createFirstEntry(final ZipArchiveOutputStream zos) throws IOException {
411         createArchiveEntry(first_payload, zos, "file1.txt");
412         return zos;
413     }
414 
415     private ZipArchiveOutputStream createSecondEntry(final ZipArchiveOutputStream zos) throws IOException {
416         createArchiveEntry(second_payload, zos, "file2.txt");
417         return zos;
418     }
419 
420 
421     private void assertSameFileContents(final File expectedFile, final File actualFile) throws IOException {
422         final int size = (int) Math.max(expectedFile.length(), actualFile.length());
423         final ZipFile expected = new ZipFile(expectedFile);
424         final ZipFile actual = new ZipFile(actualFile);
425         final byte[] expectedBuf = new byte[size];
426         final byte[] actualBuf = new byte[size];
427 
428         final Enumeration<ZipArchiveEntry> actualInOrder = actual.getEntriesInPhysicalOrder();
429         final Enumeration<ZipArchiveEntry> expectedInOrder = expected.getEntriesInPhysicalOrder();
430 
431         while (actualInOrder.hasMoreElements()){
432             final ZipArchiveEntry actualElement = actualInOrder.nextElement();
433             final ZipArchiveEntry expectedElement = expectedInOrder.nextElement();
434             assertEquals( expectedElement.getName(), actualElement.getName());
435             // Don't compare timestamps since they may vary;
436             // there's no support for stubbed out clock (TimeSource) in ZipArchiveOutputStream
437             assertEquals( expectedElement.getMethod(), actualElement.getMethod());
438             assertEquals( expectedElement.getGeneralPurposeBit(), actualElement.getGeneralPurposeBit());
439             assertEquals( expectedElement.getCrc(), actualElement.getCrc());
440             assertEquals( expectedElement.getCompressedSize(), actualElement.getCompressedSize());
441             assertEquals( expectedElement.getSize(), actualElement.getSize());
442             assertEquals( expectedElement.getExternalAttributes(), actualElement.getExternalAttributes());
443             assertEquals( expectedElement.getInternalAttributes(), actualElement.getInternalAttributes());
444 
445             final InputStream actualIs = actual.getInputStream(actualElement);
446             final InputStream expectedIs = expected.getInputStream(expectedElement);
447             IOUtils.readFully(expectedIs, expectedBuf);
448             IOUtils.readFully(actualIs, actualBuf);
449             expectedIs.close();
450             actualIs.close();
451             Assert.assertArrayEquals(expectedBuf, actualBuf); // Buffers are larger than payload. dont care
452         }
453 
454         expected.close();
455         actual.close();
456     }
457 
458 
459     private void createArchiveEntry(final String payload, final ZipArchiveOutputStream zos, final String name)
460             throws IOException {
461         final ZipArchiveEntry in = new ZipArchiveEntry(name);
462         zos.putArchiveEntry(in);
463 
464         zos.write(payload.getBytes());
465         zos.closeArchiveEntry();
466     }
467 
468     @Test
469     public void testFileEntryFromFile() throws Exception {
470         final File[] tmp = createTempDirAndFile();
471         File archive = null;
472         ZipArchiveOutputStream zos = null;
473         ZipFile zf = null;
474         FileInputStream fis = null;
475         try {
476             archive = File.createTempFile("test.", ".zip", tmp[0]);
477             archive.deleteOnExit();
478             zos = new ZipArchiveOutputStream(archive);
479             final ZipArchiveEntry in = new ZipArchiveEntry(tmp[1], "foo");
480             zos.putArchiveEntry(in);
481             final byte[] b = new byte[(int) tmp[1].length()];
482             fis = new FileInputStream(tmp[1]);
483             while (fis.read(b) > 0) {
484                 zos.write(b);
485             }
486             fis.close();
487             fis = null;
488             zos.closeArchiveEntry();
489             zos.close();
490             zos = null;
491             zf = new ZipFile(archive);
492             final ZipArchiveEntry out = zf.getEntry("foo");
493             assertNotNull(out);
494             assertEquals("foo", out.getName());
495             assertEquals(tmp[1].length(), out.getSize());
496             assertEquals(tmp[1].lastModified() / 2000,
497                          out.getLastModifiedDate().getTime() / 2000);
498             assertFalse(out.isDirectory());
499         } finally {
500             ZipFile.closeQuietly(zf);
501             if (zos != null) {
502                 zos.close();
503             }
504             tryHardToDelete(archive);
505             if (fis != null) {
506                 fis.close();
507             }
508             tryHardToDelete(tmp[1]);
509             rmdir(tmp[0]);
510         }
511     }
512 
513     @Test
514     public void testExplicitFileEntry() throws Exception {
515         final File[] tmp = createTempDirAndFile();
516         File archive = null;
517         ZipArchiveOutputStream zos = null;
518         ZipFile zf = null;
519         FileInputStream fis = null;
520         try {
521             archive = File.createTempFile("test.", ".zip", tmp[0]);
522             archive.deleteOnExit();
523             zos = new ZipArchiveOutputStream(archive);
524             final ZipArchiveEntry in = new ZipArchiveEntry("foo");
525             in.setTime(tmp[1].lastModified());
526             in.setSize(tmp[1].length());
527             zos.putArchiveEntry(in);
528             final byte[] b = new byte[(int) tmp[1].length()];
529             fis = new FileInputStream(tmp[1]);
530             while (fis.read(b) > 0) {
531                 zos.write(b);
532             }
533             fis.close();
534             fis = null;
535             zos.closeArchiveEntry();
536             zos.close();
537             zos = null;
538             zf = new ZipFile(archive);
539             final ZipArchiveEntry out = zf.getEntry("foo");
540             assertNotNull(out);
541             assertEquals("foo", out.getName());
542             assertEquals(tmp[1].length(), out.getSize());
543             assertEquals(tmp[1].lastModified() / 2000,
544                          out.getLastModifiedDate().getTime() / 2000);
545             assertFalse(out.isDirectory());
546         } finally {
547             ZipFile.closeQuietly(zf);
548             if (zos != null) {
549                 zos.close();
550             }
551             tryHardToDelete(archive);
552             if (fis != null) {
553                 fis.close();
554             }
555             tryHardToDelete(tmp[1]);
556             rmdir(tmp[0]);
557         }
558     }
559 }