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 = is.readAllBytes();
 442             if (len != -1 && b.length != len)
 443                 throw new EOFException("Expected:" + len + ", read:" + b.length);
 444 
 445             return b;
 446         }
 447     }
 448 
 449     /**
 450      * Returns an input stream for reading the contents of the specified
 451      * zip file entry.
 452      * @param ze the zip file entry
 453      * @return an input stream for reading the contents of the specified
 454      *         zip file entry
 455      * @throws ZipException if a zip file format error has occurred
 456      * @throws IOException if an I/O error has occurred
 457      * @throws SecurityException if any of the jar file entries
 458      *         are incorrectly signed.
 459      * @throws IllegalStateException
 460      *         may be thrown if the jar file has been closed
 461      */
 462     public synchronized InputStream getInputStream(ZipEntry ze)
 463         throws IOException
 464     {
 465         maybeInstantiateVerifier();
 466         if (jv == null) {
 467             return super.getInputStream(ze);
 468         }
 469         if (!jvInitialized) {
 470             initializeVerifier();
 471             jvInitialized = true;
 472             // could be set to null after a call to
 473             // initializeVerifier if we have nothing to
 474             // verify
 475             if (jv == null)
 476                 return super.getInputStream(ze);
 477         }
 478 
 479         // wrap a verifier stream around the real stream
 480         return new JarVerifier.VerifierStream(
 481             getManifestFromReference(),
 482             ze instanceof JarFileEntry ?
 483             (JarEntry) ze : getJarEntry(ze.getName()),
 484             super.getInputStream(ze),
 485             jv);
 486     }
 487 
 488     // Statics for hand-coded Boyer-Moore search
 489     private static final char[] CLASSPATH_CHARS = {'c','l','a','s','s','-','p','a','t','h'};
 490     // The bad character shift for "class-path"
 491     private static final int[] CLASSPATH_LASTOCC;
 492     // The good suffix shift for "class-path"
 493     private static final int[] CLASSPATH_OPTOSFT;
 494 
 495     static {
 496         CLASSPATH_LASTOCC = new int[128];
 497         CLASSPATH_OPTOSFT = new int[10];
 498         CLASSPATH_LASTOCC[(int)'c'] = 1;
 499         CLASSPATH_LASTOCC[(int)'l'] = 2;
 500         CLASSPATH_LASTOCC[(int)'s'] = 5;
 501         CLASSPATH_LASTOCC[(int)'-'] = 6;
 502         CLASSPATH_LASTOCC[(int)'p'] = 7;
 503         CLASSPATH_LASTOCC[(int)'a'] = 8;
 504         CLASSPATH_LASTOCC[(int)'t'] = 9;
 505         CLASSPATH_LASTOCC[(int)'h'] = 10;
 506         for (int i=0; i<9; i++)
 507             CLASSPATH_OPTOSFT[i] = 10;
 508         CLASSPATH_OPTOSFT[9]=1;
 509     }
 510 
 511     private JarEntry getManEntry() {
 512         if (manEntry == null) {
 513             // First look up manifest entry using standard name
 514             manEntry = getJarEntry(MANIFEST_NAME);
 515             if (manEntry == null) {
 516                 // If not found, then iterate through all the "META-INF/"
 517                 // entries to find a match.
 518                 String[] names = getMetaInfEntryNames();
 519                 if (names != null) {
 520                     for (String name : names) {
 521                         if (MANIFEST_NAME.equals(name.toUpperCase(Locale.ENGLISH))) {
 522                             manEntry = getJarEntry(name);
 523                             break;
 524                         }
 525                     }
 526                 }
 527             }
 528         }
 529         return manEntry;
 530     }
 531 
 532    /**
 533     * Returns {@code true} iff this JAR file has a manifest with the
 534     * Class-Path attribute
 535     */
 536     boolean hasClassPathAttribute() throws IOException {
 537         checkForSpecialAttributes();
 538         return hasClassPathAttribute;
 539     }
 540 
 541     /**
 542      * Returns true if the pattern {@code src} is found in {@code b}.
 543      * The {@code lastOcc} and {@code optoSft} arrays are the precomputed
 544      * bad character and good suffix shifts.
 545      */
 546     private boolean match(char[] src, byte[] b, int[] lastOcc, int[] optoSft) {
 547         int len = src.length;
 548         int last = b.length - len;
 549         int i = 0;
 550         next:
 551         while (i<=last) {
 552             for (int j=(len-1); j>=0; j--) {
 553                 char c = (char) b[i+j];
 554                 c = (((c-'A')|('Z'-c)) >= 0) ? (char)(c + 32) : c;
 555                 if (c != src[j]) {
 556                     i += Math.max(j + 1 - lastOcc[c&0x7F], optoSft[j]);
 557                     continue next;
 558                  }
 559             }
 560             return true;
 561         }
 562         return false;
 563     }
 564 
 565     /**
 566      * On first invocation, check if the JAR file has the Class-Path
 567      * attribute. A no-op on subsequent calls.
 568      */
 569     private void checkForSpecialAttributes() throws IOException {
 570         if (hasCheckedSpecialAttributes) return;
 571         JarEntry manEntry = getManEntry();
 572         if (manEntry != null) {
 573             byte[] b = getBytes(manEntry);
 574             if (match(CLASSPATH_CHARS, b, CLASSPATH_LASTOCC, CLASSPATH_OPTOSFT))
 575                 hasClassPathAttribute = true;
 576         }
 577         hasCheckedSpecialAttributes = true;
 578     }
 579 
 580     private synchronized void ensureInitialization() {
 581         try {
 582             maybeInstantiateVerifier();
 583         } catch (IOException e) {
 584             throw new RuntimeException(e);
 585         }
 586         if (jv != null && !jvInitialized) {
 587             initializeVerifier();
 588             jvInitialized = true;
 589         }
 590     }
 591 
 592     JarEntry newEntry(ZipEntry ze) {
 593         return new JarFileEntry(ze);
 594     }
 595 
 596     Enumeration<String> entryNames(CodeSource[] cs) {
 597         ensureInitialization();
 598         if (jv != null) {
 599             return jv.entryNames(this, cs);
 600         }
 601 
 602         /*
 603          * JAR file has no signed content. Is there a non-signing
 604          * code source?
 605          */
 606         boolean includeUnsigned = false;
 607         for (CodeSource c : cs) {
 608             if (c.getCodeSigners() == null) {
 609                 includeUnsigned = true;
 610                 break;
 611             }
 612         }
 613         if (includeUnsigned) {
 614             return unsignedEntryNames();
 615         } else {
 616             return new Enumeration<>() {
 617 
 618                 public boolean hasMoreElements() {
 619                     return false;
 620                 }
 621 
 622                 public String nextElement() {
 623                     throw new NoSuchElementException();
 624                 }
 625             };
 626         }
 627     }
 628 
 629     /**
 630      * Returns an enumeration of the zip file entries
 631      * excluding internal JAR mechanism entries and including
 632      * signed entries missing from the ZIP directory.
 633      */
 634     Enumeration<JarEntry> entries2() {
 635         ensureInitialization();
 636         if (jv != null) {
 637             return jv.entries2(this, super.entries());
 638         }
 639 
 640         // screen out entries which are never signed
 641         final Enumeration<? extends ZipEntry> enum_ = super.entries();
 642         return new Enumeration<>() {
 643 
 644             ZipEntry entry;
 645 
 646             public boolean hasMoreElements() {
 647                 if (entry != null) {
 648                     return true;
 649                 }
 650                 while (enum_.hasMoreElements()) {
 651                     ZipEntry ze = enum_.nextElement();
 652                     if (JarVerifier.isSigningRelated(ze.getName())) {
 653                         continue;
 654                     }
 655                     entry = ze;
 656                     return true;
 657                 }
 658                 return false;
 659             }
 660 
 661             public JarFileEntry nextElement() {
 662                 if (hasMoreElements()) {
 663                     ZipEntry ze = entry;
 664                     entry = null;
 665                     return new JarFileEntry(ze);
 666                 }
 667                 throw new NoSuchElementException();
 668             }
 669         };
 670     }
 671 
 672     CodeSource[] getCodeSources(URL url) {
 673         ensureInitialization();
 674         if (jv != null) {
 675             return jv.getCodeSources(this, url);
 676         }
 677 
 678         /*
 679          * JAR file has no signed content. Is there a non-signing
 680          * code source?
 681          */
 682         Enumeration<String> unsigned = unsignedEntryNames();
 683         if (unsigned.hasMoreElements()) {
 684             return new CodeSource[]{JarVerifier.getUnsignedCS(url)};
 685         } else {
 686             return null;
 687         }
 688     }
 689 
 690     private Enumeration<String> unsignedEntryNames() {
 691         final Enumeration<JarEntry> entries = entries();
 692         return new Enumeration<>() {
 693 
 694             String name;
 695 
 696             /*
 697              * Grab entries from ZIP directory but screen out
 698              * metadata.
 699              */
 700             public boolean hasMoreElements() {
 701                 if (name != null) {
 702                     return true;
 703                 }
 704                 while (entries.hasMoreElements()) {
 705                     String value;
 706                     ZipEntry e = entries.nextElement();
 707                     value = e.getName();
 708                     if (e.isDirectory() || JarVerifier.isSigningRelated(value)) {
 709                         continue;
 710                     }
 711                     name = value;
 712                     return true;
 713                 }
 714                 return false;
 715             }
 716 
 717             public String nextElement() {
 718                 if (hasMoreElements()) {
 719                     String value = name;
 720                     name = null;
 721                     return value;
 722                 }
 723                 throw new NoSuchElementException();
 724             }
 725         };
 726     }
 727 
 728     CodeSource getCodeSource(URL url, String name) {
 729         ensureInitialization();
 730         if (jv != null) {
 731             if (jv.eagerValidation) {
 732                 CodeSource cs = null;
 733                 JarEntry je = getJarEntry(name);
 734                 if (je != null) {
 735                     cs = jv.getCodeSource(url, this, je);
 736                 } else {
 737                     cs = jv.getCodeSource(url, name);
 738                 }
 739                 return cs;
 740             } else {
 741                 return jv.getCodeSource(url, name);
 742             }
 743         }
 744 
 745         return JarVerifier.getUnsignedCS(url);
 746     }
 747 
 748     void setEagerValidation(boolean eager) {
 749         try {
 750             maybeInstantiateVerifier();
 751         } catch (IOException e) {
 752             throw new RuntimeException(e);
 753         }
 754         if (jv != null) {
 755             jv.setEagerValidation(eager);
 756         }
 757     }
 758 
 759     List<Object> getManifestDigests() {
 760         ensureInitialization();
 761         if (jv != null) {
 762             return jv.getManifestDigests();
 763         }
 764         return new ArrayList<>();
 765     }
 766 }