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