001/* ZipInputStream.java -- 002 Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package java.util.zip; 040 041import java.io.EOFException; 042import java.io.IOException; 043import java.io.InputStream; 044import 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 */ 056public 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}