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          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                  File outfile = new File(resultDir.getCanonicalPath() + "/result/" + entry.getName());
91                  outfile.getParentFile().mkdirs();
92                  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         InputStream is = new FileInputStream(input);
139         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         ZipFile moby = new ZipFile(getFile("moby.zip"));
163         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         ZipArchiveInputStream zip =
181             new ZipArchiveInputStream(new FileInputStream(getFile("moby.zip")));
182         try {
183             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 (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         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                 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         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             long beforeArchiveWrite = tmp[0].lastModified();
253             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             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         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             long beforeArchiveWrite = tmp[0].lastModified();
289             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             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         public boolean test(ZipArchiveEntry zipArchiveEntry) {
317             return true;
318         }
319     };
320 
321     @Test
322     public void testCopyRawEntriesFromFile()
323             throws IOException {
324 
325         File[] tmp = createTempDirAndFile();
326         File reference = createReferenceFile(tmp[0], Zip64Mode.Never, "expected.");
327 
328         File a1 = File.createTempFile("src1.", ".zip", tmp[0]);
329         ZipArchiveOutputStream zos = new ZipArchiveOutputStream(a1);
330         zos.setUseZip64(Zip64Mode.Never);
331         createFirstEntry(zos).close();
332 
333         File a2 = File.createTempFile("src2.", ".zip", tmp[0]);
334         ZipArchiveOutputStream zos1 = new ZipArchiveOutputStream(a2);
335         zos1.setUseZip64(Zip64Mode.Never);
336         createSecondEntry(zos1).close();
337 
338         ZipFile zf1 = new ZipFile(a1);
339         ZipFile zf2 = new ZipFile(a2);
340         File fileResult = File.createTempFile("file-actual.", ".zip", tmp[0]);
341         ZipArchiveOutputStream zos2 = new ZipArchiveOutputStream(fileResult);
342         zf1.copyRawEntries(zos2, allFilesPredicate);
343         zf2.copyRawEntries(zos2, allFilesPredicate);
344         zos2.close();
345         // copyRawEntries does not add superfluous zip64 header like regular zip output stream
346         // does when using Zip64Mode.AsNeeded so all the source material has to be Zip64Mode.Never,
347         // if exact binary equality is to be achieved
348         assertSameFileContents(reference, fileResult);
349         zf1.close();
350         zf2.close();
351     }
352 
353     @Test
354     public void testCopyRawZip64EntryFromFile()
355             throws IOException {
356 
357         File[] tmp = createTempDirAndFile();
358         File reference = File.createTempFile("z64reference.", ".zip", tmp[0]);
359         ZipArchiveOutputStream zos1 = new ZipArchiveOutputStream(reference);
360         zos1.setUseZip64(Zip64Mode.Always);
361         createFirstEntry(zos1);
362         zos1.close();
363 
364         File a1 = File.createTempFile("zip64src.", ".zip", tmp[0]);
365         ZipArchiveOutputStream zos = new ZipArchiveOutputStream(a1);
366         zos.setUseZip64(Zip64Mode.Always);
367         createFirstEntry(zos).close();
368 
369         ZipFile zf1 = new ZipFile(a1);
370         File fileResult = File.createTempFile("file-actual.", ".zip", tmp[0]);
371         ZipArchiveOutputStream zos2 = new ZipArchiveOutputStream(fileResult);
372         zos2.setUseZip64(Zip64Mode.Always);
373         zf1.copyRawEntries(zos2, allFilesPredicate);
374         zos2.close();
375         assertSameFileContents(reference, fileResult);
376         zf1.close();
377     }
378 
379     @Test
380     public void testUnixModeInAddRaw() throws IOException {
381 
382         File[] tmp = createTempDirAndFile();
383 
384         File a1 = File.createTempFile("unixModeBits.", ".zip", tmp[0]);
385         ZipArchiveOutputStream zos = new ZipArchiveOutputStream(a1);
386 
387         ZipArchiveEntry archiveEntry = new ZipArchiveEntry("fred");
388         archiveEntry.setUnixMode(0664);
389         archiveEntry.setMethod(ZipEntry.DEFLATED);
390         zos.addRawArchiveEntry(archiveEntry, new ByteArrayInputStream("fud".getBytes()));
391         zos.close();
392 
393         ZipFile zf1 = new ZipFile(a1);
394         ZipArchiveEntry fred = zf1.getEntry("fred");
395         assertEquals(0664, fred.getUnixMode());
396         zf1.close();
397     }
398 
399     private File createReferenceFile(File directory, Zip64Mode zipMode, String prefix) throws IOException {
400         File reference = File.createTempFile(prefix, ".zip", directory);
401         ZipArchiveOutputStream zos = new ZipArchiveOutputStream(reference);
402         zos.setUseZip64(zipMode);
403         createFirstEntry(zos);
404         createSecondEntry(zos);
405         zos.close();
406         return reference;
407     }
408 
409     private ZipArchiveOutputStream createFirstEntry(ZipArchiveOutputStream zos) throws IOException {
410         createArchiveEntry(first_payload, zos, "file1.txt");
411         return zos;
412     }
413 
414     private ZipArchiveOutputStream createSecondEntry(ZipArchiveOutputStream zos) throws IOException {
415         createArchiveEntry(second_payload, zos, "file2.txt");
416         return zos;
417     }
418 
419 
420     private void assertSameFileContents(File expectedFile, File actualFile) throws IOException {
421         int size = (int) Math.max(expectedFile.length(), actualFile.length());
422         ZipFile expected = new ZipFile(expectedFile);
423         ZipFile actual = new ZipFile(actualFile);
424         byte[] expectedBuf = new byte[size];
425         byte[] actualBuf = new byte[size];
426 
427         Enumeration<ZipArchiveEntry> actualInOrder = actual.getEntriesInPhysicalOrder();
428         Enumeration<ZipArchiveEntry> expectedInOrder = expected.getEntriesInPhysicalOrder();
429 
430         while (actualInOrder.hasMoreElements()){
431             ZipArchiveEntry actualElement = actualInOrder.nextElement();
432             ZipArchiveEntry expectedElement = expectedInOrder.nextElement();
433             assertEquals( expectedElement.getName(), actualElement.getName());
434             // Don't compare timestamps since they may vary;
435             // there's no support for stubbed out clock (TimeSource) in ZipArchiveOutputStream
436             assertEquals( expectedElement.getMethod(), actualElement.getMethod());
437             assertEquals( expectedElement.getGeneralPurposeBit(), actualElement.getGeneralPurposeBit());
438             assertEquals( expectedElement.getCrc(), actualElement.getCrc());
439             assertEquals( expectedElement.getCompressedSize(), actualElement.getCompressedSize());
440             assertEquals( expectedElement.getSize(), actualElement.getSize());
441             assertEquals( expectedElement.getExternalAttributes(), actualElement.getExternalAttributes());
442             assertEquals( expectedElement.getInternalAttributes(), actualElement.getInternalAttributes());
443 
444             InputStream actualIs = actual.getInputStream(actualElement);
445             InputStream expectedIs = expected.getInputStream(expectedElement);
446             IOUtils.readFully(expectedIs, expectedBuf);
447             IOUtils.readFully(actualIs, actualBuf);
448             expectedIs.close();
449             actualIs.close();
450             Assert.assertArrayEquals(expectedBuf, actualBuf); // Buffers are larger than payload. dont care
451         }
452 
453         expected.close();
454         actual.close();
455     }
456 
457 
458     private void createArchiveEntry(String payload, ZipArchiveOutputStream zos, String name)
459             throws IOException {
460         ZipArchiveEntry in = new ZipArchiveEntry(name);
461         zos.putArchiveEntry(in);
462 
463         zos.write(payload.getBytes());
464         zos.closeArchiveEntry();
465     }
466 
467     @Test
468     public void testFileEntryFromFile() throws Exception {
469         File[] tmp = createTempDirAndFile();
470         File archive = null;
471         ZipArchiveOutputStream zos = null;
472         ZipFile zf = null;
473         FileInputStream fis = null;
474         try {
475             archive = File.createTempFile("test.", ".zip", tmp[0]);
476             archive.deleteOnExit();
477             zos = new ZipArchiveOutputStream(archive);
478             ZipArchiveEntry in = new ZipArchiveEntry(tmp[1], "foo");
479             zos.putArchiveEntry(in);
480             byte[] b = new byte[(int) tmp[1].length()];
481             fis = new FileInputStream(tmp[1]);
482             while (fis.read(b) > 0) {
483                 zos.write(b);
484             }
485             fis.close();
486             fis = null;
487             zos.closeArchiveEntry();
488             zos.close();
489             zos = null;
490             zf = new ZipFile(archive);
491             ZipArchiveEntry out = zf.getEntry("foo");
492             assertNotNull(out);
493             assertEquals("foo", out.getName());
494             assertEquals(tmp[1].length(), out.getSize());
495             assertEquals(tmp[1].lastModified() / 2000,
496                          out.getLastModifiedDate().getTime() / 2000);
497             assertFalse(out.isDirectory());
498         } finally {
499             ZipFile.closeQuietly(zf);
500             if (zos != null) {
501                 zos.close();
502             }
503             tryHardToDelete(archive);
504             if (fis != null) {
505                 fis.close();
506             }
507             tryHardToDelete(tmp[1]);
508             rmdir(tmp[0]);
509         }
510     }
511 
512     @Test
513     public void testExplicitFileEntry() throws Exception {
514         File[] tmp = createTempDirAndFile();
515         File archive = null;
516         ZipArchiveOutputStream zos = null;
517         ZipFile zf = null;
518         FileInputStream fis = null;
519         try {
520             archive = File.createTempFile("test.", ".zip", tmp[0]);
521             archive.deleteOnExit();
522             zos = new ZipArchiveOutputStream(archive);
523             ZipArchiveEntry in = new ZipArchiveEntry("foo");
524             in.setTime(tmp[1].lastModified());
525             in.setSize(tmp[1].length());
526             zos.putArchiveEntry(in);
527             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             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 }