234 lines
8.7 KiB
Java
234 lines
8.7 KiB
Java
/*
|
|
* Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package java.util.jar;
|
|
|
|
import java.util.zip.*;
|
|
import java.io.*;
|
|
import sun.security.util.ManifestEntryVerifier;
|
|
import jdk.internal.util.jar.JarIndex;
|
|
|
|
/**
|
|
* The <code>JarInputStream</code> class is used to read the contents of
|
|
* a JAR file from any input stream. It extends the class
|
|
* <code>java.util.zip.ZipInputStream</code> with support for reading
|
|
* an optional <code>Manifest</code> entry. The <code>Manifest</code>
|
|
* can be used to store meta-information about the JAR file and its entries.
|
|
*
|
|
* @author David Connelly
|
|
* @see Manifest
|
|
* @see java.util.zip.ZipInputStream
|
|
* @since 1.2
|
|
*/
|
|
public
|
|
class JarInputStream extends ZipInputStream {
|
|
private Manifest man;
|
|
private JarEntry first;
|
|
private JarVerifier jv;
|
|
private ManifestEntryVerifier mev;
|
|
private final boolean doVerify;
|
|
private boolean tryManifest;
|
|
|
|
/**
|
|
* Creates a new <code>JarInputStream</code> and reads the optional
|
|
* manifest. If a manifest is present, also attempts to verify
|
|
* the signatures if the JarInputStream is signed.
|
|
* @param in the actual input stream
|
|
* @exception IOException if an I/O error has occurred
|
|
*/
|
|
public JarInputStream(InputStream in) throws IOException {
|
|
this(in, true);
|
|
}
|
|
|
|
/**
|
|
* Creates a new <code>JarInputStream</code> and reads the optional
|
|
* manifest. If a manifest is present and verify is true, also attempts
|
|
* to verify the signatures if the JarInputStream is signed.
|
|
*
|
|
* @param in the actual input stream
|
|
* @param verify whether or not to verify the JarInputStream if
|
|
* it is signed.
|
|
* @exception IOException if an I/O error has occurred
|
|
*/
|
|
public JarInputStream(InputStream in, boolean verify) throws IOException {
|
|
super(in);
|
|
this.doVerify = verify;
|
|
|
|
// This implementation assumes the META-INF/MANIFEST.MF entry
|
|
// should be either the first or the second entry (when preceded
|
|
// by the dir META-INF/). It skips the META-INF/ and then
|
|
// "consumes" the MANIFEST.MF to initialize the Manifest object.
|
|
JarEntry e = (JarEntry)super.getNextEntry();
|
|
if (e != null && e.getName().equalsIgnoreCase("META-INF/"))
|
|
e = (JarEntry)super.getNextEntry();
|
|
first = checkManifest(e);
|
|
}
|
|
|
|
private JarEntry checkManifest(JarEntry e)
|
|
throws IOException
|
|
{
|
|
if (e != null && JarFile.MANIFEST_NAME.equalsIgnoreCase(e.getName())) {
|
|
man = new Manifest();
|
|
byte bytes[] = getBytes(new BufferedInputStream(this));
|
|
man.read(new ByteArrayInputStream(bytes));
|
|
closeEntry();
|
|
if (doVerify) {
|
|
jv = new JarVerifier(e.getName(), bytes);
|
|
mev = new ManifestEntryVerifier(man);
|
|
}
|
|
return (JarEntry)super.getNextEntry();
|
|
}
|
|
return e;
|
|
}
|
|
|
|
private byte[] getBytes(InputStream is)
|
|
throws IOException
|
|
{
|
|
byte[] buffer = new byte[8192];
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
|
|
int n;
|
|
while ((n = is.read(buffer, 0, buffer.length)) != -1) {
|
|
baos.write(buffer, 0, n);
|
|
}
|
|
return baos.toByteArray();
|
|
}
|
|
|
|
/**
|
|
* Returns the <code>Manifest</code> for this JAR file, or
|
|
* <code>null</code> if none.
|
|
*
|
|
* @return the <code>Manifest</code> for this JAR file, or
|
|
* <code>null</code> if none.
|
|
*/
|
|
public Manifest getManifest() {
|
|
return man;
|
|
}
|
|
|
|
/**
|
|
* Reads the next ZIP file entry and positions the stream at the
|
|
* beginning of the entry data. If verification has been enabled,
|
|
* any invalid signature detected while positioning the stream for
|
|
* the next entry will result in an exception.
|
|
* @exception ZipException if a ZIP file error has occurred
|
|
* @exception IOException if an I/O error has occurred
|
|
* @exception SecurityException if any of the jar file entries
|
|
* are incorrectly signed.
|
|
*/
|
|
public ZipEntry getNextEntry() throws IOException {
|
|
JarEntry e;
|
|
if (first == null) {
|
|
e = (JarEntry)super.getNextEntry();
|
|
if (tryManifest) {
|
|
e = checkManifest(e);
|
|
tryManifest = false;
|
|
}
|
|
} else {
|
|
e = first;
|
|
if (first.getName().equalsIgnoreCase(JarIndex.INDEX_NAME))
|
|
tryManifest = true;
|
|
first = null;
|
|
}
|
|
if (jv != null && e != null) {
|
|
// At this point, we might have parsed all the meta-inf
|
|
// entries and have nothing to verify. If we have
|
|
// nothing to verify, get rid of the JarVerifier object.
|
|
if (jv.nothingToVerify() == true) {
|
|
jv = null;
|
|
mev = null;
|
|
} else {
|
|
jv.beginEntry(e, mev);
|
|
}
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/**
|
|
* Reads the next JAR file entry and positions the stream at the
|
|
* beginning of the entry data. If verification has been enabled,
|
|
* any invalid signature detected while positioning the stream for
|
|
* the next entry will result in an exception.
|
|
* @return the next JAR file entry, or null if there are no more entries
|
|
* @exception ZipException if a ZIP file error has occurred
|
|
* @exception IOException if an I/O error has occurred
|
|
* @exception SecurityException if any of the jar file entries
|
|
* are incorrectly signed.
|
|
*/
|
|
public JarEntry getNextJarEntry() throws IOException {
|
|
return (JarEntry)getNextEntry();
|
|
}
|
|
|
|
/**
|
|
* Reads from the current JAR file entry into an array of bytes.
|
|
* If <code>len</code> is not zero, the method
|
|
* blocks until some input is available; otherwise, no
|
|
* bytes are read and <code>0</code> is returned.
|
|
* If verification has been enabled, any invalid signature
|
|
* on the current entry will be reported at some point before the
|
|
* end of the entry is reached.
|
|
* @param b the buffer into which the data is read
|
|
* @param off the start offset in the destination array <code>b</code>
|
|
* @param len the maximum number of bytes to read
|
|
* @return the actual number of bytes read, or -1 if the end of the
|
|
* entry is reached
|
|
* @exception NullPointerException If <code>b</code> is <code>null</code>.
|
|
* @exception IndexOutOfBoundsException If <code>off</code> is negative,
|
|
* <code>len</code> is negative, or <code>len</code> is greater than
|
|
* <code>b.length - off</code>
|
|
* @exception ZipException if a ZIP file error has occurred
|
|
* @exception IOException if an I/O error has occurred
|
|
* @exception SecurityException if any of the jar file entries
|
|
* are incorrectly signed.
|
|
*/
|
|
public int read(byte[] b, int off, int len) throws IOException {
|
|
int n;
|
|
if (first == null) {
|
|
n = super.read(b, off, len);
|
|
} else {
|
|
n = -1;
|
|
}
|
|
if (jv != null) {
|
|
jv.update(n, b, off, len, mev);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/**
|
|
* Creates a new <code>JarEntry</code> (<code>ZipEntry</code>) for the
|
|
* specified JAR file entry name. The manifest attributes of
|
|
* the specified JAR file entry name will be copied to the new
|
|
* <CODE>JarEntry</CODE>.
|
|
*
|
|
* @param name the name of the JAR/ZIP file entry
|
|
* @return the <code>JarEntry</code> object just created
|
|
*/
|
|
protected ZipEntry createZipEntry(String name) {
|
|
JarEntry e = new JarEntry(name);
|
|
if (man != null) {
|
|
e.attr = man.getAttributes(name);
|
|
}
|
|
return e;
|
|
}
|
|
}
|