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.CodeSource;
  38 import jdk.internal.misc.SharedSecrets;
  39 import sun.security.action.GetPropertyAction;
  40 import sun.security.util.ManifestEntryVerifier;
  41 import sun.security.util.SignatureFileVerifier;
  42 
  43 /**
  44  * The {@code JarFile} class is used to read the contents of a jar file
  45  * from any file that can be opened with {@code java.io.RandomAccessFile}.
  46  * It extends the class {@code java.util.zip.ZipFile} with support
  47  * for reading an optional {@code Manifest} entry, and support for
  48  * processing multi-release jar files.  The {@code Manifest} can be used
  49  * to specify meta-information about the jar file and its entries.
  50  *
  51  * <p>A multi-release jar file is a jar file that contains
  52  * a manifest with a main attribute named "Multi-Release",
  53  * a set of "base" entries, some of which are public classes with public
  54  * or protected methods that comprise the public interface of the jar file,
  55  * and a set of "versioned" entries contained in subdirectories of the
  56  * "META-INF/versions" directory.  The versioned entries are partitioned by the
  57  * major version of the Java platform.  A versioned entry, with a version
  58  * {@code n}, {@code 8 < n}, in the "META-INF/versions/{n}" directory overrides
  59  * the base entry as well as any entry with a version number {@code i} where
  60  * {@code 8 < i < n}.
  61  *
  62  * <p>By default, a {@code JarFile} for a multi-release jar file is configured
  63  * to process the multi-release jar file as if it were a plain (unversioned) jar
  64  * file, and as such an entry name is associated with at most one base entry.
  65  * The {@code JarFile} may be configured to process a multi-release jar file by
  66  * creating the {@code JarFile} with the
  67  * {@link JarFile#JarFile(File, boolean, int, Release)} constructor.  The
  68  * {@code Release} object sets a maximum version used when searching for
  69  * versioned entries.  When so configured, an entry name
  70  * can correspond with at most one base entry and zero or more versioned
  71  * entries. A search is required to associate the entry name with the latest
  72  * versioned entry whose version is less than or equal to the maximum version
  73  * (see {@link #getEntry(String)}).
  74  *
  75  * <p>Class loaders that utilize {@code JarFile} to load classes from the
  76  * contents of {@code JarFile} entries should construct the {@code JarFile}
  77  * by invoking the {@link JarFile#JarFile(File, boolean, int, Release)}
  78  * constructor with the value {@code Release.RUNTIME} assigned to the last
  79  * argument.  This assures that classes compatible with the major
  80  * version of the running JVM are loaded from multi-release jar files.
  81  *
  82  * <p>If the verify flag is on when opening a signed jar file, the content of
  83  * the file is verified against its signature embedded inside the file. Please
  84  * note that the verification process does not include validating the signer's
  85  * certificate. A caller should inspect the return value of
  86  * {@link JarEntry#getCodeSigners()} to further determine if the signature
  87  * can be trusted.
  88  *
  89  * <p> Unless otherwise noted, passing a {@code null} argument to a constructor
  90  * or method in this class will cause a {@link NullPointerException} to be
  91  * thrown.
  92  *
  93  * @implNote
  94  * <div class="block">
  95  * If the API can not be used to configure a {@code JarFile} (e.g. to override
  96  * the configuration of a compiled application or library), two {@code System}
  97  * properties are available.
  98  * <ul>
  99  * <li>
 100  * {@code jdk.util.jar.version} can be assigned a value that is the
 101  * {@code String} representation of a non-negative integer
 102  * {@code <= Version.current().major()}.  The value is used to set the effective
 103  * runtime version to something other than the default value obtained by
 104  * evaluating {@code Version.current().major()}. The effective runtime version
 105  * is the version that the {@link JarFile#JarFile(File, boolean, int, Release)}
 106  * constructor uses when the value of the last argument is
 107  * {@code Release.RUNTIME}.
 108  * </li>
 109  * <li>
 110  * {@code jdk.util.jar.enableMultiRelease} can be assigned one of the three
 111  * {@code String} values <em>true</em>, <em>false</em>, or <em>force</em>.  The
 112  * value <em>true</em>, the default value, enables multi-release jar file
 113  * processing.  The value <em>false</em> disables multi-release jar processing,
 114  * ignoring the "Multi-Release" manifest attribute, and the versioned
 115  * directories in a multi-release jar file if they exist.  Furthermore,
 116  * the method {@link JarFile#isMultiRelease()} returns <em>false</em>. The value
 117  * <em>force</em> causes the {@code JarFile} to be initialized to runtime
 118  * versioning after construction.  It effectively does the same as this code:
 119  * {@code (new JarFile(File, boolean, int, Release.RUNTIME)}.
 120  * </li>
 121  * </ul>
 122  * </div>
 123  *
 124  * @author  David Connelly
 125  * @see     Manifest
 126  * @see     java.util.zip.ZipFile
 127  * @see     java.util.jar.JarEntry
 128  * @since   1.2
 129  */
 130 public
 131 class JarFile extends ZipFile {
 132     private final static int BASE_VERSION;
 133     private final static int RUNTIME_VERSION;
 134     private final static boolean MULTI_RELEASE_ENABLED;
 135     private final static boolean MULTI_RELEASE_FORCED;
 136     private SoftReference<Manifest> manRef;
 137     private JarEntry manEntry;
 138     private JarVerifier jv;
 139     private boolean jvInitialized;
 140     private boolean verify;
 141     private final int version;
 142     private boolean notVersioned;
 143     private final boolean runtimeVersioned;
 144     private boolean isMultiRelease;    // is jar multi-release?
 145 
 146     // indicates if Class-Path attribute present
 147     private boolean hasClassPathAttribute;
 148     // true if manifest checked for special attributes
 149     private volatile boolean hasCheckedSpecialAttributes;
 150 
 151     static {
 152         // Set up JavaUtilJarAccess in SharedSecrets
 153         SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl());
 154 
 155         BASE_VERSION = 8;  // one less than lowest version for versioned entries
 156         int runtimeVersion = jdk.Version.current().major();
 157         String jarVersion =
 158                 GetPropertyAction.getProperty("jdk.util.jar.version");
 159         if (jarVersion != null) {
 160             int jarVer = Integer.parseInt(jarVersion);
 161             runtimeVersion = (jarVer > runtimeVersion)
 162                     ? runtimeVersion : Math.max(jarVer, 0);
 163         }
 164         RUNTIME_VERSION = runtimeVersion;
 165         String enableMultiRelease = GetPropertyAction
 166                 .getProperty("jdk.util.jar.enableMultiRelease", "true");
 167         switch (enableMultiRelease) {
 168             case "true":
 169             default:
 170                 MULTI_RELEASE_ENABLED = true;
 171                 MULTI_RELEASE_FORCED = false;
 172                 break;
 173             case "false":
 174                 MULTI_RELEASE_ENABLED = false;
 175                 MULTI_RELEASE_FORCED = false;
 176                 break;
 177             case "force":
 178                 MULTI_RELEASE_ENABLED = true;
 179                 MULTI_RELEASE_FORCED = true;
 180                 break;
 181         }
 182     }
 183 
 184     /**
 185      * A set of constants that represent the entries in either the base directory
 186      * or one of the versioned directories in a multi-release jar file.  It's
 187      * possible for a multi-release jar file to contain versioned directories
 188      * that are not represented by the constants of the {@code Release} enum.
 189      * In those cases, the entries will not be located by this {@code JarFile}
 190      * through the aliasing mechanism, but they can be directly accessed by
 191      * specifying the full path name of the entry.
 192      *
 193      * @since 9
 194      */
 195     public enum Release {
 196         /**
 197          * Represents unversioned entries, or entries in "regular", as opposed
 198          * to multi-release jar files.
 199          */
 200         BASE(BASE_VERSION),
 201 
 202         /**
 203          * Represents entries found in the META-INF/versions/9 directory of a
 204          * multi-release jar file.
 205          */
 206         VERSION_9(9),
 207 
 208         // fill in the "blanks" for future releases
 209 
 210         /**
 211          * Represents entries found in the META-INF/versions/{n} directory of a
 212          * multi-release jar file, where {@code n} is the effective runtime
 213          * version of the jar file.
 214          *
 215          * @implNote
 216          * <div class="block">
 217          * The effective runtime version is determined
 218          * by evaluating {@code Version.current().major()} or by using the value
 219          * of the {@code jdk.util.jar.version} System property if it exists.
 220          * </div>
 221          */
 222         RUNTIME(RUNTIME_VERSION);
 223 
 224         Release(int version) {
 225             this.version = version;
 226         }
 227 
 228         private static Release valueOf(int version) {
 229             return version <= BASE.value() ? BASE : valueOf("VERSION_" + version);
 230         }
 231 
 232         private final int version;
 233 
 234         private int value() {
 235             return this.version;
 236         }
 237     }
 238 
 239     private static final String META_INF = "META-INF/";
 240 
 241     private static final String META_INF_VERSIONS = META_INF + "versions/";
 242 
 243     /**
 244      * The JAR manifest file name.
 245      */
 246     public static final String MANIFEST_NAME = META_INF + "MANIFEST.MF";
 247 
 248     /**
 249      * Creates a new {@code JarFile} to read from the specified
 250      * file {@code name}. The {@code JarFile} will be verified if
 251      * it is signed.
 252      * @param name the name of the jar file to be opened for reading
 253      * @throws IOException if an I/O error has occurred
 254      * @throws SecurityException if access to the file is denied
 255      *         by the SecurityManager
 256      */
 257     public JarFile(String name) throws IOException {
 258         this(new File(name), true, ZipFile.OPEN_READ);
 259     }
 260 
 261     /**
 262      * Creates a new {@code JarFile} to read from the specified
 263      * file {@code name}.
 264      * @param name the name of the jar file to be opened for reading
 265      * @param verify whether or not to verify the jar file if
 266      * it is signed.
 267      * @throws IOException if an I/O error has occurred
 268      * @throws SecurityException if access to the file is denied
 269      *         by the SecurityManager
 270      */
 271     public JarFile(String name, boolean verify) throws IOException {
 272         this(new File(name), verify, ZipFile.OPEN_READ);
 273     }
 274 
 275     /**
 276      * Creates a new {@code JarFile} to read from the specified
 277      * {@code File} object. The {@code JarFile} will be verified if
 278      * it is signed.
 279      * @param file the jar file to be opened for reading
 280      * @throws IOException if an I/O error has occurred
 281      * @throws SecurityException if access to the file is denied
 282      *         by the SecurityManager
 283      */
 284     public JarFile(File file) throws IOException {
 285         this(file, true, ZipFile.OPEN_READ);
 286     }
 287 
 288     /**
 289      * Creates a new {@code JarFile} to read from the specified
 290      * {@code File} object.
 291      * @param file the jar file to be opened for reading
 292      * @param verify whether or not to verify the jar file if
 293      * it is signed.
 294      * @throws IOException if an I/O error has occurred
 295      * @throws SecurityException if access to the file is denied
 296      *         by the SecurityManager.
 297      */
 298     public JarFile(File file, boolean verify) throws IOException {
 299         this(file, verify, ZipFile.OPEN_READ);
 300     }
 301 
 302     /**
 303      * Creates a new {@code JarFile} to read from the specified
 304      * {@code File} object in the specified mode.  The mode argument
 305      * must be either {@code OPEN_READ} or {@code OPEN_READ | OPEN_DELETE}.
 306      *
 307      * @param file the jar file to be opened for reading
 308      * @param verify whether or not to verify the jar file if
 309      * it is signed.
 310      * @param mode the mode in which the file is to be opened
 311      * @throws IOException if an I/O error has occurred
 312      * @throws IllegalArgumentException
 313      *         if the {@code mode} argument is invalid
 314      * @throws SecurityException if access to the file is denied
 315      *         by the SecurityManager
 316      * @since 1.3
 317      */
 318     public JarFile(File file, boolean verify, int mode) throws IOException {
 319         this(file, verify, mode, Release.BASE);
 320         this.notVersioned = true;
 321     }
 322 
 323     /**
 324      * Creates a new {@code JarFile} to read from the specified
 325      * {@code File} object in the specified mode.  The mode argument
 326      * must be either {@code OPEN_READ} or {@code OPEN_READ | OPEN_DELETE}.
 327      * The version argument configures the {@code JarFile} for processing
 328      * multi-release jar files.
 329      *
 330      * @param file the jar file to be opened for reading
 331      * @param verify whether or not to verify the jar file if
 332      * it is signed.
 333      * @param mode the mode in which the file is to be opened
 334      * @param version specifies the release version for a multi-release jar file
 335      * @throws IOException if an I/O error has occurred
 336      * @throws IllegalArgumentException
 337      *         if the {@code mode} argument is invalid
 338      * @throws SecurityException if access to the file is denied
 339      *         by the SecurityManager
 340      * @throws NullPointerException if {@code version} is {@code null}
 341      * @since 9
 342      */
 343     public JarFile(File file, boolean verify, int mode, Release version) throws IOException {
 344         super(file, mode);
 345         Objects.requireNonNull(version);
 346         this.verify = verify;
 347         // version applies to multi-release jar files, ignored for regular jar files
 348         if (MULTI_RELEASE_FORCED) {
 349             this.version = RUNTIME_VERSION;
 350             version = Release.RUNTIME;
 351         } else {
 352             this.version = version.value();
 353         }
 354         this.runtimeVersioned = version == Release.RUNTIME;
 355 
 356         assert runtimeVersionExists();
 357     }
 358 
 359     private boolean runtimeVersionExists() {
 360         int version = jdk.Version.current().major();
 361         try {
 362             Release.valueOf(version);
 363             return true;
 364         } catch (IllegalArgumentException x) {
 365             System.err.println("No JarFile.Release object for release " + version);
 366             return false;
 367         }
 368     }
 369 
 370     /**
 371      * Returns the maximum version used when searching for versioned entries.
 372      *
 373      * @return the maximum version, or {@code Release.BASE} if this jar file is
 374      *         processed as if it is an unversioned jar file or is not a
 375      *         multi-release jar file
 376      * @since 9
 377      */
 378     public final Release getVersion() {
 379         if (isMultiRelease()) {
 380             return runtimeVersioned ? Release.RUNTIME : Release.valueOf(version);
 381         } else {
 382             return Release.BASE;
 383         }
 384     }
 385 
 386     /**
 387      * Indicates whether or not this jar file is a multi-release jar file.
 388      *
 389      * @return true if this JarFile is a multi-release jar file
 390      * @since 9
 391      */
 392     public final boolean isMultiRelease() {
 393         if (isMultiRelease) {
 394             return true;
 395         }
 396         if (MULTI_RELEASE_ENABLED && version != BASE_VERSION) {
 397             try {
 398                 checkForSpecialAttributes();
 399             } catch (IOException io) {
 400                 isMultiRelease = false;
 401             }
 402         }
 403         return isMultiRelease;
 404     }
 405 
 406     /**
 407      * Returns the jar file manifest, or {@code null} if none.
 408      *
 409      * @return the jar file manifest, or {@code null} if none
 410      *
 411      * @throws IllegalStateException
 412      *         may be thrown if the jar file has been closed
 413      * @throws IOException  if an I/O error has occurred
 414      */
 415     public Manifest getManifest() throws IOException {
 416         return getManifestFromReference();
 417     }
 418 
 419     private Manifest getManifestFromReference() throws IOException {
 420         Manifest man = manRef != null ? manRef.get() : null;
 421 
 422         if (man == null) {
 423 
 424             JarEntry manEntry = getManEntry();
 425 
 426             // If found then load the manifest
 427             if (manEntry != null) {
 428                 if (verify) {
 429                     byte[] b = getBytes(manEntry);
 430                     man = new Manifest(new ByteArrayInputStream(b));
 431                     if (!jvInitialized) {
 432                         jv = new JarVerifier(b);
 433                     }
 434                 } else {
 435                     man = new Manifest(super.getInputStream(manEntry));
 436                 }
 437                 manRef = new SoftReference<>(man);
 438             }
 439         }
 440         return man;
 441     }
 442 
 443     private String[] getMetaInfEntryNames() {
 444         return jdk.internal.misc.SharedSecrets.getJavaUtilZipFileAccess()
 445                                               .getMetaInfEntryNames((ZipFile)this);
 446     }
 447 
 448     /**
 449      * Returns the {@code JarEntry} for the given base entry name or
 450      * {@code null} if not found.
 451      *
 452      * <p>If this {@code JarFile} is a multi-release jar file and is configured
 453      * to be processed as such, then a search is performed to find and return
 454      * a {@code JarEntry} that is the latest versioned entry associated with the
 455      * given entry name.  The returned {@code JarEntry} is the versioned entry
 456      * corresponding to the given base entry name prefixed with the string
 457      * {@code "META-INF/versions/{n}/"}, for the largest value of {@code n} for
 458      * which an entry exists.  If such a versioned entry does not exist, then
 459      * the {@code JarEntry} for the base entry is returned, otherwise
 460      * {@code null} is returned if no entries are found.  The initial value for
 461      * the version {@code n} is the maximum version as returned by the method
 462      * {@link JarFile#getVersion()}.
 463      *
 464      * @param name the jar file entry name
 465      * @return the {@code JarEntry} for the given entry name, or
 466      *         the versioned entry name, or {@code null} if not found
 467      *
 468      * @throws IllegalStateException
 469      *         may be thrown if the jar file has been closed
 470      *
 471      * @see java.util.jar.JarEntry
 472      *
 473      * @implSpec
 474      * <div class="block">
 475      * This implementation invokes {@link JarFile#getEntry(String)}.
 476      * </div>
 477      */
 478     public JarEntry getJarEntry(String name) {
 479         return (JarEntry)getEntry(name);
 480     }
 481 
 482     /**
 483      * Returns the {@code ZipEntry} for the given base entry name or
 484      * {@code null} if not found.
 485      *
 486      * <p>If this {@code JarFile} is a multi-release jar file and is configured
 487      * to be processed as such, then a search is performed to find and return
 488      * a {@code ZipEntry} that is the latest versioned entry associated with the
 489      * given entry name.  The returned {@code ZipEntry} is the versioned entry
 490      * corresponding to the given base entry name prefixed with the string
 491      * {@code "META-INF/versions/{n}/"}, for the largest value of {@code n} for
 492      * which an entry exists.  If such a versioned entry does not exist, then
 493      * the {@code ZipEntry} for the base entry is returned, otherwise
 494      * {@code null} is returned if no entries are found.  The initial value for
 495      * the version {@code n} is the maximum version as returned by the method
 496      * {@link JarFile#getVersion()}.
 497      *
 498      * @param name the jar file entry name
 499      * @return the {@code ZipEntry} for the given entry name or
 500      *         the versioned entry name or {@code null} if not found
 501      *
 502      * @throws IllegalStateException
 503      *         may be thrown if the jar file has been closed
 504      *
 505      * @see java.util.zip.ZipEntry
 506      *
 507      * @implSpec
 508      * <div class="block">
 509      * This implementation may return a versioned entry for the requested name
 510      * even if there is not a corresponding base entry.  This can occur
 511      * if there is a private or package-private versioned entry that matches.
 512      * If a subclass overrides this method, assure that the override method
 513      * invokes {@code super.getEntry(name)} to obtain all versioned entries.
 514      * </div>
 515      */
 516     public ZipEntry getEntry(String name) {
 517         ZipEntry ze = super.getEntry(name);
 518         if (ze != null) {
 519             return new JarFileEntry(ze);
 520         }
 521         // no matching base entry, but maybe there is a versioned entry,
 522         // like a new private class
 523         if (isMultiRelease()) {
 524             ze = new ZipEntry(name);
 525             ZipEntry vze = getVersionedEntry(ze);
 526             if (ze != vze) {
 527                 return new JarFileEntry(name, vze);
 528             }
 529         }
 530         return null;
 531     }
 532 
 533     private class JarEntryIterator implements Enumeration<JarEntry>,
 534             Iterator<JarEntry>
 535     {
 536         final Enumeration<? extends ZipEntry> e = JarFile.super.entries();
 537         ZipEntry ze;
 538 
 539         public boolean hasNext() {
 540             if (notVersioned) {
 541                 return e.hasMoreElements();
 542             }
 543             if (ze != null) {
 544                 return true;
 545             }
 546             return findNext();
 547         }
 548 
 549         private boolean findNext() {
 550             while (e.hasMoreElements()) {
 551                 ZipEntry ze2 = e.nextElement();
 552                 if (!ze2.getName().startsWith(META_INF_VERSIONS)) {
 553                     ze = ze2;
 554                     return true;
 555                 }
 556             }
 557             return false;
 558         }
 559 
 560         public JarEntry next() {
 561             ZipEntry ze2;
 562 
 563             if (notVersioned) {
 564                 ze2 = e.nextElement();
 565                 return new JarFileEntry(ze2.getName(), ze2);
 566             }
 567             if (ze != null || findNext()) {
 568                 ze2 = ze;
 569                 ze = null;
 570                 return new JarFileEntry(ze2);
 571             }
 572             throw new NoSuchElementException();
 573         }
 574 
 575         public boolean hasMoreElements() {
 576             return hasNext();
 577         }
 578 
 579         public JarEntry nextElement() {
 580             return next();
 581         }
 582 
 583         public Iterator<JarEntry> asIterator() {
 584             return this;
 585         }
 586     }
 587 
 588     /**
 589      * Returns an enumeration of the jar file entries.  The set of entries
 590      * returned depends on whether or not the jar file is a multi-release jar
 591      * file, and on the constructor used to create the {@code JarFile}.  If the
 592      * jar file is not a multi-release jar file, all entries are returned,
 593      * regardless of how the {@code JarFile} is created.  If the constructor
 594      * does not take a {@code Release} argument, all entries are returned.
 595      * If the jar file is a multi-release jar file and the constructor takes a
 596      * {@code Release} argument, then the set of entries returned is equivalent
 597      * to the set of entries that would be returned if the set was built by
 598      * invoking {@link JarFile#getEntry(String)} or
 599      * {@link JarFile#getJarEntry(String)} with the name of each base entry in
 600      * the jar file.  A base entry is an entry whose path name does not start
 601      * with "META-INF/versions/".
 602      *
 603      * @return an enumeration of the jar file entries
 604      * @throws IllegalStateException
 605      *         may be thrown if the jar file has been closed
 606      */
 607     public Enumeration<JarEntry> entries() {
 608         return new JarEntryIterator();
 609     }
 610 
 611     /**
 612      * Returns an ordered {@code Stream} over all the jar file entries.
 613      * Entries appear in the {@code Stream} in the order they appear in
 614      * the central directory of the jar file.  The set of entries
 615      * returned depends on whether or not the jar file is a multi-release jar
 616      * file, and on the constructor used to create the {@code JarFile}.  If the
 617      * jar file is not a multi-release jar file, all entries are returned,
 618      * regardless of how the {@code JarFile} is created.  If the constructor
 619      * does not take a {@code Release} argument, all entries are returned.
 620      * If the jar file is a multi-release jar file and the constructor takes a
 621      * {@code Release} argument, then the set of entries returned is equivalent
 622      * to the set of entries that would be returned if the set was built by
 623      * invoking {@link JarFile#getEntry(String)} or
 624      * {@link JarFile#getJarEntry(String)} with the name of each base entry in
 625      * the jar file.  A base entry is an entry whose path name does not start
 626      * with "META-INF/versions/".
 627      * @return an ordered {@code Stream} of entries in this jar file
 628      * @throws IllegalStateException if the jar file has been closed
 629      * @since 1.8
 630      */
 631     public Stream<JarEntry> stream() {
 632         return StreamSupport.stream(Spliterators.spliterator(
 633                 new JarEntryIterator(), size(),
 634                 Spliterator.ORDERED | Spliterator.DISTINCT |
 635                         Spliterator.IMMUTABLE | Spliterator.NONNULL), false);
 636     }
 637 
 638     private ZipEntry searchForVersionedEntry(final int version, String name) {
 639         ZipEntry vze = null;
 640         String sname = "/" + name;
 641         int i = version;
 642         while (i > BASE_VERSION) {
 643             vze = super.getEntry(META_INF_VERSIONS + i + sname);
 644             if (vze != null) break;
 645             i--;
 646         }
 647         return vze;
 648     }
 649 
 650     private ZipEntry getVersionedEntry(ZipEntry ze) {
 651         ZipEntry vze = null;
 652         if (version > BASE_VERSION && !ze.isDirectory()) {
 653             String name = ze.getName();
 654             if (!name.startsWith(META_INF)) {
 655                 vze = searchForVersionedEntry(version, name);
 656             }
 657         }
 658         return vze == null ? ze : vze;
 659     }
 660 
 661     private class JarFileEntry extends JarEntry {
 662         final private String name;
 663 
 664         JarFileEntry(ZipEntry ze) {
 665             super(isMultiRelease() ? getVersionedEntry(ze) : ze);
 666             this.name = ze.getName();
 667         }
 668         JarFileEntry(String name, ZipEntry vze) {
 669             super(vze);
 670             this.name = name;
 671         }
 672         public Attributes getAttributes() throws IOException {
 673             Manifest man = JarFile.this.getManifest();
 674             if (man != null) {
 675                 return man.getAttributes(super.getName());
 676             } else {
 677                 return null;
 678             }
 679         }
 680         public Certificate[] getCertificates() {
 681             try {
 682                 maybeInstantiateVerifier();
 683             } catch (IOException e) {
 684                 throw new RuntimeException(e);
 685             }
 686             if (certs == null && jv != null) {
 687                 certs = jv.getCerts(JarFile.this, reifiedEntry());
 688             }
 689             return certs == null ? null : certs.clone();
 690         }
 691         public CodeSigner[] getCodeSigners() {
 692             try {
 693                 maybeInstantiateVerifier();
 694             } catch (IOException e) {
 695                 throw new RuntimeException(e);
 696             }
 697             if (signers == null && jv != null) {
 698                 signers = jv.getCodeSigners(JarFile.this, reifiedEntry());
 699             }
 700             return signers == null ? null : signers.clone();
 701         }
 702         JarFileEntry reifiedEntry() {
 703             if (isMultiRelease()) {
 704                 String entryName = super.getName();
 705                 return entryName.equals(this.name) ? this : new JarFileEntry(entryName, this);
 706             }
 707             return this;
 708         }
 709 
 710         @Override
 711         public String getName() {
 712             return name;
 713         }
 714     }
 715 
 716     /*
 717      * Ensures that the JarVerifier has been created if one is
 718      * necessary (i.e., the jar appears to be signed.) This is done as
 719      * a quick check to avoid processing of the manifest for unsigned
 720      * jars.
 721      */
 722     private void maybeInstantiateVerifier() throws IOException {
 723         if (jv != null) {
 724             return;
 725         }
 726 
 727         if (verify) {
 728             String[] names = getMetaInfEntryNames();
 729             if (names != null) {
 730                 for (String nameLower : names) {
 731                     String name = nameLower.toUpperCase(Locale.ENGLISH);
 732                     if (name.endsWith(".DSA") ||
 733                         name.endsWith(".RSA") ||
 734                         name.endsWith(".EC") ||
 735                         name.endsWith(".SF")) {
 736                         // Assume since we found a signature-related file
 737                         // that the jar is signed and that we therefore
 738                         // need a JarVerifier and Manifest
 739                         getManifest();
 740                         return;
 741                     }
 742                 }
 743             }
 744             // No signature-related files; don't instantiate a
 745             // verifier
 746             verify = false;
 747         }
 748     }
 749 
 750 
 751     /*
 752      * Initializes the verifier object by reading all the manifest
 753      * entries and passing them to the verifier.
 754      */
 755     private void initializeVerifier() {
 756         ManifestEntryVerifier mev = null;
 757 
 758         // Verify "META-INF/" entries...
 759         try {
 760             String[] names = getMetaInfEntryNames();
 761             if (names != null) {
 762                 for (String name : names) {
 763                     String uname = name.toUpperCase(Locale.ENGLISH);
 764                     if (MANIFEST_NAME.equals(uname)
 765                             || SignatureFileVerifier.isBlockOrSF(uname)) {
 766                         JarEntry e = getJarEntry(name);
 767                         if (e == null) {
 768                             throw new JarException("corrupted jar file");
 769                         }
 770                         if (mev == null) {
 771                             mev = new ManifestEntryVerifier
 772                                 (getManifestFromReference());
 773                         }
 774                         byte[] b = getBytes(e);
 775                         if (b != null && b.length > 0) {
 776                             jv.beginEntry(e, mev);
 777                             jv.update(b.length, b, 0, b.length, mev);
 778                             jv.update(-1, null, 0, 0, mev);
 779                         }
 780                     }
 781                 }
 782             }
 783         } catch (IOException ex) {
 784             // if we had an error parsing any blocks, just
 785             // treat the jar file as being unsigned
 786             jv = null;
 787             verify = false;
 788             if (JarVerifier.debug != null) {
 789                 JarVerifier.debug.println("jarfile parsing error!");
 790                 ex.printStackTrace();
 791             }
 792         }
 793 
 794         // if after initializing the verifier we have nothing
 795         // signed, we null it out.
 796 
 797         if (jv != null) {
 798 
 799             jv.doneWithMeta();
 800             if (JarVerifier.debug != null) {
 801                 JarVerifier.debug.println("done with meta!");
 802             }
 803 
 804             if (jv.nothingToVerify()) {
 805                 if (JarVerifier.debug != null) {
 806                     JarVerifier.debug.println("nothing to verify!");
 807                 }
 808                 jv = null;
 809                 verify = false;
 810             }
 811         }
 812     }
 813 
 814     /*
 815      * Reads all the bytes for a given entry. Used to process the
 816      * META-INF files.
 817      */
 818     private byte[] getBytes(ZipEntry ze) throws IOException {
 819         try (InputStream is = super.getInputStream(ze)) {
 820             int len = (int)ze.getSize();
 821             int bytesRead;
 822             byte[] b;
 823             // trust specified entry sizes when reasonably small
 824             if (len != -1 && len <= 65535) {
 825                 b = new byte[len];
 826                 bytesRead = is.readNBytes(b, 0, len);
 827             } else {
 828                 b = is.readAllBytes();
 829                 bytesRead = b.length;
 830             }
 831             if (len != -1 && len != bytesRead) {
 832                 throw new EOFException("Expected:" + len + ", read:" + bytesRead);
 833             }
 834             return b;
 835         }
 836     }
 837 
 838     /**
 839      * Returns an input stream for reading the contents of the specified
 840      * zip file entry.
 841      * @param ze the zip file entry
 842      * @return an input stream for reading the contents of the specified
 843      *         zip file entry
 844      * @throws ZipException if a zip file format error has occurred
 845      * @throws IOException if an I/O error has occurred
 846      * @throws SecurityException if any of the jar file entries
 847      *         are incorrectly signed.
 848      * @throws IllegalStateException
 849      *         may be thrown if the jar file has been closed
 850      */
 851     public synchronized InputStream getInputStream(ZipEntry ze)
 852         throws IOException
 853     {
 854         maybeInstantiateVerifier();
 855         if (jv == null) {
 856             return super.getInputStream(ze);
 857         }
 858         if (!jvInitialized) {
 859             initializeVerifier();
 860             jvInitialized = true;
 861             // could be set to null after a call to
 862             // initializeVerifier if we have nothing to
 863             // verify
 864             if (jv == null)
 865                 return super.getInputStream(ze);
 866         }
 867 
 868         // wrap a verifier stream around the real stream
 869         return new JarVerifier.VerifierStream(
 870             getManifestFromReference(),
 871             verifiableEntry(ze),
 872             super.getInputStream(ze),
 873             jv);
 874     }
 875 
 876     private JarEntry verifiableEntry(ZipEntry ze) {
 877         if (ze instanceof JarFileEntry) {
 878             // assure the name and entry match for verification
 879             return ((JarFileEntry)ze).reifiedEntry();
 880         }
 881         ze = getJarEntry(ze.getName());
 882         if (ze instanceof JarFileEntry) {
 883             return ((JarFileEntry)ze).reifiedEntry();
 884         }
 885         return (JarEntry)ze;
 886     }
 887 
 888     // Statics for hand-coded Boyer-Moore search
 889     private static final byte[] CLASSPATH_CHARS =
 890             {'C','L','A','S','S','-','P','A','T','H', ':', ' '};
 891 
 892     // The bad character shift for "class-path:"
 893     private static final byte[] CLASSPATH_LASTOCC;
 894 
 895     private static final byte[] MULTIRELEASE_CHARS =
 896             {'M','U','L','T','I','-','R','E','L','E', 'A', 'S', 'E', ':',
 897                     ' ', 'T', 'R', 'U', 'E'};
 898 
 899     // The bad character shift for "multi-release: "
 900     private static final byte[] MULTIRELEASE_LASTOCC;
 901 
 902     static {
 903         CLASSPATH_LASTOCC = new byte[64];
 904         CLASSPATH_LASTOCC[(int)'C' - 32] = 1;
 905         CLASSPATH_LASTOCC[(int)'L' - 32] = 2;
 906         CLASSPATH_LASTOCC[(int)'S' - 32] = 5;
 907         CLASSPATH_LASTOCC[(int)'-' - 32] = 6;
 908         CLASSPATH_LASTOCC[(int)'P' - 32] = 7;
 909         CLASSPATH_LASTOCC[(int)'A' - 32] = 8;
 910         CLASSPATH_LASTOCC[(int)'T' - 32] = 9;
 911         CLASSPATH_LASTOCC[(int)'H' - 32] = 10;
 912         CLASSPATH_LASTOCC[(int)':' - 32] = 11;
 913         CLASSPATH_LASTOCC[(int)' ' - 32] = 12;
 914 
 915         MULTIRELEASE_LASTOCC = new byte[64];
 916         MULTIRELEASE_LASTOCC[(int)'M' - 32] = 1;
 917         MULTIRELEASE_LASTOCC[(int)'I' - 32] = 5;
 918         MULTIRELEASE_LASTOCC[(int)'-' - 32] = 6;
 919         MULTIRELEASE_LASTOCC[(int)'L' - 32] = 9;
 920         MULTIRELEASE_LASTOCC[(int)'A' - 32] = 11;
 921         MULTIRELEASE_LASTOCC[(int)'S' - 32] = 12;
 922         MULTIRELEASE_LASTOCC[(int)':' - 32] = 14;
 923         MULTIRELEASE_LASTOCC[(int)' ' - 32] = 15;
 924         MULTIRELEASE_LASTOCC[(int)'T' - 32] = 16;
 925         MULTIRELEASE_LASTOCC[(int)'R' - 32] = 17;
 926         MULTIRELEASE_LASTOCC[(int)'U' - 32] = 18;
 927         MULTIRELEASE_LASTOCC[(int)'E' - 32] = 19;
 928     }
 929 
 930     private JarEntry getManEntry() {
 931         if (manEntry == null) {
 932             // First look up manifest entry using standard name
 933             ZipEntry manEntry = super.getEntry(MANIFEST_NAME);
 934             if (manEntry == null) {
 935                 // If not found, then iterate through all the "META-INF/"
 936                 // entries to find a match.
 937                 String[] names = getMetaInfEntryNames();
 938                 if (names != null) {
 939                     for (String name : names) {
 940                         if (MANIFEST_NAME.equals(name.toUpperCase(Locale.ENGLISH))) {
 941                             manEntry = super.getEntry(name);
 942                             break;
 943                         }
 944                     }
 945                 }
 946             }
 947             this.manEntry = (manEntry == null)
 948                     ? null
 949                     : new JarFileEntry(manEntry.getName(), manEntry);
 950         }
 951         return manEntry;
 952     }
 953 
 954    /**
 955     * Returns {@code true} iff this JAR file has a manifest with the
 956     * Class-Path attribute
 957     */
 958     boolean hasClassPathAttribute() throws IOException {
 959         checkForSpecialAttributes();
 960         return hasClassPathAttribute;
 961     }
 962 
 963     /**
 964      * Returns true if the pattern {@code src} is found in {@code b}.
 965      * The {@code lastOcc} array is the precomputed bad character shifts.
 966      * Since there are no repeated substring in our search strings,
 967      * the good suffix shifts can be replaced with a comparison.
 968      */
 969     private int match(byte[] src, byte[] b, byte[] lastOcc) {
 970         int len = src.length;
 971         int last = b.length - len;
 972         int i = 0;
 973         next:
 974         while (i <= last) {
 975             for (int j = (len - 1); j >= 0; j--) {
 976                 byte c = b[i + j];
 977                 if (c >= ' ' && c <= 'z') {
 978                     if (c >= 'a') c -= 32; // Canonicalize
 979 
 980                     if (c != src[j]) {
 981                         // no match
 982                         int goodShift = (j < len - 1) ? len : 1;
 983                         int badShift = lastOcc[c - 32];
 984                         i += Math.max(j + 1 - badShift, goodShift);
 985                         continue next;
 986                     }
 987                 } else {
 988                     // no match, character not valid for name
 989                     i += len;
 990                     continue next;
 991                 }
 992             }
 993             return i;
 994         }
 995         return -1;
 996     }
 997 
 998     /**
 999      * On first invocation, check if the JAR file has the Class-Path
1000      * and the Multi-Release attribute. A no-op on subsequent calls.
1001      */
1002     private void checkForSpecialAttributes() throws IOException {
1003         if (hasCheckedSpecialAttributes) {
1004             return;
1005         }
1006         synchronized (this) {
1007             if (hasCheckedSpecialAttributes) {
1008                 return;
1009             }
1010             JarEntry manEntry = getManEntry();
1011             if (manEntry != null) {
1012                 byte[] b = getBytes(manEntry);
1013                 hasClassPathAttribute = match(CLASSPATH_CHARS, b,
1014                         CLASSPATH_LASTOCC) != -1;
1015                 // is this a multi-release jar file
1016                 if (MULTI_RELEASE_ENABLED && version != BASE_VERSION) {
1017                     int i = match(MULTIRELEASE_CHARS, b, MULTIRELEASE_LASTOCC);
1018                     if (i != -1) {
1019                         i += MULTIRELEASE_CHARS.length;
1020                         if (i < b.length) {
1021                             byte c = b[i++];
1022                             // Check that the value is followed by a newline
1023                             // and does not have a continuation
1024                             if (c == '\n' &&
1025                                     (i == b.length || b[i] != ' ')) {
1026                                 isMultiRelease = true;
1027                             } else if (c == '\r') {
1028                                 if (i == b.length) {
1029                                     isMultiRelease = true;
1030                                 } else {
1031                                     c = b[i++];
1032                                     if (c == '\n') {
1033                                         if (i == b.length || b[i] != ' ') {
1034                                             isMultiRelease = true;
1035                                         }
1036                                     } else if (c != ' ') {
1037                                         isMultiRelease = true;
1038                                     }
1039                                 }
1040                             }
1041                         }
1042                     }
1043                 }
1044             }
1045             hasCheckedSpecialAttributes = true;
1046         }
1047     }
1048 
1049     private synchronized void ensureInitialization() {
1050         try {
1051             maybeInstantiateVerifier();
1052         } catch (IOException e) {
1053             throw new RuntimeException(e);
1054         }
1055         if (jv != null && !jvInitialized) {
1056             initializeVerifier();
1057             jvInitialized = true;
1058         }
1059     }
1060 
1061     JarEntry newEntry(ZipEntry ze) {
1062         return new JarFileEntry(ze);
1063     }
1064 
1065     Enumeration<String> entryNames(CodeSource[] cs) {
1066         ensureInitialization();
1067         if (jv != null) {
1068             return jv.entryNames(this, cs);
1069         }
1070 
1071         /*
1072          * JAR file has no signed content. Is there a non-signing
1073          * code source?
1074          */
1075         boolean includeUnsigned = false;
1076         for (CodeSource c : cs) {
1077             if (c.getCodeSigners() == null) {
1078                 includeUnsigned = true;
1079                 break;
1080             }
1081         }
1082         if (includeUnsigned) {
1083             return unsignedEntryNames();
1084         } else {
1085             return new Enumeration<>() {
1086 
1087                 public boolean hasMoreElements() {
1088                     return false;
1089                 }
1090 
1091                 public String nextElement() {
1092                     throw new NoSuchElementException();
1093                 }
1094             };
1095         }
1096     }
1097 
1098     /**
1099      * Returns an enumeration of the zip file entries
1100      * excluding internal JAR mechanism entries and including
1101      * signed entries missing from the ZIP directory.
1102      */
1103     Enumeration<JarEntry> entries2() {
1104         ensureInitialization();
1105         if (jv != null) {
1106             return jv.entries2(this, super.entries());
1107         }
1108 
1109         // screen out entries which are never signed
1110         final Enumeration<? extends ZipEntry> enum_ = super.entries();
1111         return new Enumeration<>() {
1112 
1113             ZipEntry entry;
1114 
1115             public boolean hasMoreElements() {
1116                 if (entry != null) {
1117                     return true;
1118                 }
1119                 while (enum_.hasMoreElements()) {
1120                     ZipEntry ze = enum_.nextElement();
1121                     if (JarVerifier.isSigningRelated(ze.getName())) {
1122                         continue;
1123                     }
1124                     entry = ze;
1125                     return true;
1126                 }
1127                 return false;
1128             }
1129 
1130             public JarFileEntry nextElement() {
1131                 if (hasMoreElements()) {
1132                     ZipEntry ze = entry;
1133                     entry = null;
1134                     return new JarFileEntry(ze);
1135                 }
1136                 throw new NoSuchElementException();
1137             }
1138         };
1139     }
1140 
1141     CodeSource[] getCodeSources(URL url) {
1142         ensureInitialization();
1143         if (jv != null) {
1144             return jv.getCodeSources(this, url);
1145         }
1146 
1147         /*
1148          * JAR file has no signed content. Is there a non-signing
1149          * code source?
1150          */
1151         Enumeration<String> unsigned = unsignedEntryNames();
1152         if (unsigned.hasMoreElements()) {
1153             return new CodeSource[]{JarVerifier.getUnsignedCS(url)};
1154         } else {
1155             return null;
1156         }
1157     }
1158 
1159     private Enumeration<String> unsignedEntryNames() {
1160         final Enumeration<JarEntry> entries = entries();
1161         return new Enumeration<>() {
1162 
1163             String name;
1164 
1165             /*
1166              * Grab entries from ZIP directory but screen out
1167              * metadata.
1168              */
1169             public boolean hasMoreElements() {
1170                 if (name != null) {
1171                     return true;
1172                 }
1173                 while (entries.hasMoreElements()) {
1174                     String value;
1175                     ZipEntry e = entries.nextElement();
1176                     value = e.getName();
1177                     if (e.isDirectory() || JarVerifier.isSigningRelated(value)) {
1178                         continue;
1179                     }
1180                     name = value;
1181                     return true;
1182                 }
1183                 return false;
1184             }
1185 
1186             public String nextElement() {
1187                 if (hasMoreElements()) {
1188                     String value = name;
1189                     name = null;
1190                     return value;
1191                 }
1192                 throw new NoSuchElementException();
1193             }
1194         };
1195     }
1196 
1197     CodeSource getCodeSource(URL url, String name) {
1198         ensureInitialization();
1199         if (jv != null) {
1200             if (jv.eagerValidation) {
1201                 CodeSource cs = null;
1202                 JarEntry je = getJarEntry(name);
1203                 if (je != null) {
1204                     cs = jv.getCodeSource(url, this, je);
1205                 } else {
1206                     cs = jv.getCodeSource(url, name);
1207                 }
1208                 return cs;
1209             } else {
1210                 return jv.getCodeSource(url, name);
1211             }
1212         }
1213 
1214         return JarVerifier.getUnsignedCS(url);
1215     }
1216 
1217     void setEagerValidation(boolean eager) {
1218         try {
1219             maybeInstantiateVerifier();
1220         } catch (IOException e) {
1221             throw new RuntimeException(e);
1222         }
1223         if (jv != null) {
1224             jv.setEagerValidation(eager);
1225         }
1226     }
1227 
1228     List<Object> getManifestDigests() {
1229         ensureInitialization();
1230         if (jv != null) {
1231             return jv.getManifestDigests();
1232         }
1233         return new ArrayList<>();
1234     }
1235 }