Home | History | Annotate | Download | only in jar
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package java.util.jar;
     19 
     20 import java.io.ByteArrayOutputStream;
     21 import java.io.IOException;
     22 import java.io.InputStream;
     23 import java.io.OutputStream;
     24 import java.util.Locale;
     25 import java.util.zip.ZipEntry;
     26 import java.util.zip.ZipInputStream;
     27 
     28 /**
     29  * The input stream from which the JAR file to be read may be fetched. It is
     30  * used like the {@code ZipInputStream}.
     31  *
     32  * @see ZipInputStream
     33  */
     34 public class JarInputStream extends ZipInputStream {
     35 
     36     private Manifest manifest;
     37 
     38     private boolean eos = false;
     39 
     40     private JarEntry mEntry;
     41 
     42     private JarEntry jarEntry;
     43 
     44     private boolean isMeta;
     45 
     46     private JarVerifier verifier;
     47 
     48     private OutputStream verStream;
     49 
     50     /**
     51      * Constructs a new {@code JarInputStream} from an input stream.
     52      *
     53      * @param stream
     54      *            the input stream containing the JAR file.
     55      * @param verify
     56      *            if the file should be verified with a {@code JarVerifier}.
     57      * @throws IOException
     58      *             If an error occurs reading entries from the input stream.
     59      * @see ZipInputStream#ZipInputStream(InputStream)
     60      */
     61     public JarInputStream(InputStream stream, boolean verify) throws IOException {
     62         super(stream);
     63         if (verify) {
     64             verifier = new JarVerifier("JarInputStream");
     65         }
     66         if ((mEntry = getNextJarEntry()) == null) {
     67             return;
     68         }
     69         if (mEntry.getName().equalsIgnoreCase(JarFile.META_DIR)) {
     70             mEntry = null; // modifies behavior of getNextJarEntry()
     71             closeEntry();
     72             mEntry = getNextJarEntry();
     73         }
     74         if (mEntry.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)) {
     75             mEntry = null;
     76             manifest = new Manifest(this, verify);
     77             closeEntry();
     78             if (verify) {
     79                 verifier.setManifest(manifest);
     80                 if (manifest != null) {
     81                     verifier.mainAttributesEnd = manifest.getMainAttributesEnd();
     82                 }
     83             }
     84 
     85         } else {
     86             Attributes temp = new Attributes(3);
     87             temp.map.put("hidden", null);
     88             mEntry.setAttributes(temp);
     89             /*
     90              * if not from the first entry, we will not get enough
     91              * information,so no verify will be taken out.
     92              */
     93             verifier = null;
     94         }
     95     }
     96 
     97     /**
     98      * Constructs a new {@code JarInputStream} from an input stream.
     99      *
    100      * @param stream
    101      *            the input stream containing the JAR file.
    102      * @throws IOException
    103      *             If an error occurs reading entries from the input stream.
    104      * @see ZipInputStream#ZipInputStream(InputStream)
    105      */
    106     public JarInputStream(InputStream stream) throws IOException {
    107         this(stream, true);
    108     }
    109 
    110     /**
    111      * Returns the {@code Manifest} object associated with this {@code
    112      * JarInputStream} or {@code null} if no manifest entry exists.
    113      *
    114      * @return the MANIFEST specifying the contents of the JAR file.
    115      */
    116     public Manifest getManifest() {
    117         return manifest;
    118     }
    119 
    120     /**
    121      * Returns the next {@code JarEntry} contained in this stream or {@code
    122      * null} if no more entries are present.
    123      *
    124      * @return the next JAR entry.
    125      * @throws IOException
    126      *             if an error occurs while reading the entry.
    127      */
    128     public JarEntry getNextJarEntry() throws IOException {
    129         return (JarEntry) getNextEntry();
    130     }
    131 
    132     /**
    133      * Reads up to {@code length} of decompressed data and stores it in
    134      * {@code buffer} starting at {@code offset}.
    135      *
    136      * @param buffer
    137      *            Buffer to store into
    138      * @param offset
    139      *            offset in buffer to store at
    140      * @param length
    141      *            number of bytes to store
    142      * @return Number of uncompressed bytes read
    143      * @throws IOException
    144      *             if an IOException occurs.
    145      */
    146     @Override
    147     public int read(byte[] buffer, int offset, int length) throws IOException {
    148         if (mEntry != null) {
    149             return -1;
    150         }
    151         int r = super.read(buffer, offset, length);
    152         if (verStream != null && !eos) {
    153             if (r == -1) {
    154                 eos = true;
    155                 if (verifier != null) {
    156                     if (isMeta) {
    157                         verifier.addMetaEntry(jarEntry.getName(),
    158                                 ((ByteArrayOutputStream) verStream)
    159                                         .toByteArray());
    160                         try {
    161                             verifier.readCertificates();
    162                         } catch (SecurityException e) {
    163                             verifier = null;
    164                             throw e;
    165                         }
    166                     } else {
    167                         ((JarVerifier.VerifierEntry) verStream).verify();
    168                     }
    169                 }
    170             } else {
    171                 verStream.write(buffer, offset, r);
    172             }
    173         }
    174         return r;
    175     }
    176 
    177     /**
    178      * Returns the next {@code ZipEntry} contained in this stream or {@code
    179      * null} if no more entries are present.
    180      *
    181      * @return the next extracted ZIP entry.
    182      * @throws IOException
    183      *             if an error occurs while reading the entry.
    184      */
    185     @Override
    186     public ZipEntry getNextEntry() throws IOException {
    187         if (mEntry != null) {
    188             jarEntry = mEntry;
    189             mEntry = null;
    190             jarEntry.setAttributes(null);
    191         } else {
    192             jarEntry = (JarEntry) super.getNextEntry();
    193             if (jarEntry == null) {
    194                 return null;
    195             }
    196             if (verifier != null) {
    197                 isMeta = jarEntry.getName().toUpperCase(Locale.US).startsWith(JarFile.META_DIR);
    198                 if (isMeta) {
    199                     verStream = new ByteArrayOutputStream();
    200                 } else {
    201                     verStream = verifier.initEntry(jarEntry.getName());
    202                 }
    203             }
    204         }
    205         eos = false;
    206         return jarEntry;
    207     }
    208 
    209     @Override
    210     protected ZipEntry createZipEntry(String name) {
    211         JarEntry entry = new JarEntry(name);
    212         if (manifest != null) {
    213             entry.setAttributes(manifest.getAttributes(name));
    214         }
    215         return entry;
    216     }
    217 }
    218