1:
38:
39:
40: package ;
41:
42: import ;
43:
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54:
55:
66: public class ZipFile implements ZipConstants
67: {
68:
69:
72: public static final int OPEN_READ = 0x1;
73:
74:
77: public static final int OPEN_DELETE = 0x4;
78:
79:
80: private final String name;
81:
82:
83: private final RandomAccessFile raf;
84:
85:
86: private HashMap entries;
87:
88: private boolean closed = false;
89:
90:
96: public ZipFile(String name) throws ZipException, IOException
97: {
98: this.raf = new RandomAccessFile(name, "r");
99: this.name = name;
100: checkZipFile();
101: }
102:
103:
109: public ZipFile(File file) throws ZipException, IOException
110: {
111: this.raf = new RandomAccessFile(file, "r");
112: this.name = file.getPath();
113: checkZipFile();
114: }
115:
116:
132: public ZipFile(File file, int mode) throws ZipException, IOException
133: {
134: if (mode != OPEN_READ && mode != (OPEN_READ | OPEN_DELETE))
135: throw new IllegalArgumentException("invalid mode");
136: if ((mode & OPEN_DELETE) != 0)
137: file.deleteOnExit();
138: this.raf = new RandomAccessFile(file, "r");
139: this.name = file.getPath();
140: checkZipFile();
141: }
142:
143: private void checkZipFile() throws IOException, ZipException
144: {
145: byte[] magicBuf = new byte[4];
146: raf.read(magicBuf);
147:
148: if (readLeInt(magicBuf, 0) != LOCSIG)
149: {
150: raf.close();
151: throw new ZipException("Not a valid zip file");
152: }
153: }
154:
155:
158: private void checkClosed()
159: {
160: if (closed)
161: throw new IllegalStateException("ZipFile has closed: " + name);
162: }
163:
164:
175: private int readLeShort(DataInput di, byte[] b) throws IOException
176: {
177: di.readFully(b, 0, 2);
178: return (b[0] & 0xff) | (b[1] & 0xff) << 8;
179: }
180:
181:
192: private int readLeInt(DataInput di, byte[] b) throws IOException
193: {
194: di.readFully(b, 0, 4);
195: return ((b[0] & 0xff) | (b[1] & 0xff) << 8)
196: | ((b[2] & 0xff) | (b[3] & 0xff) << 8) << 16;
197: }
198:
199:
207: private int readLeShort(byte[] b, int off)
208: {
209: return (b[off] & 0xff) | (b[off+1] & 0xff) << 8;
210: }
211:
212:
220: private int readLeInt(byte[] b, int off)
221: {
222: return ((b[off] & 0xff) | (b[off+1] & 0xff) << 8)
223: | ((b[off+2] & 0xff) | (b[off+3] & 0xff) << 8) << 16;
224: }
225:
226:
227:
235: private void readEntries() throws ZipException, IOException
236: {
237:
242: long pos = raf.length() - ENDHDR;
243: byte[] ebs = new byte[CENHDR];
244:
245: do
246: {
247: if (pos < 0)
248: throw new ZipException
249: ("central directory not found, probably not a zip file: " + name);
250: raf.seek(pos--);
251: }
252: while (readLeInt(raf, ebs) != ENDSIG);
253:
254: if (raf.skipBytes(ENDTOT - ENDNRD) != ENDTOT - ENDNRD)
255: throw new EOFException(name);
256: int count = readLeShort(raf, ebs);
257: if (raf.skipBytes(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ)
258: throw new EOFException(name);
259: int centralOffset = readLeInt(raf, ebs);
260:
261: entries = new HashMap(count+count/2);
262: raf.seek(centralOffset);
263:
264: byte[] buffer = new byte[16];
265: for (int i = 0; i < count; i++)
266: {
267: raf.readFully(ebs);
268: if (readLeInt(ebs, 0) != CENSIG)
269: throw new ZipException("Wrong Central Directory signature: " + name);
270:
271: int method = readLeShort(ebs, CENHOW);
272: int dostime = readLeInt(ebs, CENTIM);
273: int crc = readLeInt(ebs, CENCRC);
274: int csize = readLeInt(ebs, CENSIZ);
275: int size = readLeInt(ebs, CENLEN);
276: int nameLen = readLeShort(ebs, CENNAM);
277: int extraLen = readLeShort(ebs, CENEXT);
278: int commentLen = readLeShort(ebs, CENCOM);
279:
280: int offset = readLeInt(ebs, CENOFF);
281:
282: int needBuffer = Math.max(nameLen, commentLen);
283: if (buffer.length < needBuffer)
284: buffer = new byte[needBuffer];
285:
286: raf.readFully(buffer, 0, nameLen);
287: String name = new String(buffer, 0, 0, nameLen);
288:
289: ZipEntry entry = new ZipEntry(name);
290: entry.setMethod(method);
291: entry.setCrc(crc & 0xffffffffL);
292: entry.setSize(size & 0xffffffffL);
293: entry.setCompressedSize(csize & 0xffffffffL);
294: entry.setDOSTime(dostime);
295: if (extraLen > 0)
296: {
297: byte[] extra = new byte[extraLen];
298: raf.readFully(extra);
299: entry.setExtra(extra);
300: }
301: if (commentLen > 0)
302: {
303: raf.readFully(buffer, 0, commentLen);
304: entry.setComment(new String(buffer, 0, commentLen));
305: }
306: entry.offset = offset;
307: entries.put(name, entry);
308: }
309: }
310:
311:
318: public void close() throws IOException
319: {
320: synchronized (raf)
321: {
322: closed = true;
323: entries = null;
324: raf.close();
325: }
326: }
327:
328:
332: protected void finalize() throws IOException
333: {
334: if (!closed && raf != null) close();
335: }
336:
337:
342: public Enumeration entries()
343: {
344: checkClosed();
345:
346: try
347: {
348: return new ZipEntryEnumeration(getEntries().values().iterator());
349: }
350: catch (IOException ioe)
351: {
352: return EmptyEnumeration.getInstance();
353: }
354: }
355:
356:
362: private HashMap getEntries() throws IOException
363: {
364: synchronized(raf)
365: {
366: checkClosed();
367:
368: if (entries == null)
369: readEntries();
370:
371: return entries;
372: }
373: }
374:
375:
384: public ZipEntry getEntry(String name)
385: {
386: checkClosed();
387:
388: try
389: {
390: HashMap entries = getEntries();
391: ZipEntry entry = (ZipEntry) entries.get(name);
392:
393: if (entry == null && !name.endsWith("/"))
394: entry = (ZipEntry) entries.get(name + '/');
395: return entry != null ? new ZipEntry(entry, name) : null;
396: }
397: catch (IOException ioe)
398: {
399: return null;
400: }
401: }
402:
403:
404:
405: private byte[] locBuf = new byte[LOCHDR];
406:
407:
418: private long checkLocalHeader(ZipEntry entry) throws IOException
419: {
420: synchronized (raf)
421: {
422: raf.seek(entry.offset);
423: raf.readFully(locBuf);
424:
425: if (readLeInt(locBuf, 0) != LOCSIG)
426: throw new ZipException("Wrong Local header signature: " + name);
427:
428: if (entry.getMethod() != readLeShort(locBuf, LOCHOW))
429: throw new ZipException("Compression method mismatch: " + name);
430:
431: if (entry.getName().length() != readLeShort(locBuf, LOCNAM))
432: throw new ZipException("file name length mismatch: " + name);
433:
434: int extraLen = entry.getName().length() + readLeShort(locBuf, LOCEXT);
435: return entry.offset + LOCHDR + extraLen;
436: }
437: }
438:
439:
461: public InputStream getInputStream(ZipEntry entry) throws IOException
462: {
463: checkClosed();
464:
465: HashMap entries = getEntries();
466: String name = entry.getName();
467: ZipEntry zipEntry = (ZipEntry) entries.get(name);
468: if (zipEntry == null)
469: return null;
470:
471: long start = checkLocalHeader(zipEntry);
472: int method = zipEntry.getMethod();
473: InputStream is = new BufferedInputStream(new PartialInputStream
474: (raf, start, zipEntry.getCompressedSize()));
475: switch (method)
476: {
477: case ZipOutputStream.STORED:
478: return is;
479: case ZipOutputStream.DEFLATED:
480: return new InflaterInputStream(is, new Inflater(true));
481: default:
482: throw new ZipException("Unknown compression method " + method);
483: }
484: }
485:
486:
489: public String getName()
490: {
491: return name;
492: }
493:
494:
499: public int size()
500: {
501: checkClosed();
502:
503: try
504: {
505: return getEntries().size();
506: }
507: catch (IOException ioe)
508: {
509: return 0;
510: }
511: }
512:
513: private static class ZipEntryEnumeration implements Enumeration
514: {
515: private final Iterator elements;
516:
517: public ZipEntryEnumeration(Iterator elements)
518: {
519: this.elements = elements;
520: }
521:
522: public boolean hasMoreElements()
523: {
524: return elements.hasNext();
525: }
526:
527: public Object nextElement()
528: {
529:
532: return ((ZipEntry)elements.next()).clone();
533: }
534: }
535:
536: private static class PartialInputStream extends InputStream
537: {
538: private final RandomAccessFile raf;
539: long filepos, end;
540:
541: public PartialInputStream(RandomAccessFile raf, long start, long len)
542: {
543: this.raf = raf;
544: filepos = start;
545: end = start + len;
546: }
547:
548: public int available()
549: {
550: long amount = end - filepos;
551: if (amount > Integer.MAX_VALUE)
552: return Integer.MAX_VALUE;
553: return (int) amount;
554: }
555:
556: public int read() throws IOException
557: {
558: if (filepos == end)
559: return -1;
560: synchronized (raf)
561: {
562: raf.seek(filepos++);
563: return raf.read();
564: }
565: }
566:
567: public int read(byte[] b, int off, int len) throws IOException
568: {
569: if (len > end - filepos)
570: {
571: len = (int) (end - filepos);
572: if (len == 0)
573: return -1;
574: }
575: synchronized (raf)
576: {
577: raf.seek(filepos);
578: int count = raf.read(b, off, len);
579: if (count > 0)
580: filepos += len;
581: return count;
582: }
583: }
584:
585: public long skip(long amount)
586: {
587: if (amount < 0)
588: throw new IllegalArgumentException();
589: if (amount > end - filepos)
590: amount = end - filepos;
591: filepos += amount;
592: return amount;
593: }
594: }
595: }