1 /*
2 * Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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
32 /**
33 * The <code>JarInputStream</code> class is used to read the contents of
34 * a JAR file from any input stream. It extends the class
35 * <code>java.util.zip.ZipInputStream</code> with support for reading
36 * an optional <code>Manifest</code> entry. The <code>Manifest</code>
37 * can be used to store meta-information about the JAR file and its entries.
38 *
39 * @author David Connelly
40 * @see Manifest
41 * @see java.util.zip.ZipInputStream
42 * @since 1.2
43 */
44 public
45 class JarInputStream extends ZipInputStream {
46 private Manifest man;
47 private JarEntry first;
48 private JarVerifier jv;
49 private ManifestEntryVerifier mev;
50
51
52 /**
53 * Creates a new <code>JarInputStream</code> and reads the optional
54 * manifest. If a manifest is present, also attempts to verify
55 * the signatures if the JarInputStream is signed.
56 * @param in the actual input stream
57 * @exception IOException if an I/O error has occurred
58 */
59 public JarInputStream(InputStream in) throws IOException {
60 this(in, true);
61 }
62
63 /**
64 * Creates a new <code>JarInputStream</code> and reads the optional
65 * manifest. If a manifest is present and verify is true, also attempts
66 * to verify the signatures if the JarInputStream is signed.
67 *
68 * @param in the actual input stream
69 * @param verify whether or not to verify the JarInputStream if
70 * it is signed.
71 * @exception IOException if an I/O error has occurred
72 */
73 public JarInputStream(InputStream in, boolean verify) throws IOException {
74 super(in);
75 JarEntry e = (JarEntry)super.getNextEntry();
76
77 if (e != null && e.getName().equalsIgnoreCase("META-INF/"))
78 e = (JarEntry)super.getNextEntry();
79
80 if (e != null && JarFile.MANIFEST_NAME.equalsIgnoreCase(e.getName())) {
81 man = new Manifest();
82 byte bytes[] = getBytes(new BufferedInputStream(this));
83 man.read(new ByteArrayInputStream(bytes));
84 //man.read(new BufferedInputStream(this));
85 closeEntry();
86 if (verify) {
87 jv = new JarVerifier(bytes);
88 mev = new ManifestEntryVerifier(man);
89 }
90 first = getNextJarEntry();
91 } else {
92 first = e;
93 }
94 }
95
96 private byte[] getBytes(InputStream is)
97 throws IOException
98 {
99 byte[] buffer = new byte[8192];
100 ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
101
102 int n;
103
104 baos.reset();
105 while ((n = is.read(buffer, 0, buffer.length)) != -1) {
106 baos.write(buffer, 0, n);
107 }
108 return baos.toByteArray();
109 }
110
111 /**
112 * Returns the <code>Manifest</code> for this JAR file, or
113 * <code>null</code> if none.
114 *
115 * @return the <code>Manifest</code> for this JAR file, or
116 * <code>null</code> if none.
117 */
118 public Manifest getManifest() {
119 return man;
120 }
121
122 /**
123 * Reads the next ZIP file entry and positions the stream at the
124 * beginning of the entry data. If verification has been enabled,
125 * any invalid signature detected while positioning the stream for
126 * the next entry will result in an exception.
127 * @exception ZipException if a ZIP file error has occurred
128 * @exception IOException if an I/O error has occurred
129 * @exception SecurityException if any of the jar file entries
130 * are incorrectly signed.
131 */
132 public ZipEntry getNextEntry() throws IOException {
133 JarEntry e;
134 if (first == null) {
135 e = (JarEntry)super.getNextEntry();
136 } else {
137 e = first;
138 first = null;
139 }
140 if (jv != null && e != null) {
141 // At this point, we might have parsed all the meta-inf
142 // entries and have nothing to verify. If we have
143 // nothing to verify, get rid of the JarVerifier object.
144 if (jv.nothingToVerify() == true) {
145 jv = null;
146 mev = null;
147 } else {
148 jv.beginEntry(e, mev);
149 }
150 }
151 return e;
152 }
153
154 /**
155 * Reads the next JAR file entry and positions the stream at the
156 * beginning of the entry data. If verification has been enabled,
157 * any invalid signature detected while positioning the stream for
158 * the next entry will result in an exception.
159 * @return the next JAR file entry, or null if there are no more entries
160 * @exception ZipException if a ZIP file error has occurred
161 * @exception IOException if an I/O error has occurred
162 * @exception SecurityException if any of the jar file entries
163 * are incorrectly signed.
164 */
165 public JarEntry getNextJarEntry() throws IOException {
166 return (JarEntry)getNextEntry();
167 }
168
169 /**
170 * Reads from the current JAR file entry into an array of bytes.
171 * If <code>len</code> is not zero, the method
172 * blocks until some input is available; otherwise, no
173 * bytes are read and <code>0</code> is returned.
174 * If verification has been enabled, any invalid signature
175 * on the current entry will be reported at some point before the
176 * end of the entry is reached.
177 * @param b the buffer into which the data is read
178 * @param off the start offset in the destination array <code>b</code>
179 * @param len the maximum number of bytes to read
180 * @return the actual number of bytes read, or -1 if the end of the
181 * entry is reached
182 * @exception NullPointerException If <code>b</code> is <code>null</code>.
183 * @exception IndexOutOfBoundsException If <code>off</code> is negative,
184 * <code>len</code> is negative, or <code>len</code> is greater than
185 * <code>b.length - off</code>
186 * @exception ZipException if a ZIP file error has occurred
187 * @exception IOException if an I/O error has occurred
188 * @exception SecurityException if any of the jar file entries
189 * are incorrectly signed.
190 */
191 public int read(byte[] b, int off, int len) throws IOException {
192 int n;
193 if (first == null) {
194 n = super.read(b, off, len);
195 } else {
196 n = -1;
197 }
198 if (jv != null) {
199 jv.update(n, b, off, len, mev);
200 }
201 return n;
202 }
203
204 /**
205 * Creates a new <code>JarEntry</code> (<code>ZipEntry</code>) for the
206 * specified JAR file entry name. The manifest attributes of
207 * the specified JAR file entry name will be copied to the new
208 * <CODE>JarEntry</CODE>.
209 *
210 * @param name the name of the JAR/ZIP file entry
211 * @return the <code>JarEntry</code> object just created
212 */
213 protected ZipEntry createZipEntry(String name) {
214 JarEntry e = new JarEntry(name);
215 if (man != null) {
216 e.attr = man.getAttributes(name);
217 }
218 return e;
219 }
220 }