1 18 19 package org.apache.tools.zip; 20 21 import java.io.File ; 22 import java.io.IOException ; 23 import java.io.InputStream ; 24 import java.io.RandomAccessFile ; 25 import java.io.UnsupportedEncodingException ; 26 import java.util.Calendar ; 27 import java.util.Date ; 28 import java.util.Enumeration ; 29 import java.util.Hashtable ; 30 import java.util.zip.Inflater ; 31 import java.util.zip.InflaterInputStream ; 32 import java.util.zip.ZipException ; 33 34 61 public class ZipFile { 62 63 67 private Hashtable entries = new Hashtable (509); 68 69 72 private Hashtable nameMap = new Hashtable (509); 73 74 private static final class OffsetEntry { 75 private long headerOffset = -1; 76 private long dataOffset = -1; 77 } 78 79 86 private String encoding = null; 87 88 91 private RandomAccessFile archive; 92 93 101 public ZipFile(File f) throws IOException { 102 this(f, null); 103 } 104 105 113 public ZipFile(String name) throws IOException { 114 this(new File (name), null); 115 } 116 117 126 public ZipFile(String name, String encoding) throws IOException { 127 this(new File (name), encoding); 128 } 129 130 139 public ZipFile(File f, String encoding) throws IOException { 140 this.encoding = encoding; 141 archive = new RandomAccessFile (f, "r"); 142 try { 143 populateFromCentralDirectory(); 144 resolveLocalFileHeaderData(); 145 } catch (IOException e) { 146 try { 147 archive.close(); 148 } catch (IOException e2) { 149 } 151 throw e; 152 } 153 } 154 155 160 public String getEncoding() { 161 return encoding; 162 } 163 164 168 public void close() throws IOException { 169 archive.close(); 170 } 171 172 177 public static void closeQuietly(ZipFile zipfile) { 178 if (zipfile != null) { 179 try { 180 zipfile.close(); 181 } catch (IOException e) { 182 } 184 } 185 } 186 187 191 public Enumeration getEntries() { 192 return entries.keys(); 193 } 194 195 202 public ZipEntry getEntry(String name) { 203 return (ZipEntry) nameMap.get(name); 204 } 205 206 213 public InputStream getInputStream(ZipEntry ze) 214 throws IOException , ZipException { 215 OffsetEntry offsetEntry = (OffsetEntry) entries.get(ze); 216 if (offsetEntry == null) { 217 return null; 218 } 219 long start = offsetEntry.dataOffset; 220 BoundedInputStream bis = 221 new BoundedInputStream(start, ze.getCompressedSize()); 222 switch (ze.getMethod()) { 223 case ZipEntry.STORED: 224 return bis; 225 case ZipEntry.DEFLATED: 226 bis.addDummy(); 227 return new InflaterInputStream (bis, new Inflater (true)); 228 default: 229 throw new ZipException ("Found unsupported compression method " 230 + ze.getMethod()); 231 } 232 } 233 234 private static final int CFH_LEN = 235 2 236 + 2 237 + 2 238 + 2 239 + 2 240 + 2 241 + 4 242 + 4 243 + 4 244 + 2 245 + 2 246 + 2 247 + 2 248 + 2 249 + 4 250 + 4; 251 252 260 private void populateFromCentralDirectory() 261 throws IOException { 262 positionAtCentralDirectory(); 263 264 byte[] cfh = new byte[CFH_LEN]; 265 266 byte[] signatureBytes = new byte[4]; 267 archive.readFully(signatureBytes); 268 long sig = ZipLong.getValue(signatureBytes); 269 final long cfhSig = ZipLong.getValue(ZipOutputStream.CFH_SIG); 270 while (sig == cfhSig) { 271 archive.readFully(cfh); 272 int off = 0; 273 ZipEntry ze = new ZipEntry(); 274 275 int versionMadeBy = ZipShort.getValue(cfh, off); 276 off += 2; 277 ze.setPlatform((versionMadeBy >> 8) & 0x0F); 278 279 off += 4; 281 ze.setMethod(ZipShort.getValue(cfh, off)); 282 off += 2; 283 284 long time = dosToJavaTime(ZipLong.getValue(cfh, off)); 288 ze.setTime(time); 289 off += 4; 290 291 ze.setCrc(ZipLong.getValue(cfh, off)); 292 off += 4; 293 294 ze.setCompressedSize(ZipLong.getValue(cfh, off)); 295 off += 4; 296 297 ze.setSize(ZipLong.getValue(cfh, off)); 298 off += 4; 299 300 int fileNameLen = ZipShort.getValue(cfh, off); 301 off += 2; 302 303 int extraLen = ZipShort.getValue(cfh, off); 304 off += 2; 305 306 int commentLen = ZipShort.getValue(cfh, off); 307 off += 2; 308 309 off += 2; 311 ze.setInternalAttributes(ZipShort.getValue(cfh, off)); 312 off += 2; 313 314 ze.setExternalAttributes(ZipLong.getValue(cfh, off)); 315 off += 4; 316 317 byte[] fileName = new byte[fileNameLen]; 318 archive.readFully(fileName); 319 ze.setName(getString(fileName)); 320 321 322 OffsetEntry offset = new OffsetEntry(); 324 offset.headerOffset = ZipLong.getValue(cfh, off); 325 entries.put(ze, offset); 327 328 nameMap.put(ze.getName(), ze); 329 330 archive.skipBytes(extraLen); 331 332 byte[] comment = new byte[commentLen]; 333 archive.readFully(comment); 334 ze.setComment(getString(comment)); 335 336 archive.readFully(signatureBytes); 337 sig = ZipLong.getValue(signatureBytes); 338 } 339 } 340 341 private static final int MIN_EOCD_SIZE = 342 4 343 + 2 344 345 + 2 346 347 + 2 348 349 + 2 350 + 4 351 352 353 + 4 354 + 2; 355 356 private static final int CFD_LOCATOR_OFFSET = 357 4 358 + 2 359 360 + 2 361 362 + 2 363 364 + 2 365 + 4; 366 367 372 private void positionAtCentralDirectory() 373 throws IOException { 374 boolean found = false; 375 long off = archive.length() - MIN_EOCD_SIZE; 376 if (off >= 0) { 377 archive.seek(off); 378 byte[] sig = ZipOutputStream.EOCD_SIG; 379 int curr = archive.read(); 380 while (curr != -1) { 381 if (curr == sig[0]) { 382 curr = archive.read(); 383 if (curr == sig[1]) { 384 curr = archive.read(); 385 if (curr == sig[2]) { 386 curr = archive.read(); 387 if (curr == sig[3]) { 388 found = true; 389 break; 390 } 391 } 392 } 393 } 394 archive.seek(--off); 395 curr = archive.read(); 396 } 397 } 398 if (!found) { 399 throw new ZipException ("archive is not a ZIP archive"); 400 } 401 archive.seek(off + CFD_LOCATOR_OFFSET); 402 byte[] cfdOffset = new byte[4]; 403 archive.readFully(cfdOffset); 404 archive.seek(ZipLong.getValue(cfdOffset)); 405 } 406 407 411 private static final long LFH_OFFSET_FOR_FILENAME_LENGTH = 412 4 413 + 2 414 + 2 415 + 2 416 + 2 417 + 2 418 + 4 419 + 4 420 + 4; 421 422 429 private void resolveLocalFileHeaderData() 430 throws IOException { 431 Enumeration e = getEntries(); 432 while (e.hasMoreElements()) { 433 ZipEntry ze = (ZipEntry) e.nextElement(); 434 OffsetEntry offsetEntry = (OffsetEntry) entries.get(ze); 435 long offset = offsetEntry.headerOffset; 436 archive.seek(offset + LFH_OFFSET_FOR_FILENAME_LENGTH); 437 byte[] b = new byte[2]; 438 archive.readFully(b); 439 int fileNameLen = ZipShort.getValue(b); 440 archive.readFully(b); 441 int extraFieldLen = ZipShort.getValue(b); 442 archive.skipBytes(fileNameLen); 443 byte[] localExtraData = new byte[extraFieldLen]; 444 archive.readFully(localExtraData); 445 ze.setExtra(localExtraData); 446 450 offsetEntry.dataOffset = offset + LFH_OFFSET_FOR_FILENAME_LENGTH 451 + 2 + 2 + fileNameLen + extraFieldLen; 452 } 453 } 454 455 461 protected static Date fromDosTime(ZipLong zipDosTime) { 462 long dosTime = zipDosTime.getValue(); 463 return new Date (dosToJavaTime(dosTime)); 464 } 465 466 469 private static long dosToJavaTime(long dosTime) { 470 Calendar cal = Calendar.getInstance(); 471 cal.set(Calendar.YEAR, (int) ((dosTime >> 25) & 0x7f) + 1980); 472 cal.set(Calendar.MONTH, (int) ((dosTime >> 21) & 0x0f) - 1); 473 cal.set(Calendar.DATE, (int) (dosTime >> 16) & 0x1f); 474 cal.set(Calendar.HOUR_OF_DAY, (int) (dosTime >> 11) & 0x1f); 475 cal.set(Calendar.MINUTE, (int) (dosTime >> 5) & 0x3f); 476 cal.set(Calendar.SECOND, (int) (dosTime << 1) & 0x3e); 477 return cal.getTime().getTime(); 478 } 479 480 481 489 protected String getString(byte[] bytes) throws ZipException { 490 if (encoding == null) { 491 return new String (bytes); 492 } else { 493 try { 494 return new String (bytes, encoding); 495 } catch (UnsupportedEncodingException uee) { 496 throw new ZipException (uee.getMessage()); 497 } 498 } 499 } 500 501 506 private class BoundedInputStream extends InputStream { 507 private long remaining; 508 private long loc; 509 private boolean addDummyByte = false; 510 511 BoundedInputStream(long start, long remaining) { 512 this.remaining = remaining; 513 loc = start; 514 } 515 516 public int read() throws IOException { 517 if (remaining-- <= 0) { 518 if (addDummyByte) { 519 addDummyByte = false; 520 return 0; 521 } 522 return -1; 523 } 524 synchronized (archive) { 525 archive.seek(loc++); 526 return archive.read(); 527 } 528 } 529 530 public int read(byte[] b, int off, int len) throws IOException { 531 if (remaining <= 0) { 532 if (addDummyByte) { 533 addDummyByte = false; 534 b[off] = 0; 535 return 1; 536 } 537 return -1; 538 } 539 540 if (len <= 0) { 541 return 0; 542 } 543 544 if (len > remaining) { 545 len = (int) remaining; 546 } 547 int ret = -1; 548 synchronized (archive) { 549 archive.seek(loc); 550 ret = archive.read(b, off, len); 551 } 552 if (ret > 0) { 553 loc += ret; 554 remaining -= ret; 555 } 556 return ret; 557 } 558 559 563 void addDummy() { 564 addDummyByte = true; 565 } 566 } 567 568 } 569 | Popular Tags |