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 HashMap 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 HashMap(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 HashMap 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: HashMap 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: HashMap 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: final Inflater inf = new Inflater(true);
449: final int sz = (int) entry.getSize();
450: return new InflaterInputStream(inp, inf)
451: {
452: public int available() throws IOException
453: {
454: if (sz == -1)
455: return super.available();
456: if (super.available() != 0)
457: return sz - inf.getTotalOut();
458: return 0;
459: }
460: };
461: default:
462: throw new ZipException("Unknown compression method " + method);
463: }
464: }
465:
466:
469: public String getName()
470: {
471: return name;
472: }
473:
474:
479: public int size()
480: {
481: checkClosed();
482:
483: try
484: {
485: return getEntries().size();
486: }
487: catch (IOException ioe)
488: {
489: return 0;
490: }
491: }
492:
493: private static class ZipEntryEnumeration implements Enumeration
494: {
495: private final Iterator elements;
496:
497: public ZipEntryEnumeration(Iterator elements)
498: {
499: this.elements = elements;
500: }
501:
502: public boolean hasMoreElements()
503: {
504: return elements.hasNext();
505: }
506:
507: public Object nextElement()
508: {
509:
512: return ((ZipEntry)elements.next()).clone();
513: }
514: }
515:
516: private static final class PartialInputStream extends InputStream
517: {
518: private final RandomAccessFile raf;
519: private final byte[] buffer;
520: private long bufferOffset;
521: private int pos;
522: private long end;
523:
524: public PartialInputStream(RandomAccessFile raf, int bufferSize)
525: throws IOException
526: {
527: this.raf = raf;
528: buffer = new byte[bufferSize];
529: bufferOffset = -buffer.length;
530: pos = buffer.length;
531: end = raf.length();
532: }
533:
534: void setLength(long length)
535: {
536: end = bufferOffset + pos + length;
537: }
538:
539: private void fillBuffer() throws IOException
540: {
541: synchronized (raf)
542: {
543: raf.seek(bufferOffset);
544: raf.readFully(buffer, 0, (int) Math.min(buffer.length, end - bufferOffset));
545: }
546: }
547:
548: public int available()
549: {
550: long amount = end - (bufferOffset + pos);
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 (bufferOffset + pos >= end)
559: return -1;
560: if (pos == buffer.length)
561: {
562: bufferOffset += buffer.length;
563: pos = 0;
564: fillBuffer();
565: }
566:
567: return buffer[pos++] & 0xFF;
568: }
569:
570: public int read(byte[] b, int off, int len) throws IOException
571: {
572: if (len > end - (bufferOffset + pos))
573: {
574: len = (int) (end - (bufferOffset + pos));
575: if (len == 0)
576: return -1;
577: }
578:
579: int totalBytesRead = Math.min(buffer.length - pos, len);
580: System.arraycopy(buffer, pos, b, off, totalBytesRead);
581: pos += totalBytesRead;
582: off += totalBytesRead;
583: len -= totalBytesRead;
584:
585: while (len > 0)
586: {
587: bufferOffset += buffer.length;
588: pos = 0;
589: fillBuffer();
590: int remain = Math.min(buffer.length, len);
591: System.arraycopy(buffer, pos, b, off, remain);
592: pos += remain;
593: off += remain;
594: len -= remain;
595: totalBytesRead += remain;
596: }
597:
598: return totalBytesRead;
599: }
600:
601: public long skip(long amount) throws IOException
602: {
603: if (amount < 0)
604: return 0;
605: if (amount > end - (bufferOffset + pos))
606: amount = end - (bufferOffset + pos);
607: seek(bufferOffset + pos + amount);
608: return amount;
609: }
610:
611: void seek(long newpos) throws IOException
612: {
613: long offset = newpos - bufferOffset;
614: if (offset >= 0 && offset <= buffer.length)
615: {
616: pos = (int) offset;
617: }
618: else
619: {
620: bufferOffset = newpos;
621: pos = 0;
622: fillBuffer();
623: }
624: }
625:
626: void readFully(byte[] buf) throws IOException
627: {
628: if (read(buf, 0, buf.length) != buf.length)
629: throw new EOFException();
630: }
631:
632: void readFully(byte[] buf, int off, int len) throws IOException
633: {
634: if (read(buf, off, len) != len)
635: throw new EOFException();
636: }
637:
638: int readLeShort() throws IOException
639: {
640: int b0 = read();
641: int b1 = read();
642: if (b1 == -1)
643: throw new EOFException();
644: return (b0 & 0xff) | (b1 & 0xff) << 8;
645: }
646:
647: int readLeInt() throws IOException
648: {
649: int b0 = read();
650: int b1 = read();
651: int b2 = read();
652: int b3 = read();
653: if (b3 == -1)
654: throw new EOFException();
655: return ((b0 & 0xff) | (b1 & 0xff) << 8)
656: | ((b2 & 0xff) | (b3 & 0xff) << 8) << 16;
657: }
658:
659: String readString(int length) throws IOException
660: {
661: if (length > end - (bufferOffset + pos))
662: throw new EOFException();
663:
664: try
665: {
666: if (buffer.length - pos >= length)
667: {
668: String s = new String(buffer, pos, length, "UTF-8");
669: pos += length;
670: return s;
671: }
672: else
673: {
674: byte[] b = new byte[length];
675: readFully(b);
676: return new String(b, 0, length, "UTF-8");
677: }
678: }
679: catch (UnsupportedEncodingException uee)
680: {
681: throw new AssertionError(uee);
682: }
683: }
684: }
685: }