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