001: /*
002: * @(#)JarFile.java 1.46 06/10/11
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.util.jar;
029:
030: import java.io.*;
031: import java.util.*;
032: import java.util.zip.*;
033: import java.security.cert.Certificate;
034: import sun.security.util.ManifestEntryVerifier;
035:
036: /**
037: * The <code>JarFile</code> class is used to read the contents of a JAR file
038: * from any file that can be opened with <code>java.io.RandomAccessFile</code>.
039: * It extends the class <code>java.util.zip.ZipFile</code> with support
040: * for reading an optional <code>Manifest</code> entry. The
041: * <code>Manifest</code> can be used to specify meta-information about the
042: * JAR file and its entries.
043: *
044: * @author David Connelly
045: * @version 1.38, 02/02/00
046: * @see Manifest
047: * @see java.util.zip.ZipFile
048: * @see java.util.jar.JarEntry
049: * @since 1.2
050: */
051: public class JarFile extends ZipFile {
052: private Manifest man;
053: private JarEntry manEntry;
054: private boolean manLoaded;
055: private JarVerifier jv;
056: private boolean jvInitialized;
057: private boolean verify;
058:
059: /**
060: * The JAR manifest file name.
061: */
062: public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
063:
064: /**
065: * Creates a new <code>JarFile</code> to read from the specified
066: * file <code>name</code>. The <code>JarFile</code> will be verified if
067: * it is signed.
068: * @param name the name of the JAR file to be opened for reading
069: * @exception IOException if an I/O error has occurred
070: * @exception SecurityException if access to the file is denied
071: * by the SecurityManager
072: */
073: public JarFile(String name) throws IOException {
074: this (new File(name), true, ZipFile.OPEN_READ);
075: }
076:
077: /**
078: * Creates a new <code>JarFile</code> to read from the specified
079: * file <code>name</code>.
080: * @param name the name of the JAR file to be opened for reading
081: * @param verify whether or not to verify the JarFile if
082: * it is signed.
083: * @exception IOException if an I/O error has occurred
084: * @exception SecurityException if access to the file is denied
085: * by the SecurityManager
086: */
087: public JarFile(String name, boolean verify) throws IOException {
088: this (new File(name), verify, ZipFile.OPEN_READ);
089: }
090:
091: /**
092: * Creates a new <code>JarFile</code> to read from the specified
093: * <code>File</code> object. The <code>JarFile</code> will be verified if
094: * it is signed.
095: * @param file the JAR file to be opened for reading
096: * @exception IOException if an I/O error has occurred
097: * @exception SecurityException if access to the file is denied
098: * by the SecurityManager
099: */
100: public JarFile(File file) throws IOException {
101: this (file, true, ZipFile.OPEN_READ);
102: }
103:
104: /**
105: * Creates a new <code>JarFile</code> to read from the specified
106: * <code>File</code> object.
107: * @param file the JAR file to be opened for reading
108: * @param verify whether or not to verify the JarFile if
109: * it is signed.
110: * @exception IOException if an I/O error has occurred
111: * @exception SecurityException if access to the file is denied
112: * by the SecurityManager.
113: */
114: public JarFile(File file, boolean verify) throws IOException {
115: this (file, verify, ZipFile.OPEN_READ);
116: }
117:
118: /**
119: * Creates a new <code>JarFile</code> to read from the specified
120: * <code>File</code> object in the specified mode. The mode argument
121: * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
122: *
123: * @param file the JAR file to be opened for reading
124: * @param verify whether or not to verify the JarFile if
125: * it is signed.
126: * @param mode the mode in which the file is to be opened
127: * @exception IOException if an I/O error has occurred
128: * @exception IllegalArgumentException
129: * If the <tt>mode</tt> argument is invalid
130: * @exception SecurityException if access to the file is denied
131: * by the SecurityManager
132: */
133: public JarFile(File file, boolean verify, int mode)
134: throws IOException {
135: super (file, mode);
136: this .verify = verify;
137: }
138:
139: /**
140: * Returns the JAR file manifest, or <code>null</code> if none.
141: *
142: * @return the JAR file manifest, or <code>null</code> if none
143: */
144: public Manifest getManifest() throws IOException {
145: if (!manLoaded) {
146: // First look up manifest entry using standard name
147: manEntry = getJarEntry(MANIFEST_NAME);
148: if (manEntry == null) {
149: // If not found, then iterate through all the "META-INF/"
150: // entries to find a match.
151: String[] names = getMetaInfEntryNames();
152: if (names != null) {
153: for (int i = 0; i < names.length; i++) {
154: if (MANIFEST_NAME
155: .equals(names[i].toUpperCase())) {
156: manEntry = getJarEntry(names[i]);
157: break;
158: }
159: }
160: }
161: }
162: // If found then load the manifest
163: if (manEntry != null) {
164: if (verify) {
165: byte[] b = getBytes(manEntry);
166: man = new Manifest(new ByteArrayInputStream(b));
167: jv = new JarVerifier(man, b);
168: } else {
169: man = new Manifest(super .getInputStream(manEntry));
170: }
171: }
172: manLoaded = true;
173: }
174: return man;
175: }
176:
177: private native String[] getMetaInfEntryNames();
178:
179: /**
180: * Returns the <code>JarEntry</code> for the given entry name or
181: * <code>null</code> if not found.
182: *
183: * @param name the JAR file entry name
184: * @return the <code>JarEntry</code> for the given entry name or
185: * <code>null</code> if not found.
186: * @see java.util.jar.JarEntry
187: */
188: public JarEntry getJarEntry(String name) {
189: return (JarEntry) getEntry(name);
190: }
191:
192: /**
193: * Returns the <code>ZipEntry</code> for the given entry name or
194: * <code>null</code> if not found.
195: *
196: * @param name the JAR file entry name
197: * @return the <code>ZipEntry</code> for the given entry name or
198: * <code>null</code> if not found
199: * @see java.util.zip.ZipEntry
200: */
201: public ZipEntry getEntry(String name) {
202: ZipEntry ze = super .getEntry(name);
203: if (ze != null) {
204: return new JarFileEntry(ze);
205: }
206: return null;
207: }
208:
209: /**
210: * Returns an enumeration of the ZIP file entries.
211: */
212: public Enumeration entries() {
213: final Enumeration enum_ = super .entries();
214: return new Enumeration() {
215: public boolean hasMoreElements() {
216: return enum_.hasMoreElements();
217: }
218:
219: public Object nextElement() {
220: ZipEntry ze = (ZipEntry) enum_.nextElement();
221: return new JarFileEntry(ze);
222: }
223: };
224: }
225:
226: private class JarFileEntry extends JarEntry {
227: JarFileEntry(ZipEntry ze) {
228: super (ze);
229: }
230:
231: public Attributes getAttributes() throws IOException {
232: Manifest man = JarFile.this .getManifest();
233: if (man != null) {
234: return man.getAttributes(getName());
235: } else {
236: return null;
237: }
238: }
239:
240: public java.security.cert.Certificate[] getCertificates() {
241: if (certs == null && jv != null) {
242: Certificate[] cs = jv.getCerts(getName());
243: if (cs != null) {
244: certs = (Certificate[]) cs.clone();
245: }
246: }
247: return certs;
248: }
249: }
250:
251: /*
252: * Initializes the verifier object by reading all the manifest
253: * entries and passing them to the verifier.
254: */
255: private void initializeVerifier() {
256: ManifestEntryVerifier mev = null;
257:
258: // Verify "META-INF/" entries...
259: try {
260: String[] names = getMetaInfEntryNames();
261: if (names != null) {
262: for (int i = 0; i < names.length; i++) {
263: JarEntry e = getJarEntry(names[i]);
264: if (!e.isDirectory()) {
265: if (mev == null) {
266: mev = new ManifestEntryVerifier(man);
267: }
268: byte[] b = getBytes(e);
269: if (b != null && b.length > 0) {
270: jv.beginEntry(e, mev);
271: jv.update(b.length, b, 0, b.length, mev);
272: jv.update(-1, null, 0, 0, mev);
273: }
274: }
275: }
276: }
277: } catch (IOException ex) {
278: // if we had an error parsing any blocks, just
279: // treat the jar file as being unsigned
280: jv = null;
281: }
282:
283: // if after initializing the verifier we have nothing
284: // signed, we null it out.
285:
286: if (jv != null) {
287:
288: jv.doneWithMeta();
289: if (JarVerifier.debug != null) {
290: JarVerifier.debug.println("done with meta!");
291: }
292:
293: if (jv.nothingToVerify()) {
294: if (JarVerifier.debug != null) {
295: JarVerifier.debug.println("nothing to verify!");
296: }
297: jv = null;
298: }
299: }
300: }
301:
302: /*
303: * Reads all the bytes for a given entry. Used to process the
304: * the META-INF files.
305: */
306: private byte[] getBytes(ZipEntry ze) throws IOException {
307: byte[] b = new byte[(int) ze.getSize()];
308: DataInputStream is = new DataInputStream(super
309: .getInputStream(ze));
310: is.readFully(b, 0, b.length);
311: is.close();
312: return b;
313: }
314:
315: /**
316: * Returns an input stream for reading the contents of the specified
317: * ZIP file entry.
318: * @param ze the zip file entry
319: * @return an input stream for reading the contents of the specified
320: * ZIP file entry
321: * @exception ZipException if a ZIP format error has occurred
322: * @exception IOException if an I/O error has occurred
323: * @exception SecurityException if any of the JarFile entries are incorrectly signed.
324: */
325: public synchronized InputStream getInputStream(ZipEntry ze)
326: throws IOException {
327: if (!manLoaded) {
328: getManifest();
329: }
330: if (jv == null) {
331: return super .getInputStream(ze);
332: }
333: if (!jvInitialized) {
334: initializeVerifier();
335: jvInitialized = true;
336: // could be set to null after a call to
337: // initializeVerifier if we have nothing to
338: // verify
339: if (jv == null)
340: return super .getInputStream(ze);
341: }
342:
343: // wrap a verifier stream around the real stream
344: return new JarVerifier.VerifierStream(man, (JarEntry) ze, super
345: .getInputStream(ze), jv);
346: }
347: }
|