| /* |
| * 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.zip.ZipEntry; |
| import java.util.zip.ZipInputStream; |
| import org.apache.harmony.luni.util.Util; |
| |
| /** |
| * 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; |
| } |
| String name = Util.toASCIIUpperCase(mEntry.getName()); |
| if (name.equals(JarFile.META_DIR)) { |
| mEntry = null; // modifies behavior of getNextJarEntry() |
| closeEntry(); |
| mEntry = getNextJarEntry(); |
| name = mEntry.getName().toUpperCase(); |
| } |
| if (name.equals(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 = Util.toASCIIUpperCase(jarEntry.getName()).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; |
| } |
| } |