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 18package java.util.jar; 19 20import java.io.ByteArrayOutputStream; 21import java.io.IOException; 22import java.io.InputStream; 23import java.io.OutputStream; 24import java.util.zip.ZipEntry; 25import java.util.zip.ZipInputStream; 26 27import org.apache.harmony.archive.util.Util; 28 29/** 30 * The input stream from which the JAR file to be read may be fetched. It is 31 * used like the {@code ZipInputStream}. 32 * 33 * @see ZipInputStream 34 * @since Android 1.0 35 */ 36public class JarInputStream extends ZipInputStream { 37 38 private Manifest manifest; 39 40 private boolean eos = false; 41 42 private JarEntry mEntry; 43 44 private JarEntry jarEntry; 45 46 private boolean isMeta; 47 48 private JarVerifier verifier; 49 50 private OutputStream verStream; 51 52 /** 53 * Constructs a new {@code JarInputStream} from an input stream. 54 * 55 * @param stream 56 * the input stream containing the JAR file. 57 * @param verify 58 * if the file should be verified with a {@code JarVerifier}. 59 * @throws IOException 60 * If an error occurs reading entries from the input stream. 61 * @see ZipInputStream#ZipInputStream(InputStream) 62 * @since Android 1.0 63 */ 64 public JarInputStream(InputStream stream, boolean verify) 65 throws IOException { 66 super(stream); 67 if (verify) { 68 verifier = new JarVerifier("JarInputStream"); //$NON-NLS-1$ 69 } 70 if ((mEntry = getNextJarEntry()) == null) { 71 return; 72 } 73 String name = Util.toASCIIUpperCase(mEntry.getName()); 74 if (name.equals(JarFile.META_DIR)) { 75 mEntry = null; // modifies behavior of getNextJarEntry() 76 closeEntry(); 77 mEntry = getNextJarEntry(); 78 name = mEntry.getName().toUpperCase(); 79 } 80 if (name.equals(JarFile.MANIFEST_NAME)) { 81 mEntry = null; 82 manifest = new Manifest(this, verify); 83 closeEntry(); 84 if (verify) { 85 verifier.setManifest(manifest); 86 if (manifest != null) { 87 verifier.mainAttributesChunk = manifest 88 .getMainAttributesChunk(); 89 } 90 } 91 92 } else { 93 Attributes temp = new Attributes(3); 94 temp.map.put("hidden", null); //$NON-NLS-1$ 95 mEntry.setAttributes(temp); 96 /* 97 * if not from the first entry, we will not get enough 98 * information,so no verify will be taken out. 99 */ 100 verifier = null; 101 } 102 } 103 104 /** 105 * Constructs a new {@code JarInputStream} from an input stream. 106 * 107 * @param stream 108 * the input stream containing the JAR file. 109 * @throws IOException 110 * If an error occurs reading entries from the input stream. 111 * @see ZipInputStream#ZipInputStream(InputStream) 112 * @since Android 1.0 113 */ 114 public JarInputStream(InputStream stream) throws IOException { 115 this(stream, true); 116 } 117 118 /** 119 * Returns the {@code Manifest} object associated with this {@code 120 * JarInputStream} or {@code null} if no manifest entry exists. 121 * 122 * @return the MANIFEST specifying the contents of the JAR file. 123 * @since Android 1.0 124 */ 125 public Manifest getManifest() { 126 return manifest; 127 } 128 129 /** 130 * Returns the next {@code JarEntry} contained in this stream or {@code 131 * null} if no more entries are present. 132 * 133 * @return the next JAR entry. 134 * @throws IOException 135 * if an error occurs while reading the entry. 136 * @since Android 1.0 137 */ 138 public JarEntry getNextJarEntry() throws IOException { 139 return (JarEntry) getNextEntry(); 140 } 141 142 /** 143 * Reads up to {@code length} of decompressed data and stores it in 144 * {@code buffer} starting at {@code offset}. 145 * 146 * @param buffer 147 * Buffer to store into 148 * @param offset 149 * offset in buffer to store at 150 * @param length 151 * number of bytes to store 152 * @return Number of uncompressed bytes read 153 * @throws IOException 154 * if an IOException occurs. 155 * @since Android 1.0 156 */ 157 @Override 158 public int read(byte[] buffer, int offset, int length) throws IOException { 159 if (mEntry != null) { 160 return -1; 161 } 162 int r = super.read(buffer, offset, length); 163 if (verStream != null && !eos) { 164 if (r == -1) { 165 eos = true; 166 if (verifier != null) { 167 if (isMeta) { 168 verifier.addMetaEntry(jarEntry.getName(), 169 ((ByteArrayOutputStream) verStream) 170 .toByteArray()); 171 try { 172 verifier.readCertificates(); 173 } catch (SecurityException e) { 174 verifier = null; 175 throw e; 176 } 177 } else { 178 verifier.verifySignatures( 179 (JarVerifier.VerifierEntry) verStream, 180 jarEntry); 181 } 182 } 183 } else { 184 verStream.write(buffer, offset, r); 185 } 186 } 187 return r; 188 } 189 190 /** 191 * Returns the next {@code ZipEntry} contained in this stream or {@code 192 * null} if no more entries are present. 193 * 194 * @return the next extracted ZIP entry. 195 * @throws IOException 196 * if an error occurs while reading the entry. 197 * @since Android 1.0 198 */ 199 @Override 200 public ZipEntry getNextEntry() throws IOException { 201 if (mEntry != null) { 202 jarEntry = mEntry; 203 mEntry = null; 204 jarEntry.setAttributes(null); 205 } else { 206 jarEntry = (JarEntry) super.getNextEntry(); 207 if (jarEntry == null) { 208 return null; 209 } 210 if (verifier != null) { 211 isMeta = Util.toASCIIUpperCase(jarEntry.getName()).startsWith( 212 JarFile.META_DIR); 213 if (isMeta) { 214 verStream = new ByteArrayOutputStream(); 215 } else { 216 verStream = verifier.initEntry(jarEntry.getName()); 217 } 218 } 219 } 220 eos = false; 221 return jarEntry; 222 } 223 224 @Override 225 protected ZipEntry createZipEntry(String name) { 226 JarEntry entry = new JarEntry(name); 227 if (manifest != null) { 228 entry.setAttributes(manifest.getAttributes(name)); 229 } 230 return entry; 231 } 232} 233