1. /*
  2. * @(#)ZipInputStream.java 1.37 04/06/11
  3. *
  4. * Copyright 2004 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.37, 06/11/04
  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 the stream at the
  53. * beginning of the entry data.
  54. * @return the next ZIP file entry, or null if there are no more entries
  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 this input stream and releases any system resources associated
  187. * with the stream.
  188. * @exception IOException if an I/O error has occurred
  189. */
  190. public void close() throws IOException {
  191. if (!closed) {
  192. super.close();
  193. closed = true;
  194. }
  195. }
  196. private byte[] b = new byte[256];
  197. /*
  198. * Reads local file (LOC) header for next entry.
  199. */
  200. private ZipEntry readLOC() throws IOException {
  201. try {
  202. readFully(tmpbuf, 0, LOCHDR);
  203. } catch (EOFException e) {
  204. return null;
  205. }
  206. if (get32(tmpbuf, 0) != LOCSIG) {
  207. return null;
  208. }
  209. // get the entry name and create the ZipEntry first
  210. int len = get16(tmpbuf, LOCNAM);
  211. if (len == 0) {
  212. throw new ZipException("missing entry name");
  213. }
  214. int blen = b.length;
  215. if (len > blen) {
  216. do
  217. blen = blen * 2;
  218. while (len > blen);
  219. b = new byte[blen];
  220. }
  221. readFully(b, 0, len);
  222. ZipEntry e = createZipEntry(getUTF8String(b, 0, len));
  223. // now get the remaining fields for the entry
  224. e.version = get16(tmpbuf, LOCVER);
  225. e.flag = get16(tmpbuf, LOCFLG);
  226. if ((e.flag & 1) == 1) {
  227. throw new ZipException("encrypted ZIP entry not supported");
  228. }
  229. e.method = get16(tmpbuf, LOCHOW);
  230. e.time = get32(tmpbuf, LOCTIM);
  231. if ((e.flag & 8) == 8) {
  232. /* EXT descriptor present */
  233. if (e.method != DEFLATED) {
  234. throw new ZipException(
  235. "only DEFLATED entries can have EXT descriptor");
  236. }
  237. } else {
  238. e.crc = get32(tmpbuf, LOCCRC);
  239. e.csize = get32(tmpbuf, LOCSIZ);
  240. e.size = get32(tmpbuf, LOCLEN);
  241. }
  242. len = get16(tmpbuf, LOCEXT);
  243. if (len > 0) {
  244. byte[] bb = new byte[len];
  245. readFully(bb, 0, len);
  246. e.extra = bb;
  247. }
  248. return e;
  249. }
  250. /*
  251. * Fetches a UTF8-encoded String from the specified byte array.
  252. */
  253. private static String getUTF8String(byte[] b, int off, int len) {
  254. // First, count the number of characters in the sequence
  255. int count = 0;
  256. int max = off + len;
  257. int i = off;
  258. while (i < max) {
  259. int c = b[i++] & 0xff;
  260. switch (c >> 4) {
  261. case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
  262. // 0xxxxxxx
  263. count++;
  264. break;
  265. case 12: case 13:
  266. // 110xxxxx 10xxxxxx
  267. if ((int)(b[i++] & 0xc0) != 0x80) {
  268. throw new IllegalArgumentException();
  269. }
  270. count++;
  271. break;
  272. case 14:
  273. // 1110xxxx 10xxxxxx 10xxxxxx
  274. if (((int)(b[i++] & 0xc0) != 0x80) ||
  275. ((int)(b[i++] & 0xc0) != 0x80)) {
  276. throw new IllegalArgumentException();
  277. }
  278. count++;
  279. break;
  280. default:
  281. // 10xxxxxx, 1111xxxx
  282. throw new IllegalArgumentException();
  283. }
  284. }
  285. if (i != max) {
  286. throw new IllegalArgumentException();
  287. }
  288. // Now decode the characters...
  289. char[] cs = new char[count];
  290. i = 0;
  291. while (off < max) {
  292. int c = b[off++] & 0xff;
  293. switch (c >> 4) {
  294. case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
  295. // 0xxxxxxx
  296. cs[i++] = (char)c;
  297. break;
  298. case 12: case 13:
  299. // 110xxxxx 10xxxxxx
  300. cs[i++] = (char)(((c & 0x1f) << 6) | (b[off++] & 0x3f));
  301. break;
  302. case 14:
  303. // 1110xxxx 10xxxxxx 10xxxxxx
  304. int t = (b[off++] & 0x3f) << 6;
  305. cs[i++] = (char)(((c & 0x0f) << 12) | t | (b[off++] & 0x3f));
  306. break;
  307. default:
  308. // 10xxxxxx, 1111xxxx
  309. throw new IllegalArgumentException();
  310. }
  311. }
  312. return new String(cs, 0, count);
  313. }
  314. /**
  315. * Creates a new <code>ZipEntry</code> object for the specified
  316. * entry name.
  317. *
  318. * @param name the ZIP file entry name
  319. * @return the ZipEntry just created
  320. */
  321. protected ZipEntry createZipEntry(String name) {
  322. return new ZipEntry(name);
  323. }
  324. /*
  325. * Reads end of deflated entry as well as EXT descriptor if present.
  326. */
  327. private void readEnd(ZipEntry e) throws IOException {
  328. int n = inf.getRemaining();
  329. if (n > 0) {
  330. ((PushbackInputStream)in).unread(buf, len - n, n);
  331. }
  332. if ((e.flag & 8) == 8) {
  333. /* EXT descriptor present */
  334. readFully(tmpbuf, 0, EXTHDR);
  335. long sig = get32(tmpbuf, 0);
  336. if (sig != EXTSIG) { // no EXTSIG present
  337. e.crc = sig;
  338. e.csize = get32(tmpbuf, EXTSIZ - EXTCRC);
  339. e.size = get32(tmpbuf, EXTLEN - EXTCRC);
  340. ((PushbackInputStream)in).unread(
  341. tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC);
  342. } else {
  343. e.crc = get32(tmpbuf, EXTCRC);
  344. e.csize = get32(tmpbuf, EXTSIZ);
  345. e.size = get32(tmpbuf, EXTLEN);
  346. }
  347. }
  348. if (e.size != inf.getBytesWritten()) {
  349. throw new ZipException(
  350. "invalid entry size (expected " + e.size +
  351. " but got " + inf.getBytesWritten() + " bytes)");
  352. }
  353. if (e.csize != inf.getBytesRead()) {
  354. throw new ZipException(
  355. "invalid entry compressed size (expected " + e.csize +
  356. " but got " + inf.getBytesRead() + " bytes)");
  357. }
  358. if (e.crc != crc.getValue()) {
  359. throw new ZipException(
  360. "invalid entry CRC (expected 0x" + Long.toHexString(e.crc) +
  361. " but got 0x" + Long.toHexString(crc.getValue()) + ")");
  362. }
  363. }
  364. /*
  365. * Reads bytes, blocking until all bytes are read.
  366. */
  367. private void readFully(byte[] b, int off, int len) throws IOException {
  368. while (len > 0) {
  369. int n = in.read(b, off, len);
  370. if (n == -1) {
  371. throw new EOFException();
  372. }
  373. off += n;
  374. len -= n;
  375. }
  376. }
  377. /*
  378. * Fetches unsigned 16-bit value from byte array at specified offset.
  379. * The bytes are assumed to be in Intel (little-endian) byte order.
  380. */
  381. private static final int get16(byte b[], int off) {
  382. return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8);
  383. }
  384. /*
  385. * Fetches unsigned 32-bit value from byte array at specified offset.
  386. * The bytes are assumed to be in Intel (little-endian) byte order.
  387. */
  388. private static final long get32(byte b[], int off) {
  389. return get16(b, off) | ((long)get16(b, off+2) << 16);
  390. }
  391. }