• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 only, as
8  * published by the Free Software Foundation.  Oracle designates this
9  * particular file as subject to the "Classpath" exception as provided
10  * by Oracle in the LICENSE file that accompanied this code.
11  *
12  * This code is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15  * version 2 for more details (a copy is included in the LICENSE file that
16  * accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License version
19  * 2 along with this work; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23  * or visit www.oracle.com if you need additional information or have any
24  * questions.
25  */
26 
27 package java.util.jar;
28 
29 import java.io.*;
30 import java.lang.ref.SoftReference;
31 import java.util.*;
32 import java.util.stream.Stream;
33 import java.util.stream.StreamSupport;
34 import java.util.zip.*;
35 import java.security.CodeSigner;
36 import java.security.cert.Certificate;
37 import java.security.AccessController;
38 import sun.misc.IOUtils;
39 import sun.security.action.GetPropertyAction;
40 import sun.security.util.ManifestEntryVerifier;
41 import sun.security.util.SignatureFileVerifier;
42 
43 /**
44  * The <code>JarFile</code> class is used to read the contents of a jar file
45  * from any file that can be opened with <code>java.io.RandomAccessFile</code>.
46  * It extends the class <code>java.util.zip.ZipFile</code> with support
47  * for reading an optional <code>Manifest</code> entry. The
48  * <code>Manifest</code> can be used to specify meta-information about the
49  * jar file and its entries.
50  *
51  * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
52  * or method in this class will cause a {@link NullPointerException} to be
53  * thrown.
54  *
55  * If the verify flag is on when opening a signed jar file, the content of the
56  * file is verified against its signature embedded inside the file. Please note
57  * that the verification process does not include validating the signer's
58  * certificate. A caller should inspect the return value of
59  * {@link JarEntry#getCodeSigners()} to further determine if the signature
60  * can be trusted.
61  *
62  * @author  David Connelly
63  * @see     Manifest
64  * @see     java.util.zip.ZipFile
65  * @see     java.util.jar.JarEntry
66  * @since   1.2
67  */
68 public
69 class JarFile extends ZipFile {
70     // Android-changed: Hold the Manifest via a hard reference. http://b/28692091
71     // private SoftReference<Manifest> manRef;
72     private Manifest manifest;
73     private JarEntry manEntry;
74     private JarVerifier jv;
75     private boolean jvInitialized;
76     private boolean verify;
77 
78     // indicates if Class-Path attribute present (only valid if hasCheckedSpecialAttributes true)
79     private boolean hasClassPathAttribute;
80     // true if manifest checked for special attributes
81     private volatile boolean hasCheckedSpecialAttributes;
82 
83     // Android-removed: SharedSecrets.setJavaUtilJarAccess
84     /*
85     // Set up JavaUtilJarAccess in SharedSecrets
86     static {
87         SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl());
88     }
89     */
90 
91     /**
92      * The JAR manifest file name.
93      */
94     public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
95 
96     /**
97      * Creates a new <code>JarFile</code> to read from the specified
98      * file <code>name</code>. The <code>JarFile</code> will be verified if
99      * it is signed.
100      * @param name the name of the jar file to be opened for reading
101      * @throws IOException if an I/O error has occurred
102      * @throws SecurityException if access to the file is denied
103      *         by the SecurityManager
104      */
JarFile(String name)105     public JarFile(String name) throws IOException {
106         this(new File(name), true, ZipFile.OPEN_READ);
107     }
108 
109     /**
110      * Creates a new <code>JarFile</code> to read from the specified
111      * file <code>name</code>.
112      * @param name the name of the jar file to be opened for reading
113      * @param verify whether or not to verify the jar file if
114      * it is signed.
115      * @throws IOException if an I/O error has occurred
116      * @throws SecurityException if access to the file is denied
117      *         by the SecurityManager
118      */
JarFile(String name, boolean verify)119     public JarFile(String name, boolean verify) throws IOException {
120         this(new File(name), verify, ZipFile.OPEN_READ);
121     }
122 
123     /**
124      * Creates a new <code>JarFile</code> to read from the specified
125      * <code>File</code> object. The <code>JarFile</code> will be verified if
126      * it is signed.
127      * @param file the jar file to be opened for reading
128      * @throws IOException if an I/O error has occurred
129      * @throws SecurityException if access to the file is denied
130      *         by the SecurityManager
131      */
JarFile(File file)132     public JarFile(File file) throws IOException {
133         this(file, true, ZipFile.OPEN_READ);
134     }
135 
136 
137     /**
138      * Creates a new <code>JarFile</code> to read from the specified
139      * <code>File</code> object.
140      * @param file the jar file to be opened for reading
141      * @param verify whether or not to verify the jar file if
142      * it is signed.
143      * @throws IOException if an I/O error has occurred
144      * @throws SecurityException if access to the file is denied
145      *         by the SecurityManager.
146      */
JarFile(File file, boolean verify)147     public JarFile(File file, boolean verify) throws IOException {
148         this(file, verify, ZipFile.OPEN_READ);
149     }
150 
151 
152     // Android-changed: Use of the hidden constructor with a new argument for zip path validation.
153     /**
154      * Creates a new <code>JarFile</code> to read from the specified
155      * <code>File</code> object in the specified mode.  The mode argument
156      * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
157      *
158      * @param file the jar file to be opened for reading
159      * @param verify whether or not to verify the jar file if
160      * it is signed.
161      * @param mode the mode in which the file is to be opened
162      * @throws IOException if an I/O error has occurred
163      * @throws IllegalArgumentException
164      *         if the <tt>mode</tt> argument is invalid
165      * @throws SecurityException if access to the file is denied
166      *         by the SecurityManager
167      * @since 1.3
168      */
JarFile(File file, boolean verify, int mode)169     public JarFile(File file, boolean verify, int mode) throws IOException {
170         this(file, /* enableZipPathValidator */ true, verify, mode);
171     }
172 
173     // Android-added: New hidden constructor with an argument for zip path validation and verify.
174     /** @hide */
JarFile(String name, boolean enableZipPathValidator, boolean verify)175     public JarFile(String name, boolean enableZipPathValidator, boolean verify) throws IOException {
176         this(new File(name), enableZipPathValidator, verify, ZipFile.OPEN_READ);
177     }
178 
179     // Android-added: New hidden constructor with all available arguments.
180     /** @hide */
JarFile(File file, boolean enableZipPathValidator, boolean verify, int mode)181     public JarFile(File file, boolean enableZipPathValidator, boolean verify, int mode) throws
182             IOException {
183         super(file, mode, enableZipPathValidator);
184         this.verify = verify;
185     }
186 
187     /**
188      * Returns the jar file manifest, or <code>null</code> if none.
189      *
190      * @return the jar file manifest, or <code>null</code> if none
191      *
192      * @throws IllegalStateException
193      *         may be thrown if the jar file has been closed
194      * @throws IOException  if an I/O error has occurred
195      */
getManifest()196     public Manifest getManifest() throws IOException {
197         return getManifestFromReference();
198     }
199 
200     // BEGIN Android-changed: Fix JarFile to be thread safe. http://b/27826114
201     // A volatile field might also work instead of synchronized. http://b/81505612
202     // private Manifest getManifestFromReference() throws IOException {
getManifestFromReference()203     private synchronized Manifest getManifestFromReference() throws IOException {
204     // END Android-changed: Fix JarFile to be thread safe. http://b/27826114
205         // Android-changed: Hold the Manifest via a hard reference. http://b/28692091
206         // Manifest man = manRef != null ? manRef.get() : null;
207         Manifest man = manifest;
208         if (man == null) {
209 
210             JarEntry manEntry = getManEntry();
211 
212             // If found then load the manifest
213             if (manEntry != null) {
214                 if (verify) {
215                     byte[] b = getBytes(manEntry);
216                     man = new Manifest(new ByteArrayInputStream(b));
217                     if (!jvInitialized) {
218                         jv = new JarVerifier(manEntry.getName(), b);
219                     }
220                 } else {
221                     man = new Manifest(super.getInputStream(manEntry));
222                 }
223                 // Android-changed: Hold the Manifest via a hard reference. http://b/28692091
224                 // manRef = new SoftReference<>(man);
225                 manifest = man;
226             }
227         }
228         return man;
229     }
230 
getMetaInfEntryNames()231     private native String[] getMetaInfEntryNames();
232 
233     /**
234      * Returns the <code>JarEntry</code> for the given entry name or
235      * <code>null</code> if not found.
236      *
237      * @param name the jar file entry name
238      * @return the <code>JarEntry</code> for the given entry name or
239      *         <code>null</code> if not found.
240      *
241      * @throws IllegalStateException
242      *         may be thrown if the jar file has been closed
243      *
244      * @see java.util.jar.JarEntry
245      */
getJarEntry(String name)246     public JarEntry getJarEntry(String name) {
247         return (JarEntry)getEntry(name);
248     }
249 
250     /**
251      * Returns the <code>ZipEntry</code> for the given entry name or
252      * <code>null</code> if not found.
253      *
254      * @param name the jar file entry name
255      * @return the <code>ZipEntry</code> for the given entry name or
256      *         <code>null</code> if not found
257      *
258      * @throws IllegalStateException
259      *         may be thrown if the jar file has been closed
260      *
261      * @see java.util.zip.ZipEntry
262      */
getEntry(String name)263     public ZipEntry getEntry(String name) {
264         ZipEntry ze = super.getEntry(name);
265         if (ze != null) {
266             return new JarFileEntry(ze);
267         }
268         return null;
269     }
270 
271     private class JarEntryIterator implements Enumeration<JarEntry>,
272             Iterator<JarEntry>
273     {
274         final Enumeration<? extends ZipEntry> e = JarFile.super.entries();
275 
hasNext()276         public boolean hasNext() {
277             return e.hasMoreElements();
278         }
279 
next()280         public JarEntry next() {
281             ZipEntry ze = e.nextElement();
282             return new JarFileEntry(ze);
283         }
284 
hasMoreElements()285         public boolean hasMoreElements() {
286             return hasNext();
287         }
288 
nextElement()289         public JarEntry nextElement() {
290             return next();
291         }
292     }
293 
294     /**
295      * Returns an enumeration of the zip file entries.
296      */
entries()297     public Enumeration<JarEntry> entries() {
298         return new JarEntryIterator();
299     }
300 
301     @Override
stream()302     public Stream<JarEntry> stream() {
303         return StreamSupport.stream(Spliterators.spliterator(
304                 new JarEntryIterator(), size(),
305                 Spliterator.ORDERED | Spliterator.DISTINCT |
306                         Spliterator.IMMUTABLE | Spliterator.NONNULL), false);
307     }
308 
309     private class JarFileEntry extends JarEntry {
JarFileEntry(ZipEntry ze)310         JarFileEntry(ZipEntry ze) {
311             super(ze);
312         }
getAttributes()313         public Attributes getAttributes() throws IOException {
314             Manifest man = JarFile.this.getManifest();
315             if (man != null) {
316                 return man.getAttributes(getName());
317             } else {
318                 return null;
319             }
320         }
getCertificates()321         public Certificate[] getCertificates() {
322             try {
323                 maybeInstantiateVerifier();
324             } catch (IOException e) {
325                 throw new RuntimeException(e);
326             }
327             if (certs == null && jv != null) {
328                 certs = jv.getCerts(JarFile.this, this);
329             }
330             return certs == null ? null : certs.clone();
331         }
getCodeSigners()332         public CodeSigner[] getCodeSigners() {
333             try {
334                 maybeInstantiateVerifier();
335             } catch (IOException e) {
336                 throw new RuntimeException(e);
337             }
338             if (signers == null && jv != null) {
339                 signers = jv.getCodeSigners(JarFile.this, this);
340             }
341             return signers == null ? null : signers.clone();
342         }
343     }
344 
345     /*
346      * Ensures that the JarVerifier has been created if one is
347      * necessary (i.e., the jar appears to be signed.) This is done as
348      * a quick check to avoid processing of the manifest for unsigned
349      * jars.
350      */
maybeInstantiateVerifier()351     private void maybeInstantiateVerifier() throws IOException {
352         if (jv != null) {
353             return;
354         }
355 
356         if (verify) {
357             String[] names = getMetaInfEntryNames();
358             if (names != null) {
359                 for (int i = 0; i < names.length; i++) {
360                     String name = names[i].toUpperCase(Locale.ENGLISH);
361                     if (name.endsWith(".DSA") ||
362                         name.endsWith(".RSA") ||
363                         name.endsWith(".EC") ||
364                         name.endsWith(".SF")) {
365                         // Assume since we found a signature-related file
366                         // that the jar is signed and that we therefore
367                         // need a JarVerifier and Manifest
368                         getManifest();
369                         return;
370                     }
371                 }
372             }
373             // No signature-related files; don't instantiate a
374             // verifier
375             verify = false;
376         }
377     }
378 
379 
380     /*
381      * Initializes the verifier object by reading all the manifest
382      * entries and passing them to the verifier.
383      */
initializeVerifier()384     private void initializeVerifier() {
385         ManifestEntryVerifier mev = null;
386 
387         // Verify "META-INF/" entries...
388         try {
389             String[] names = getMetaInfEntryNames();
390             if (names != null) {
391                 for (int i = 0; i < names.length; i++) {
392                     String uname = names[i].toUpperCase(Locale.ENGLISH);
393                     if (MANIFEST_NAME.equals(uname)
394                             || SignatureFileVerifier.isBlockOrSF(uname)) {
395                         JarEntry e = getJarEntry(names[i]);
396                         if (e == null) {
397                             throw new JarException("corrupted jar file");
398                         }
399                         if (mev == null) {
400                             mev = new ManifestEntryVerifier
401                                 (getManifestFromReference());
402                         }
403                         byte[] b = getBytes(e);
404                         if (b != null && b.length > 0) {
405                             jv.beginEntry(e, mev);
406                             jv.update(b.length, b, 0, b.length, mev);
407                             jv.update(-1, null, 0, 0, mev);
408                         }
409                     }
410                 }
411             }
412         } catch (IOException ex) {
413             // if we had an error parsing any blocks, just
414             // treat the jar file as being unsigned
415             jv = null;
416             verify = false;
417             if (JarVerifier.debug != null) {
418                 JarVerifier.debug.println("jarfile parsing error!");
419                 ex.printStackTrace();
420             }
421         }
422 
423         // if after initializing the verifier we have nothing
424         // signed, we null it out.
425 
426         if (jv != null) {
427 
428             jv.doneWithMeta();
429             if (JarVerifier.debug != null) {
430                 JarVerifier.debug.println("done with meta!");
431             }
432 
433             if (jv.nothingToVerify()) {
434                 if (JarVerifier.debug != null) {
435                     JarVerifier.debug.println("nothing to verify!");
436                 }
437                 jv = null;
438                 verify = false;
439             }
440         }
441     }
442 
443     /*
444      * Reads all the bytes for a given entry. Used to process the
445      * META-INF files.
446      */
getBytes(ZipEntry ze)447     private byte[] getBytes(ZipEntry ze) throws IOException {
448         try (InputStream is = super.getInputStream(ze)) {
449             return IOUtils.readFully(is, (int)ze.getSize(), true);
450         }
451     }
452 
453     /**
454      * Returns an input stream for reading the contents of the specified
455      * zip file entry.
456      * @param ze the zip file entry
457      * @return an input stream for reading the contents of the specified
458      *         zip file entry
459      * @throws ZipException if a zip file format error has occurred
460      * @throws IOException if an I/O error has occurred
461      * @throws SecurityException if any of the jar file entries
462      *         are incorrectly signed.
463      * @throws IllegalStateException
464      *         may be thrown if the jar file has been closed
465      */
getInputStream(ZipEntry ze)466     public synchronized InputStream getInputStream(ZipEntry ze)
467         throws IOException
468     {
469         maybeInstantiateVerifier();
470         if (jv == null) {
471             return super.getInputStream(ze);
472         }
473         if (!jvInitialized) {
474             initializeVerifier();
475             jvInitialized = true;
476             // could be set to null after a call to
477             // initializeVerifier if we have nothing to
478             // verify
479             if (jv == null)
480                 return super.getInputStream(ze);
481         }
482 
483         // wrap a verifier stream around the real stream
484         return new JarVerifier.VerifierStream(
485             getManifestFromReference(),
486             ze instanceof JarFileEntry ?
487             (JarEntry) ze : getJarEntry(ze.getName()),
488             super.getInputStream(ze),
489             jv);
490     }
491 
492     // Statics for hand-coded Boyer-Moore search
493     private static final char[] CLASSPATH_CHARS = {'c','l','a','s','s','-','p','a','t','h'};
494     // The bad character shift for "class-path"
495     private static final int[] CLASSPATH_LASTOCC;
496     // The good suffix shift for "class-path"
497     private static final int[] CLASSPATH_OPTOSFT;
498 
499     static {
500         CLASSPATH_LASTOCC = new int[128];
501         CLASSPATH_OPTOSFT = new int[10];
502         CLASSPATH_LASTOCC[(int)'c'] = 1;
503         CLASSPATH_LASTOCC[(int)'l'] = 2;
504         CLASSPATH_LASTOCC[(int)'s'] = 5;
505         CLASSPATH_LASTOCC[(int)'-'] = 6;
506         CLASSPATH_LASTOCC[(int)'p'] = 7;
507         CLASSPATH_LASTOCC[(int)'a'] = 8;
508         CLASSPATH_LASTOCC[(int)'t'] = 9;
509         CLASSPATH_LASTOCC[(int)'h'] = 10;
510         for (int i=0; i<9; i++)
511             CLASSPATH_OPTOSFT[i] = 10;
512         CLASSPATH_OPTOSFT[9]=1;
513     }
514 
515     // BEGIN Android-changed: Fix JarFile to be thread safe. http://b/27826114
516     // A volatile field might also work instead of synchronized. http://b/81505612
517     // private JarEntry getManEntry() {
getManEntry()518     private synchronized JarEntry getManEntry() {
519     // END Android-changed: Fix JarFile to be thread safe. http://b/27826114
520         if (manEntry == null) {
521             // First look up manifest entry using standard name
522             manEntry = getJarEntry(MANIFEST_NAME);
523             if (manEntry == null) {
524                 // If not found, then iterate through all the "META-INF/"
525                 // entries to find a match.
526                 String[] names = getMetaInfEntryNames();
527                 if (names != null) {
528                     for (int i = 0; i < names.length; i++) {
529                         if (MANIFEST_NAME.equals(
530                                                  names[i].toUpperCase(Locale.ENGLISH))) {
531                             manEntry = getJarEntry(names[i]);
532                             break;
533                         }
534                     }
535                 }
536             }
537         }
538         return manEntry;
539     }
540 
541    /**
542     * Returns {@code true} iff this JAR file has a manifest with the
543     * Class-Path attribute
544     * @hide
545     */
546     // Android-changed: Make hasClassPathAttribute() @hide public, for internal use.
547     // Used by URLClassPath.JarLoader.
548     // boolean hasClassPathAttribute() throws IOException {
hasClassPathAttribute()549     public boolean hasClassPathAttribute() throws IOException {
550         checkForSpecialAttributes();
551         return hasClassPathAttribute;
552     }
553 
554     /**
555      * Returns true if the pattern {@code src} is found in {@code b}.
556      * The {@code lastOcc} and {@code optoSft} arrays are the precomputed
557      * bad character and good suffix shifts.
558      */
match(char[] src, byte[] b, int[] lastOcc, int[] optoSft)559     private boolean match(char[] src, byte[] b, int[] lastOcc, int[] optoSft) {
560         int len = src.length;
561         int last = b.length - len;
562         int i = 0;
563         next:
564         while (i<=last) {
565             for (int j=(len-1); j>=0; j--) {
566                 char c = (char) b[i+j];
567                 c = (((c-'A')|('Z'-c)) >= 0) ? (char)(c + 32) : c;
568                 if (c != src[j]) {
569                     i += Math.max(j + 1 - lastOcc[c&0x7F], optoSft[j]);
570                     continue next;
571                  }
572             }
573             return true;
574         }
575         return false;
576     }
577 
578     /**
579      * On first invocation, check if the JAR file has the Class-Path
580      * attribute. A no-op on subsequent calls.
581      */
checkForSpecialAttributes()582     private void checkForSpecialAttributes() throws IOException {
583         if (hasCheckedSpecialAttributes) return;
584         // Android-changed: Special handling of well-known .jar files specific to OpenJDK.
585         // if (!isKnownNotToHaveSpecialAttributes()) {
586         {
587             JarEntry manEntry = getManEntry();
588             if (manEntry != null) {
589                 byte[] b = getBytes(manEntry);
590                 if (match(CLASSPATH_CHARS, b, CLASSPATH_LASTOCC, CLASSPATH_OPTOSFT))
591                     hasClassPathAttribute = true;
592             }
593         }
594         hasCheckedSpecialAttributes = true;
595     }
596 
597 
598     // Android-removed: Special handling of well-known .jar files specific to OpenJDK.
599     /*
600     private static String javaHome;
601     private static volatile String[] jarNames;
602     private boolean isKnownNotToHaveSpecialAttributes() {
603         // Optimize away even scanning of manifest for jar files we
604         // deliver which don't have a class-path attribute. If one of
605         // these jars is changed to include such an attribute this code
606         // must be changed.
607         if (javaHome == null) {
608             javaHome = AccessController.doPrivileged(
609                 new GetPropertyAction("java.home"));
610         }
611         if (jarNames == null) {
612             String[] names = new String[11];
613             String fileSep = File.separator;
614             int i = 0;
615             names[i++] = fileSep + "rt.jar";
616             names[i++] = fileSep + "jsse.jar";
617             names[i++] = fileSep + "jce.jar";
618             names[i++] = fileSep + "charsets.jar";
619             names[i++] = fileSep + "dnsns.jar";
620             names[i++] = fileSep + "zipfs.jar";
621             names[i++] = fileSep + "localedata.jar";
622             names[i++] = fileSep = "cldrdata.jar";
623             names[i++] = fileSep + "sunjce_provider.jar";
624             names[i++] = fileSep + "sunpkcs11.jar";
625             names[i++] = fileSep + "sunec.jar";
626             jarNames = names;
627         }
628 
629         String name = getName();
630         String localJavaHome = javaHome;
631         if (name.startsWith(localJavaHome)) {
632             String[] names = jarNames;
633             for (int i = 0; i < names.length; i++) {
634                 if (name.endsWith(names[i])) {
635                     return true;
636                 }
637             }
638         }
639         return false;
640     }
641     */
642 
643     // Android-removed: Unused method ensureInitialization().
644     /*
645     private synchronized void ensureInitialization() {
646         try {
647             maybeInstantiateVerifier();
648         } catch (IOException e) {
649             throw new RuntimeException(e);
650         }
651         if (jv != null && !jvInitialized) {
652             initializeVerifier();
653             jvInitialized = true;
654         }
655     }
656     */
657 
newEntry(ZipEntry ze)658     JarEntry newEntry(ZipEntry ze) {
659         return new JarFileEntry(ze);
660     }
661 
662     // Android-removed: Unused methods entryNames(), entries2().
663     /*
664     Enumeration<String> entryNames(CodeSource[] cs) {
665         ensureInitialization();
666         if (jv != null) {
667             return jv.entryNames(this, cs);
668         }
669 
670         /*
671          * JAR file has no signed content. Is there a non-signing
672          * code source?
673          *
674         boolean includeUnsigned = false;
675         for (int i = 0; i < cs.length; i++) {
676             if (cs[i].getCodeSigners() == null) {
677                 includeUnsigned = true;
678                 break;
679             }
680         }
681         if (includeUnsigned) {
682             return unsignedEntryNames();
683         } else {
684             return new Enumeration<String>() {
685 
686                 public boolean hasMoreElements() {
687                     return false;
688                 }
689 
690                 public String nextElement() {
691                     throw new NoSuchElementException();
692                 }
693             };
694         }
695     }
696 
697     /**
698      * Returns an enumeration of the zip file entries
699      * excluding internal JAR mechanism entries and including
700      * signed entries missing from the ZIP directory.
701      *
702     Enumeration<JarEntry> entries2() {
703         ensureInitialization();
704         if (jv != null) {
705             return jv.entries2(this, super.entries());
706         }
707 
708         // screen out entries which are never signed
709         final Enumeration<? extends ZipEntry> enum_ = super.entries();
710         return new Enumeration<JarEntry>() {
711 
712             ZipEntry entry;
713 
714             public boolean hasMoreElements() {
715                 if (entry != null) {
716                     return true;
717                 }
718                 while (enum_.hasMoreElements()) {
719                     ZipEntry ze = enum_.nextElement();
720                     if (JarVerifier.isSigningRelated(ze.getName())) {
721                         continue;
722                     }
723                     entry = ze;
724                     return true;
725                 }
726                 return false;
727             }
728 
729             public JarFileEntry nextElement() {
730                 if (hasMoreElements()) {
731                     ZipEntry ze = entry;
732                     entry = null;
733                     return new JarFileEntry(ze);
734                 }
735                 throw new NoSuchElementException();
736             }
737         };
738     }
739 
740     CodeSource[] getCodeSources(URL url) {
741         ensureInitialization();
742         if (jv != null) {
743             return jv.getCodeSources(this, url);
744         }
745 
746         /*
747          * JAR file has no signed content. Is there a non-signing
748          * code source?
749          *
750         Enumeration<String> unsigned = unsignedEntryNames();
751         if (unsigned.hasMoreElements()) {
752             return new CodeSource[]{JarVerifier.getUnsignedCS(url)};
753         } else {
754             return null;
755         }
756     }
757 
758     private Enumeration<String> unsignedEntryNames() {
759         final Enumeration<JarEntry> entries = entries();
760         return new Enumeration<String>() {
761 
762             String name;
763 
764             /*
765              * Grab entries from ZIP directory but screen out
766              * metadata.
767              *
768             public boolean hasMoreElements() {
769                 if (name != null) {
770                     return true;
771                 }
772                 while (entries.hasMoreElements()) {
773                     String value;
774                     ZipEntry e = entries.nextElement();
775                     value = e.getName();
776                     if (e.isDirectory() || JarVerifier.isSigningRelated(value)) {
777                         continue;
778                     }
779                     name = value;
780                     return true;
781                 }
782                 return false;
783             }
784 
785             public String nextElement() {
786                 if (hasMoreElements()) {
787                     String value = name;
788                     name = null;
789                     return value;
790                 }
791                 throw new NoSuchElementException();
792             }
793         };
794     }
795 
796     CodeSource getCodeSource(URL url, String name) {
797         ensureInitialization();
798         if (jv != null) {
799             if (jv.eagerValidation) {
800                 CodeSource cs = null;
801                 JarEntry je = getJarEntry(name);
802                 if (je != null) {
803                     cs = jv.getCodeSource(url, this, je);
804                 } else {
805                     cs = jv.getCodeSource(url, name);
806                 }
807                 return cs;
808             } else {
809                 return jv.getCodeSource(url, name);
810             }
811         }
812 
813         return JarVerifier.getUnsignedCS(url);
814     }
815 
816     void setEagerValidation(boolean eager) {
817         try {
818             maybeInstantiateVerifier();
819         } catch (IOException e) {
820             throw new RuntimeException(e);
821         }
822         if (jv != null) {
823             jv.setEagerValidation(eager);
824         }
825     }
826 
827     List<Object> getManifestDigests() {
828         ensureInitialization();
829         if (jv != null) {
830             return jv.getManifestDigests();
831         }
832         return new ArrayList<Object>();
833     }
834     */
835 }
836