OSDN Git Service

Merge commit 'remotes/korg/cupcake'
[android-x86/dalvik.git] / libcore / archive / src / main / java / java / util / zip / ZipInputStream.java
1 /* 
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  * 
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  * 
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 package java.util.zip;
19
20
21 import java.io.EOFException;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.PushbackInputStream;
25 import java.util.jar.Attributes;
26 import java.util.jar.JarEntry;
27
28 import org.apache.harmony.archive.internal.nls.Messages;
29 import org.apache.harmony.luni.util.Util;
30
31 /**
32  * This class provides an implementation of {@code FilterInputStream} that
33  * uncompresses data from a <i>ZIP-archive</i> input stream.
34  * <p>
35  * A <i>ZIP-archive</i> is a collection of compressed (or uncompressed) files -
36  * the so called ZIP entries. Therefore when reading from a {@code
37  * ZipInputStream} first the entry's attributes will be retrieved with {@code
38  * getNextEntry} before its data is read.
39  * </p>
40  * <p>
41  * While {@code InflaterInputStream} can read a compressed <i>ZIP-archive</i>
42  * entry, this extension can read uncompressed entries as well.
43  * </p>
44  * <p>
45  * Use {@code ZipFile} if you can access the archive as a file directly.
46  * </p>
47  * 
48  * @see ZipEntry
49  * @see ZipFile
50  * @since Android 1.0
51  */
52 public class ZipInputStream extends InflaterInputStream implements ZipConstants {
53     static final int DEFLATED = 8;
54
55     static final int STORED = 0;
56
57     static final int ZIPDataDescriptorFlag = 8;
58
59     static final int ZIPLocalHeaderVersionNeeded = 20;
60
61     private boolean zipClosed = false;
62
63     private boolean entriesEnd = false;
64
65     private boolean hasDD = false;
66
67     private int entryIn = 0;
68
69     private int inRead, lastRead = 0;
70
71     ZipEntry currentEntry;
72
73     private final byte[] hdrBuf = new byte[LOCHDR - LOCVER];
74
75     private final CRC32 crc = new CRC32();
76
77     private byte[] nameBuf = new byte[256];
78
79     private char[] charBuf = new char[256];
80
81     /**
82      * Constructs a new {@code ZipInputStream} from the specified input stream.
83      * 
84      * @param stream
85      *            the input stream to representing a ZIP archive.
86      * @since Android 1.0
87      */
88     public ZipInputStream(InputStream stream) {
89         super(new PushbackInputStream(stream, BUF_SIZE), new Inflater(true));
90         if (stream == null) {
91             throw new NullPointerException();
92         }
93     }
94
95     /**
96      * Closes this {@code ZipInputStream}.
97      * 
98      * @throws IOException
99      *             if an {@code IOException} occurs.
100      * @since Android 1.0
101      */
102     @Override
103     public void close() throws IOException {
104         if (zipClosed != true) {
105             closeEntry(); // Close the current entry
106             zipClosed = true;
107             super.close();
108         }
109     }
110
111     /**
112      * Closes the current ZIP entry and positions to read the next entry.
113      * 
114      * @throws IOException
115      *             if an {@code IOException} occurs.
116      * @since Android 1.0
117      */
118     public void closeEntry() throws IOException {
119         if (zipClosed) {
120             throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
121         }
122         if (currentEntry == null) {
123             return;
124         }
125         if (currentEntry instanceof java.util.jar.JarEntry) {
126             Attributes temp = ((JarEntry) currentEntry).getAttributes();
127             if (temp != null && temp.containsKey("hidden")) { //$NON-NLS-1$
128                 return;
129             }
130         }
131         // Ensure all entry bytes are read
132         skip(Long.MAX_VALUE);
133         int inB, out;
134         if (currentEntry.compressionMethod == DEFLATED) {
135             inB = inf.getTotalIn();
136             out = inf.getTotalOut();
137         } else {
138             inB = inRead;
139             out = inRead;
140         }
141         int diff = 0;
142         // Pushback any required bytes
143         if ((diff = entryIn - inB) != 0) {
144             ((PushbackInputStream) in).unread(buf, len - diff, diff);
145         }
146
147         if (hasDD) {
148             in.read(hdrBuf, 0, EXTHDR);
149             if (getLong(hdrBuf, 0) != EXTSIG) {
150                 throw new ZipException(Messages.getString("archive.1F")); //$NON-NLS-1$
151             }
152             currentEntry.crc = getLong(hdrBuf, EXTCRC);
153             currentEntry.compressedSize = getLong(hdrBuf, EXTSIZ);
154             currentEntry.size = getLong(hdrBuf, EXTLEN);
155         }
156         if (currentEntry.crc != crc.getValue()) {
157             throw new ZipException(Messages.getString("archive.20")); //$NON-NLS-1$
158         }
159         if (currentEntry.compressedSize != inB || currentEntry.size != out) {
160             throw new ZipException(Messages.getString("archive.21")); //$NON-NLS-1$
161         }
162
163         inf.reset();
164         lastRead = inRead = entryIn = len = 0;
165         crc.reset();
166         currentEntry = null;
167     }
168
169     /**
170      * Reads the next entry from this {@code ZipInputStream}.
171      * 
172      * @return the next {@code ZipEntry} contained in the input stream.
173      * @throws IOException
174      *             if the stream is not positioned at the beginning of an entry
175      *             or if an other {@code IOException} occurs.
176      * @see ZipEntry
177      * @since Android 1.0
178      */
179     public ZipEntry getNextEntry() throws IOException {
180         if (currentEntry != null) {
181             closeEntry();
182         }
183         if (entriesEnd) {
184             return null;
185         }
186
187         int x = 0, count = 0;
188         while (count != 4) {
189             count += x = in.read(hdrBuf, count, 4 - count);
190             if (x == -1) {
191                 return null;
192             }
193         }
194         long hdr = getLong(hdrBuf, 0);
195         if (hdr == CENSIG) {
196             entriesEnd = true;
197             return null;
198         }
199         if (hdr != LOCSIG) {
200             return null;
201         }
202
203         // Read the local header
204         count = 0;
205         while (count != (LOCHDR - LOCVER)) {
206             count += x = in.read(hdrBuf, count, (LOCHDR - LOCVER) - count);
207             if (x == -1) {
208                 throw new EOFException();
209             }
210         }
211         int version = getShort(hdrBuf, 0) & 0xff;
212         if (version > ZIPLocalHeaderVersionNeeded) {
213             throw new ZipException(Messages.getString("archive.22")); //$NON-NLS-1$
214         }
215         int flags = getShort(hdrBuf, LOCFLG - LOCVER);
216         hasDD = ((flags & ZIPDataDescriptorFlag) == ZIPDataDescriptorFlag);
217         int cetime = getShort(hdrBuf, LOCTIM - LOCVER);
218         int cemodDate = getShort(hdrBuf, LOCTIM - LOCVER + 2);
219         int cecompressionMethod = getShort(hdrBuf, LOCHOW - LOCVER);
220         long cecrc = 0, cecompressedSize = 0, cesize = -1;
221         if (!hasDD) {
222             cecrc = getLong(hdrBuf, LOCCRC - LOCVER);
223             cecompressedSize = getLong(hdrBuf, LOCSIZ - LOCVER);
224             cesize = getLong(hdrBuf, LOCLEN - LOCVER);
225         }
226         int flen = getShort(hdrBuf, LOCNAM - LOCVER);
227         if (flen == 0) {
228             throw new ZipException(Messages.getString("archive.23")); //$NON-NLS-1$
229         }
230         int elen = getShort(hdrBuf, LOCEXT - LOCVER);
231
232         count = 0;
233         if (flen > nameBuf.length) {
234             nameBuf = new byte[flen];
235             charBuf = new char[flen];
236         }
237         while (count != flen) {
238             count += x = in.read(nameBuf, count, flen - count);
239             if (x == -1) {
240                 throw new EOFException();
241             }
242         }
243         currentEntry = createZipEntry(Util.convertUTF8WithBuf(nameBuf, charBuf,
244                 0, flen));
245         currentEntry.time = cetime;
246         currentEntry.modDate = cemodDate;
247         currentEntry.setMethod(cecompressionMethod);
248         if (cesize != -1) {
249             currentEntry.setCrc(cecrc);
250             currentEntry.setSize(cesize);
251             currentEntry.setCompressedSize(cecompressedSize);
252         }
253         if (elen > 0) {
254             count = 0;
255             byte[] e = new byte[elen];
256             while (count != elen) {
257                 count += x = in.read(e, count, elen - count);
258                 if (x == -1) {
259                     throw new EOFException();
260                 }
261             }
262             currentEntry.setExtra(e);
263         }
264         return currentEntry;
265     }
266
267     /* Read 4 bytes from the buffer and store it as an int */
268
269     @Override
270     public int read(byte[] buffer, int start, int length) throws IOException {
271         if (zipClosed) {
272             throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
273         }
274         if (inf.finished() || currentEntry == null) {
275             return -1;
276         }
277         // avoid int overflow, check null buffer
278         if (start <= buffer.length && length >= 0 && start >= 0
279                 && buffer.length - start >= length) {
280             if (currentEntry.compressionMethod == STORED) {
281                 int csize = (int) currentEntry.size;
282                 if (inRead >= csize) {
283                     return -1;
284                 }
285                 if (lastRead >= len) {
286                     lastRead = 0;
287                     if ((len = in.read(buf)) == -1) {
288                         return -1;
289                     }
290                     entryIn += len;
291                 }
292                 // BEGIN android-changed
293                 int toRead = length > (len - lastRead) ? len - lastRead : length;
294                 // END android-changed
295                 if ((csize - inRead) < toRead) {
296                     toRead = csize - inRead;
297                 }
298                 System.arraycopy(buf, lastRead, buffer, start, toRead);
299                 lastRead += toRead;
300                 inRead += toRead;
301                 crc.update(buffer, start, toRead);
302                 return toRead;
303             }
304             if (inf.needsInput()) {
305                 fill();
306                 if (len > 0) {
307                     entryIn += len;
308                 }
309             }
310             int read = 0;
311             try {
312                 read = inf.inflate(buffer, start, length);
313             } catch (DataFormatException e) {
314                 throw new ZipException(e.getMessage());
315             }
316             if (read == 0 && inf.finished()) {
317                 return -1;
318             }
319             crc.update(buffer, start, read);
320             return read;
321         }
322         throw new ArrayIndexOutOfBoundsException();
323     }
324
325     /**
326      * Skips up to the specified number of bytes in the current ZIP entry.
327      * 
328      * @param value
329      *            the number of bytes to skip.
330      * @return the number of bytes skipped.
331      * @throws IOException
332      *             if an {@code IOException} occurs.
333      * @since Android 1.0
334      */
335     @Override
336     public long skip(long value) throws IOException {
337         if (value >= 0) {
338             long skipped = 0;
339             byte[] b = new byte[1024];
340             while (skipped != value) {
341                 long rem = value - skipped;
342                 int x = read(b, 0, (int) (b.length > rem ? rem : b.length));
343                 if (x == -1) {
344                     return skipped;
345                 }
346                 skipped += x;
347             }
348             return skipped;
349         }
350         throw new IllegalArgumentException();
351 }
352
353     /**
354      * Returns 0 if the {@code EOF} has been reached, otherwise returns 1.
355      * 
356      * @return 0 after {@code EOF} of current entry, 1 otherwise.
357      * @throws IOException
358      *             if an IOException occurs.
359      * @since Android 1.0
360      */
361     @Override
362     public int available() throws IOException {
363         if (zipClosed) {
364             throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
365         }
366         if (currentEntry == null) {
367             return 1;
368         }
369         if (currentEntry.compressionMethod == STORED) {
370             if (inRead >= currentEntry.size) {
371                 return 0;
372             }
373         } else {
374             if (inf.finished()) {
375                 return 0;
376             }
377         }
378         return 1;
379     }
380
381     /**
382      * creates a {@link ZipEntry } with the given name.
383      * 
384      * @param name
385      *            the name of the entry.
386      * @return the created {@code ZipEntry}.
387      * @since Android 1.0
388      */
389     protected ZipEntry createZipEntry(String name) {
390         return new ZipEntry(name);
391     }
392
393     private int getShort(byte[] buffer, int off) {
394         return (buffer[off] & 0xFF) | ((buffer[off + 1] & 0xFF) << 8);
395     }
396
397     private long getLong(byte[] buffer, int off) {
398         long l = 0;
399         l |= (buffer[off] & 0xFF);
400         l |= (buffer[off + 1] & 0xFF) << 8;
401         l |= (buffer[off + 2] & 0xFF) << 16;
402         l |= ((long) (buffer[off + 3] & 0xFF)) << 24;
403         return l;
404     }
405 }