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