package org.zywx.wbpalmstar.base.zip; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; import java.io.UnsupportedEncodingException; import java.util.zip.CRC32; import java.util.zip.Inflater; import java.util.zip.ZipException; public class CnZipInputStream extends InflaterInputStream implements ZipConstants { private ZipEntry entry; private CRC32 crc = new CRC32(); private long remaining; private byte[] tmpbuf = new byte[512]; // private static final int STORED = 0; // private static final int DEFLATED = 8; private boolean closed = false; private boolean entryEOF = false; public String encoding = "UTF-8"; private byte[] b = new byte[256]; public CnZipInputStream(InputStream in) { super(new PushbackInputStream(in, 512), new Inflater(true), 512); this.usesDefaultInflater = true; if (in == null) throw new NullPointerException("in is null"); } public CnZipInputStream(InputStream in, String encoding) { this(in); this.encoding = encoding; } private void ensureOpen() throws IOException { if (this.closed) throw new IOException("Stream closed"); } public ZipEntry getNextEntry() throws IOException { ensureOpen(); if (this.entry != null) { closeEntry(); } this.crc.reset(); this.inf.reset(); if ((this.entry = readLOC()) == null) { return null; } if (this.entry.method == 0) { this.remaining = this.entry.size; } this.entryEOF = false; return this.entry; } public void closeEntry() throws IOException { ensureOpen(); while (read(this.tmpbuf, 0, this.tmpbuf.length) != -1) ; this.entryEOF = true; } public int available() throws IOException { ensureOpen(); if (this.entryEOF) { return 0; } return 1; } public int read(byte[] b, int off, int len) throws IOException { ensureOpen(); if ((off < 0) || (len < 0) || (off > b.length - len)) throw new IndexOutOfBoundsException(); if (len == 0) { return 0; } if (this.entry == null) { return -1; } switch (this.entry.method) { case 8: len = super.read(b, off, len); if (len == -1) { readEnd(this.entry); this.entryEOF = true; this.entry = null; } else { this.crc.update(b, off, len); } return len; case 0: if (this.remaining <= 0L) { this.entryEOF = true; this.entry = null; return -1; } if (len > this.remaining) { len = (int) this.remaining; } len = this.in.read(b, off, len); if (len == -1) { throw new ZipException("unexpected EOF"); } this.crc.update(b, off, len); this.remaining -= len; return len; } throw new InternalError("invalid compression method"); } public long skip(long n) throws IOException { if (n < 0L) { throw new IllegalArgumentException("negative skip length"); } ensureOpen(); int max = (int) Math.min(n, 2147483647L); int total = 0; while (total < max) { int len = max - total; if (len > this.tmpbuf.length) { len = this.tmpbuf.length; } len = read(this.tmpbuf, 0, len); if (len == -1) { this.entryEOF = true; break; } total += len; } return total; } public void close() throws IOException { if (!this.closed) { super.close(); this.closed = true; } } private ZipEntry readLOC() throws IOException { try { readFully(this.tmpbuf, 0, 30); } catch (EOFException e) { return null; } if (get32(this.tmpbuf, 0) != 67324752L) { return null; } int len = get16(this.tmpbuf, 26); if (len == 0) { throw new ZipException("missing entry name"); } int blen = this.b.length; if (len > blen) { do blen *= 2; while (len > blen); this.b = new byte[blen]; } readFully(this.b, 0, len); ZipEntry e = null; try { if (this.encoding.toUpperCase().equals("UTF-8")) e = createZipEntry(getUTF8String(this.b, 0, len)); else e = createZipEntry(new String(this.b, 0, len, this.encoding)); } catch (Exception byteE) { e = createZipEntry(getUTF8String(this.b, 0, len)); } e.version = get16(this.tmpbuf, 4); e.flag = get16(this.tmpbuf, 6); if ((e.flag & 0x1) == 1) { throw new ZipException("encrypted ZIP entry not supported"); } e.method = get16(this.tmpbuf, 8); e.time = get32(this.tmpbuf, 10); if ((e.flag & 0x8) == 8) { if (e.method != 8) throw new ZipException( "only DEFLATED entries can have EXT descriptor"); } else { e.crc = get32(this.tmpbuf, 14); e.csize = get32(this.tmpbuf, 18); e.size = get32(this.tmpbuf, 22); } len = get16(this.tmpbuf, 28); if (len > 0) { byte[] bb = new byte[len]; readFully(bb, 0, len); e.extra = bb; } return e; } private static String getUTF8String(byte[] b, int off, int len) { int count; char[] cs; try { String s = new String(b, off, len, "gbk"); return s; } catch (UnsupportedEncodingException e) { e.printStackTrace(); count = 0; int max = off + len; int i = off; while (i < max) { int c = b[(i++)] & 0xFF; switch (c >> 4) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: count++; break; case 12: case 13: if ((b[(i++)] & 0xC0) != 128) { throw new IllegalArgumentException(); } count++; break; case 14: if (((b[(i++)] & 0xC0) != 128) || ((b[(i++)] & 0xC0) != 128)) { throw new IllegalArgumentException(); } count++; break; case 8: case 9: case 10: case 11: default: throw new IllegalArgumentException(); } } if (i != max) { throw new IllegalArgumentException(); } cs = new char[count]; i = 0; while (off < max) { int c = b[(off++)] & 0xFF; switch (c >> 4) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: cs[(i++)] = (char) c; break; case 12: case 13: cs[(i++)] = (char) ((c & 0x1F) << 6 | b[(off++)] & 0x3F); break; case 14: int t = (b[(off++)] & 0x3F) << 6; cs[(i++)] = (char) ((c & 0xF) << 12 | t | b[(off++)] & 0x3F); break; case 8: case 9: case 10: case 11: default: throw new IllegalArgumentException(); } } } return new String(cs, 0, count); } protected ZipEntry createZipEntry(String name) { return new ZipEntry(name); } private void readEnd(ZipEntry e) throws IOException { int n = this.inf.getRemaining(); if (n > 0) { ((PushbackInputStream) this.in).unread(this.buf, this.len - n, n); } if ((e.flag & 0x8) == 8) { readFully(this.tmpbuf, 0, 16); long sig = get32(this.tmpbuf, 0); if (sig != 134695760L) { e.crc = sig; e.csize = get32(this.tmpbuf, 4); e.size = get32(this.tmpbuf, 8); ((PushbackInputStream) this.in).unread(this.tmpbuf, 11, 4); } else { e.crc = get32(this.tmpbuf, 4); e.csize = get32(this.tmpbuf, 8); e.size = get32(this.tmpbuf, 12); } } if (e.size != this.inf.getTotalOut()) { throw new ZipException("invalid entry size (expected " + e.size + " but got " + this.inf.getTotalOut() + " bytes)"); } if (e.csize != this.inf.getTotalIn()) { throw new ZipException("invalid entry compressed size (expected " + e.csize + " but got " + this.inf.getTotalIn() + " bytes)"); } if (e.crc != this.crc.getValue()) throw new ZipException("invalid entry CRC (expected 0x" + Long.toHexString(e.crc) + " but got 0x" + Long.toHexString(this.crc.getValue()) + ")"); } private void readFully(byte[] b, int off, int len) throws IOException { while (len > 0) { int n = this.in.read(b, off, len); if (n == -1) { throw new EOFException(); } off += n; len -= n; } } private static final int get16(byte b[], int off) { return (b[off] & 0xff) | ((b[off + 1] & 0xff) << 8); } /* * Fetches unsigned 32-bit value from byte array at specified offset. The * bytes are assumed to be in Intel (little-endian) byte order. */ private static final long get32(byte b[], int off) { return get16(b, off) | ((long) get16(b, off + 2) << 16); } }