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:
82: static final int ENDNRD = 4;
83:
84:
85: private final String name;
86:
87:
88: private final RandomAccessFile raf;
89:
90:
91: private LinkedHashMap entries;
92:
93: private boolean closed = false;
94:
95:
96:
106: private RandomAccessFile openFile(String name,
107: File file)
108: throws ZipException, IOException
109: {
110: try
111: {
112: return
113: (name != null)
114: ? new RandomAccessFile(name, "r")
115: : new RandomAccessFile(file, "r");
116: }
117: catch (FileNotFoundException f)
118: {
119: ZipException ze = new ZipException(f.getMessage());
120: ze.initCause(f);
121: throw ze;
122: }
123: }
124:
125:
126:
132: public ZipFile(String name) throws ZipException, IOException
133: {
134: this.raf = openFile(name,null);
135: this.name = name;
136: checkZipFile();
137: }
138:
139:
145: public ZipFile(File file) throws ZipException, IOException
146: {
147: this.raf = openFile(null,file);
148: this.name = file.getPath();
149: checkZipFile();
150: }
151:
152:
168: public ZipFile(File file, int mode) throws ZipException, IOException
169: {
170: if (mode != OPEN_READ && mode != (OPEN_READ | OPEN_DELETE))
171: throw new IllegalArgumentException("invalid mode");
172: if ((mode & OPEN_DELETE) != 0)
173: file.deleteOnExit();
174: this.raf = openFile(null,file);
175: this.name = file.getPath();
176: checkZipFile();
177: }
178:
179: private void checkZipFile() throws ZipException
180: {
181: boolean valid = false;
182:
183: try
184: {
185: byte[] buf = new byte[4];
186: raf.readFully(buf);
187: int sig = buf[0] & 0xFF
188: | ((buf[1] & 0xFF) << 8)
189: | ((buf[2] & 0xFF) << 16)
190: | ((buf[3] & 0xFF) << 24);
191: valid = sig == LOCSIG;
192: }
193: catch (IOException _)
194: {
195: }
196:
197: if (!valid)
198: {
199: try
200: {
201: raf.close();
202: }
203: catch (IOException _)
204: {
205: }
206: throw new ZipException("Not a valid zip file");
207: }
208: }
209:
210:
213: private void checkClosed()
214: {
215: if (closed)
216: throw new IllegalStateException("ZipFile has closed: " + name);
217: }
218:
219:
227: private void readEntries() throws ZipException, IOException
228: {
229:
234: PartialInputStream inp = new PartialInputStream(raf, 4096);
235: long pos = raf.length() - ENDHDR;
236: long top = Math.max(0, pos - 65536);
237: do
238: {
239: if (pos < top)
240: throw new ZipException
241: ("central directory not found, probably not a zip file: " + name);
242: inp.seek(pos--);
243: }
244: while (inp.readLeInt() != ENDSIG);
245:
246: if (inp.skip(ENDTOT - ENDNRD) != ENDTOT - ENDNRD)
247: throw new EOFException(name);
248: int count = inp.readLeShort();
249: if (inp.skip(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ)
250: throw new EOFException(name);
251: int centralOffset = inp.readLeInt();
252:
253: entries = new LinkedHashMap(count+count/2);
254: inp.seek(centralOffset);
255:
256: for (int i = 0; i < count; i++)
257: {
258: if (inp.readLeInt() != CENSIG)
259: throw new ZipException("Wrong Central Directory signature: " + name);
260:
261: inp.skip(6);
262: int method = inp.readLeShort();
263: int dostime = inp.readLeInt();
264: int crc = inp.readLeInt();
265: int csize = inp.readLeInt();
266: int size = inp.readLeInt();
267: int nameLen = inp.readLeShort();
268: int extraLen = inp.readLeShort();
269: int commentLen = inp.readLeShort();
270: inp.skip(8);
271: int offset = inp.readLeInt();
272: String name = inp.readString(nameLen);
273:
274: ZipEntry entry = new ZipEntry(name);
275: entry.setMethod(method);
276: entry.setCrc(crc & 0xffffffffL);
277: entry.setSize(size & 0xffffffffL);
278: entry.setCompressedSize(csize & 0xffffffffL);
279: entry.setDOSTime(dostime);
280: if (extraLen > 0)
281: {
282: byte[] extra = new byte[extraLen];
283: inp.readFully(extra);
284: entry.setExtra(extra);
285: }
286: if (commentLen > 0)
287: {
288: entry.setComment(inp.readString(commentLen));
289: }
290: entry.offset = offset;
291: entries.put(name, entry);
292: }
293: }
294:
295:
302: public void close() throws IOException
303: {
304: RandomAccessFile raf = this.raf;
305: if (raf == null)
306: return;
307:
308: synchronized (raf)
309: {
310: closed = true;
311: entries = null;
312: raf.close();
313: }
314: }
315:
316:
320: protected void finalize() throws IOException
321: {
322: if (!closed && raf != null) close();
323: }
324:
325:
330: public Enumeration entries()
331: {
332: checkClosed();
333:
334: try
335: {
336: return new ZipEntryEnumeration(getEntries().values().iterator());
337: }
338: catch (IOException ioe)
339: {
340: return EmptyEnumeration.getInstance();
341: }
342: }
343:
344:
350: private LinkedHashMap getEntries() throws IOException
351: {
352: synchronized(raf)
353: {
354: checkClosed();
355:
356: if (entries == null)
357: readEntries();
358:
359: return entries;
360: }
361: }
362:
363:
372: public ZipEntry getEntry(String name)
373: {
374: checkClosed();
375:
376: try
377: {
378: LinkedHashMap entries = getEntries();
379: ZipEntry entry = (ZipEntry) entries.get(name);
380:
381: if (entry == null && !name.endsWith("/"))
382: entry = (ZipEntry) entries.get(name + '/');
383: return entry != null ? new ZipEntry(entry, name) : null;
384: }
385: catch (IOException ioe)
386: {
387: return null;
388: }
389: }
390:
391:
413: public InputStream getInputStream(ZipEntry entry) throws IOException
414: {
415: checkClosed();
416:
417: LinkedHashMap entries = getEntries();
418: String name = entry.getName();
419: ZipEntry zipEntry = (ZipEntry) entries.get(name);
420: if (zipEntry == null)
421: return null;
422:
423: PartialInputStream inp = new PartialInputStream(raf, 1024);
424: inp.seek(zipEntry.offset);
425:
426: if (inp.readLeInt() != LOCSIG)
427: throw new ZipException("Wrong Local header signature: " + name);
428:
429: inp.skip(4);
430:
431: if (zipEntry.getMethod() != inp.readLeShort())
432: throw new ZipException("Compression method mismatch: " + name);
433:
434: inp.skip(16);
435:
436: int nameLen = inp.readLeShort();
437: int extraLen = inp.readLeShort();
438: inp.skip(nameLen + extraLen);
439:
440: inp.setLength(zipEntry.getCompressedSize());
441:
442: int method = zipEntry.getMethod();
443: switch (method)
444: {
445: case ZipOutputStream.STORED:
446: return inp;
447: case ZipOutputStream.DEFLATED:
448: inp.addDummyByte();
449: final Inflater inf = new Inflater(true);
450: final int sz = (int) entry.getSize();
451: return new InflaterInputStream(inp, inf)
452: {
453: public int available() throws IOException
454: {
455: if (sz == -1)
456: return super.available();
457: if (super.available() != 0)
458: return sz - inf.getTotalOut();
459: return 0;
460: }
461: };
462: default:
463: throw new ZipException("Unknown compression method " + method);
464: }
465: }
466:
467:
470: public String getName()
471: {
472: return name;
473: }
474:
475:
480: public int size()
481: {
482: checkClosed();
483:
484: try
485: {
486: return getEntries().size();
487: }
488: catch (IOException ioe)
489: {
490: return 0;
491: }
492: }
493:
494: private static class ZipEntryEnumeration implements Enumeration
495: {
496: private final Iterator elements;
497:
498: public ZipEntryEnumeration(Iterator elements)
499: {
500: this.elements = elements;
501: }
502:
503: public boolean hasMoreElements()
504: {
505: return elements.hasNext();
506: }
507:
508: public Object nextElement()
509: {
510:
513: return ((ZipEntry)elements.next()).clone();
514: }
515: }
516:
517: private static final class PartialInputStream extends InputStream
518: {
519: private final RandomAccessFile raf;
520: private final byte[] buffer;
521: private long bufferOffset;
522: private int pos;
523: private long end;
524:
525:
526:
527:
528: private int dummyByteCount;
529:
530: public PartialInputStream(RandomAccessFile raf, int bufferSize)
531: throws IOException
532: {
533: this.raf = raf;
534: buffer = new byte[bufferSize];
535: bufferOffset = -buffer.length;
536: pos = buffer.length;
537: end = raf.length();
538: }
539:
540: void setLength(long length)
541: {
542: end = bufferOffset + pos + length;
543: }
544:
545: private void fillBuffer() throws IOException
546: {
547: synchronized (raf)
548: {
549: long len = end - bufferOffset;
550: if (len == 0 && dummyByteCount > 0)
551: {
552: buffer[0] = 0;
553: dummyByteCount = 0;
554: }
555: else
556: {
557: raf.seek(bufferOffset);
558: raf.readFully(buffer, 0, (int) Math.min(buffer.length, len));
559: }
560: }
561: }
562:
563: public int available()
564: {
565: long amount = end - (bufferOffset + pos);
566: if (amount > Integer.MAX_VALUE)
567: return Integer.MAX_VALUE;
568: return (int) amount;
569: }
570:
571: public int read() throws IOException
572: {
573: if (bufferOffset + pos >= end + dummyByteCount)
574: return -1;
575: if (pos == buffer.length)
576: {
577: bufferOffset += buffer.length;
578: pos = 0;
579: fillBuffer();
580: }
581:
582: return buffer[pos++] & 0xFF;
583: }
584:
585: public int read(byte[] b, int off, int len) throws IOException
586: {
587: if (len > end + dummyByteCount - (bufferOffset + pos))
588: {
589: len = (int) (end + dummyByteCount - (bufferOffset + pos));
590: if (len == 0)
591: return -1;
592: }
593:
594: int totalBytesRead = Math.min(buffer.length - pos, len);
595: System.arraycopy(buffer, pos, b, off, totalBytesRead);
596: pos += totalBytesRead;
597: off += totalBytesRead;
598: len -= totalBytesRead;
599:
600: while (len > 0)
601: {
602: bufferOffset += buffer.length;
603: pos = 0;
604: fillBuffer();
605: int remain = Math.min(buffer.length, len);
606: System.arraycopy(buffer, pos, b, off, remain);
607: pos += remain;
608: off += remain;
609: len -= remain;
610: totalBytesRead += remain;
611: }
612:
613: return totalBytesRead;
614: }
615:
616: public long skip(long amount) throws IOException
617: {
618: if (amount < 0)
619: return 0;
620: if (amount > end - (bufferOffset + pos))
621: amount = end - (bufferOffset + pos);
622: seek(bufferOffset + pos + amount);
623: return amount;
624: }
625:
626: void seek(long newpos) throws IOException
627: {
628: long offset = newpos - bufferOffset;
629: if (offset >= 0 && offset <= buffer.length)
630: {
631: pos = (int) offset;
632: }
633: else
634: {
635: bufferOffset = newpos;
636: pos = 0;
637: fillBuffer();
638: }
639: }
640:
641: void readFully(byte[] buf) throws IOException
642: {
643: if (read(buf, 0, buf.length) != buf.length)
644: throw new EOFException();
645: }
646:
647: void readFully(byte[] buf, int off, int len) throws IOException
648: {
649: if (read(buf, off, len) != len)
650: throw new EOFException();
651: }
652:
653: int readLeShort() throws IOException
654: {
655: int b0 = read();
656: int b1 = read();
657: if (b1 == -1)
658: throw new EOFException();
659: return (b0 & 0xff) | (b1 & 0xff) << 8;
660: }
661:
662: int readLeInt() throws IOException
663: {
664: int b0 = read();
665: int b1 = read();
666: int b2 = read();
667: int b3 = read();
668: if (b3 == -1)
669: throw new EOFException();
670: return ((b0 & 0xff) | (b1 & 0xff) << 8)
671: | ((b2 & 0xff) | (b3 & 0xff) << 8) << 16;
672: }
673:
674: String readString(int length) throws IOException
675: {
676: if (length > end - (bufferOffset + pos))
677: throw new EOFException();
678:
679: try
680: {
681: if (buffer.length - pos >= length)
682: {
683: String s = new String(buffer, pos, length, "UTF-8");
684: pos += length;
685: return s;
686: }
687: else
688: {
689: byte[] b = new byte[length];
690: readFully(b);
691: return new String(b, 0, length, "UTF-8");
692: }
693: }
694: catch (UnsupportedEncodingException uee)
695: {
696: throw new AssertionError(uee);
697: }
698: }
699:
700: public void addDummyByte()
701: {
702: dummyByteCount = 1;
703: }
704: }
705: }