• 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     /**
153      * Creates a new <code>JarFile</code> to read from the specified
154      * <code>File</code> object in the specified mode.  The mode argument
155      * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
156      *
157      * @param file the jar file to be opened for reading
158      * @param verify whether or not to verify the jar file if
159      * it is signed.
160      * @param mode the mode in which the file is to be opened
161      * @throws IOException if an I/O error has occurred
162      * @throws IllegalArgumentException
163      *         if the <tt>mode</tt> argument is invalid
164      * @throws SecurityException if access to the file is denied
165      *         by the SecurityManager
166      * @since 1.3
167      */
JarFile(File file, boolean verify, int mode)168     public JarFile(File file, boolean verify, int mode) throws IOException {
169         super(file, mode);
170         this.verify = verify;
171     }
172 
173     /**
174      * Returns the jar file manifest, or <code>null</code> if none.
175      *
176      * @return the jar file manifest, or <code>null</code> if none
177      *
178      * @throws IllegalStateException
179      *         may be thrown if the jar file has been closed
180      * @throws IOException  if an I/O error has occurred
181      */
getManifest()182     public Manifest getManifest() throws IOException {
183         return getManifestFromReference();
184     }
185 
186     // BEGIN Android-changed: Fix JarFile to be thread safe. http://b/27826114
187     // A volatile field might also work instead of synchronized. http://b/81505612
188     // private Manifest getManifestFromReference() throws IOException {
getManifestFromReference()189     private synchronized Manifest getManifestFromReference() throws IOException {
190     // END Android-changed: Fix JarFile to be thread safe. http://b/27826114
191         // Android-changed: Hold the Manifest via a hard reference. http://b/28692091
192         // Manifest man = manRef != null ? manRef.get() : null;
193         Manifest man = manifest;
194         if (man == null) {
195 
196             JarEntry manEntry = getManEntry();
197 
198             // If found then load the manifest
199             if (manEntry != null) {
200                 if (verify) {
201                     byte[] b = getBytes(manEntry);
202                     man = new Manifest(new ByteArrayInputStream(b));
203                     if (!jvInitialized) {
204                         jv = new JarVerifier(b);
205                     }
206                 } else {
207                     man = new Manifest(super.getInputStream(manEntry));
208                 }
209                 // Android-changed: Hold the Manifest via a hard reference. http://b/28692091
210                 // manRef = new SoftReference<>(man);
211                 manifest = man;
212             }
213         }
214         return man;
215     }
216 
getMetaInfEntryNames()217     private native String[] getMetaInfEntryNames();
218 
219     /**
220      * Returns the <code>JarEntry</code> for the given entry name or
221      * <code>null</code> if not found.
222      *
223      * @param name the jar file entry name
224      * @return the <code>JarEntry</code> for the given entry name or
225      *         <code>null</code> if not found.
226      *
227      * @throws IllegalStateException
228      *         may be thrown if the jar file has been closed
229      *
230      * @see java.util.jar.JarEntry
231      */
getJarEntry(String name)232     public JarEntry getJarEntry(String name) {
233         return (JarEntry)getEntry(name);
234     }
235 
236     /**
237      * Returns the <code>ZipEntry</code> for the given entry name or
238      * <code>null</code> if not found.
239      *
240      * @param name the jar file entry name
241      * @return the <code>ZipEntry</code> for the given entry name or
242      *         <code>null</code> if not found
243      *
244      * @throws IllegalStateException
245      *         may be thrown if the jar file has been closed
246      *
247      * @see java.util.zip.ZipEntry
248      */
getEntry(String name)249     public ZipEntry getEntry(String name) {
250         ZipEntry ze = super.getEntry(name);
251         if (ze != null) {
252             return new JarFileEntry(ze);
253         }
254         return null;
255     }
256 
257     private class JarEntryIterator implements Enumeration<JarEntry>,
258             Iterator<JarEntry>
259     {
260         final Enumeration<? extends ZipEntry> e = JarFile.super.entries();
261 
hasNext()262         public boolean hasNext() {
263             return e.hasMoreElements();
264         }
265 
next()266         public JarEntry next() {
267             ZipEntry ze = e.nextElement();
268             return new JarFileEntry(ze);
269         }
270 
hasMoreElements()271         public boolean hasMoreElements() {
272             return hasNext();
273         }
274 
nextElement()275         public JarEntry nextElement() {
276             return next();
277         }
278     }
279 
280     /**
281      * Returns an enumeration of the zip file entries.
282      */
entries()283     public Enumeration<JarEntry> entries() {
284         return new JarEntryIterator();
285     }
286 
287     @Override
stream()288     public Stream<JarEntry> stream() {
289         return StreamSupport.stream(Spliterators.spliterator(
290                 new JarEntryIterator(), size(),
291                 Spliterator.ORDERED | Spliterator.DISTINCT |
292                         Spliterator.IMMUTABLE | Spliterator.NONNULL), false);
293     }
294 
295     private class JarFileEntry extends JarEntry {
JarFileEntry(ZipEntry ze)296         JarFileEntry(ZipEntry ze) {
297             super(ze);
298         }
getAttributes()299         public Attributes getAttributes() throws IOException {
300             Manifest man = JarFile.this.getManifest();
301             if (man != null) {
302                 return man.getAttributes(getName());
303             } else {
304                 return null;
305             }
306         }
getCertificates()307         public Certificate[] getCertificates() {
308             try {
309                 maybeInstantiateVerifier();
310             } catch (IOException e) {
311                 throw new RuntimeException(e);
312             }
313             if (certs == null && jv != null) {
314                 certs = jv.getCerts(JarFile.this, this);
315             }
316             return certs == null ? null : certs.clone();
317         }
getCodeSigners()318         public CodeSigner[] getCodeSigners() {
319             try {
320                 maybeInstantiateVerifier();
321             } catch (IOException e) {
322                 throw new RuntimeException(e);
323             }
324             if (signers == null && jv != null) {
325                 signers = jv.getCodeSigners(JarFile.this, this);
326             }
327             return signers == null ? null : signers.clone();
328         }
329     }
330 
331     /*
332      * Ensures that the JarVerifier has been created if one is
333      * necessary (i.e., the jar appears to be signed.) This is done as
334      * a quick check to avoid processing of the manifest for unsigned
335      * jars.
336      */
maybeInstantiateVerifier()337     private void maybeInstantiateVerifier() throws IOException {
338         if (jv != null) {
339             return;
340         }
341 
342         if (verify) {
343             String[] names = getMetaInfEntryNames();
344             if (names != null) {
345                 for (int i = 0; i < names.length; i++) {
346                     String name = names[i].toUpperCase(Locale.ENGLISH);
347                     if (name.endsWith(".DSA") ||
348                         name.endsWith(".RSA") ||
349                         name.endsWith(".EC") ||
350                         name.endsWith(".SF")) {
351                         // Assume since we found a signature-related file
352                         // that the jar is signed and that we therefore
353                         // need a JarVerifier and Manifest
354                         getManifest();
355                         return;
356                     }
357                 }
358             }
359             // No signature-related files; don't instantiate a
360             // verifier
361             verify = false;
362         }
363     }
364 
365 
366     /*
367      * Initializes the verifier object by reading all the manifest
368      * entries and passing them to the verifier.
369      */
initializeVerifier()370     private void initializeVerifier() {
371         ManifestEntryVerifier mev = null;
372 
373         // Verify "META-INF/" entries...
374         try {
375             String[] names = getMetaInfEntryNames();
376             if (names != null) {
377                 for (int i = 0; i < names.length; i++) {
378                     String uname = names[i].toUpperCase(Locale.ENGLISH);
379                     if (MANIFEST_NAME.equals(uname)
380                             || SignatureFileVerifier.isBlockOrSF(uname)) {
381                         JarEntry e = getJarEntry(names[i]);
382                         if (e == null) {
383                             throw new JarException("corrupted jar file");
384                         }
385                         if (mev == null) {
386                             mev = new ManifestEntryVerifier
387                                 (getManifestFromReference());
388                         }
389                         byte[] b = getBytes(e);
390                         if (b != null && b.length > 0) {
391                             jv.beginEntry(e, mev);
392                             jv.update(b.length, b, 0, b.length, mev);
393                             jv.update(-1, null, 0, 0, mev);
394                         }
395                     }
396                 }
397             }
398         } catch (IOException ex) {
399             // if we had an error parsing any blocks, just
400             // treat the jar file as being unsigned
401             jv = null;
402             verify = false;
403             if (JarVerifier.debug != null) {
404                 JarVerifier.debug.println("jarfile parsing error!");
405                 ex.printStackTrace();
406             }
407         }
408 
409         // if after initializing the verifier we have nothing
410         // signed, we null it out.
411 
412         if (jv != null) {
413 
414             jv.doneWithMeta();
415             if (JarVerifier.debug != null) {
416                 JarVerifier.debug.println("done with meta!");
417             }
418 
419             if (jv.nothingToVerify()) {
420                 if (JarVerifier.debug != null) {
421                     JarVerifier.debug.println("nothing to verify!");
422                 }
423                 jv = null;
424                 verify = false;
425             }
426         }
427     }
428 
429     /*
430      * Reads all the bytes for a given entry. Used to process the
431      * META-INF files.
432      */
getBytes(ZipEntry ze)433     private byte[] getBytes(ZipEntry ze) throws IOException {
434         try (InputStream is = super.getInputStream(ze)) {
435             return IOUtils.readFully(is, (int)ze.getSize(), true);
436         }
437     }
438 
439     /**
440      * Returns an input stream for reading the contents of the specified
441      * zip file entry.
442      * @param ze the zip file entry
443      * @return an input stream for reading the contents of the specified
444      *         zip file entry
445      * @throws ZipException if a zip file format error has occurred
446      * @throws IOException if an I/O error has occurred
447      * @throws SecurityException if any of the jar file entries
448      *         are incorrectly signed.
449      * @throws IllegalStateException
450      *         may be thrown if the jar file has been closed
451      */
getInputStream(ZipEntry ze)452     public synchronized InputStream getInputStream(ZipEntry ze)
453         throws IOException
454     {
455         maybeInstantiateVerifier();
456         if (jv == null) {
457             return super.getInputStream(ze);
458         }
459         if (!jvInitialized) {
460             initializeVerifier();
461             jvInitialized = true;
462             // could be set to null after a call to
463             // initializeVerifier if we have nothing to
464             // verify
465             if (jv == null)
466                 return super.getInputStream(ze);
467         }
468 
469         // wrap a verifier stream around the real stream
470         return new JarVerifier.VerifierStream(
471             getManifestFromReference(),
472             ze instanceof JarFileEntry ?
473             (JarEntry) ze : getJarEntry(ze.getName()),
474             super.getInputStream(ze),
475             jv);
476     }
477 
478     // Statics for hand-coded Boyer-Moore search
479     private static final char[] CLASSPATH_CHARS = {'c','l','a','s','s','-','p','a','t','h'};
480     // The bad character shift for "class-path"
481     private static final int[] CLASSPATH_LASTOCC;
482     // The good suffix shift for "class-path"
483     private static final int[] CLASSPATH_OPTOSFT;
484 
485     static {
486         CLASSPATH_LASTOCC = new int[128];
487         CLASSPATH_OPTOSFT = new int[10];
488         CLASSPATH_LASTOCC[(int)'c'] = 1;
489         CLASSPATH_LASTOCC[(int)'l'] = 2;
490         CLASSPATH_LASTOCC[(int)'s'] = 5;
491         CLASSPATH_LASTOCC[(int)'-'] = 6;
492         CLASSPATH_LASTOCC[(int)'p'] = 7;
493         CLASSPATH_LASTOCC[(int)'a'] = 8;
494         CLASSPATH_LASTOCC[(int)'t'] = 9;
495         CLASSPATH_LASTOCC[(int)'h'] = 10;
496         for (int i=0; i<9; i++)
497             CLASSPATH_OPTOSFT[i] = 10;
498         CLASSPATH_OPTOSFT[9]=1;
499     }
500 
501     // BEGIN Android-changed: Fix JarFile to be thread safe. http://b/27826114
502     // A volatile field might also work instead of synchronized. http://b/81505612
503     // private JarEntry getManEntry() {
getManEntry()504     private synchronized JarEntry getManEntry() {
505     // END Android-changed: Fix JarFile to be thread safe. http://b/27826114
506         if (manEntry == null) {
507             // First look up manifest entry using standard name
508             manEntry = getJarEntry(MANIFEST_NAME);
509             if (manEntry == null) {
510                 // If not found, then iterate through all the "META-INF/"
511                 // entries to find a match.
512                 String[] names = getMetaInfEntryNames();
513                 if (names != null) {
514                     for (int i = 0; i < names.length; i++) {
515                         if (MANIFEST_NAME.equals(
516                                                  names[i].toUpperCase(Locale.ENGLISH))) {
517                             manEntry = getJarEntry(names[i]);
518                             break;
519                         }
520                     }
521                 }
522             }
523         }
524         return manEntry;
525     }
526 
527    /**
528     * Returns {@code true} iff this JAR file has a manifest with the
529     * Class-Path attribute
530     * @hide
531     */
532     // Android-changed: Make hasClassPathAttribute() @hide public, for internal use.
533     // Used by URLClassPath.JarLoader.
534     // boolean hasClassPathAttribute() throws IOException {
hasClassPathAttribute()535     public boolean hasClassPathAttribute() throws IOException {
536         checkForSpecialAttributes();
537         return hasClassPathAttribute;
538     }
539 
540     /**
541      * Returns true if the pattern {@code src} is found in {@code b}.
542      * The {@code lastOcc} and {@code optoSft} arrays are the precomputed
543      * bad character and good suffix shifts.
544      */
match(char[] src, byte[] b, int[] lastOcc, int[] optoSft)545     private boolean match(char[] src, byte[] b, int[] lastOcc, int[] optoSft) {
546         int len = src.length;
547         int last = b.length - len;
548         int i = 0;
549         next:
550         while (i<=last) {
551             for (int j=(len-1); j>=0; j--) {
552                 char c = (char) b[i+j];
553                 c = (((c-'A')|('Z'-c)) >= 0) ? (char)(c + 32) : c;
554                 if (c != src[j]) {
555                     i += Math.max(j + 1 - lastOcc[c&0x7F], optoSft[j]);
556                     continue next;
557                  }
558             }
559             return true;
560         }
561         return false;
562     }
563 
564     /**
565      * On first invocation, check if the JAR file has the Class-Path
566      * attribute. A no-op on subsequent calls.
567      */
checkForSpecialAttributes()568     private void checkForSpecialAttributes() throws IOException {
569         if (hasCheckedSpecialAttributes) return;
570         // Android-changed: Special handling of well-known .jar files specific to OpenJDK.
571         // if (!isKnownNotToHaveSpecialAttributes()) {
572         {
573             JarEntry manEntry = getManEntry();
574             if (manEntry != null) {
575                 byte[] b = getBytes(manEntry);
576                 if (match(CLASSPATH_CHARS, b, CLASSPATH_LASTOCC, CLASSPATH_OPTOSFT))
577                     hasClassPathAttribute = true;
578             }
579         }
580         hasCheckedSpecialAttributes = true;
581     }
582 
583 
584     // Android-removed: Special handling of well-known .jar files specific to OpenJDK.
585     /*
586     private static String javaHome;
587     private static volatile String[] jarNames;
588     private boolean isKnownNotToHaveSpecialAttributes() {
589         // Optimize away even scanning of manifest for jar files we
590         // deliver which don't have a class-path attribute. If one of
591         // these jars is changed to include such an attribute this code
592         // must be changed.
593         if (javaHome == null) {
594             javaHome = AccessController.doPrivileged(
595                 new GetPropertyAction("java.home"));
596         }
597         if (jarNames == null) {
598             String[] names = new String[11];
599             String fileSep = File.separator;
600             int i = 0;
601             names[i++] = fileSep + "rt.jar";
602             names[i++] = fileSep + "jsse.jar";
603             names[i++] = fileSep + "jce.jar";
604             names[i++] = fileSep + "charsets.jar";
605             names[i++] = fileSep + "dnsns.jar";
606             names[i++] = fileSep + "zipfs.jar";
607             names[i++] = fileSep + "localedata.jar";
608             names[i++] = fileSep = "cldrdata.jar";
609             names[i++] = fileSep + "sunjce_provider.jar";
610             names[i++] = fileSep + "sunpkcs11.jar";
611             names[i++] = fileSep + "sunec.jar";
612             jarNames = names;
613         }
614 
615         String name = getName();
616         String localJavaHome = javaHome;
617         if (name.startsWith(localJavaHome)) {
618             String[] names = jarNames;
619             for (int i = 0; i < names.length; i++) {
620                 if (name.endsWith(names[i])) {
621                     return true;
622                 }
623             }
624         }
625         return false;
626     }
627     */
628 
629     // Android-removed: Unused method ensureInitialization().
630     /*
631     private synchronized void ensureInitialization() {
632         try {
633             maybeInstantiateVerifier();
634         } catch (IOException e) {
635             throw new RuntimeException(e);
636         }
637         if (jv != null && !jvInitialized) {
638             initializeVerifier();
639             jvInitialized = true;
640         }
641     }
642     */
643 
newEntry(ZipEntry ze)644     JarEntry newEntry(ZipEntry ze) {
645         return new JarFileEntry(ze);
646     }
647 
648     // Android-removed: Unused methods entryNames(), entries2().
649     /*
650     Enumeration<String> entryNames(CodeSource[] cs) {
651         ensureInitialization();
652         if (jv != null) {
653             return jv.entryNames(this, cs);
654         }
655 
656         /*
657          * JAR file has no signed content. Is there a non-signing
658          * code source?
659          *
660         boolean includeUnsigned = false;
661         for (int i = 0; i < cs.length; i++) {
662             if (cs[i].getCodeSigners() == null) {
663                 includeUnsigned = true;
664                 break;
665             }
666         }
667         if (includeUnsigned) {
668             return unsignedEntryNames();
669         } else {
670             return new Enumeration<String>() {
671 
672                 public boolean hasMoreElements() {
673                     return false;
674                 }
675 
676                 public String nextElement() {
677                     throw new NoSuchElementException();
678                 }
679             };
680         }
681     }
682 
683     /**
684      * Returns an enumeration of the zip file entries
685      * excluding internal JAR mechanism entries and including
686      * signed entries missing from the ZIP directory.
687      *
688     Enumeration<JarEntry> entries2() {
689         ensureInitialization();
690         if (jv != null) {
691             return jv.entries2(this, super.entries());
692         }
693 
694         // screen out entries which are never signed
695         final Enumeration<? extends ZipEntry> enum_ = super.entries();
696         return new Enumeration<JarEntry>() {
697 
698             ZipEntry entry;
699 
700             public boolean hasMoreElements() {
701                 if (entry != null) {
702                     return true;
703                 }
704                 while (enum_.hasMoreElements()) {
705                     ZipEntry ze = enum_.nextElement();
706                     if (JarVerifier.isSigningRelated(ze.getName())) {
707                         continue;
708                     }
709                     entry = ze;
710                     return true;
711                 }
712                 return false;
713             }
714 
715             public JarFileEntry nextElement() {
716                 if (hasMoreElements()) {
717                     ZipEntry ze = entry;
718                     entry = null;
719                     return new JarFileEntry(ze);
720                 }
721                 throw new NoSuchElementException();
722             }
723         };
724     }
725 
726     CodeSource[] getCodeSources(URL url) {
727         ensureInitialization();
728         if (jv != null) {
729             return jv.getCodeSources(this, url);
730         }
731 
732         /*
733          * JAR file has no signed content. Is there a non-signing
734          * code source?
735          *
736         Enumeration<String> unsigned = unsignedEntryNames();
737         if (unsigned.hasMoreElements()) {
738             return new CodeSource[]{JarVerifier.getUnsignedCS(url)};
739         } else {
740             return null;
741         }
742     }
743 
744     private Enumeration<String> unsignedEntryNames() {
745         final Enumeration<JarEntry> entries = entries();
746         return new Enumeration<String>() {
747 
748             String name;
749 
750             /*
751              * Grab entries from ZIP directory but screen out
752              * metadata.
753              *
754             public boolean hasMoreElements() {
755                 if (name != null) {
756                     return true;
757                 }
758                 while (entries.hasMoreElements()) {
759                     String value;
760                     ZipEntry e = entries.nextElement();
761                     value = e.getName();
762                     if (e.isDirectory() || JarVerifier.isSigningRelated(value)) {
763                         continue;
764                     }
765                     name = value;
766                     return true;
767                 }
768                 return false;
769             }
770 
771             public String nextElement() {
772                 if (hasMoreElements()) {
773                     String value = name;
774                     name = null;
775                     return value;
776                 }
777                 throw new NoSuchElementException();
778             }
779         };
780     }
781 
782     CodeSource getCodeSource(URL url, String name) {
783         ensureInitialization();
784         if (jv != null) {
785             if (jv.eagerValidation) {
786                 CodeSource cs = null;
787                 JarEntry je = getJarEntry(name);
788                 if (je != null) {
789                     cs = jv.getCodeSource(url, this, je);
790                 } else {
791                     cs = jv.getCodeSource(url, name);
792                 }
793                 return cs;
794             } else {
795                 return jv.getCodeSource(url, name);
796             }
797         }
798 
799         return JarVerifier.getUnsignedCS(url);
800     }
801 
802     void setEagerValidation(boolean eager) {
803         try {
804             maybeInstantiateVerifier();
805         } catch (IOException e) {
806             throw new RuntimeException(e);
807         }
808         if (jv != null) {
809             jv.setEagerValidation(eager);
810         }
811     }
812 
813     List<Object> getManifestDigests() {
814         ensureInitialization();
815         if (jv != null) {
816             return jv.getManifestDigests();
817         }
818         return new ArrayList<Object>();
819     }
820     */
821 }
822