blob: 03f765fa10076cd67da93fa906bc753210386ae7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.util.jar;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Locale;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* The input stream from which the JAR file to be read may be fetched. It is
* used like the {@code ZipInputStream}.
*
* @see ZipInputStream
*/
public class JarInputStream extends ZipInputStream {
private Manifest manifest;
private boolean eos = false;
private JarEntry mEntry;
private JarEntry jarEntry;
private boolean isMeta;
private JarVerifier verifier;
private OutputStream verStream;
/**
* Constructs a new {@code JarInputStream} from an input stream.
*
* @param stream
* the input stream containing the JAR file.
* @param verify
* if the file should be verified with a {@code JarVerifier}.
* @throws IOException
* If an error occurs reading entries from the input stream.
* @see ZipInputStream#ZipInputStream(InputStream)
*/
public JarInputStream(InputStream stream, boolean verify) throws IOException {
super(stream);
if (verify) {
verifier = new JarVerifier("JarInputStream");
}
if ((mEntry = getNextJarEntry()) == null) {
return;
}
if (mEntry.getName().equalsIgnoreCase(JarFile.META_DIR)) {
mEntry = null; // modifies behavior of getNextJarEntry()
closeEntry();
mEntry = getNextJarEntry();
}
if (mEntry.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)) {
mEntry = null;
manifest = new Manifest(this, verify);
closeEntry();
if (verify) {
verifier.setManifest(manifest);
if (manifest != null) {
verifier.mainAttributesEnd = manifest.getMainAttributesEnd();
}
}
} else {
Attributes temp = new Attributes(3);
temp.map.put("hidden", null);
mEntry.setAttributes(temp);
/*
* if not from the first entry, we will not get enough
* information,so no verify will be taken out.
*/
verifier = null;
}
}
/**
* Constructs a new {@code JarInputStream} from an input stream.
*
* @param stream
* the input stream containing the JAR file.
* @throws IOException
* If an error occurs reading entries from the input stream.
* @see ZipInputStream#ZipInputStream(InputStream)
*/
public JarInputStream(InputStream stream) throws IOException {
this(stream, true);
}
/**
* Returns the {@code Manifest} object associated with this {@code
* JarInputStream} or {@code null} if no manifest entry exists.
*
* @return the MANIFEST specifying the contents of the JAR file.
*/
public Manifest getManifest() {
return manifest;
}
/**
* Returns the next {@code JarEntry} contained in this stream or {@code
* null} if no more entries are present.
*
* @return the next JAR entry.
* @throws IOException
* if an error occurs while reading the entry.
*/
public JarEntry getNextJarEntry() throws IOException {
return (JarEntry) getNextEntry();
}
/**
* Reads up to {@code length} of decompressed data and stores it in
* {@code buffer} starting at {@code offset}.
*
* @param buffer
* Buffer to store into
* @param offset
* offset in buffer to store at
* @param length
* number of bytes to store
* @return Number of uncompressed bytes read
* @throws IOException
* if an IOException occurs.
*/
@Override
public int read(byte[] buffer, int offset, int length) throws IOException {
if (mEntry != null) {
return -1;
}
int r = super.read(buffer, offset, length);
if (verStream != null && !eos) {
if (r == -1) {
eos = true;
if (verifier != null) {
if (isMeta) {
verifier.addMetaEntry(jarEntry.getName(),
((ByteArrayOutputStream) verStream)
.toByteArray());
try {
verifier.readCertificates();
} catch (SecurityException e) {
verifier = null;
throw e;
}
} else {
((JarVerifier.VerifierEntry) verStream).verify();
}
}
} else {
verStream.write(buffer, offset, r);
}
}
return r;
}
/**
* Returns the next {@code ZipEntry} contained in this stream or {@code
* null} if no more entries are present.
*
* @return the next extracted ZIP entry.
* @throws IOException
* if an error occurs while reading the entry.
*/
@Override
public ZipEntry getNextEntry() throws IOException {
if (mEntry != null) {
jarEntry = mEntry;
mEntry = null;
jarEntry.setAttributes(null);
} else {
jarEntry = (JarEntry) super.getNextEntry();
if (jarEntry == null) {
return null;
}
if (verifier != null) {
isMeta = jarEntry.getName().toUpperCase(Locale.US).startsWith(JarFile.META_DIR);
if (isMeta) {
verStream = new ByteArrayOutputStream();
} else {
verStream = verifier.initEntry(jarEntry.getName());
}
}
}
eos = false;
return jarEntry;
}
@Override
protected ZipEntry createZipEntry(String name) {
JarEntry entry = new JarEntry(name);
if (manifest != null) {
entry.setAttributes(manifest.getAttributes(name));
}
return entry;
}
}