001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.util.zip;
019:
020: import java.io.EOFException;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.io.PushbackInputStream;
024: import java.util.jar.Attributes;
025: import java.util.jar.JarEntry;
026:
027: import org.apache.harmony.archive.internal.nls.Messages;
028: import org.apache.harmony.luni.util.Util;
029:
030: /**
031: * ZipInputStream is an input stream for reading zip files.
032: *
033: * @see ZipEntry
034: * @see ZipFile
035: */
036: public class ZipInputStream extends InflaterInputStream implements
037: ZipConstants {
038: static final int DEFLATED = 8;
039:
040: static final int STORED = 0;
041:
042: static final int ZIPDataDescriptorFlag = 8;
043:
044: static final int ZIPLocalHeaderVersionNeeded = 20;
045:
046: private boolean zipClosed = false;
047:
048: private boolean entriesEnd = false;
049:
050: private boolean hasDD = false;
051:
052: private int entryIn = 0;
053:
054: private int inRead, lastRead = 0;
055:
056: ZipEntry currentEntry;
057:
058: private final byte[] hdrBuf = new byte[LOCHDR - LOCVER];
059:
060: private final CRC32 crc = new CRC32();
061:
062: private byte[] nameBuf = new byte[256];
063:
064: private char[] charBuf = new char[256];
065:
066: /**
067: * Constructs a new ZipInputStream on the specified input stream.
068: *
069: * @param stream
070: * the input stream
071: */
072: public ZipInputStream(InputStream stream) {
073: super (new PushbackInputStream(stream, BUF_SIZE), new Inflater(
074: true));
075: if (stream == null) {
076: throw new NullPointerException();
077: }
078: }
079:
080: /**
081: * Closes this ZipInputStream.
082: */
083: @Override
084: public void close() throws IOException {
085: if (zipClosed != true) {
086: closeEntry(); // Close the current entry
087: zipClosed = true;
088: super .close();
089: }
090: }
091:
092: /**
093: * Closes the current zip entry and positions to read the next entry.
094: *
095: * @throws IOException
096: * if an IO exception occurs closing the entry
097: */
098: public void closeEntry() throws IOException {
099: if (zipClosed) {
100: throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
101: }
102: if (currentEntry == null) {
103: return;
104: }
105: if (currentEntry instanceof java.util.jar.JarEntry) {
106: Attributes temp = ((JarEntry) currentEntry).getAttributes();
107: if (temp != null && temp.containsKey("hidden")) { //$NON-NLS-1$
108: return;
109: }
110: }
111: // Ensure all entry bytes are read
112: skip(Long.MAX_VALUE);
113: int inB, out;
114: if (currentEntry.compressionMethod == DEFLATED) {
115: inB = inf.getTotalIn();
116: out = inf.getTotalOut();
117: } else {
118: inB = inRead;
119: out = inRead;
120: }
121: int diff = 0;
122: // Pushback any required bytes
123: if ((diff = entryIn - inB) != 0) {
124: ((PushbackInputStream) in).unread(buf, len - diff, diff);
125: }
126:
127: if (hasDD) {
128: in.read(hdrBuf, 0, EXTHDR);
129: if (getLong(hdrBuf, 0) != EXTSIG) {
130: throw new ZipException(Messages.getString("archive.1F")); //$NON-NLS-1$
131: }
132: currentEntry.crc = getLong(hdrBuf, EXTCRC);
133: currentEntry.compressedSize = getLong(hdrBuf, EXTSIZ);
134: currentEntry.size = getLong(hdrBuf, EXTLEN);
135: }
136: if (currentEntry.crc != crc.getValue()) {
137: throw new ZipException(Messages.getString("archive.20")); //$NON-NLS-1$
138: }
139: if (currentEntry.compressedSize != inB
140: || currentEntry.size != out) {
141: throw new ZipException(Messages.getString("archive.21")); //$NON-NLS-1$
142: }
143:
144: inf.reset();
145: lastRead = inRead = entryIn = len = 0;
146: crc.reset();
147: currentEntry = null;
148: }
149:
150: /**
151: * Reads the next zip entry from this ZipInputStream.
152: *
153: * @return the next entry
154: * @throws IOException
155: * if an IO exception occurs reading the next entry
156: */
157: public ZipEntry getNextEntry() throws IOException {
158: if (currentEntry != null) {
159: closeEntry();
160: }
161: if (entriesEnd) {
162: return null;
163: }
164:
165: int x = 0, count = 0;
166: while (count != 4) {
167: count += x = in.read(hdrBuf, count, 4 - count);
168: if (x == -1) {
169: return null;
170: }
171: }
172: long hdr = getLong(hdrBuf, 0);
173: if (hdr == CENSIG) {
174: entriesEnd = true;
175: return null;
176: }
177: if (hdr != LOCSIG) {
178: return null;
179: }
180:
181: // Read the local header
182: count = 0;
183: while (count != (LOCHDR - LOCVER)) {
184: count += x = in.read(hdrBuf, count, (LOCHDR - LOCVER)
185: - count);
186: if (x == -1) {
187: throw new EOFException();
188: }
189: }
190: int version = getShort(hdrBuf, 0) & 0xff;
191: if (version > ZIPLocalHeaderVersionNeeded) {
192: throw new ZipException(Messages.getString("archive.22")); //$NON-NLS-1$
193: }
194: int flags = getShort(hdrBuf, LOCFLG - LOCVER);
195: hasDD = ((flags & ZIPDataDescriptorFlag) == ZIPDataDescriptorFlag);
196: int cetime = getShort(hdrBuf, LOCTIM - LOCVER);
197: int cemodDate = getShort(hdrBuf, LOCTIM - LOCVER + 2);
198: int cecompressionMethod = getShort(hdrBuf, LOCHOW - LOCVER);
199: long cecrc = 0, cecompressedSize = 0, cesize = -1;
200: if (!hasDD) {
201: cecrc = getLong(hdrBuf, LOCCRC - LOCVER);
202: cecompressedSize = getLong(hdrBuf, LOCSIZ - LOCVER);
203: cesize = getLong(hdrBuf, LOCLEN - LOCVER);
204: }
205: int flen = getShort(hdrBuf, LOCNAM - LOCVER);
206: if (flen == 0) {
207: throw new ZipException(Messages.getString("archive.23")); //$NON-NLS-1$
208: }
209: int elen = getShort(hdrBuf, LOCEXT - LOCVER);
210:
211: count = 0;
212: if (flen > nameBuf.length) {
213: nameBuf = new byte[flen];
214: charBuf = new char[flen];
215: }
216: while (count != flen) {
217: count += x = in.read(nameBuf, count, flen - count);
218: if (x == -1) {
219: throw new EOFException();
220: }
221: }
222: currentEntry = createZipEntry(Util.convertUTF8WithBuf(nameBuf,
223: charBuf, 0, flen));
224: currentEntry.time = cetime;
225: currentEntry.modDate = cemodDate;
226: currentEntry.setMethod(cecompressionMethod);
227: if (cesize != -1) {
228: currentEntry.setCrc(cecrc);
229: currentEntry.setSize(cesize);
230: currentEntry.setCompressedSize(cecompressedSize);
231: }
232: if (elen > 0) {
233: count = 0;
234: byte[] e = new byte[elen];
235: while (count != elen) {
236: count += x = in.read(e, count, elen - count);
237: if (x == -1) {
238: throw new EOFException();
239: }
240: }
241: currentEntry.setExtra(e);
242: }
243: return currentEntry;
244: }
245:
246: /* Read 4 bytes from the buffer and store it as an int */
247:
248: /**
249: * Reads up to the specified number of uncompressed bytes into the buffer
250: * starting at the offset.
251: *
252: * @param buffer
253: * a byte array
254: * @param start
255: * the starting offset into the buffer
256: * @param length
257: * the number of bytes to read
258: * @return the number of bytes read
259: */
260: @Override
261: public int read(byte[] buffer, int start, int length)
262: throws IOException {
263: if (zipClosed) {
264: throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
265: }
266: if (inf.finished() || currentEntry == null) {
267: return -1;
268: }
269: // avoid int overflow, check null buffer
270: if (start <= buffer.length && length >= 0 && start >= 0
271: && buffer.length - start >= length) {
272: if (currentEntry.compressionMethod == STORED) {
273: int csize = (int) currentEntry.size;
274: if (inRead >= csize) {
275: return -1;
276: }
277: if (lastRead >= len) {
278: lastRead = 0;
279: if ((len = in.read(buf)) == -1) {
280: return -1;
281: }
282: entryIn += len;
283: }
284: int toRead = length > len ? len - lastRead : length;
285: if ((csize - inRead) < toRead) {
286: toRead = csize - inRead;
287: }
288: System.arraycopy(buf, lastRead, buffer, start, toRead);
289: lastRead += toRead;
290: inRead += toRead;
291: crc.update(buffer, start, toRead);
292: return toRead;
293: }
294: if (inf.needsInput()) {
295: fill();
296: if (len > 0) {
297: entryIn += len;
298: }
299: }
300: int read = 0;
301: try {
302: read = inf.inflate(buffer, start, length);
303: } catch (DataFormatException e) {
304: throw new ZipException(e.getMessage());
305: }
306: if (read == 0 && inf.finished()) {
307: return -1;
308: }
309: crc.update(buffer, start, read);
310: return read;
311: }
312: throw new ArrayIndexOutOfBoundsException();
313: }
314:
315: /**
316: * Skips up to the specified number of bytes in the current zip entry.
317: *
318: * @param value
319: * the number of bytes to skip
320: * @return the number of bytes skipped
321: */
322: @Override
323: public long skip(long value) throws IOException {
324: if (value >= 0) {
325: long skipped = 0;
326: byte[] b = new byte[1024];
327: while (skipped != value) {
328: long rem = value - skipped;
329: int x = read(b, 0, (int) (b.length > rem ? rem
330: : b.length));
331: if (x == -1) {
332: return skipped;
333: }
334: skipped += x;
335: }
336: return skipped;
337: }
338: throw new IllegalArgumentException();
339: }
340:
341: /**
342: * Answers 1 if the EOF has been reached, otherwise returns 0.
343: *
344: * @return 0 after EOF of current entry, 1 otherwise
345: */
346: @Override
347: public int available() throws IOException {
348: if (zipClosed) {
349: throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
350: }
351: if (currentEntry == null) {
352: return 1;
353: }
354: if (currentEntry.compressionMethod == STORED) {
355: if (inRead >= currentEntry.size) {
356: return 0;
357: }
358: } else {
359: if (inf.finished()) {
360: return 0;
361: }
362: }
363: return 1;
364: }
365:
366: protected ZipEntry createZipEntry(String name) {
367: return new ZipEntry(name);
368: }
369:
370: private int getShort(byte[] buffer, int off) {
371: return (buffer[off] & 0xFF) | ((buffer[off + 1] & 0xFF) << 8);
372: }
373:
374: private long getLong(byte[] buffer, int off) {
375: long l = 0;
376: l |= (buffer[off] & 0xFF);
377: l |= (buffer[off + 1] & 0xFF) << 8;
378: l |= (buffer[off + 2] & 0xFF) << 16;
379: l |= ((long) (buffer[off + 3] & 0xFF)) << 24;
380: return l;
381: }
382: }
|