001 /* ZipInputStream.java --
002 Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package java.util.zip;
040
041 import java.io.EOFException;
042 import java.io.IOException;
043 import java.io.InputStream;
044 import java.io.UnsupportedEncodingException;
045
046 /**
047 * This is a FilterInputStream that reads the files in an zip archive
048 * one after another. It has a special method to get the zip entry of
049 * the next file. The zip entry contains information about the file name
050 * size, compressed size, CRC, etc.
051 *
052 * It includes support for STORED and DEFLATED entries.
053 *
054 * @author Jochen Hoenicke
055 */
056 public class ZipInputStream extends InflaterInputStream implements ZipConstants
057 {
058 private CRC32 crc = new CRC32();
059 private ZipEntry entry = null;
060
061 private int csize;
062 private int size;
063 private int method;
064 private int flags;
065 private int avail;
066 private boolean entryAtEOF;
067
068 /**
069 * Creates a new Zip input stream, reading a zip archive.
070 */
071 public ZipInputStream(InputStream in)
072 {
073 super(in, new Inflater(true));
074 }
075
076 private void fillBuf() throws IOException
077 {
078 avail = len = in.read(buf, 0, buf.length);
079 }
080
081 private int readBuf(byte[] out, int offset, int length) throws IOException
082 {
083 if (avail <= 0)
084 {
085 fillBuf();
086 if (avail <= 0)
087 return -1;
088 }
089 if (length > avail)
090 length = avail;
091 System.arraycopy(buf, len - avail, out, offset, length);
092 avail -= length;
093 return length;
094 }
095
096 private void readFully(byte[] out) throws IOException
097 {
098 int off = 0;
099 int len = out.length;
100 while (len > 0)
101 {
102 int count = readBuf(out, off, len);
103 if (count == -1)
104 throw new EOFException();
105 off += count;
106 len -= count;
107 }
108 }
109
110 private int readLeByte() throws IOException
111 {
112 if (avail <= 0)
113 {
114 fillBuf();
115 if (avail <= 0)
116 throw new ZipException("EOF in header");
117 }
118 return buf[len - avail--] & 0xff;
119 }
120
121 /**
122 * Read an unsigned short in little endian byte order.
123 */
124 private int readLeShort() throws IOException
125 {
126 return readLeByte() | (readLeByte() << 8);
127 }
128
129 /**
130 * Read an int in little endian byte order.
131 */
132 private int readLeInt() throws IOException
133 {
134 return readLeShort() | (readLeShort() << 16);
135 }
136
137 /**
138 * Open the next entry from the zip archive, and return its description.
139 * If the previous entry wasn't closed, this method will close it.
140 */
141 public ZipEntry getNextEntry() throws IOException
142 {
143 if (crc == null)
144 throw new IOException("Stream closed.");
145 if (entry != null)
146 closeEntry();
147
148 int header = readLeInt();
149 if (header == CENSIG)
150 {
151 /* Central Header reached. */
152 close();
153 return null;
154 }
155 if (header != LOCSIG)
156 throw new ZipException("Wrong Local header signature: "
157 + Integer.toHexString(header));
158 /* skip version */
159 readLeShort();
160 flags = readLeShort();
161 method = readLeShort();
162 int dostime = readLeInt();
163 int crc = readLeInt();
164 csize = readLeInt();
165 size = readLeInt();
166 int nameLen = readLeShort();
167 int extraLen = readLeShort();
168
169 if (method == ZipOutputStream.STORED && csize != size)
170 throw new ZipException("Stored, but compressed != uncompressed");
171
172
173 byte[] buffer = new byte[nameLen];
174 readFully(buffer);
175 String name;
176 try
177 {
178 name = new String(buffer, "UTF-8");
179 }
180 catch (UnsupportedEncodingException uee)
181 {
182 throw new AssertionError(uee);
183 }
184
185 entry = createZipEntry(name);
186 entryAtEOF = false;
187 entry.setMethod(method);
188 if ((flags & 8) == 0)
189 {
190 entry.setCrc(crc & 0xffffffffL);
191 entry.setSize(size & 0xffffffffL);
192 entry.setCompressedSize(csize & 0xffffffffL);
193 }
194 entry.setDOSTime(dostime);
195 if (extraLen > 0)
196 {
197 byte[] extra = new byte[extraLen];
198 readFully(extra);
199 entry.setExtra(extra);
200 }
201
202 if (method == ZipOutputStream.DEFLATED && avail > 0)
203 {
204 System.arraycopy(buf, len - avail, buf, 0, avail);
205 len = avail;
206 avail = 0;
207 inf.setInput(buf, 0, len);
208 }
209 return entry;
210 }
211
212 private void readDataDescr() throws IOException
213 {
214 if (readLeInt() != EXTSIG)
215 throw new ZipException("Data descriptor signature not found");
216 entry.setCrc(readLeInt() & 0xffffffffL);
217 csize = readLeInt();
218 size = readLeInt();
219 entry.setSize(size & 0xffffffffL);
220 entry.setCompressedSize(csize & 0xffffffffL);
221 }
222
223 /**
224 * Closes the current zip entry and moves to the next one.
225 */
226 public void closeEntry() throws IOException
227 {
228 if (crc == null)
229 throw new IOException("Stream closed.");
230 if (entry == null)
231 return;
232
233 if (method == ZipOutputStream.DEFLATED)
234 {
235 if ((flags & 8) != 0)
236 {
237 /* We don't know how much we must skip, read until end. */
238 byte[] tmp = new byte[2048];
239 while (read(tmp) > 0)
240 ;
241
242 /* read will close this entry */
243 return;
244 }
245 csize -= inf.getTotalIn();
246 avail = inf.getRemaining();
247 }
248
249 if (avail > csize && csize >= 0)
250 avail -= csize;
251 else
252 {
253 csize -= avail;
254 avail = 0;
255 while (csize != 0)
256 {
257 long skipped = in.skip(csize & 0xffffffffL);
258 if (skipped <= 0)
259 throw new ZipException("zip archive ends early.");
260 csize -= skipped;
261 }
262 }
263
264 size = 0;
265 crc.reset();
266 if (method == ZipOutputStream.DEFLATED)
267 inf.reset();
268 entry = null;
269 entryAtEOF = true;
270 }
271
272 public int available() throws IOException
273 {
274 return entryAtEOF ? 0 : 1;
275 }
276
277 /**
278 * Reads a byte from the current zip entry.
279 * @return the byte or -1 on EOF.
280 * @exception IOException if a i/o error occured.
281 * @exception ZipException if the deflated stream is corrupted.
282 */
283 public int read() throws IOException
284 {
285 byte[] b = new byte[1];
286 if (read(b, 0, 1) <= 0)
287 return -1;
288 return b[0] & 0xff;
289 }
290
291 /**
292 * Reads a block of bytes from the current zip entry.
293 * @return the number of bytes read (may be smaller, even before
294 * EOF), or -1 on EOF.
295 * @exception IOException if a i/o error occured.
296 * @exception ZipException if the deflated stream is corrupted.
297 */
298 public int read(byte[] b, int off, int len) throws IOException
299 {
300 if (len == 0)
301 return 0;
302 if (crc == null)
303 throw new IOException("Stream closed.");
304 if (entry == null)
305 return -1;
306 boolean finished = false;
307 switch (method)
308 {
309 case ZipOutputStream.DEFLATED:
310 len = super.read(b, off, len);
311 if (len < 0)
312 {
313 if (!inf.finished())
314 throw new ZipException("Inflater not finished!?");
315 avail = inf.getRemaining();
316 if ((flags & 8) != 0)
317 readDataDescr();
318
319 if (inf.getTotalIn() != csize
320 || inf.getTotalOut() != size)
321 throw new ZipException("size mismatch: "+csize+";"+size+" <-> "+inf.getTotalIn()+";"+inf.getTotalOut());
322 inf.reset();
323 finished = true;
324 }
325 break;
326
327 case ZipOutputStream.STORED:
328
329 if (len > csize && csize >= 0)
330 len = csize;
331
332 len = readBuf(b, off, len);
333 if (len > 0)
334 {
335 csize -= len;
336 size -= len;
337 }
338
339 if (csize == 0)
340 finished = true;
341 else if (len < 0)
342 throw new ZipException("EOF in stored block");
343 break;
344 }
345
346 if (len > 0)
347 crc.update(b, off, len);
348
349 if (finished)
350 {
351 if ((crc.getValue() & 0xffffffffL) != entry.getCrc())
352 throw new ZipException("CRC mismatch");
353 crc.reset();
354 entry = null;
355 entryAtEOF = true;
356 }
357 return len;
358 }
359
360 /**
361 * Closes the zip file.
362 * @exception IOException if a i/o error occured.
363 */
364 public void close() throws IOException
365 {
366 super.close();
367 crc = null;
368 entry = null;
369 entryAtEOF = true;
370 }
371
372 /**
373 * Creates a new zip entry for the given name. This is equivalent
374 * to new ZipEntry(name).
375 * @param name the name of the zip entry.
376 */
377 protected ZipEntry createZipEntry(String name)
378 {
379 return new ZipEntry(name);
380 }
381 }