1 /* java.util.zip.ZipInputStream
2    Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package java.util.zip;
40 
41 import java.io.EOFException;
42 import java.io.InputStream;
43 import java.io.IOException;
44 
45 /**
46  * This is a FilterInputStream that reads the files in an zip archive
47  * one after another.  It has a special method to get the zip entry of
48  * the next file.  The zip entry contains information about the file name
49  * size, compressed size, CRC, etc.
50  *
51  * It includes support for STORED and DEFLATED entries.
52  *
53  * @author Jochen Hoenicke
54  */
55 public class ZipInputStream extends InflaterInputStream implements ZipConstants
56 {
57   private CRC32 crc = new CRC32();
58   private ZipEntry entry = null;
59 
60   private int csize;
61   private int size;
62   private int method;
63   private int flags;
64   private int avail;
65   private boolean entryAtEOF;
66 
67   /**
68    * Creates a new Zip input stream, reading a zip archive.
69    */
ZipInputStream(InputStream in)70   public ZipInputStream(InputStream in)
71   {
72     super(in, new Inflater(true));
73   }
74 
fillBuf()75   private void fillBuf() throws IOException
76   {
77     avail = len = in.read(buf, 0, buf.length);
78   }
79 
readBuf(byte[] out, int offset, int length)80   private int readBuf(byte[] out, int offset, int length) throws IOException
81   {
82     if (avail <= 0)
83       {
84 	fillBuf();
85 	if (avail <= 0)
86 	  return -1;
87       }
88     if (length > avail)
89       length = avail;
90     System.arraycopy(buf, len - avail, out, offset, length);
91     avail -= length;
92     return length;
93   }
94 
readFully(byte[] out)95   private void readFully(byte[] out) throws IOException
96   {
97     int off = 0;
98     int len = out.length;
99     while (len > 0)
100       {
101 	int count = readBuf(out, off, len);
102 	if (count == -1)
103 	  throw new EOFException();
104 	off += count;
105 	len -= count;
106       }
107   }
108 
readLeByte()109   private final int readLeByte() throws IOException
110   {
111     if (avail <= 0)
112       {
113 	fillBuf();
114 	if (avail <= 0)
115 	  throw new ZipException("EOF in header");
116       }
117     return buf[len - avail--] & 0xff;
118   }
119 
120   /**
121    * Read an unsigned short in little endian byte order.
122    */
readLeShort()123   private final int readLeShort() throws IOException
124   {
125     return readLeByte() | (readLeByte() << 8);
126   }
127 
128   /**
129    * Read an int in little endian byte order.
130    */
readLeInt()131   private final int readLeInt() throws IOException
132   {
133     return readLeShort() | (readLeShort() << 16);
134   }
135 
136   /**
137    * Open the next entry from the zip archive, and return its description.
138    * If the previous entry wasn't closed, this method will close it.
139    */
getNextEntry()140   public ZipEntry getNextEntry() throws IOException
141   {
142     if (crc == null)
143       throw new IOException("Stream closed.");
144     if (entry != null)
145       closeEntry();
146 
147     int header = readLeInt();
148     if (header == CENSIG)
149       {
150 	/* Central Header reached. */
151 	close();
152 	return null;
153       }
154     if (header != LOCSIG)
155       throw new ZipException("Wrong Local header signature: "
156 			     + Integer.toHexString(header));
157     /* skip version */
158     readLeShort();
159     flags = readLeShort();
160     method = readLeShort();
161     int dostime = readLeInt();
162     int crc = readLeInt();
163     csize = readLeInt();
164     size = readLeInt();
165     int nameLen = readLeShort();
166     int extraLen = readLeShort();
167 
168     if (method == ZipOutputStream.STORED && csize != size)
169       throw new ZipException("Stored, but compressed != uncompressed");
170 
171 
172     byte[] buffer = new byte[nameLen];
173     readFully(buffer);
174     String name = new String(buffer);
175 
176     entry = createZipEntry(name);
177     entryAtEOF = false;
178     entry.setMethod(method);
179     if ((flags & 8) == 0)
180       {
181 	entry.setCrc(crc & 0xffffffffL);
182 	entry.setSize(size & 0xffffffffL);
183 	entry.setCompressedSize(csize & 0xffffffffL);
184       }
185     entry.setDOSTime(dostime);
186     if (extraLen > 0)
187       {
188 	byte[] extra = new byte[extraLen];
189 	readFully(extra);
190 	entry.setExtra(extra);
191       }
192 
193     if (method == ZipOutputStream.DEFLATED && avail > 0)
194       {
195 	System.arraycopy(buf, len - avail, buf, 0, avail);
196 	len = avail;
197 	avail = 0;
198 	inf.setInput(buf, 0, len);
199       }
200     return entry;
201   }
202 
readDataDescr()203   private void readDataDescr() throws IOException
204   {
205     if (readLeInt() != EXTSIG)
206       throw new ZipException("Data descriptor signature not found");
207     entry.setCrc(readLeInt() & 0xffffffffL);
208     csize = readLeInt();
209     size = readLeInt();
210     entry.setSize(size & 0xffffffffL);
211     entry.setCompressedSize(csize & 0xffffffffL);
212   }
213 
214   /**
215    * Closes the current zip entry and moves to the next one.
216    */
closeEntry()217   public void closeEntry() throws IOException
218   {
219     if (crc == null)
220       throw new IOException("Stream closed.");
221     if (entry == null)
222       return;
223 
224     if (method == ZipOutputStream.DEFLATED)
225       {
226 	if ((flags & 8) != 0)
227 	  {
228 	    /* We don't know how much we must skip, read until end. */
229 	    byte[] tmp = new byte[2048];
230 	    while (read(tmp) > 0)
231 	      ;
232 	    /* read will close this entry */
233 	    return;
234 	  }
235 	csize -= inf.getTotalIn();
236 	avail = inf.getRemaining();
237       }
238 
239     if (avail > csize && csize >= 0)
240       avail -= csize;
241     else
242       {
243 	csize -= avail;
244 	avail = 0;
245 	while (csize != 0)
246 	  {
247 	    long skipped = in.skip(csize & 0xffffffffL);
248 	    if (skipped <= 0)
249 	      throw new ZipException("zip archive ends early.");
250 	    csize -= skipped;
251 	  }
252       }
253 
254     size = 0;
255     crc.reset();
256     if (method == ZipOutputStream.DEFLATED)
257       inf.reset();
258     entry = null;
259     entryAtEOF = true;
260   }
261 
available()262   public int available() throws IOException
263   {
264     return entryAtEOF ? 0 : 1;
265   }
266 
267   /**
268    * Reads a byte from the current zip entry.
269    * @return the byte or -1 on EOF.
270    * @exception IOException if a i/o error occured.
271    * @exception ZipException if the deflated stream is corrupted.
272    */
read()273   public int read() throws IOException
274   {
275     byte[] b = new byte[1];
276     if (read(b, 0, 1) <= 0)
277       return -1;
278     return b[0] & 0xff;
279   }
280 
281   /**
282    * Reads a block of bytes from the current zip entry.
283    * @return the number of bytes read (may be smaller, even before
284    * EOF), or -1 on EOF.
285    * @exception IOException if a i/o error occured.
286    * @exception ZipException if the deflated stream is corrupted.
287    */
read(byte[] b, int off, int len)288   public int read(byte[] b, int off, int len) throws IOException
289   {
290     if (len == 0)
291       return 0;
292     if (crc == null)
293       throw new IOException("Stream closed.");
294     if (entry == null)
295       return -1;
296     boolean finished = false;
297     switch (method)
298       {
299       case ZipOutputStream.DEFLATED:
300 	len = super.read(b, off, len);
301 	if (len < 0)
302 	  {
303 	    if (!inf.finished())
304 	      throw new ZipException("Inflater not finished!?");
305 	    avail = inf.getRemaining();
306 	    if ((flags & 8) != 0)
307 	      readDataDescr();
308 
309 	    if (inf.getTotalIn() != csize
310 		|| inf.getTotalOut() != size)
311 	      throw new ZipException("size mismatch: "+csize+";"+size+" <-> "+inf.getTotalIn()+";"+inf.getTotalOut());
312 	    inf.reset();
313 	    finished = true;
314 	  }
315 	break;
316 
317       case ZipOutputStream.STORED:
318 
319 	if (len > csize && csize >= 0)
320 	  len = csize;
321 
322 	len = readBuf(b, off, len);
323 	if (len > 0)
324 	  {
325 	    csize -= len;
326 	    size -= len;
327 	  }
328 
329 	if (csize == 0)
330 	  finished = true;
331 	else if (len < 0)
332 	  throw new ZipException("EOF in stored block");
333 	break;
334       }
335 
336     if (len > 0)
337       crc.update(b, off, len);
338 
339     if (finished)
340       {
341 	if ((crc.getValue() & 0xffffffffL) != entry.getCrc())
342 	  throw new ZipException("CRC mismatch");
343 	crc.reset();
344 	entry = null;
345 	entryAtEOF = true;
346       }
347     return len;
348   }
349 
350   /**
351    * Closes the zip file.
352    * @exception IOException if a i/o error occured.
353    */
close()354   public void close() throws IOException
355   {
356     super.close();
357     crc = null;
358     entry = null;
359     entryAtEOF = true;
360   }
361 
362   /**
363    * Creates a new zip entry for the given name.  This is equivalent
364    * to new ZipEntry(name).
365    * @param name the name of the zip entry.
366    */
createZipEntry(String name)367   protected ZipEntry createZipEntry(String name)
368   {
369     return new ZipEntry(name);
370   }
371 }
372