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