OSDN Git Service

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