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