1. /*
  2. * @(#)ZipInputStream.java 1.33 03/02/07
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.util.zip;
  8. import java.io.InputStream;
  9. import java.io.IOException;
  10. import java.io.EOFException;
  11. import java.io.PushbackInputStream;
  12. /**
  13. * This class implements an input stream filter for reading files in the
  14. * ZIP file format. Includes support for both compressed and uncompressed
  15. * entries.
  16. *
  17. * @author David Connelly
  18. * @version 1.33, 02/07/03
  19. */
  20. public
  21. class ZipInputStream extends InflaterInputStream implements ZipConstants {
  22. private ZipEntry entry;
  23. private CRC32 crc = new CRC32();
  24. private long remaining;
  25. private byte[] tmpbuf = new byte[512];
  26. private static final int STORED = ZipEntry.STORED;
  27. private static final int DEFLATED = ZipEntry.DEFLATED;
  28. private boolean closed = false;
  29. // this flag is set to true after EOF has reached for
  30. // one entry
  31. private boolean entryEOF = false;
  32. /**
  33. * Check to make sure that this stream has not been closed
  34. */
  35. private void ensureOpen() throws IOException {
  36. if (closed) {
  37. throw new IOException("Stream closed");
  38. }
  39. }
  40. /**
  41. * Creates a new ZIP input stream.
  42. * @param in the actual input stream
  43. */
  44. public ZipInputStream(InputStream in) {
  45. super(new PushbackInputStream(in, 512), new Inflater(true), 512);
  46. usesDefaultInflater = true;
  47. if(in == null) {
  48. throw new NullPointerException("in is null");
  49. }
  50. }
  51. /**
  52. * Reads the next ZIP file entry and positions stream at the beginning
  53. * of the entry data.
  54. * @return the ZipEntry just read
  55. * @exception ZipException if a ZIP file error has occurred
  56. * @exception IOException if an I/O error has occurred
  57. */
  58. public ZipEntry getNextEntry() throws IOException {
  59. ensureOpen();
  60. if (entry != null) {
  61. closeEntry();
  62. }
  63. crc.reset();
  64. inf.reset();
  65. if ((entry = readLOC()) == null) {
  66. return null;
  67. }
  68. if (entry.method == STORED) {
  69. remaining = entry.size;
  70. }
  71. entryEOF = false;
  72. return entry;
  73. }
  74. /**
  75. * Closes the current ZIP entry and positions the stream for reading the
  76. * next entry.
  77. * @exception ZipException if a ZIP file error has occurred
  78. * @exception IOException if an I/O error has occurred
  79. */
  80. public void closeEntry() throws IOException {
  81. ensureOpen();
  82. while (read(tmpbuf, 0, tmpbuf.length) != -1) ;
  83. entryEOF = true;
  84. }
  85. /**
  86. * Returns 0 after EOF has reached for the current entry data,
  87. * otherwise always return 1.
  88. * <p>
  89. * Programs should not count on this method to return the actual number
  90. * of bytes that could be read without blocking.
  91. *
  92. * @return 1 before EOF and 0 after EOF has reached for current entry.
  93. * @exception IOException if an I/O error occurs.
  94. *
  95. */
  96. public int available() throws IOException {
  97. ensureOpen();
  98. if (entryEOF) {
  99. return 0;
  100. } else {
  101. return 1;
  102. }
  103. }
  104. /**
  105. * Reads from the current ZIP entry into an array of bytes. Blocks until
  106. * some input is available.
  107. * @param b the buffer into which the data is read
  108. * @param off the start offset of the data
  109. * @param len the maximum number of bytes read
  110. * @return the actual number of bytes read, or -1 if the end of the
  111. * entry is reached
  112. * @exception ZipException if a ZIP file error has occurred
  113. * @exception IOException if an I/O error has occurred
  114. */
  115. public int read(byte[] b, int off, int len) throws IOException {
  116. ensureOpen();
  117. if (off < 0 || len < 0 || off > b.length - len) {
  118. throw new IndexOutOfBoundsException();
  119. } else if (len == 0) {
  120. return 0;
  121. }
  122. if (entry == null) {
  123. return -1;
  124. }
  125. switch (entry.method) {
  126. case DEFLATED:
  127. len = super.read(b, off, len);
  128. if (len == -1) {
  129. readEnd(entry);
  130. entryEOF = true;
  131. entry = null;
  132. } else {
  133. crc.update(b, off, len);
  134. }
  135. return len;
  136. case STORED:
  137. if (remaining <= 0) {
  138. entryEOF = true;
  139. entry = null;
  140. return -1;
  141. }
  142. if (len > remaining) {
  143. len = (int)remaining;
  144. }
  145. len = in.read(b, off, len);
  146. if (len == -1) {
  147. throw new ZipException("unexpected EOF");
  148. }
  149. crc.update(b, off, len);
  150. remaining -= len;
  151. return len;
  152. default:
  153. throw new InternalError("invalid compression method");
  154. }
  155. }
  156. /**
  157. * Skips specified number of bytes in the current ZIP entry.
  158. * @param n the number of bytes to skip
  159. * @return the actual number of bytes skipped
  160. * @exception ZipException if a ZIP file error has occurred
  161. * @exception IOException if an I/O error has occurred
  162. * @exception IllegalArgumentException if n < 0
  163. */
  164. public long skip(long n) throws IOException {
  165. if (n < 0) {
  166. throw new IllegalArgumentException("negative skip length");
  167. }
  168. ensureOpen();
  169. int max = (int)Math.min(n, Integer.MAX_VALUE);
  170. int total = 0;
  171. while (total < max) {
  172. int len = max - total;
  173. if (len > tmpbuf.length) {
  174. len = tmpbuf.length;
  175. }
  176. len = read(tmpbuf, 0, len);
  177. if (len == -1) {
  178. entryEOF = true;
  179. break;
  180. }
  181. total += len;
  182. }
  183. return total;
  184. }
  185. /**
  186. * Closes the ZIP input stream.
  187. * @exception IOException if an I/O error has occurred
  188. */
  189. public void close() throws IOException {
  190. if (!closed) {
  191. super.close();
  192. closed = true;
  193. }
  194. }
  195. private byte[] b = new byte[256];
  196. /*
  197. * Reads local file (LOC) header for next entry.
  198. */
  199. private ZipEntry readLOC() throws IOException {
  200. try {
  201. readFully(tmpbuf, 0, LOCHDR);
  202. } catch (EOFException e) {
  203. return null;
  204. }
  205. if (get32(tmpbuf, 0) != LOCSIG) {
  206. return null;
  207. }
  208. // get the entry name and create the ZipEntry first
  209. int len = get16(tmpbuf, LOCNAM);
  210. if (len == 0) {
  211. throw new ZipException("missing entry name");
  212. }
  213. int blen = b.length;
  214. if (len > blen) {
  215. do
  216. blen = blen * 2;
  217. while (len > blen);
  218. b = new byte[blen];
  219. }
  220. readFully(b, 0, len);
  221. ZipEntry e = createZipEntry(getUTF8String(b, 0, len));
  222. // now get the remaining fields for the entry
  223. e.version = get16(tmpbuf, LOCVER);
  224. e.flag = get16(tmpbuf, LOCFLG);
  225. if ((e.flag & 1) == 1) {
  226. throw new ZipException("encrypted ZIP entry not supported");
  227. }
  228. e.method = get16(tmpbuf, LOCHOW);
  229. e.time = get32(tmpbuf, LOCTIM);
  230. if ((e.flag & 8) == 8) {
  231. /* EXT descriptor present */
  232. if (e.method != DEFLATED) {
  233. throw new ZipException(
  234. "only DEFLATED entries can have EXT descriptor");
  235. }
  236. } else {
  237. e.crc = get32(tmpbuf, LOCCRC);
  238. e.csize = get32(tmpbuf, LOCSIZ);
  239. e.size = get32(tmpbuf, LOCLEN);
  240. }
  241. len = get16(tmpbuf, LOCEXT);
  242. if (len > 0) {
  243. byte[] bb = new byte[len];
  244. readFully(bb, 0, len);
  245. e.extra = bb;
  246. }
  247. return e;
  248. }
  249. /*
  250. * Fetches a UTF8-encoded String from the specified byte array.
  251. */
  252. private static String getUTF8String(byte[] b, int off, int len) {
  253. // First, count the number of characters in the sequence
  254. int count = 0;
  255. int max = off + len;
  256. int i = off;
  257. while (i < max) {
  258. int c = b[i++] & 0xff;
  259. switch (c >> 4) {
  260. case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
  261. // 0xxxxxxx
  262. count++;
  263. break;
  264. case 12: case 13:
  265. // 110xxxxx 10xxxxxx
  266. if ((int)(b[i++] & 0xc0) != 0x80) {
  267. throw new IllegalArgumentException();
  268. }
  269. count++;
  270. break;
  271. case 14:
  272. // 1110xxxx 10xxxxxx 10xxxxxx
  273. if (((int)(b[i++] & 0xc0) != 0x80) ||
  274. ((int)(b[i++] & 0xc0) != 0x80)) {
  275. throw new IllegalArgumentException();
  276. }
  277. count++;
  278. break;
  279. default:
  280. // 10xxxxxx, 1111xxxx
  281. throw new IllegalArgumentException();
  282. }
  283. }
  284. if (i != max) {
  285. throw new IllegalArgumentException();
  286. }
  287. // Now decode the characters...
  288. char[] cs = new char[count];
  289. i = 0;
  290. while (off < max) {
  291. int c = b[off++] & 0xff;
  292. switch (c >> 4) {
  293. case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
  294. // 0xxxxxxx
  295. cs[i++] = (char)c;
  296. break;
  297. case 12: case 13:
  298. // 110xxxxx 10xxxxxx
  299. cs[i++] = (char)(((c & 0x1f) << 6) | (b[off++] & 0x3f));
  300. break;
  301. case 14:
  302. // 1110xxxx 10xxxxxx 10xxxxxx
  303. int t = (b[off++] & 0x3f) << 6;
  304. cs[i++] = (char)(((c & 0x0f) << 12) | t | (b[off++] & 0x3f));
  305. break;
  306. default:
  307. // 10xxxxxx, 1111xxxx
  308. throw new IllegalArgumentException();
  309. }
  310. }
  311. return new String(cs, 0, count);
  312. }
  313. /**
  314. * Creates a new <code>ZipEntry</code> object for the specified
  315. * entry name.
  316. *
  317. * @param name the ZIP file entry name
  318. * @return the ZipEntry just created
  319. */
  320. protected ZipEntry createZipEntry(String name) {
  321. return new ZipEntry(name);
  322. }
  323. /*
  324. * Reads end of deflated entry as well as EXT descriptor if present.
  325. */
  326. private void readEnd(ZipEntry e) throws IOException {
  327. int n = inf.getRemaining();
  328. if (n > 0) {
  329. ((PushbackInputStream)in).unread(buf, len - n, n);
  330. }
  331. if ((e.flag & 8) == 8) {
  332. /* EXT descriptor present */
  333. readFully(tmpbuf, 0, EXTHDR);
  334. long sig = get32(tmpbuf, 0);
  335. if (sig != EXTSIG) { // no EXTSIG present
  336. e.crc = sig;
  337. e.csize = get32(tmpbuf, EXTSIZ - EXTCRC);
  338. e.size = get32(tmpbuf, EXTLEN - EXTCRC);
  339. ((PushbackInputStream)in).unread(
  340. tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC);
  341. } else {
  342. e.crc = get32(tmpbuf, EXTCRC);
  343. e.csize = get32(tmpbuf, EXTSIZ);
  344. e.size = get32(tmpbuf, EXTLEN);
  345. }
  346. }
  347. if (e.size != inf.getTotalOut()) {
  348. throw new ZipException(
  349. "invalid entry size (expected " + e.size + " but got " +
  350. inf.getTotalOut() + " bytes)");
  351. }
  352. if (e.csize != inf.getTotalIn()) {
  353. throw new ZipException(
  354. "invalid entry compressed size (expected " + e.csize +
  355. " but got " + inf.getTotalIn() + " bytes)");
  356. }
  357. if (e.crc != crc.getValue()) {
  358. throw new ZipException(
  359. "invalid entry CRC (expected 0x" + Long.toHexString(e.crc) +
  360. " but got 0x" + Long.toHexString(crc.getValue()) + ")");
  361. }
  362. }
  363. /*
  364. * Reads bytes, blocking until all bytes are read.
  365. */
  366. private void readFully(byte[] b, int off, int len) throws IOException {
  367. while (len > 0) {
  368. int n = in.read(b, off, len);
  369. if (n == -1) {
  370. throw new EOFException();
  371. }
  372. off += n;
  373. len -= n;
  374. }
  375. }
  376. /*
  377. * Fetches unsigned 16-bit value from byte array at specified offset.
  378. * The bytes are assumed to be in Intel (little-endian) byte order.
  379. */
  380. private static final int get16(byte b[], int off) {
  381. return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8);
  382. }
  383. /*
  384. * Fetches unsigned 32-bit value from byte array at specified offset.
  385. * The bytes are assumed to be in Intel (little-endian) byte order.
  386. */
  387. private static final long get32(byte b[], int off) {
  388. return get16(b, off) | ((long)get16(b, off+2) << 16);
  389. }
  390. }