1 /* 2 * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.util.jar; 27 28 import java.util.zip.*; 29 import java.io.*; 30 import sun.security.util.ManifestEntryVerifier; 31 import sun.misc.JarIndex; 32 33 /** 34 * The <code>JarInputStream</code> class is used to read the contents of 35 * a JAR file from any input stream. It extends the class 36 * <code>java.util.zip.ZipInputStream</code> with support for reading 37 * an optional <code>Manifest</code> entry. The <code>Manifest</code> 38 * can be used to store meta-information about the JAR file and its entries. 39 * 40 * @author David Connelly 41 * @see Manifest 42 * @see java.util.zip.ZipInputStream 43 * @since 1.2 44 */ 45 public 46 class JarInputStream extends ZipInputStream { 47 private Manifest man; 48 private JarEntry first; 49 private JarVerifier jv; 50 private ManifestEntryVerifier mev; 51 private final boolean doVerify; 52 private boolean tryManifest; 53 54 /** 55 * Creates a new <code>JarInputStream</code> and reads the optional 56 * manifest. If a manifest is present, also attempts to verify 57 * the signatures if the JarInputStream is signed. 58 * @param in the actual input stream 59 * @exception IOException if an I/O error has occurred 60 */ JarInputStream(InputStream in)61 public JarInputStream(InputStream in) throws IOException { 62 this(in, true); 63 } 64 65 /** 66 * Creates a new <code>JarInputStream</code> and reads the optional 67 * manifest. If a manifest is present and verify is true, also attempts 68 * to verify the signatures if the JarInputStream is signed. 69 * 70 * @param in the actual input stream 71 * @param verify whether or not to verify the JarInputStream if 72 * it is signed. 73 * @exception IOException if an I/O error has occurred 74 */ JarInputStream(InputStream in, boolean verify)75 public JarInputStream(InputStream in, boolean verify) throws IOException { 76 super(in); 77 this.doVerify = verify; 78 79 // This implementation assumes the META-INF/MANIFEST.MF entry 80 // should be either the first or the second entry (when preceded 81 // by the dir META-INF/). It skips the META-INF/ and then 82 // "consumes" the MANIFEST.MF to initialize the Manifest object. 83 JarEntry e = (JarEntry)super.getNextEntry(); 84 if (e != null && e.getName().equalsIgnoreCase("META-INF/")) 85 e = (JarEntry)super.getNextEntry(); 86 first = checkManifest(e); 87 } 88 checkManifest(JarEntry e)89 private JarEntry checkManifest(JarEntry e) 90 throws IOException 91 { 92 if (e != null && JarFile.MANIFEST_NAME.equalsIgnoreCase(e.getName())) { 93 man = new Manifest(); 94 byte bytes[] = getBytes(new BufferedInputStream(this)); 95 man.read(new ByteArrayInputStream(bytes)); 96 closeEntry(); 97 if (doVerify) { 98 jv = new JarVerifier(bytes); 99 mev = new ManifestEntryVerifier(man); 100 } 101 return (JarEntry)super.getNextEntry(); 102 } 103 return e; 104 } 105 getBytes(InputStream is)106 private byte[] getBytes(InputStream is) 107 throws IOException 108 { 109 byte[] buffer = new byte[8192]; 110 ByteArrayOutputStream baos = new ByteArrayOutputStream(2048); 111 int n; 112 while ((n = is.read(buffer, 0, buffer.length)) != -1) { 113 baos.write(buffer, 0, n); 114 } 115 return baos.toByteArray(); 116 } 117 118 /** 119 * Returns the <code>Manifest</code> for this JAR file, or 120 * <code>null</code> if none. 121 * 122 * @return the <code>Manifest</code> for this JAR file, or 123 * <code>null</code> if none. 124 */ getManifest()125 public Manifest getManifest() { 126 return man; 127 } 128 129 /** 130 * Reads the next ZIP file entry and positions the stream at the 131 * beginning of the entry data. If verification has been enabled, 132 * any invalid signature detected while positioning the stream for 133 * the next entry will result in an exception. 134 * @exception ZipException if a ZIP file error has occurred 135 * @exception IOException if an I/O error has occurred 136 * @exception SecurityException if any of the jar file entries 137 * are incorrectly signed. 138 */ getNextEntry()139 public ZipEntry getNextEntry() throws IOException { 140 JarEntry e; 141 if (first == null) { 142 e = (JarEntry)super.getNextEntry(); 143 if (tryManifest) { 144 e = checkManifest(e); 145 tryManifest = false; 146 } 147 } else { 148 e = first; 149 if (first.getName().equalsIgnoreCase(JarIndex.INDEX_NAME)) 150 tryManifest = true; 151 first = null; 152 } 153 if (jv != null && e != null) { 154 // At this point, we might have parsed all the meta-inf 155 // entries and have nothing to verify. If we have 156 // nothing to verify, get rid of the JarVerifier object. 157 if (jv.nothingToVerify() == true) { 158 jv = null; 159 mev = null; 160 } else { 161 jv.beginEntry(e, mev); 162 } 163 } 164 return e; 165 } 166 167 /** 168 * Reads the next JAR file entry and positions the stream at the 169 * beginning of the entry data. If verification has been enabled, 170 * any invalid signature detected while positioning the stream for 171 * the next entry will result in an exception. 172 * @return the next JAR file entry, or null if there are no more entries 173 * @exception ZipException if a ZIP file error has occurred 174 * @exception IOException if an I/O error has occurred 175 * @exception SecurityException if any of the jar file entries 176 * are incorrectly signed. 177 */ getNextJarEntry()178 public JarEntry getNextJarEntry() throws IOException { 179 return (JarEntry)getNextEntry(); 180 } 181 182 /** 183 * Reads from the current JAR file entry into an array of bytes. 184 * If <code>len</code> is not zero, the method 185 * blocks until some input is available; otherwise, no 186 * bytes are read and <code>0</code> is returned. 187 * If verification has been enabled, any invalid signature 188 * on the current entry will be reported at some point before the 189 * end of the entry is reached. 190 * @param b the buffer into which the data is read 191 * @param off the start offset in the destination array <code>b</code> 192 * @param len the maximum number of bytes to read 193 * @return the actual number of bytes read, or -1 if the end of the 194 * entry is reached 195 * @exception NullPointerException If <code>b</code> is <code>null</code>. 196 * @exception IndexOutOfBoundsException If <code>off</code> is negative, 197 * <code>len</code> is negative, or <code>len</code> is greater than 198 * <code>b.length - off</code> 199 * @exception ZipException if a ZIP file error has occurred 200 * @exception IOException if an I/O error has occurred 201 * @exception SecurityException if any of the jar file entries 202 * are incorrectly signed. 203 */ read(byte[] b, int off, int len)204 public int read(byte[] b, int off, int len) throws IOException { 205 int n; 206 if (first == null) { 207 n = super.read(b, off, len); 208 } else { 209 n = -1; 210 } 211 if (jv != null) { 212 jv.update(n, b, off, len, mev); 213 } 214 return n; 215 } 216 217 /** 218 * Creates a new <code>JarEntry</code> (<code>ZipEntry</code>) for the 219 * specified JAR file entry name. The manifest attributes of 220 * the specified JAR file entry name will be copied to the new 221 * <CODE>JarEntry</CODE>. 222 * 223 * @param name the name of the JAR/ZIP file entry 224 * @return the <code>JarEntry</code> object just created 225 */ createZipEntry(String name)226 protected ZipEntry createZipEntry(String name) { 227 JarEntry e = new JarEntry(name); 228 if (man != null) { 229 e.attr = man.getAttributes(name); 230 } 231 return e; 232 } 233 } 234