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