001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.util.jar;
019:
020: import java.io.ByteArrayInputStream;
021: import java.io.File;
022: import java.io.FilterInputStream;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.security.MessageDigest;
026: import java.util.Enumeration;
027: import java.util.zip.ZipEntry;
028: import java.util.zip.ZipFile;
029:
030: import org.apache.harmony.archive.util.Util;
031:
032: /**
033: * JarFile is used to read jar entries and their associated data from jar files.
034: *
035: * @see JarInputStream
036: * @see JarEntry
037: */
038: public class JarFile extends ZipFile {
039:
040: public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; //$NON-NLS-1$
041:
042: static final String META_DIR = "META-INF/"; //$NON-NLS-1$
043:
044: private Manifest manifest;
045:
046: private ZipEntry manifestEntry;
047:
048: JarVerifier verifier;
049:
050: static final class JarFileInputStream extends FilterInputStream {
051: private long count;
052:
053: private ZipEntry zipEntry;
054:
055: private JarVerifier verifier;
056:
057: private JarVerifier.VerifierEntry entry;
058:
059: private MessageDigest digest;
060:
061: JarFileInputStream(InputStream is, ZipEntry ze, JarVerifier ver) {
062: super (is);
063: if (ver != null) {
064: zipEntry = ze;
065: verifier = ver;
066: count = zipEntry.getSize();
067: entry = verifier.initEntry(ze.getName());
068: if (entry != null) {
069: digest = entry.digest;
070: }
071: }
072: }
073:
074: @Override
075: public int read() throws IOException {
076: int r = super .read();
077: if (entry != null) {
078: if (r != -1) {
079: digest.update((byte) r);
080: count--;
081: }
082: if (r == -1 || count <= 0) {
083: JarVerifier.VerifierEntry temp = entry;
084: entry = null;
085: verifier.verifySignatures(temp, zipEntry);
086: }
087: }
088: return r;
089: }
090:
091: @Override
092: public int read(byte[] buf, int off, int nbytes)
093: throws IOException {
094: int r = super .read(buf, off, nbytes);
095: if (entry != null) {
096: if (r != -1) {
097: int size = r;
098: if (count < size) {
099: size = (int) count;
100: }
101: digest.update(buf, off, size);
102: count -= r;
103: }
104: if (r == -1 || count <= 0) {
105: JarVerifier.VerifierEntry temp = entry;
106: entry = null;
107: verifier.verifySignatures(temp, zipEntry);
108: }
109: }
110: return r;
111: }
112:
113: @Override
114: public long skip(long nbytes) throws IOException {
115: long cnt = 0, rem = 0;
116: byte[] buf = new byte[4096];
117: while (cnt < nbytes) {
118: int x = read(buf, 0,
119: (rem = nbytes - cnt) > buf.length ? buf.length
120: : (int) rem);
121: if (x == -1) {
122: return cnt;
123: }
124: cnt += x;
125: }
126: return cnt;
127: }
128: }
129:
130: /**
131: * Create a new JarFile using the contents of file.
132: *
133: * @param file
134: * java.io.File
135: * @exception java.io.IOException
136: * If the file cannot be read.
137: */
138: public JarFile(File file) throws IOException {
139: this (file, true);
140: }
141:
142: /**
143: * Create a new JarFile using the contents of file.
144: *
145: * @param file
146: * java.io.File
147: * @param verify
148: * verify a signed jar file
149: * @exception java.io.IOException
150: * If the file cannot be read.
151: */
152: public JarFile(File file, boolean verify) throws IOException {
153: super (file);
154: if (verify) {
155: verifier = new JarVerifier(file.getPath());
156: }
157: readMetaEntries();
158: }
159:
160: /**
161: * Create a new JarFile using the contents of file.
162: *
163: * @param file
164: * java.io.File
165: * @param verify
166: * verify a signed jar file
167: * @param mode
168: * the mode to use, either OPEN_READ or OPEN_READ | OPEN_DELETE
169: * @exception java.io.IOException
170: * If the file cannot be read.
171: */
172: public JarFile(File file, boolean verify, int mode)
173: throws IOException {
174: super (file, mode);
175: if (verify) {
176: verifier = new JarVerifier(file.getPath());
177: }
178: readMetaEntries();
179: }
180:
181: /**
182: * Create a new JarFile from the contents of the file specified by filename.
183: *
184: * @param filename
185: * java.lang.String
186: * @exception java.io.IOException
187: * If fileName cannot be opened for reading.
188: */
189: public JarFile(String filename) throws IOException {
190: this (filename, true);
191:
192: }
193:
194: /**
195: * Create a new JarFile from the contents of the file specified by filename.
196: *
197: * @param filename
198: * java.lang.String
199: * @param verify
200: * verify a signed jar file
201: * @exception java.io.IOException
202: * If fileName cannot be opened for reading.
203: */
204: public JarFile(String filename, boolean verify) throws IOException {
205: super (filename);
206: if (verify) {
207: verifier = new JarVerifier(filename);
208: }
209: readMetaEntries();
210: }
211:
212: /**
213: * Return an enumeration containing the JarEntrys contained in this JarFile.
214: *
215: * @return java.util.Enumeration
216: * @exception java.lang.IllegalStateException
217: * If this JarFile has been closed.
218: */
219: @Override
220: public Enumeration<JarEntry> entries() {
221: class JarFileEnumerator implements Enumeration<JarEntry> {
222: Enumeration<? extends ZipEntry> ze;
223:
224: JarFile jf;
225:
226: JarFileEnumerator(Enumeration<? extends ZipEntry> zenum,
227: JarFile jf) {
228: ze = zenum;
229: this .jf = jf;
230: }
231:
232: public boolean hasMoreElements() {
233: return ze.hasMoreElements();
234: }
235:
236: public JarEntry nextElement() {
237: JarEntry je = new JarEntry(ze.nextElement());
238: je.parentJar = jf;
239: return je;
240: }
241: }
242: return new JarFileEnumerator(super .entries(), this );
243: }
244:
245: /**
246: * Return the JarEntry specified by name or null if no such entry exists.
247: *
248: * @param name
249: * the name of the entry in the jar file
250: * @return java.util.jar.JarEntry
251: */
252: public JarEntry getJarEntry(String name) {
253: return (JarEntry) getEntry(name);
254: }
255:
256: /**
257: * Returns the Manifest object associated with this JarFile or null if no
258: * manifest entry exists.
259: *
260: * @return java.util.jar.Manifest
261: */
262: public Manifest getManifest() throws IOException {
263: if (manifest != null) {
264: return manifest;
265: }
266: try {
267: ByteArrayInputStream is = (ByteArrayInputStream) super
268: .getInputStream(manifestEntry);
269: if (verifier != null) {
270: byte[] buf = new byte[is.available()];
271: is.mark(buf.length);
272: is.read(buf, 0, buf.length);
273: is.reset();
274: verifier.addMetaEntry(manifestEntry.getName(), buf);
275: }
276: try {
277: manifest = new Manifest(is, verifier != null);
278: } finally {
279: is.close();
280: }
281: manifestEntry = null;
282: } catch (NullPointerException e) {
283: manifestEntry = null;
284: }
285: return manifest;
286: }
287:
288: private void readMetaEntries() throws IOException {
289: ZipEntry[] metaEntries = getMetaEntriesImpl(null);
290: int dirLength = META_DIR.length();
291:
292: boolean signed = false;
293:
294: if (null != metaEntries) {
295: for (ZipEntry entry : metaEntries) {
296: String entryName = entry.getName();
297: if (manifestEntry == null
298: && manifest == null
299: && Util.ASCIIIgnoreCaseRegionMatches(entryName,
300: dirLength, MANIFEST_NAME, dirLength,
301: MANIFEST_NAME.length() - dirLength)) {
302: manifestEntry = entry;
303: if (verifier == null) {
304: break;
305: }
306: } else if (verifier != null
307: && entryName.length() > dirLength
308: && (Util.ASCIIIgnoreCaseRegionMatches(
309: entryName, entryName.length() - 3,
310: ".SF", 0, 3) //$NON-NLS-1$
311: || Util.ASCIIIgnoreCaseRegionMatches(
312: entryName,
313: entryName.length() - 4,
314: ".DSA", 0, 4) //$NON-NLS-1$
315: || Util.ASCIIIgnoreCaseRegionMatches(entryName,
316: entryName.length() - 4, ".RSA", 0, 4))) { //$NON-NLS-1$
317: signed = true;
318: InputStream is = super .getInputStream(entry);
319: byte[] buf = new byte[is.available()];
320: try {
321: is.read(buf, 0, buf.length);
322: } finally {
323: is.close();
324: }
325: verifier.addMetaEntry(entryName, buf);
326: }
327: }
328: }
329: if (!signed) {
330: verifier = null;
331: }
332: }
333:
334: /**
335: * Return an InputStream for reading the decompressed contents of ze.
336: *
337: * @param ze
338: * the ZipEntry to read from
339: * @return java.io.InputStream
340: * @exception java.io.IOException
341: * If an error occured while creating the InputStream.
342: */
343: @Override
344: public InputStream getInputStream(ZipEntry ze) throws IOException {
345: if (manifestEntry != null) {
346: getManifest();
347: }
348: if (verifier != null) {
349: verifier.setManifest(getManifest());
350: if (manifest != null) {
351: verifier.mainAttributesChunk = manifest
352: .getMainAttributesChunk();
353: }
354: if (verifier.readCertificates()) {
355: verifier.removeMetaEntries();
356: if (manifest != null) {
357: manifest.removeChunks();
358: }
359: if (!verifier.isSignedJar()) {
360: verifier = null;
361: }
362: }
363: }
364: InputStream in = super .getInputStream(ze);
365: if (in == null) {
366: return null;
367: }
368: return new JarFileInputStream(in, ze,
369: ze.getSize() >= 0 ? verifier : null);
370: }
371:
372: /**
373: * Return the JarEntry specified by name or null if no such entry exists
374: *
375: * @param name
376: * the name of the entry in the jar file
377: * @return java.util.jar.JarEntry
378: */
379: @Override
380: public ZipEntry getEntry(String name) {
381: ZipEntry ze = super .getEntry(name);
382: if (ze == null) {
383: return ze;
384: }
385: JarEntry je = new JarEntry(ze);
386: je.parentJar = this ;
387: return je;
388: }
389:
390: private native ZipEntry[] getMetaEntriesImpl(byte[] buf);
391:
392: }
|