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.apache.commons.compress.AbstractTestCase.getFile;
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertNull;
24  import static org.junit.Assert.assertTrue;
25  import static org.junit.Assert.fail;
26  
27  import java.io.BufferedInputStream;
28  import java.io.ByteArrayInputStream;
29  import java.io.ByteArrayOutputStream;
30  import java.io.FileInputStream;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.lang.reflect.Field;
34  
35  import org.apache.commons.compress.MockEvilInputStream;
36  import org.apache.commons.compress.archivers.arj.ArjArchiveInputStream;
37  import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream;
38  import org.apache.commons.compress.archivers.dump.DumpArchiveInputStream;
39  import org.apache.commons.compress.archivers.jar.JarArchiveInputStream;
40  import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
41  import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
42  import org.junit.Test;
43  
44  public class ArchiveStreamFactoryTest {
45  
46      private static final String UNKNOWN = "??";
47  
48      /**
49       * see https://issues.apache.org/jira/browse/COMPRESS-171
50       */
51      @Test
52      public void shortTextFilesAreNoTARs() throws Exception {
53          try {
54              new ArchiveStreamFactory()
55                  .createArchiveInputStream(new ByteArrayInputStream("This certainly is not a tar archive, really, no kidding".getBytes()));
56              fail("created an input stream for a non-archive");
57          } catch (final ArchiveException ae) {
58              assertTrue(ae.getMessage().startsWith("No Archiver found"));
59          }
60      }
61  
62      /**
63       * see https://issues.apache.org/jira/browse/COMPRESS-191
64       */
65      @Test
66      public void aiffFilesAreNoTARs() throws Exception {
67          try (FileInputStream fis = new FileInputStream("src/test/resources/testAIFF.aif")) {
68              try (InputStream is = new BufferedInputStream(fis)) {
69                  new ArchiveStreamFactory().createArchiveInputStream(is);
70                  fail("created an input stream for a non-archive");
71              } catch (final ArchiveException ae) {
72                  assertTrue(ae.getMessage().startsWith("No Archiver found"));
73              }
74          }
75      }
76  
77      @Test
78      public void testCOMPRESS209() throws Exception {
79          try (FileInputStream fis = new FileInputStream("src/test/resources/testCompress209.doc")) {
80              try (InputStream bis = new BufferedInputStream(fis)) {
81                  new ArchiveStreamFactory().createArchiveInputStream(bis);
82                  fail("created an input stream for a non-archive");
83              } catch (final ArchiveException ae) {
84                  assertTrue(ae.getMessage().startsWith("No Archiver found"));
85              }
86          }
87      }
88  
89      @Test(expected = StreamingNotSupportedException.class)
90      public void cantRead7zFromStream() throws Exception {
91          new ArchiveStreamFactory()
92              .createArchiveInputStream(ArchiveStreamFactory.SEVEN_Z,
93                                        new ByteArrayInputStream(new byte[0]));
94      }
95  
96      @Test(expected = StreamingNotSupportedException.class)
97      public void cantWrite7zToStream() throws Exception {
98          new ArchiveStreamFactory()
99              .createArchiveOutputStream(ArchiveStreamFactory.SEVEN_Z,
100                                        new ByteArrayOutputStream());
101     }
102 
103     /**
104      * Test case for 
105      * <a href="https://issues.apache.org/jira/browse/COMPRESS-267"
106      * >COMPRESS-267</a>.
107      */
108     @Test
109     public void detectsAndThrowsFor7z() throws Exception {
110         try (FileInputStream fis = new FileInputStream("src/test/resources/bla.7z")) {
111             try (InputStream bis = new BufferedInputStream(fis)) {
112                 new ArchiveStreamFactory().createArchiveInputStream(bis);
113                 fail("Expected a StreamingNotSupportedException");
114             } catch (final StreamingNotSupportedException ex) {
115                 assertEquals(ArchiveStreamFactory.SEVEN_Z, ex.getFormat());
116             }
117         }
118     }
119 
120     /**
121      * Test case for 
122      * <a href="https://issues.apache.org/jira/browse/COMPRESS-208"
123      * >COMPRESS-208</a>.
124      */
125     @Test
126     public void skipsPK00Prefix() throws Exception {
127         try (FileInputStream fis = new FileInputStream("src/test/resources/COMPRESS-208.zip")) {
128             try (InputStream bis = new BufferedInputStream(fis)) {
129                 try (ArchiveInputStream ais = new ArchiveStreamFactory().createArchiveInputStream(bis)) {
130                     assertTrue(ais instanceof ZipArchiveInputStream);
131                 }
132             }
133         }
134     }
135     
136     @Test
137     public void testEncodingCtor() {
138         ArchiveStreamFactory fac = new ArchiveStreamFactory();
139         assertNull(fac.getEntryEncoding());
140         fac = new ArchiveStreamFactory(null);
141         assertNull(fac.getEntryEncoding());
142         fac = new ArchiveStreamFactory("UTF-8");
143         assertEquals("UTF-8", fac.getEntryEncoding());
144     }
145 
146     @Test
147     @SuppressWarnings("deprecation")
148     public void testEncodingDeprecated() {
149         ArchiveStreamFactory fac = new ArchiveStreamFactory();
150         assertNull(fac.getEntryEncoding());
151         fac.setEntryEncoding("UTF-8");
152         assertEquals("UTF-8", fac.getEntryEncoding());
153         fac.setEntryEncoding("US_ASCII");
154         assertEquals("US_ASCII", fac.getEntryEncoding());
155         fac = new ArchiveStreamFactory("UTF-8");
156         assertEquals("UTF-8", fac.getEntryEncoding());
157         try {
158             fac.setEntryEncoding("US_ASCII");
159             fail("Expected IllegalStateException");
160         } catch (final IllegalStateException ise) {
161             // expected
162         }
163     }
164 
165     static class TestData {
166         final String testFile;
167         final String expectedEncoding;
168         final ArchiveStreamFactory fac;
169         final String fieldName;
170         final String type;
171         final boolean hasOutputStream;
172         
173         TestData(final String testFile, final String type, final boolean hasOut, final String expectedEncoding, final ArchiveStreamFactory fac, final String fieldName) {
174             this.testFile = testFile;
175             this.expectedEncoding = expectedEncoding;
176             this.fac = fac;
177             this.fieldName = fieldName;
178             this.type = type;
179             this.hasOutputStream = hasOut;
180         }
181         
182         @Override
183         public String toString() {
184             return "TestData [testFile=" + testFile + ", expectedEncoding=" + expectedEncoding + ", fac=" + fac
185                     + ", fieldName=" + fieldName + ", type=" + type + ", hasOutputStream=" + hasOutputStream + "]";
186         }
187     }
188 
189     @SuppressWarnings("deprecation") // test of deprecated method
190     static ArchiveStreamFactory getFactory(final String entryEncoding) {
191         final ArchiveStreamFactory fac = new ArchiveStreamFactory();
192         fac.setEntryEncoding(entryEncoding);
193         return fac;
194     }
195     // The different factory types
196     private static final ArchiveStreamFactory FACTORY = new ArchiveStreamFactory();
197     private static final ArchiveStreamFactory FACTORY_UTF8 = new ArchiveStreamFactory("UTF-8");
198     private static final ArchiveStreamFactory FACTORY_ASCII = new ArchiveStreamFactory("ASCII");
199     private static final ArchiveStreamFactory FACTORY_SET_UTF8 = getFactory("UTF-8");
200     private static final ArchiveStreamFactory FACTORY_SET_ASCII = getFactory("ASCII");
201 
202     // Default encoding if none is provided (not even null)
203     // The test currently assumes that the output default is the same as the input default
204     private static final String ARJ_DEFAULT;
205     private static final String DUMP_DEFAULT;
206 
207     private static final String ZIP_DEFAULT = getField(new ZipArchiveInputStream(null),"encoding");
208     private static final String CPIO_DEFAULT = getField(new CpioArchiveInputStream(null),"encoding");
209     private static final String TAR_DEFAULT = getField(new TarArchiveInputStream(null),"encoding");
210     private static final String JAR_DEFAULT = getField(new JarArchiveInputStream(null),"encoding");
211 
212     static {
213         String dflt;
214         dflt = UNKNOWN;
215         try {
216             dflt = getField(new ArjArchiveInputStream(new FileInputStream(getFile("bla.arj"))), "charsetName");
217         } catch (final Exception e) {
218             e.printStackTrace();
219         }
220         ARJ_DEFAULT = dflt;
221         dflt = UNKNOWN;
222         try {
223             dflt = getField(new DumpArchiveInputStream(new FileInputStream(getFile("bla.dump"))), "encoding");
224         } catch (final Exception e) {
225             e.printStackTrace();
226         }
227         DUMP_DEFAULT = dflt;
228     }
229 
230     @Test
231     public void testDetect() throws Exception {
232         for (String extension : new String[]{
233                 ArchiveStreamFactory.AR,
234                 ArchiveStreamFactory.ARJ,
235                 ArchiveStreamFactory.CPIO,
236                 ArchiveStreamFactory.DUMP,
237                 // Compress doesn't know how to detect JARs, see COMPRESS-91
238  //               ArchiveStreamFactory.JAR,
239                 ArchiveStreamFactory.SEVEN_Z,
240                 ArchiveStreamFactory.TAR,
241                 ArchiveStreamFactory.ZIP
242         }) {
243             assertEquals(extension, detect("bla."+extension));
244         }
245 
246         try {
247             ArchiveStreamFactory.detect(new BufferedInputStream(new ByteArrayInputStream(new byte[0])));
248             fail("shouldn't be able to detect empty stream");
249         } catch (ArchiveException e) {
250             assertEquals("No Archiver found for the stream signature", e.getMessage());
251         }
252 
253         try {
254             ArchiveStreamFactory.detect(null);
255             fail("shouldn't be able to detect null stream");
256         } catch (IllegalArgumentException e) {
257             assertEquals("Stream must not be null.", e.getMessage());
258         }
259 
260         try {
261             ArchiveStreamFactory.detect(new BufferedInputStream(new MockEvilInputStream()));
262             fail("Expected ArchiveException");
263         } catch (ArchiveException e) {
264             assertEquals("IOException while reading signature.", e.getMessage());
265         }
266     }
267 
268     private String detect(String resource) throws IOException, ArchiveException {
269         try(InputStream in = new BufferedInputStream(new FileInputStream(
270                 getFile(resource)))) {
271             return ArchiveStreamFactory.detect(in);
272         }
273     }
274 
275     static final TestData[] TESTS = {
276         new TestData("bla.arj", ArchiveStreamFactory.ARJ, false, ARJ_DEFAULT, FACTORY, "charsetName"),
277         new TestData("bla.arj", ArchiveStreamFactory.ARJ, false, "UTF-8", FACTORY_UTF8, "charsetName"),
278         new TestData("bla.arj", ArchiveStreamFactory.ARJ, false, "ASCII", FACTORY_ASCII, "charsetName"),
279         new TestData("bla.arj", ArchiveStreamFactory.ARJ, false, "UTF-8", FACTORY_SET_UTF8, "charsetName"),
280         new TestData("bla.arj", ArchiveStreamFactory.ARJ, false, "ASCII", FACTORY_SET_ASCII, "charsetName"),
281 
282         new TestData("bla.cpio", ArchiveStreamFactory.CPIO, true, CPIO_DEFAULT, FACTORY, "encoding"),
283         new TestData("bla.cpio", ArchiveStreamFactory.CPIO, true, "UTF-8", FACTORY_UTF8, "encoding"),
284         new TestData("bla.cpio", ArchiveStreamFactory.CPIO, true, "ASCII", FACTORY_ASCII, "encoding"),
285         new TestData("bla.cpio", ArchiveStreamFactory.CPIO, true, "UTF-8", FACTORY_SET_UTF8, "encoding"),
286         new TestData("bla.cpio", ArchiveStreamFactory.CPIO, true, "ASCII", FACTORY_SET_ASCII, "encoding"),
287 
288         new TestData("bla.dump", ArchiveStreamFactory.DUMP, false, DUMP_DEFAULT, FACTORY, "encoding"),
289         new TestData("bla.dump", ArchiveStreamFactory.DUMP, false, "UTF-8", FACTORY_UTF8, "encoding"),
290         new TestData("bla.dump", ArchiveStreamFactory.DUMP, false, "ASCII", FACTORY_ASCII, "encoding"),
291         new TestData("bla.dump", ArchiveStreamFactory.DUMP, false, "UTF-8", FACTORY_SET_UTF8, "encoding"),
292         new TestData("bla.dump", ArchiveStreamFactory.DUMP, false, "ASCII", FACTORY_SET_ASCII, "encoding"),
293 
294         new TestData("bla.tar", ArchiveStreamFactory.TAR, true, TAR_DEFAULT, FACTORY, "encoding"),
295         new TestData("bla.tar", ArchiveStreamFactory.TAR, true, "UTF-8", FACTORY_UTF8, "encoding"),
296         new TestData("bla.tar", ArchiveStreamFactory.TAR, true, "ASCII", FACTORY_ASCII, "encoding"),
297         new TestData("bla.tar", ArchiveStreamFactory.TAR, true, "UTF-8", FACTORY_SET_UTF8, "encoding"),
298         new TestData("bla.tar", ArchiveStreamFactory.TAR, true, "ASCII", FACTORY_SET_ASCII, "encoding"),
299 
300         new TestData("bla.jar", ArchiveStreamFactory.JAR, true, JAR_DEFAULT, FACTORY, "encoding"),
301         new TestData("bla.jar", ArchiveStreamFactory.JAR, true, "UTF-8", FACTORY_UTF8, "encoding"),
302         new TestData("bla.jar", ArchiveStreamFactory.JAR, true, "ASCII", FACTORY_ASCII, "encoding"),
303         new TestData("bla.jar", ArchiveStreamFactory.JAR, true, "UTF-8", FACTORY_SET_UTF8, "encoding"),
304         new TestData("bla.jar", ArchiveStreamFactory.JAR, true, "ASCII", FACTORY_SET_ASCII, "encoding"),
305 
306         new TestData("bla.zip", ArchiveStreamFactory.ZIP, true, ZIP_DEFAULT, FACTORY, "encoding"),
307         new TestData("bla.zip", ArchiveStreamFactory.ZIP, true, "UTF-8", FACTORY_UTF8, "encoding"),
308         new TestData("bla.zip", ArchiveStreamFactory.ZIP, true, "ASCII", FACTORY_ASCII, "encoding"),
309         new TestData("bla.zip", ArchiveStreamFactory.ZIP, true, "UTF-8", FACTORY_SET_UTF8, "encoding"),
310         new TestData("bla.zip", ArchiveStreamFactory.ZIP, true, "ASCII", FACTORY_SET_ASCII, "encoding"),
311     };
312 
313     @Test
314     public void testEncodingInputStreamAutodetect() throws Exception {
315         int failed = 0;
316         for (int i = 1; i <= TESTS.length; i++) {
317             final TestData test = TESTS[i - 1];
318             try (final ArchiveInputStream ais = getInputStreamFor(test.testFile, test.fac)) {
319                 final String field = getField(ais, test.fieldName);
320                 if (!eq(test.expectedEncoding, field)) {
321                     System.out.println("Failed test " + i + ". expected: " + test.expectedEncoding + " actual: " + field
322                             + " type: " + test.type);
323                     failed++;
324                 }
325             }
326         }
327         if (failed > 0) {
328             fail("Tests failed: " + failed + " out of " + TESTS.length);
329         }
330     }
331 
332     @Test
333     public void testEncodingInputStream() throws Exception {
334         int failed = 0;
335         for (int i = 1; i <= TESTS.length; i++) {
336             final TestData test = TESTS[i - 1];
337             try (final ArchiveInputStream ais = getInputStreamFor(test.type, test.testFile, test.fac)) {
338                 final String field = getField(ais, test.fieldName);
339                 if (!eq(test.expectedEncoding, field)) {
340                     System.out.println("Failed test " + i + ". expected: " + test.expectedEncoding + " actual: " + field
341                             + " type: " + test.type);
342                     failed++;
343                 }
344             }
345         }
346         if (failed > 0) {
347             fail("Tests failed: " + failed + " out of " + TESTS.length);
348         }
349     }
350 
351     @Test
352     public void testEncodingOutputStream() throws Exception {
353         int failed = 0;
354         for(int i = 1; i <= TESTS.length; i++) {
355             final TestData test = TESTS[i-1];
356             if (test.hasOutputStream) {
357                 try (final ArchiveOutputStream ais = getOutputStreamFor(test.type, test.fac)) {
358                     final String field = getField(ais, test.fieldName);
359                     if (!eq(test.expectedEncoding, field)) {
360                         System.out.println("Failed test " + i + ". expected: " + test.expectedEncoding + " actual: "
361                                 + field + " type: " + test.type);
362                         failed++;
363                     }
364                 }
365             }
366         }
367         if (failed > 0) {
368             fail("Tests failed: " + failed + " out of " + TESTS.length);
369         }
370     }
371 
372     // equals allowing null
373     private static boolean eq(final String exp, final String act) {
374         if (exp == null) {
375             return act == null;
376         }
377         return exp.equals(act);
378     }
379 
380     private static String getField(final Object instance, final String name) {
381         final Class<?> cls = instance.getClass();
382         Field fld;
383         try {
384             fld = cls.getDeclaredField(name);
385         } catch (final NoSuchFieldException nsfe) {
386                 try {
387                     fld = cls.getSuperclass().getDeclaredField(name);
388                 } catch (final NoSuchFieldException e) {
389                     System.out.println("Cannot find " + name + " in class " + instance.getClass().getSimpleName());
390                     return UNKNOWN;
391                 }                
392         }
393         final boolean isAccessible = fld.isAccessible();
394         try {
395             if (!isAccessible) {
396                 fld.setAccessible(true);
397             }
398             final Object object = fld.get(instance);
399             if (object instanceof String || object == null) {
400                 return (String) object;
401             }
402             System.out.println("Wrong type: " + object.getClass().getCanonicalName() + " for " + name + " in class " + instance.getClass().getSimpleName());
403             return UNKNOWN;
404         } catch (final Exception e) {
405             e.printStackTrace();
406             return UNKNOWN;
407         } finally {
408             if (!isAccessible) {
409                 fld.setAccessible(isAccessible);
410             }
411         }
412     }
413 
414     private ArchiveInputStream getInputStreamFor(final String resource, final ArchiveStreamFactory factory)
415             throws IOException, ArchiveException {
416         return factory.createArchiveInputStream(
417                    new BufferedInputStream(new FileInputStream(
418                        getFile(resource))));
419     }
420 
421     private ArchiveInputStream getInputStreamFor(final String type, final String resource, final ArchiveStreamFactory factory)
422             throws IOException, ArchiveException {
423         return factory.createArchiveInputStream(
424                    type,
425                    new BufferedInputStream(new FileInputStream(
426                        getFile(resource))));
427     }
428 
429     private ArchiveOutputStream getOutputStreamFor(final String type, final ArchiveStreamFactory factory)
430             throws IOException, ArchiveException {
431         return factory.createArchiveOutputStream(type, new ByteArrayOutputStream());
432     }
433 }