1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17package org.apache.harmony.archive.tests.java.util.jar;
18
19
20import junit.framework.TestCase;
21
22import tests.support.Support_PlatformFile;
23import tests.support.resource.Support_Resources;
24
25import java.io.ByteArrayOutputStream;
26import java.io.File;
27import java.io.FileOutputStream;
28import java.io.IOException;
29import java.io.InputStream;
30import java.net.URL;
31import java.security.Permission;
32import java.security.cert.Certificate;
33import java.util.Enumeration;
34import java.util.Vector;
35import java.util.jar.Attributes;
36import java.util.jar.JarEntry;
37import java.util.jar.JarFile;
38import java.util.jar.JarOutputStream;
39import java.util.jar.Manifest;
40import java.util.zip.ZipEntry;
41import java.util.zip.ZipException;
42import java.util.zip.ZipFile;
43
44
45public class JarFileTest extends TestCase {
46
47    // BEGIN android-added
48    public byte[] getAllBytesFromStream(InputStream is) throws IOException {
49        ByteArrayOutputStream bs = new ByteArrayOutputStream();
50        byte[] buf = new byte[666];
51        int iRead;
52        int off;
53        while (is.available() > 0) {
54            iRead = is.read(buf, 0, buf.length);
55            if (iRead > 0) bs.write(buf, 0, iRead);
56        }
57        return bs.toByteArray();
58    }
59
60    // END android-added
61
62    private final String jarName = "hyts_patch.jar"; // a 'normal' jar file
63
64    private final String jarName2 = "hyts_patch2.jar";
65
66    private final String jarName3 = "hyts_manifest1.jar";
67
68    private final String jarName4 = "hyts_signed.jar";
69
70    private final String jarName5 = "hyts_signed_inc.jar";
71
72    private final String jarName6 = "hyts_signed_sha256withrsa.jar";
73
74    private final String jarName7 = "hyts_signed_sha256digest_sha256withrsa.jar";
75
76    private final String jarName8 = "hyts_signed_sha512digest_sha512withecdsa.jar";
77
78    private final String authAttrsJar = "hyts_signed_authAttrs.jar";
79
80    private final String entryName = "foo/bar/A.class";
81
82    private final String entryName3 = "coucou/FileAccess.class";
83
84    private final String integrateJar = "Integrate.jar";
85
86    private final String integrateJarEntry = "Test.class";
87
88    private final String emptyEntryJar = "EmptyEntries_signed.jar";
89
90    private final String emptyEntry1 = "subfolder/internalSubset01.js";
91
92    private final String emptyEntry2 = "svgtest.js";
93
94    private final String emptyEntry3 = "svgunit.js";
95
96    private File resources;
97
98    // custom security manager
99    SecurityManager sm = new SecurityManager() {
100        final String forbidenPermissionName = "user.dir";
101
102        public void checkPermission(Permission perm) {
103            if (perm.getName().equals(forbidenPermissionName)) {
104                throw new SecurityException();
105            }
106        }
107    };
108
109    @Override
110    protected void setUp() {
111        resources = Support_Resources.createTempFolder();
112    }
113
114    /**
115     * java.util.jar.JarFile#JarFile(java.io.File)
116     */
117    public void test_ConstructorLjava_io_File() {
118        try {
119            JarFile jarFile = new JarFile(new File("Wrong.file"));
120            fail("Should throw IOException");
121        } catch (IOException e) {
122            // expected
123        }
124
125        try {
126            Support_Resources.copyFile(resources, null, jarName);
127            JarFile jarFile = new JarFile(new File(resources, jarName));
128        } catch (IOException e) {
129            fail("Should not throw IOException");
130        }
131    }
132
133    /**
134     * java.util.jar.JarFile#JarFile(java.lang.String)
135     */
136    public void test_ConstructorLjava_lang_String() {
137        try {
138            JarFile jarFile = new JarFile("Wrong.file");
139            fail("Should throw IOException");
140        } catch (IOException e) {
141            // expected
142        }
143
144        try {
145            Support_Resources.copyFile(resources, null, jarName);
146            String fileName = (new File(resources, jarName)).getCanonicalPath();
147            JarFile jarFile = new JarFile(fileName);
148        } catch (IOException e) {
149            fail("Should not throw IOException");
150        }
151    }
152
153    /**
154     * java.util.jar.JarFile#JarFile(java.lang.String, boolean)
155     */
156    public void test_ConstructorLjava_lang_StringZ() {
157        try {
158            JarFile jarFile = new JarFile("Wrong.file", false);
159            fail("Should throw IOException");
160        } catch (IOException e) {
161            // expected
162        }
163
164        try {
165            Support_Resources.copyFile(resources, null, jarName);
166            String fileName = (new File(resources, jarName)).getCanonicalPath();
167            JarFile jarFile = new JarFile(fileName, true);
168        } catch (IOException e) {
169            fail("Should not throw IOException");
170        }
171    }
172
173    /**
174     * java.util.jar.JarFile#JarFile(java.io.File, boolean)
175     */
176    public void test_ConstructorLjava_io_FileZ() {
177        try {
178            JarFile jarFile = new JarFile(new File("Wrong.file"), true);
179            fail("Should throw IOException");
180        } catch (IOException e) {
181            // expected
182        }
183
184        try {
185            Support_Resources.copyFile(resources, null, jarName);
186            JarFile jarFile = new JarFile(new File(resources, jarName), false);
187        } catch (IOException e) {
188            fail("Should not throw IOException");
189        }
190    }
191
192    /**
193     * java.util.jar.JarFile#JarFile(java.io.File, boolean, int)
194     */
195    public void test_ConstructorLjava_io_FileZI() {
196        try {
197            JarFile jarFile = new JarFile(new File("Wrong.file"), true,
198                    ZipFile.OPEN_READ);
199            fail("Should throw IOException");
200        } catch (IOException e) {
201            // expected
202        }
203
204        try {
205            Support_Resources.copyFile(resources, null, jarName);
206            JarFile jarFile = new JarFile(new File(resources, jarName), false,
207                    ZipFile.OPEN_READ);
208        } catch (IOException e) {
209            fail("Should not throw IOException");
210        }
211
212        try {
213            Support_Resources.copyFile(resources, null, jarName);
214            JarFile jarFile = new JarFile(new File(resources, jarName), false,
215                    ZipFile.OPEN_READ | ZipFile.OPEN_DELETE + 33);
216            fail("Should throw IllegalArgumentException");
217        } catch (IOException e) {
218            fail("Should not throw IOException");
219        } catch (IllegalArgumentException e) {
220            // expected
221        }
222    }
223
224    /**
225     * Constructs JarFile object.
226     *
227     * java.util.jar.JarFile#JarFile(java.io.File)
228     * java.util.jar.JarFile#JarFile(java.lang.String)
229     */
230    public void testConstructor_file() throws IOException {
231        File f = new File(resources, jarName);
232        Support_Resources.copyFile(resources, null, jarName);
233        assertTrue(new JarFile(f).getEntry(entryName).getName().equals(
234                entryName));
235        assertTrue(new JarFile(f.getPath()).getEntry(entryName).getName()
236                .equals(entryName));
237    }
238
239    /**
240     * java.util.jar.JarFile#entries()
241     */
242    public void test_entries() throws Exception {
243        /*
244         * Note only (and all of) the following should be contained in the file
245         * META-INF/ META-INF/MANIFEST.MF foo/ foo/bar/ foo/bar/A.class Blah.txt
246         */
247        Support_Resources.copyFile(resources, null, jarName);
248        JarFile jarFile = new JarFile(new File(resources, jarName));
249        Enumeration<JarEntry> e = jarFile.entries();
250        int i;
251        for (i = 0; e.hasMoreElements(); i++) {
252            e.nextElement();
253        }
254        assertEquals(jarFile.size(), i);
255        jarFile.close();
256        assertEquals(6, i);
257    }
258
259    public void test_entries2() throws Exception {
260        Support_Resources.copyFile(resources, null, jarName);
261        JarFile jarFile = new JarFile(new File(resources, jarName));
262        Enumeration<JarEntry> enumeration = jarFile.entries();
263        jarFile.close();
264        try {
265            enumeration.hasMoreElements();
266            fail("hasMoreElements() did not detect a closed jar file");
267        } catch (IllegalStateException e) {
268        }
269        Support_Resources.copyFile(resources, null, jarName);
270        jarFile = new JarFile(new File(resources, jarName));
271        enumeration = jarFile.entries();
272        jarFile.close();
273        try {
274            enumeration.nextElement();
275            fail("nextElement() did not detect closed jar file");
276        } catch (IllegalStateException e) {
277        }
278    }
279
280    /**
281     * @throws IOException
282     * java.util.jar.JarFile#getJarEntry(java.lang.String)
283     */
284    public void test_getEntryLjava_lang_String() throws IOException {
285        try {
286            Support_Resources.copyFile(resources, null, jarName);
287            JarFile jarFile = new JarFile(new File(resources, jarName));
288            assertEquals("Error in returned entry", 311, jarFile.getEntry(
289                    entryName).getSize());
290            jarFile.close();
291        } catch (Exception e) {
292            fail("Exception during test: " + e.toString());
293        }
294
295        Support_Resources.copyFile(resources, null, jarName);
296        JarFile jarFile = new JarFile(new File(resources, jarName));
297        Enumeration<JarEntry> enumeration = jarFile.entries();
298        assertTrue(enumeration.hasMoreElements());
299        while (enumeration.hasMoreElements()) {
300            JarEntry je = enumeration.nextElement();
301            jarFile.getEntry(je.getName());
302        }
303
304        enumeration = jarFile.entries();
305        assertTrue(enumeration.hasMoreElements());
306        JarEntry je = enumeration.nextElement();
307        try {
308            jarFile.close();
309            jarFile.getEntry(je.getName());
310            // fail("IllegalStateException expected.");
311        } catch (IllegalStateException ee) { // Per documentation exception
312            // may be thrown.
313            // expected
314        }
315    }
316
317    /**
318     * @throws IOException
319     * java.util.jar.JarFile#getJarEntry(java.lang.String)
320     */
321    public void test_getJarEntryLjava_lang_String() throws IOException {
322        try {
323            Support_Resources.copyFile(resources, null, jarName);
324            JarFile jarFile = new JarFile(new File(resources, jarName));
325            assertEquals("Error in returned entry", 311, jarFile.getJarEntry(
326                    entryName).getSize());
327            jarFile.close();
328        } catch (Exception e) {
329            fail("Exception during test: " + e.toString());
330        }
331
332        Support_Resources.copyFile(resources, null, jarName);
333        JarFile jarFile = new JarFile(new File(resources, jarName));
334        Enumeration<JarEntry> enumeration = jarFile.entries();
335        assertTrue(enumeration.hasMoreElements());
336        while (enumeration.hasMoreElements()) {
337            JarEntry je = enumeration.nextElement();
338            jarFile.getJarEntry(je.getName());
339        }
340
341        enumeration = jarFile.entries();
342        assertTrue(enumeration.hasMoreElements());
343        JarEntry je = enumeration.nextElement();
344        try {
345            jarFile.close();
346            jarFile.getJarEntry(je.getName());
347            // fail("IllegalStateException expected.");
348        } catch (IllegalStateException ee) { // Per documentation exception
349            // may be thrown.
350            // expected
351        }
352    }
353
354
355    /**
356     * java.util.jar.JarFile#getJarEntry(java.lang.String)
357     */
358    public void testGetJarEntry() throws Exception {
359        Support_Resources.copyFile(resources, null, jarName);
360        JarFile jarFile = new JarFile(new File(resources, jarName));
361        assertEquals("Error in returned entry", 311, jarFile.getEntry(
362                entryName).getSize());
363        jarFile.close();
364
365        // tests for signed jars
366        // test all signed jars in the /Testres/Internal/SignedJars directory
367        String jarDirUrl = Support_Resources
368                .getResourceURL("/../internalres/signedjars");
369        Vector<String> signedJars = new Vector<String>();
370        try {
371            InputStream is = new URL(jarDirUrl + "/jarlist.txt").openStream();
372            while (is.available() > 0) {
373                StringBuilder linebuff = new StringBuilder(80); // Typical line
374                // length
375                done: while (true) {
376                    int nextByte = is.read();
377                    switch (nextByte) {
378                        case -1:
379                            break done;
380                        case (byte) '\r':
381                            if (linebuff.length() == 0) {
382                                // ignore
383                            }
384                            break done;
385                        case (byte) '\n':
386                            if (linebuff.length() == 0) {
387                                // ignore
388                            }
389                            break done;
390                        default:
391                            linebuff.append((char) nextByte);
392                    }
393                }
394                if (linebuff.length() == 0) {
395                    break;
396                }
397                String line = linebuff.toString();
398                signedJars.add(line);
399            }
400            is.close();
401        } catch (IOException e) {
402            // no list of jars found
403        }
404
405        for (int i = 0; i < signedJars.size(); i++) {
406            String jarName = signedJars.get(i);
407            try {
408                File file = Support_Resources.getExternalLocalFile(jarDirUrl
409                        + "/" + jarName);
410                jarFile = new JarFile(file, true);
411                boolean foundCerts = false;
412                Enumeration<JarEntry> e = jarFile.entries();
413                while (e.hasMoreElements()) {
414                    JarEntry entry = e.nextElement();
415                    InputStream is = jarFile.getInputStream(entry);
416                    is.skip(100000);
417                    is.close();
418                    Certificate[] certs = entry.getCertificates();
419                    if (certs != null && certs.length > 0) {
420                        foundCerts = true;
421                        break;
422                    }
423                }
424                assertTrue(
425                        "No certificates found during signed jar test for jar \""
426                                + jarName + "\"", foundCerts);
427            } catch (IOException e) {
428                fail("Exception during signed jar test for jar \"" + jarName
429                        + "\": " + e.toString());
430            }
431        }
432    }
433
434    /**
435     * java.util.jar.JarFile#getManifest()
436     */
437    public void test_getManifest() {
438        // Test for method java.util.jar.Manifest
439        // java.util.jar.JarFile.getManifest()
440        try {
441            Support_Resources.copyFile(resources, null, jarName);
442            JarFile jarFile = new JarFile(new File(resources, jarName));
443            assertNotNull("Error--Manifest not returned", jarFile.getManifest());
444            jarFile.close();
445        } catch (Exception e) {
446            fail("Exception during 1st test: " + e.toString());
447        }
448        try {
449            Support_Resources.copyFile(resources, null, jarName2);
450            JarFile jarFile = new JarFile(new File(resources, jarName2));
451            assertNull("Error--should have returned null", jarFile
452                    .getManifest());
453            jarFile.close();
454        } catch (Exception e) {
455            fail("Exception during 2nd test: " + e.toString());
456        }
457
458        try {
459            // jarName3 was created using the following test
460            Support_Resources.copyFile(resources, null, jarName3);
461            JarFile jarFile = new JarFile(new File(resources, jarName3));
462            assertNotNull("Should find manifest without verifying", jarFile
463                    .getManifest());
464            jarFile.close();
465        } catch (Exception e) {
466            fail("Exception during 3rd test: " + e.toString());
467        }
468
469        try {
470            // this is used to create jarName3 used in the previous test
471            Manifest manifest = new Manifest();
472            Attributes attributes = manifest.getMainAttributes();
473            attributes.put(new Attributes.Name("Manifest-Version"), "1.0");
474            ByteArrayOutputStream manOut = new ByteArrayOutputStream();
475            manifest.write(manOut);
476            byte[] manBytes = manOut.toByteArray();
477            File file = File.createTempFile(
478                    Support_PlatformFile.getNewPlatformFile("hyts_manifest1",
479                            ""), ".jar");
480            JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(
481                    file.getAbsolutePath()));
482            ZipEntry entry = new ZipEntry("META-INF/");
483            entry.setSize(0);
484            jarOut.putNextEntry(entry);
485            entry = new ZipEntry(JarFile.MANIFEST_NAME);
486            entry.setSize(manBytes.length);
487            jarOut.putNextEntry(entry);
488            jarOut.write(manBytes);
489            entry = new ZipEntry("myfile");
490            entry.setSize(1);
491            jarOut.putNextEntry(entry);
492            jarOut.write(65);
493            jarOut.close();
494            JarFile jar = new JarFile(file.getAbsolutePath(), false);
495            assertNotNull("Should find manifest without verifying", jar
496                    .getManifest());
497            jar.close();
498            file.delete();
499        } catch (IOException e) {
500            fail("IOException 3");
501        }
502        try {
503            Support_Resources.copyFile(resources, null, jarName2);
504            JarFile jF = new JarFile(new File(resources, jarName2));
505            jF.close();
506            jF.getManifest();
507            fail("FAILED: expected IllegalStateException");
508        } catch (IllegalStateException ise) {
509            // expected;
510        } catch (Exception e) {
511            fail("Exception during 4th test: " + e.toString());
512        }
513
514        Support_Resources.copyFile(resources, null, "Broken_manifest.jar");
515        JarFile jf;
516        try {
517            jf = new JarFile(new File(resources, "Broken_manifest.jar"));
518            jf.getManifest();
519            fail("IOException expected.");
520        } catch (IOException e) {
521            // expected.
522        }
523    }
524
525    /**
526     * java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry)
527     */
528    // This test doesn't pass on RI. If entry size is set up incorrectly,
529    // SecurityException is thrown. But SecurityException is thrown on RI only
530    // if jar file is signed incorrectly.
531    public void test_getInputStreamLjava_util_jar_JarEntry_subtest0() throws Exception {
532        File signedFile = null;
533        try {
534            Support_Resources.copyFile(resources, null, jarName4);
535            signedFile = new File(resources, jarName4);
536        } catch (Exception e) {
537            fail("Failed to create local file 2: " + e);
538        }
539
540        try {
541            JarFile jar = new JarFile(signedFile);
542            JarEntry entry = new JarEntry(entryName3);
543            InputStream in = jar.getInputStream(entry);
544            in.read();
545        } catch (Exception e) {
546            fail("Exception during test 3: " + e);
547        }
548
549        try {
550            JarFile jar = new JarFile(signedFile);
551            JarEntry entry = new JarEntry(entryName3);
552            InputStream in = jar.getInputStream(entry);
553            // BEGIN android-added
554            byte[] dummy = getAllBytesFromStream(in);
555            // END android-added
556            assertNull("found certificates", entry.getCertificates());
557        } catch (Exception e) {
558            fail("Exception during test 4: " + e);
559        }
560
561        try {
562            JarFile jar = new JarFile(signedFile);
563            JarEntry entry = new JarEntry(entryName3);
564            entry.setSize(1076);
565            InputStream in = jar.getInputStream(entry);
566            // BEGIN android-added
567            byte[] dummy = getAllBytesFromStream(in);
568            // END android-added
569            fail("SecurityException should be thrown.");
570        } catch (SecurityException e) {
571            // expected
572        } catch (Exception e) {
573            fail("Exception during test 5: " + e);
574        }
575
576        try {
577            Support_Resources.copyFile(resources, null, jarName5);
578            signedFile = new File(resources, jarName5);
579        } catch (Exception e) {
580            fail("Failed to create local file 5: " + e);
581        }
582
583        try {
584            JarFile jar = new JarFile(signedFile);
585            JarEntry entry = new JarEntry(entryName3);
586            InputStream in = jar.getInputStream(entry);
587            fail("SecurityException should be thrown.");
588        } catch (SecurityException e) {
589            // expected
590        } catch (Exception e) {
591            fail("Exception during test 5: " + e);
592        }
593
594        // SHA1 digest, SHA256withRSA signed JAR
595        checkSignedJar(jarName6);
596
597        // SHA-256 digest, SHA256withRSA signed JAR
598        checkSignedJar(jarName7);
599
600        // SHA-512 digest, SHA512withECDSA signed JAR
601        checkSignedJar(jarName8);
602
603        // JAR with a signature that has PKCS#7 Authenticated Attributes
604        checkSignedJar(authAttrsJar);
605    }
606
607    private void checkSignedJar(String jarName) throws Exception {
608        Support_Resources.copyFile(resources, null, jarName);
609
610        File file = new File(resources, jarName);
611
612        JarFile jarFile = new JarFile(file, true);
613
614        boolean foundCerts = false;
615
616        Enumeration<JarEntry> e = jarFile.entries();
617        while (e.hasMoreElements()) {
618            JarEntry entry = e.nextElement();
619            InputStream is = jarFile.getInputStream(entry);
620            is.skip(100000);
621            is.close();
622            Certificate[] certs = entry.getCertificates();
623            if (certs != null && certs.length > 0) {
624                foundCerts = true;
625                break;
626            }
627        }
628
629        assertTrue(
630                "No certificates found during signed jar test for jar \""
631                        + jarName + "\"", foundCerts);
632    }
633
634    /*
635     * The jar created by 1.4 which does not provide a
636     * algorithm-Digest-Manifest-Main-Attributes entry in .SF file.
637     */
638    public void test_Jar_created_before_java_5() throws IOException {
639        String modifiedJarName = "Created_by_1_4.jar";
640        Support_Resources.copyFile(resources, null, modifiedJarName);
641        JarFile jarFile = new JarFile(new File(resources, modifiedJarName),
642                true);
643        Enumeration<JarEntry> entries = jarFile.entries();
644        while (entries.hasMoreElements()) {
645            ZipEntry zipEntry = entries.nextElement();
646            jarFile.getInputStream(zipEntry);
647        }
648    }
649
650    /* The jar is intact, then everything is all right. */
651    public void test_JarFile_Integrate_Jar() throws IOException {
652        String modifiedJarName = "Integrate.jar";
653        Support_Resources.copyFile(resources, null, modifiedJarName);
654        JarFile jarFile = new JarFile(new File(resources, modifiedJarName),
655                true);
656        Enumeration<JarEntry> entries = jarFile.entries();
657        while (entries.hasMoreElements()) {
658            ZipEntry zipEntry = entries.nextElement();
659            jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE);
660        }
661    }
662
663    /**
664     * The jar is intact, but the entry object is modified.
665     */
666    public void testJarVerificationModifiedEntry() throws IOException {
667        Support_Resources.copyFile(resources, null, integrateJar);
668        File f = new File(resources, integrateJar);
669
670        JarFile jarFile = new JarFile(f);
671        ZipEntry zipEntry = jarFile.getJarEntry(integrateJarEntry);
672        zipEntry.setSize(zipEntry.getSize() + 1);
673        jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE);
674
675        jarFile = new JarFile(f);
676        zipEntry = jarFile.getJarEntry(integrateJarEntry);
677        zipEntry.setSize(zipEntry.getSize() - 1);
678        try {
679            //jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE);
680            jarFile.getInputStream(zipEntry).read(new byte[5000], 0, 5000);
681            fail("SecurityException expected");
682        } catch (SecurityException e) {
683            // desired
684        }
685    }
686
687    /*
688     * If another entry is inserted into Manifest, no security exception will be
689     * thrown out.
690     */
691    public void test_JarFile_InsertEntry_in_Manifest_Jar() throws IOException {
692        String modifiedJarName = "Inserted_Entry_Manifest.jar";
693        Support_Resources.copyFile(resources, null, modifiedJarName);
694        JarFile jarFile = new JarFile(new File(resources, modifiedJarName),
695                true);
696        Enumeration<JarEntry> entries = jarFile.entries();
697        int count = 0;
698        while (entries.hasMoreElements()) {
699
700            ZipEntry zipEntry = entries.nextElement();
701            jarFile.getInputStream(zipEntry);
702            count++;
703        }
704        assertEquals(5, count);
705    }
706
707    /*
708     * If another entry is inserted into Manifest, no security exception will be
709     * thrown out.
710     */
711    public void test_Inserted_Entry_Manifest_with_DigestCode()
712            throws IOException {
713        String modifiedJarName = "Inserted_Entry_Manifest_with_DigestCode.jar";
714        Support_Resources.copyFile(resources, null, modifiedJarName);
715        JarFile jarFile = new JarFile(new File(resources, modifiedJarName),
716                true);
717        Enumeration<JarEntry> entries = jarFile.entries();
718        int count = 0;
719        while (entries.hasMoreElements()) {
720            ZipEntry zipEntry = entries.nextElement();
721            jarFile.getInputStream(zipEntry);
722            count++;
723        }
724        assertEquals(5, count);
725    }
726
727    /*
728     * The content of Test.class is modified, jarFile.getInputStream will not
729     * throw security Exception, but it will anytime before the inputStream got
730     * from getInputStream method has been read to end.
731     */
732    public void test_JarFile_Modified_Class() throws IOException {
733        String modifiedJarName = "Modified_Class.jar";
734        Support_Resources.copyFile(resources, null, modifiedJarName);
735        JarFile jarFile = new JarFile(new File(resources, modifiedJarName),
736                true);
737        Enumeration<JarEntry> entries = jarFile.entries();
738        while (entries.hasMoreElements()) {
739            ZipEntry zipEntry = entries.nextElement();
740            jarFile.getInputStream(zipEntry);
741        }
742        /* The content of Test.class has been tampered. */
743        ZipEntry zipEntry = jarFile.getEntry("Test.class");
744        InputStream in = jarFile.getInputStream(zipEntry);
745        byte[] buffer = new byte[1024];
746        try {
747            while (in.available() > 0) {
748                in.read(buffer);
749            }
750            fail("SecurityException expected");
751        } catch (SecurityException e) {
752            // desired
753        }
754    }
755
756    /*
757     * In the Modified.jar, the main attributes of META-INF/MANIFEST.MF is
758     * tampered manually. Hence the RI 5.0 JarFile.getInputStream of any
759     * JarEntry will throw security exception.
760     */
761    public void test_JarFile_Modified_Manifest_MainAttributes()
762            throws IOException {
763        String modifiedJarName = "Modified_Manifest_MainAttributes.jar";
764        Support_Resources.copyFile(resources, null, modifiedJarName);
765        JarFile jarFile = new JarFile(new File(resources, modifiedJarName),
766                true);
767        Enumeration<JarEntry> entries = jarFile.entries();
768        while (entries.hasMoreElements()) {
769            ZipEntry zipEntry = entries.nextElement();
770            try {
771                jarFile.getInputStream(zipEntry);
772                fail("SecurityException expected");
773            } catch (SecurityException e) {
774                // desired
775            }
776        }
777    }
778
779    /*
780     * It is all right in our original JarFile. If the Entry Attributes, for
781     * example Test.class in our jar, the jarFile.getInputStream will throw
782     * Security Exception.
783     */
784    public void test_JarFile_Modified_Manifest_EntryAttributes()
785            throws IOException {
786        String modifiedJarName = "Modified_Manifest_EntryAttributes.jar";
787        Support_Resources.copyFile(resources, null, modifiedJarName);
788        JarFile jarFile = new JarFile(new File(resources, modifiedJarName),
789                true);
790        Enumeration<JarEntry> entries = jarFile.entries();
791        while (entries.hasMoreElements()) {
792            ZipEntry zipEntry = entries.nextElement();
793            try {
794                jarFile.getInputStream(zipEntry);
795                fail("should throw Security Exception");
796            } catch (SecurityException e) {
797                // desired
798            }
799        }
800    }
801
802    /*
803     * If the content of the .SA file is modified, no matter what it resides,
804     * JarFile.getInputStream of any JarEntry will throw Security Exception.
805     */
806    public void test_JarFile_Modified_SF_EntryAttributes() throws IOException {
807        String modifiedJarName = "Modified_SF_EntryAttributes.jar";
808        Support_Resources.copyFile(resources, null, modifiedJarName);
809        JarFile jarFile = new JarFile(new File(resources, modifiedJarName),
810                true);
811        Enumeration<JarEntry> entries = jarFile.entries();
812        while (entries.hasMoreElements()) {
813            ZipEntry zipEntry = entries.nextElement();
814            try {
815                jarFile.getInputStream(zipEntry);
816                fail("should throw Security Exception");
817            } catch (SecurityException e) {
818                // desired
819            }
820        }
821    }
822
823    public void test_close() throws IOException {
824        String modifiedJarName = "Modified_SF_EntryAttributes.jar";
825        Support_Resources.copyFile(resources, null, modifiedJarName);
826        JarFile jarFile = new JarFile(new File(resources, modifiedJarName),
827                true);
828        Enumeration<JarEntry> entries = jarFile.entries();
829
830        jarFile.close();
831        jarFile.close();
832
833        // Can not check IOException
834    }
835
836    /**
837     * @throws IOException
838     * java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry)
839     */
840    public void test_getInputStreamLjava_util_jar_JarEntry() throws IOException {
841        File localFile = null;
842        try {
843            Support_Resources.copyFile(resources, null, jarName);
844            localFile = new File(resources, jarName);
845        } catch (Exception e) {
846            fail("Failed to create local file: " + e);
847        }
848
849        byte[] b = new byte[1024];
850        try {
851            JarFile jf = new JarFile(localFile);
852            java.io.InputStream is = jf.getInputStream(jf.getEntry(entryName));
853            // BEGIN android-removed
854            // jf.close();
855            // END android-removed
856            assertTrue("Returned invalid stream", is.available() > 0);
857            int r = is.read(b, 0, 1024);
858            is.close();
859            StringBuffer sb = new StringBuffer(r);
860            for (int i = 0; i < r; i++) {
861                sb.append((char) (b[i] & 0xff));
862            }
863            String contents = sb.toString();
864            assertTrue("Incorrect stream read", contents.indexOf("bar") > 0);
865            // BEGIN android-added
866            jf.close();
867            // END android-added
868        } catch (Exception e) {
869            fail("Exception during test: " + e.toString());
870        }
871
872        try {
873            JarFile jf = new JarFile(localFile);
874            InputStream in = jf.getInputStream(new JarEntry("invalid"));
875            assertNull("Got stream for non-existent entry", in);
876        } catch (Exception e) {
877            fail("Exception during test 2: " + e);
878        }
879
880        try {
881            Support_Resources.copyFile(resources, null, jarName);
882            File signedFile = new File(resources, jarName);
883            JarFile jf = new JarFile(signedFile);
884            JarEntry jre = new JarEntry("foo/bar/A.class");
885            jf.getInputStream(jre);
886            // InputStream returned in any way, exception can be thrown in case
887            // of reading from this stream only.
888            // fail("Should throw ZipException");
889        } catch (ZipException ee) {
890            // expected
891        }
892
893        try {
894            Support_Resources.copyFile(resources, null, jarName);
895            File signedFile = new File(resources, jarName);
896            JarFile jf = new JarFile(signedFile);
897            JarEntry jre = new JarEntry("foo/bar/A.class");
898            jf.close();
899            jf.getInputStream(jre);
900            // InputStream returned in any way, exception can be thrown in case
901            // of reading from this stream only.
902            // The same for IOException
903            fail("Should throw IllegalStateException");
904        } catch (IllegalStateException ee) {
905            // expected
906        }
907    }
908
909    /**
910     * The jar is intact, but the entry object is modified.
911     */
912    // Regression test for issue introduced by HARMONY-4569: signed archives containing files with size 0 could not get verified.
913    public void testJarVerificationEmptyEntry() throws IOException {
914        Support_Resources.copyFile(resources, null, emptyEntryJar);
915        File f = new File(resources, emptyEntryJar);
916
917        JarFile jarFile = new JarFile(f);
918
919        ZipEntry zipEntry = jarFile.getJarEntry(emptyEntry1);
920        int res = jarFile.getInputStream(zipEntry).read(new byte[100], 0, 100);
921        assertEquals("Wrong length of empty jar entry", -1, res);
922
923        zipEntry = jarFile.getJarEntry(emptyEntry2);
924        res = jarFile.getInputStream(zipEntry).read(new byte[100], 0, 100);
925        assertEquals("Wrong length of empty jar entry", -1, res);
926
927        zipEntry = jarFile.getJarEntry(emptyEntry3);
928        res = jarFile.getInputStream(zipEntry).read();
929        assertEquals("Wrong length of empty jar entry", -1, res);
930    }
931}
932