1 /*
2 * Copyright 1996-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.zip;
27
28 import java.io.InputStream;
29 import java.io.IOException;
30 import java.io.EOFException;
31 import java.io.PushbackInputStream;
32
33 /**
34 * This class implements an input stream filter for reading files in the
35 * ZIP file format. Includes support for both compressed and uncompressed
36 * entries.
37 *
38 * @author David Connelly
39 */
40 public
41 class ZipInputStream extends InflaterInputStream implements ZipConstants {
42 private ZipEntry entry;
43 private int flag;
44 private CRC32 crc = new CRC32();
45 private long remaining;
46 private byte[] tmpbuf = new byte[512];
47
48 private static final int STORED = ZipEntry.STORED;
49 private static final int DEFLATED = ZipEntry.DEFLATED;
50
51 private boolean closed = false;
52 // this flag is set to true after EOF has reached for
53 // one entry
54 private boolean entryEOF = false;
55
56 /**
57 * Check to make sure that this stream has not been closed
58 */
59 private void ensureOpen() throws IOException {
60 if (closed) {
61 throw new IOException("Stream closed");
62 }
63 }
64
65 /**
66 * Creates a new ZIP input stream.
67 * @param in the actual input stream
68 */
69 public ZipInputStream(InputStream in) {
70 super(new PushbackInputStream(in, 512), new Inflater(true), 512);
71 usesDefaultInflater = true;
72 if(in == null) {
73 throw new NullPointerException("in is null");
74 }
75 }
76
77 /**
78 * Reads the next ZIP file entry and positions the stream at the
79 * beginning of the entry data.
80 * @return the next ZIP file entry, or null if there are no more entries
81 * @exception ZipException if a ZIP file error has occurred
82 * @exception IOException if an I/O error has occurred
83 */
84 public ZipEntry getNextEntry() throws IOException {
85 ensureOpen();
86 if (entry != null) {
87 closeEntry();
88 }
89 crc.reset();
90 inf.reset();
91 if ((entry = readLOC()) == null) {
92 return null;
93 }
94 if (entry.method == STORED) {
95 remaining = entry.size;
96 }
97 entryEOF = false;
98 return entry;
99 }
100
101 /**
102 * Closes the current ZIP entry and positions the stream for reading the
103 * next entry.
104 * @exception ZipException if a ZIP file error has occurred
105 * @exception IOException if an I/O error has occurred
106 */
107 public void closeEntry() throws IOException {
108 ensureOpen();
109 while (read(tmpbuf, 0, tmpbuf.length) != -1) ;
110 entryEOF = true;
111 }
112
113 /**
114 * Returns 0 after EOF has reached for the current entry data,
115 * otherwise always return 1.
116 * <p>
117 * Programs should not count on this method to return the actual number
118 * of bytes that could be read without blocking.
119 *
120 * @return 1 before EOF and 0 after EOF has reached for current entry.
121 * @exception IOException if an I/O error occurs.
122 *
123 */
124 public int available() throws IOException {
125 ensureOpen();
126 if (entryEOF) {
127 return 0;
128 } else {
129 return 1;
130 }
131 }
132
133 /**
134 * Reads from the current ZIP entry into an array of bytes.
135 * If <code>len</code> is not zero, the method
136 * blocks until some input is available; otherwise, no
137 * bytes are read and <code>0</code> is returned.
138 * @param b the buffer into which the data is read
139 * @param off the start offset in the destination array <code>b</code>
140 * @param len the maximum number of bytes read
141 * @return the actual number of bytes read, or -1 if the end of the
142 * entry is reached
143 * @exception NullPointerException If <code>b</code> is <code>null</code>.
144 * @exception IndexOutOfBoundsException If <code>off</code> is negative,
145 * <code>len</code> is negative, or <code>len</code> is greater than
146 * <code>b.length - off</code>
147 * @exception ZipException if a ZIP file error has occurred
148 * @exception IOException if an I/O error has occurred
149 */
150 public int read(byte[] b, int off, int len) throws IOException {
151 ensureOpen();
152 if (off < 0 || len < 0 || off > b.length - len) {
153 throw new IndexOutOfBoundsException();
154 } else if (len == 0) {
155 return 0;
156 }
157
158 if (entry == null) {
159 return -1;
160 }
161 switch (entry.method) {
162 case DEFLATED:
163 len = super.read(b, off, len);
164 if (len == -1) {
165 readEnd(entry);
166 entryEOF = true;
167 entry = null;
168 } else {
169 crc.update(b, off, len);
170 }
171 return len;
172 case STORED:
173 if (remaining <= 0) {
174 entryEOF = true;
175 entry = null;
176 return -1;
177 }
178 if (len > remaining) {
179 len = (int)remaining;
180 }
181 len = in.read(b, off, len);
182 if (len == -1) {
183 throw new ZipException("unexpected EOF");
184 }
185 crc.update(b, off, len);
186 remaining -= len;
187 if (remaining == 0 && entry.crc != crc.getValue()) {
188 throw new ZipException(
189 "invalid entry CRC (expected 0x" + Long.toHexString(entry.crc) +
190 " but got 0x" + Long.toHexString(crc.getValue()) + ")");
191 }
192 return len;
193 default:
194 throw new ZipException("invalid compression method");
195 }
196 }
197
198 /**
199 * Skips specified number of bytes in the current ZIP entry.
200 * @param n the number of bytes to skip
201 * @return the actual number of bytes skipped
202 * @exception ZipException if a ZIP file error has occurred
203 * @exception IOException if an I/O error has occurred
204 * @exception IllegalArgumentException if n < 0
205 */
206 public long skip(long n) throws IOException {
207 if (n < 0) {
208 throw new IllegalArgumentException("negative skip length");
209 }
210 ensureOpen();
211 int max = (int)Math.min(n, Integer.MAX_VALUE);
212 int total = 0;
213 while (total < max) {
214 int len = max - total;
215 if (len > tmpbuf.length) {
216 len = tmpbuf.length;
217 }
218 len = read(tmpbuf, 0, len);
219 if (len == -1) {
220 entryEOF = true;
221 break;
222 }
223 total += len;
224 }
225 return total;
226 }
227
228 /**
229 * Closes this input stream and releases any system resources associated
230 * with the stream.
231 * @exception IOException if an I/O error has occurred
232 */
233 public void close() throws IOException {
234 if (!closed) {
235 super.close();
236 closed = true;
237 }
238 }
239
240 private byte[] b = new byte[256];
241
242 /*
243 * Reads local file (LOC) header for next entry.
244 */
245 private ZipEntry readLOC() throws IOException {
246 try {
247 readFully(tmpbuf, 0, LOCHDR);
248 } catch (EOFException e) {
249 return null;
250 }
251 if (get32(tmpbuf, 0) != LOCSIG) {
252 return null;
253 }
254 // get the entry name and create the ZipEntry first
255 int len = get16(tmpbuf, LOCNAM);
256 int blen = b.length;
257 if (len > blen) {
258 do
259 blen = blen * 2;
260 while (len > blen);
261 b = new byte[blen];
262 }
263 readFully(b, 0, len);
264 ZipEntry e = createZipEntry(getUTF8String(b, 0, len));
265 // now get the remaining fields for the entry
266 flag = get16(tmpbuf, LOCFLG);
267 if ((flag & 1) == 1) {
268 throw new ZipException("encrypted ZIP entry not supported");
269 }
270 e.method = get16(tmpbuf, LOCHOW);
271 e.time = get32(tmpbuf, LOCTIM);
272 if ((flag & 8) == 8) {
273 /* "Data Descriptor" present */
274 if (e.method != DEFLATED) {
275 throw new ZipException(
276 "only DEFLATED entries can have EXT descriptor");
277 }
278 } else {
279 e.crc = get32(tmpbuf, LOCCRC);
280 e.csize = get32(tmpbuf, LOCSIZ);
281 e.size = get32(tmpbuf, LOCLEN);
282 }
283 len = get16(tmpbuf, LOCEXT);
284 if (len > 0) {
285 byte[] bb = new byte[len];
286 readFully(bb, 0, len);
287 e.setExtra(bb);
288 }
289 return e;
290 }
291
292 /*
293 * Fetches a UTF8-encoded String from the specified byte array.
294 */
295 private static String getUTF8String(byte[] b, int off, int len) {
296 // First, count the number of characters in the sequence
297 int count = 0;
298 int max = off + len;
299 int i = off;
300 while (i < max) {
301 int c = b[i++] & 0xff;
302 switch (c >> 4) {
303 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
304 // 0xxxxxxx
305 count++;
306 break;
307 case 12: case 13:
308 // 110xxxxx 10xxxxxx
309 if ((b[i++] & 0xc0) != 0x80) {
310 throw new IllegalArgumentException();
311 }
312 count++;
313 break;
314 case 14:
315 // 1110xxxx 10xxxxxx 10xxxxxx
316 if (((b[i++] & 0xc0) != 0x80) ||
317 ((b[i++] & 0xc0) != 0x80)) {
318 throw new IllegalArgumentException();
319 }
320 count++;
321 break;
322 default:
323 // 10xxxxxx, 1111xxxx
324 throw new IllegalArgumentException();
325 }
326 }
327 if (i != max) {
328 throw new IllegalArgumentException();
329 }
330 // Now decode the characters...
331 char[] cs = new char[count];
332 i = 0;
333 while (off < max) {
334 int c = b[off++] & 0xff;
335 switch (c >> 4) {
336 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
337 // 0xxxxxxx
338 cs[i++] = (char)c;
339 break;
340 case 12: case 13:
341 // 110xxxxx 10xxxxxx
342 cs[i++] = (char)(((c & 0x1f) << 6) | (b[off++] & 0x3f));
343 break;
344 case 14:
345 // 1110xxxx 10xxxxxx 10xxxxxx
346 int t = (b[off++] & 0x3f) << 6;
347 cs[i++] = (char)(((c & 0x0f) << 12) | t | (b[off++] & 0x3f));
348 break;
349 default:
350 // 10xxxxxx, 1111xxxx
351 throw new IllegalArgumentException();
352 }
353 }
354 return new String(cs, 0, count);
355 }
356
357 /**
358 * Creates a new <code>ZipEntry</code> object for the specified
359 * entry name.
360 *
361 * @param name the ZIP file entry name
362 * @return the ZipEntry just created
363 */
364 protected ZipEntry createZipEntry(String name) {
365 return new ZipEntry(name);
366 }
367
368 /*
369 * Reads end of deflated entry as well as EXT descriptor if present.
370 */
371 private void readEnd(ZipEntry e) throws IOException {
372 int n = inf.getRemaining();
373 if (n > 0) {
374 ((PushbackInputStream)in).unread(buf, len - n, n);
375 }
376 if ((flag & 8) == 8) {
377 /* "Data Descriptor" present */
378 readFully(tmpbuf, 0, EXTHDR);
379 long sig = get32(tmpbuf, 0);
380 if (sig != EXTSIG) { // no EXTSIG present
381 e.crc = sig;
382 e.csize = get32(tmpbuf, EXTSIZ - EXTCRC);
383 e.size = get32(tmpbuf, EXTLEN - EXTCRC);
384 ((PushbackInputStream)in).unread(
385 tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC);
386 } else {
387 e.crc = get32(tmpbuf, EXTCRC);
388 e.csize = get32(tmpbuf, EXTSIZ);
389 e.size = get32(tmpbuf, EXTLEN);
390 }
391 }
392 if (e.size != inf.getBytesWritten()) {
393 throw new ZipException(
394 "invalid entry size (expected " + e.size +
395 " but got " + inf.getBytesWritten() + " bytes)");
396 }
397 if (e.csize != inf.getBytesRead()) {
398 throw new ZipException(
399 "invalid entry compressed size (expected " + e.csize +
400 " but got " + inf.getBytesRead() + " bytes)");
401 }
402 if (e.crc != crc.getValue()) {
403 throw new ZipException(
404 "invalid entry CRC (expected 0x" + Long.toHexString(e.crc) +
405 " but got 0x" + Long.toHexString(crc.getValue()) + ")");
406 }
407 }
408
409 /*
410 * Reads bytes, blocking until all bytes are read.
411 */
412 private void readFully(byte[] b, int off, int len) throws IOException {
413 while (len > 0) {
414 int n = in.read(b, off, len);
415 if (n == -1) {
416 throw new EOFException();
417 }
418 off += n;
419 len -= n;
420 }
421 }
422
423 /*
424 * Fetches unsigned 16-bit value from byte array at specified offset.
425 * The bytes are assumed to be in Intel (little-endian) byte order.
426 */
427 private static final int get16(byte b[], int off) {
428 return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8);
429 }
430
431 /*
432 * Fetches unsigned 32-bit value from byte array at specified offset.
433 * The bytes are assumed to be in Intel (little-endian) byte order.
434 */
435 private static final long get32(byte b[], int off) {
436 return get16(b, off) | ((long)get16(b, off+2) << 16);
437 }
438 }