1 /* ZipInputStream.java -- 2 Copyright (C) 2001, 2002, 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 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.IOException; 43 import java.io.InputStream; 44 import java.io.UnsupportedEncodingException; 45 46 /** 47 * This is a FilterInputStream that reads the files in an zip archive 48 * one after another. It has a special method to get the zip entry of 49 * the next file. The zip entry contains information about the file name 50 * size, compressed size, CRC, etc. 51 * 52 * It includes support for STORED and DEFLATED entries. 53 * 54 * @author Jochen Hoenicke 55 */ 56 public class ZipInputStream extends InflaterInputStream implements ZipConstants 57 { 58 private CRC32 crc = new CRC32(); 59 private ZipEntry entry = null; 60 61 private int csize; 62 private int size; 63 private int method; 64 private int flags; 65 private int avail; 66 private boolean entryAtEOF; 67 68 /** 69 * Creates a new Zip input stream, reading a zip archive. 70 */ ZipInputStream(InputStream in)71 public ZipInputStream(InputStream in) 72 { 73 super(in, new Inflater(true)); 74 } 75 fillBuf()76 private void fillBuf() throws IOException 77 { 78 avail = len = in.read(buf, 0, buf.length); 79 } 80 readBuf(byte[] out, int offset, int length)81 private int readBuf(byte[] out, int offset, int length) throws IOException 82 { 83 if (avail <= 0) 84 { 85 fillBuf(); 86 if (avail <= 0) 87 return -1; 88 } 89 if (length > avail) 90 length = avail; 91 System.arraycopy(buf, len - avail, out, offset, length); 92 avail -= length; 93 return length; 94 } 95 readFully(byte[] out)96 private void readFully(byte[] out) throws IOException 97 { 98 int off = 0; 99 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 readLeByte()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 */ readLeShort()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 */ readLeInt()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 */ getNextEntry()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 readDataDescr()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 */ closeEntry()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 /* read will close this entry */ 242 return; 243 } 244 csize -= inf.getTotalIn(); 245 avail = inf.getRemaining(); 246 } 247 248 if (avail > csize && csize >= 0) 249 avail -= csize; 250 else 251 { 252 csize -= avail; 253 avail = 0; 254 while (csize != 0) 255 { 256 long skipped = in.skip(csize & 0xffffffffL); 257 if (skipped <= 0) 258 throw new ZipException("zip archive ends early."); 259 csize -= skipped; 260 } 261 } 262 263 size = 0; 264 crc.reset(); 265 if (method == ZipOutputStream.DEFLATED) 266 inf.reset(); 267 entry = null; 268 entryAtEOF = true; 269 } 270 available()271 public int available() throws IOException 272 { 273 return entryAtEOF ? 0 : 1; 274 } 275 276 /** 277 * Reads a byte from the current zip entry. 278 * @return the byte or -1 on EOF. 279 * @exception IOException if a i/o error occured. 280 * @exception ZipException if the deflated stream is corrupted. 281 */ read()282 public int read() throws IOException 283 { 284 byte[] b = new byte[1]; 285 if (read(b, 0, 1) <= 0) 286 return -1; 287 return b[0] & 0xff; 288 } 289 290 /** 291 * Reads a block of bytes from the current zip entry. 292 * @return the number of bytes read (may be smaller, even before 293 * EOF), or -1 on EOF. 294 * @exception IOException if a i/o error occured. 295 * @exception ZipException if the deflated stream is corrupted. 296 */ read(byte[] b, int off, int len)297 public int read(byte[] b, int off, int len) throws IOException 298 { 299 if (len == 0) 300 return 0; 301 if (crc == null) 302 throw new IOException("Stream closed."); 303 if (entry == null) 304 return -1; 305 boolean finished = false; 306 switch (method) 307 { 308 case ZipOutputStream.DEFLATED: 309 len = super.read(b, off, len); 310 if (len < 0) 311 { 312 if (!inf.finished()) 313 throw new ZipException("Inflater not finished!?"); 314 avail = inf.getRemaining(); 315 if ((flags & 8) != 0) 316 readDataDescr(); 317 318 if (inf.getTotalIn() != csize 319 || inf.getTotalOut() != size) 320 throw new ZipException("size mismatch: "+csize+";"+size+" <-> "+inf.getTotalIn()+";"+inf.getTotalOut()); 321 inf.reset(); 322 finished = true; 323 } 324 break; 325 326 case ZipOutputStream.STORED: 327 328 if (len > csize && csize >= 0) 329 len = csize; 330 331 len = readBuf(b, off, len); 332 if (len > 0) 333 { 334 csize -= len; 335 size -= len; 336 } 337 338 if (csize == 0) 339 finished = true; 340 else if (len < 0) 341 throw new ZipException("EOF in stored block"); 342 break; 343 } 344 345 if (len > 0) 346 crc.update(b, off, len); 347 348 if (finished) 349 { 350 if ((crc.getValue() & 0xffffffffL) != entry.getCrc()) 351 throw new ZipException("CRC mismatch"); 352 crc.reset(); 353 entry = null; 354 entryAtEOF = true; 355 } 356 return len; 357 } 358 359 /** 360 * Closes the zip file. 361 * @exception IOException if a i/o error occured. 362 */ close()363 public void close() throws IOException 364 { 365 super.close(); 366 crc = null; 367 entry = null; 368 entryAtEOF = true; 369 } 370 371 /** 372 * Creates a new zip entry for the given name. This is equivalent 373 * to new ZipEntry(name). 374 * @param name the name of the zip entry. 375 */ createZipEntry(String name)376 protected ZipEntry createZipEntry(String name) 377 { 378 return new ZipEntry(name); 379 } 380 } 381