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 byteCount} bytes of decompressed data and stores it in
    134      * {@code buffer} starting at {@code byteOffset}. Returns the number of uncompressed bytes read.
    135      *
    136      * @throws IOException
    137      *             if an IOException occurs.
    138      */
    139     @Override
    140     public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
    141         if (mEntry != null) {
    142             return -1;
    143         }
    144         int r = super.read(buffer, byteOffset, byteCount);
    145         if (verStream != null && !eos) {
    146             if (r == -1) {
    147                 eos = true;
    148                 if (verifier != null) {
    149                     if (isMeta) {
    150                         verifier.addMetaEntry(jarEntry.getName(),
    151                                 ((ByteArrayOutputStream) verStream)
    152                                         .toByteArray());
    153                         try {
    154                             verifier.readCertificates();
    155                         } catch (SecurityException e) {
    156                             verifier = null;
    157                             throw e;
    158                         }
    159                     } else {
    160                         ((JarVerifier.VerifierEntry) verStream).verify();
    161                     }
    162                 }
    163             } else {
    164                 verStream.write(buffer, byteOffset, r);
    165             }
    166         }
    167         return r;
    168     }
    169 
    170     /**
    171      * Returns the next {@code ZipEntry} contained in this stream or {@code
    172      * null} if no more entries are present.
    173      *
    174      * @return the next extracted ZIP entry.
    175      * @throws IOException
    176      *             if an error occurs while reading the entry.
    177      */
    178     @Override
    179     public ZipEntry getNextEntry() throws IOException {
    180         if (mEntry != null) {
    181             jarEntry = mEntry;
    182             mEntry = null;
    183             jarEntry.setAttributes(null);
    184         } else {
    185             jarEntry = (JarEntry) super.getNextEntry();
    186             if (jarEntry == null) {
    187                 return null;
    188             }
    189             if (verifier != null) {
    190                 isMeta = jarEntry.getName().toUpperCase(Locale.US).startsWith(JarFile.META_DIR);
    191                 if (isMeta) {
    192                     verStream = new ByteArrayOutputStream();
    193                 } else {
    194                     verStream = verifier.initEntry(jarEntry.getName());
    195                 }
    196             }
    197         }
    198         eos = false;
    199         return jarEntry;
    200     }
    201 
    202     @Override
    203     protected ZipEntry createZipEntry(String name) {
    204         JarEntry entry = new JarEntry(name);
    205         if (manifest != null) {
    206             entry.setAttributes(manifest.getAttributes(name));
    207         }
    208         return entry;
    209     }
    210 }
    211