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