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