1
2
3
4
5
6
7
8 package org.varienaja.util;
9
10 import java.io.EOFException;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.io.PushbackInputStream;
14 import java.util.zip.CRC32;
15 import java.util.zip.Inflater;
16 import java.util.zip.InflaterInputStream;
17 import java.util.zip.ZipEntry;
18 import java.util.zip.ZipException;
19
20
21
22
23
24
25
26
27
28
29
30
31 public
32 class TolerantZipInputStream extends InflaterInputStream implements ZipConstants {
33 private ZipEntry entry;
34 private int flag;
35 private CRC32 crc = new CRC32();
36 private long remaining;
37 private byte[] tmpbuf = new byte[512];
38
39 private static final int STORED = ZipEntry.STORED;
40 private static final int DEFLATED = ZipEntry.DEFLATED;
41
42 private boolean closed = false;
43
44
45 private boolean entryEOF = false;
46
47
48
49
50 private void ensureOpen() throws IOException {
51 if (closed) {
52 throw new IOException("Stream closed");
53 }
54 }
55
56
57
58
59
60 public TolerantZipInputStream(InputStream in) {
61 super(new PushbackInputStream(in, 512), new Inflater(true), 512);
62 if(in == null) {
63 throw new NullPointerException("in is null");
64 }
65 }
66
67
68
69
70
71
72
73
74 public ZipEntry getNextEntry() throws IOException {
75 ensureOpen();
76 if (entry != null) {
77 closeEntry();
78 }
79 crc.reset();
80 inf.reset();
81 if ((entry = readLOC()) == null) {
82 return null;
83 }
84 if (entry.getMethod() == STORED) {
85 remaining = entry.getSize();
86 }
87 entryEOF = false;
88 return entry;
89 }
90
91
92
93
94
95
96
97 public void closeEntry() throws IOException {
98 ensureOpen();
99 while (read(tmpbuf, 0, tmpbuf.length) != -1) ;
100 entryEOF = true;
101 }
102
103
104
105
106
107
108
109
110
111
112
113
114 public int available() throws IOException {
115 ensureOpen();
116 if (entryEOF) {
117 return 0;
118 } else {
119 return 1;
120 }
121 }
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140 public int read(byte[] b, int off, int len) throws IOException {
141 ensureOpen();
142 if (off < 0 || len < 0 || off > b.length - len) {
143 throw new IndexOutOfBoundsException();
144 } else if (len == 0) {
145 return 0;
146 }
147
148 if (entry == null) {
149 return -1;
150 }
151 switch (entry.getMethod()) {
152 case DEFLATED:
153 len = super.read(b, off, len);
154 if (len == -1) {
155 readEnd(entry);
156 entryEOF = true;
157 entry = null;
158 } else {
159 crc.update(b, off, len);
160 }
161 return len;
162 case STORED:
163 if (remaining <= 0) {
164 entryEOF = true;
165 entry = null;
166 return -1;
167 }
168 if (len > remaining) {
169 len = (int)remaining;
170 }
171 len = in.read(b, off, len);
172 if (len == -1) {
173 throw new ZipException("unexpected EOF");
174 }
175 crc.update(b, off, len);
176 remaining -= len;
177 return len;
178 default:
179 throw new ZipException("invalid compression method");
180 }
181 }
182
183
184
185
186
187
188
189
190
191 public long skip(long n) throws IOException {
192 if (n < 0) {
193 throw new IllegalArgumentException("negative skip length");
194 }
195 ensureOpen();
196 int max = (int)Math.min(n, Integer.MAX_VALUE);
197 int total = 0;
198 while (total < max) {
199 int len = max - total;
200 if (len > tmpbuf.length) {
201 len = tmpbuf.length;
202 }
203 len = read(tmpbuf, 0, len);
204 if (len == -1) {
205 entryEOF = true;
206 break;
207 }
208 total += len;
209 }
210 return total;
211 }
212
213
214
215
216
217
218 public void close() throws IOException {
219 if (!closed) {
220 super.close();
221 closed = true;
222 }
223 }
224
225 private byte[] b = new byte[256];
226
227
228
229
230 private ZipEntry readLOC() throws IOException {
231 try {
232 readFully(tmpbuf, 0, LOCHDR);
233 } catch (EOFException e) {
234 return null;
235 }
236 if (get32(tmpbuf, 0) != LOCSIG) {
237 return null;
238 }
239
240 int len = get16(tmpbuf, LOCNAM);
241 int blen = b.length;
242 if (len > blen) {
243 do
244 blen = blen * 2;
245 while (len > blen);
246 b = new byte[blen];
247 }
248 readFully(b, 0, len);
249 ZipEntry e = createZipEntry(getUTF8String(b, 0, len));
250
251 flag = get16(tmpbuf, LOCFLG);
252 if ((flag & 1) == 1) {
253 throw new ZipException("encrypted ZIP entry not supported");
254 }
255 e.setMethod(get16(tmpbuf, LOCHOW));
256 e.setTime(get32(tmpbuf, LOCTIM));
257 if ((flag & 8) == 8) {
258
259 if (e.getMethod() != DEFLATED) {
260 throw new ZipException(
261 "only DEFLATED entries can have EXT descriptor");
262 }
263 } else {
264 e.setCrc(get32(tmpbuf, LOCCRC));
265 e.setCompressedSize(get32(tmpbuf, LOCSIZ));
266 e.setSize(get32(tmpbuf, LOCLEN));
267 }
268 len = get16(tmpbuf, LOCEXT);
269 if (len > 0) {
270 byte[] bb = new byte[len];
271 readFully(bb, 0, len);
272 e.setExtra(bb);
273 }
274 return e;
275 }
276
277
278
279
280 private static String getUTF8String(byte[] b, int off, int len) {
281
282 int count = 0;
283 int max = off + len;
284 int i = off;
285 while (i < max) {
286 int c = b[i++] & 0xff;
287 switch (c >> 4) {
288 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
289
290 count++;
291 break;
292 case 12: case 13:
293
294 if ((int)(b[i++] & 0xc0) != 0x80) {
295 throw new IllegalArgumentException();
296 }
297 count++;
298 break;
299 case 14:
300
301 if (((int)(b[i++] & 0xc0) != 0x80) ||
302 ((int)(b[i++] & 0xc0) != 0x80)) {
303 throw new IllegalArgumentException();
304 }
305 count++;
306 break;
307 default:
308
309 count++;
310 }
311 }
312 if (i != max) {
313 throw new IllegalArgumentException();
314 }
315
316 char[] cs = new char[count];
317 i = 0;
318 while (off < max) {
319 int c = b[off++] & 0xff;
320 switch (c >> 4) {
321 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
322
323 cs[i++] = (char)c;
324 break;
325 case 12: case 13:
326
327 cs[i++] = (char)(((c & 0x1f) << 6) | (b[off++] & 0x3f));
328 break;
329 case 14:
330
331 int t = (b[off++] & 0x3f) << 6;
332 cs[i++] = (char)(((c & 0x0f) << 12) | t | (b[off++] & 0x3f));
333 break;
334 default:
335
336 cs[i++] = 'X';
337 }
338 }
339 return new String(cs, 0, count);
340 }
341
342
343
344
345
346
347
348
349 protected ZipEntry createZipEntry(String name) {
350 return new ZipEntry(name);
351 }
352
353
354
355
356 private void readEnd(ZipEntry e) throws IOException {
357 int n = inf.getRemaining();
358 if (n > 0) {
359 ((PushbackInputStream)in).unread(buf, len - n, n);
360 }
361 if ((flag & 8) == 8) {
362
363 readFully(tmpbuf, 0, EXTHDR);
364 long sig = get32(tmpbuf, 0);
365 if (sig != EXTSIG) {
366 e.setCrc(sig);
367 e.setCompressedSize(get32(tmpbuf, EXTSIZ - EXTCRC));
368 e.setSize(get32(tmpbuf, EXTLEN - EXTCRC));
369 ((PushbackInputStream)in).unread(
370 tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC);
371 } else {
372 e.setCrc(get32(tmpbuf, EXTCRC));
373 e.setCompressedSize(get32(tmpbuf, EXTSIZ));
374 e.setSize(get32(tmpbuf, EXTLEN));
375 }
376 }
377 if (e.getSize() != inf.getBytesWritten()) {
378 throw new ZipException(
379 "invalid entry size (expected " + e.getSize() +
380 " but got " + inf.getBytesWritten() + " bytes)");
381 }
382 if (e.getCompressedSize() != inf.getBytesRead()) {
383 throw new ZipException(
384 "invalid entry compressed size (expected " + e.getCompressedSize() +
385 " but got " + inf.getBytesRead() + " bytes)");
386 }
387 if (e.getCrc() != crc.getValue()) {
388 throw new ZipException(
389 "invalid entry CRC (expected 0x" + Long.toHexString(e.getCrc()) +
390 " but got 0x" + Long.toHexString(crc.getValue()) + ")");
391 }
392 }
393
394
395
396
397 private void readFully(byte[] b, int off, int len) throws IOException {
398 while (len > 0) {
399 int n = in.read(b, off, len);
400 if (n == -1) {
401 throw new EOFException();
402 }
403 off += n;
404 len -= n;
405 }
406 }
407
408
409
410
411
412 private static final int get16(byte b[], int off) {
413 return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8);
414 }
415
416
417
418
419
420 private static final long get32(byte b[], int off) {
421 return get16(b, off) | ((long)get16(b, off+2) << 16);
422 }
423 }