1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 package java.util.jar; 28 29 import java.io.*; 30 import java.lang.ref.SoftReference; 31 import java.util.*; 32 import java.util.stream.Stream; 33 import java.util.stream.StreamSupport; 34 import java.util.zip.*; 35 import java.security.CodeSigner; 36 import java.security.cert.Certificate; 37 import java.security.AccessController; 38 import sun.misc.IOUtils; 39 import sun.security.action.GetPropertyAction; 40 import sun.security.util.ManifestEntryVerifier; 41 import sun.security.util.SignatureFileVerifier; 42 43 /** 44 * The <code>JarFile</code> class is used to read the contents of a jar file 45 * from any file that can be opened with <code>java.io.RandomAccessFile</code>. 46 * It extends the class <code>java.util.zip.ZipFile</code> with support 47 * for reading an optional <code>Manifest</code> entry. The 48 * <code>Manifest</code> can be used to specify meta-information about the 49 * jar file and its entries. 50 * 51 * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor 52 * or method in this class will cause a {@link NullPointerException} to be 53 * thrown. 54 * 55 * If the verify flag is on when opening a signed jar file, the content of the 56 * file is verified against its signature embedded inside the file. Please note 57 * that the verification process does not include validating the signer's 58 * certificate. A caller should inspect the return value of 59 * {@link JarEntry#getCodeSigners()} to further determine if the signature 60 * can be trusted. 61 * 62 * @author David Connelly 63 * @see Manifest 64 * @see java.util.zip.ZipFile 65 * @see java.util.jar.JarEntry 66 * @since 1.2 67 */ 68 public 69 class JarFile extends ZipFile { 70 // Android-changed: Hold the Manifest via a hard reference. http://b/28692091 71 // private SoftReference<Manifest> manRef; 72 private Manifest manifest; 73 private JarEntry manEntry; 74 private JarVerifier jv; 75 private boolean jvInitialized; 76 private boolean verify; 77 78 // indicates if Class-Path attribute present (only valid if hasCheckedSpecialAttributes true) 79 private boolean hasClassPathAttribute; 80 // true if manifest checked for special attributes 81 private volatile boolean hasCheckedSpecialAttributes; 82 83 // Android-removed: SharedSecrets.setJavaUtilJarAccess 84 /* 85 // Set up JavaUtilJarAccess in SharedSecrets 86 static { 87 SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl()); 88 } 89 */ 90 91 /** 92 * The JAR manifest file name. 93 */ 94 public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; 95 96 /** 97 * Creates a new <code>JarFile</code> to read from the specified 98 * file <code>name</code>. The <code>JarFile</code> will be verified if 99 * it is signed. 100 * @param name the name of the jar file to be opened for reading 101 * @throws IOException if an I/O error has occurred 102 * @throws SecurityException if access to the file is denied 103 * by the SecurityManager 104 */ JarFile(String name)105 public JarFile(String name) throws IOException { 106 this(new File(name), true, ZipFile.OPEN_READ); 107 } 108 109 /** 110 * Creates a new <code>JarFile</code> to read from the specified 111 * file <code>name</code>. 112 * @param name the name of the jar file to be opened for reading 113 * @param verify whether or not to verify the jar file if 114 * it is signed. 115 * @throws IOException if an I/O error has occurred 116 * @throws SecurityException if access to the file is denied 117 * by the SecurityManager 118 */ JarFile(String name, boolean verify)119 public JarFile(String name, boolean verify) throws IOException { 120 this(new File(name), verify, ZipFile.OPEN_READ); 121 } 122 123 /** 124 * Creates a new <code>JarFile</code> to read from the specified 125 * <code>File</code> object. The <code>JarFile</code> will be verified if 126 * it is signed. 127 * @param file the jar file to be opened for reading 128 * @throws IOException if an I/O error has occurred 129 * @throws SecurityException if access to the file is denied 130 * by the SecurityManager 131 */ JarFile(File file)132 public JarFile(File file) throws IOException { 133 this(file, true, ZipFile.OPEN_READ); 134 } 135 136 137 /** 138 * Creates a new <code>JarFile</code> to read from the specified 139 * <code>File</code> object. 140 * @param file the jar file to be opened for reading 141 * @param verify whether or not to verify the jar file if 142 * it is signed. 143 * @throws IOException if an I/O error has occurred 144 * @throws SecurityException if access to the file is denied 145 * by the SecurityManager. 146 */ JarFile(File file, boolean verify)147 public JarFile(File file, boolean verify) throws IOException { 148 this(file, verify, ZipFile.OPEN_READ); 149 } 150 151 152 /** 153 * Creates a new <code>JarFile</code> to read from the specified 154 * <code>File</code> object in the specified mode. The mode argument 155 * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>. 156 * 157 * @param file the jar file to be opened for reading 158 * @param verify whether or not to verify the jar file if 159 * it is signed. 160 * @param mode the mode in which the file is to be opened 161 * @throws IOException if an I/O error has occurred 162 * @throws IllegalArgumentException 163 * if the <tt>mode</tt> argument is invalid 164 * @throws SecurityException if access to the file is denied 165 * by the SecurityManager 166 * @since 1.3 167 */ JarFile(File file, boolean verify, int mode)168 public JarFile(File file, boolean verify, int mode) throws IOException { 169 super(file, mode); 170 this.verify = verify; 171 } 172 173 /** 174 * Returns the jar file manifest, or <code>null</code> if none. 175 * 176 * @return the jar file manifest, or <code>null</code> if none 177 * 178 * @throws IllegalStateException 179 * may be thrown if the jar file has been closed 180 * @throws IOException if an I/O error has occurred 181 */ getManifest()182 public Manifest getManifest() throws IOException { 183 return getManifestFromReference(); 184 } 185 186 // BEGIN Android-changed: Fix JarFile to be thread safe. http://b/27826114 187 // A volatile field might also work instead of synchronized. http://b/81505612 188 // private Manifest getManifestFromReference() throws IOException { getManifestFromReference()189 private synchronized Manifest getManifestFromReference() throws IOException { 190 // END Android-changed: Fix JarFile to be thread safe. http://b/27826114 191 // Android-changed: Hold the Manifest via a hard reference. http://b/28692091 192 // Manifest man = manRef != null ? manRef.get() : null; 193 Manifest man = manifest; 194 if (man == null) { 195 196 JarEntry manEntry = getManEntry(); 197 198 // If found then load the manifest 199 if (manEntry != null) { 200 if (verify) { 201 byte[] b = getBytes(manEntry); 202 man = new Manifest(new ByteArrayInputStream(b)); 203 if (!jvInitialized) { 204 jv = new JarVerifier(b); 205 } 206 } else { 207 man = new Manifest(super.getInputStream(manEntry)); 208 } 209 // Android-changed: Hold the Manifest via a hard reference. http://b/28692091 210 // manRef = new SoftReference<>(man); 211 manifest = man; 212 } 213 } 214 return man; 215 } 216 getMetaInfEntryNames()217 private native String[] getMetaInfEntryNames(); 218 219 /** 220 * Returns the <code>JarEntry</code> for the given entry name or 221 * <code>null</code> if not found. 222 * 223 * @param name the jar file entry name 224 * @return the <code>JarEntry</code> for the given entry name or 225 * <code>null</code> if not found. 226 * 227 * @throws IllegalStateException 228 * may be thrown if the jar file has been closed 229 * 230 * @see java.util.jar.JarEntry 231 */ getJarEntry(String name)232 public JarEntry getJarEntry(String name) { 233 return (JarEntry)getEntry(name); 234 } 235 236 /** 237 * Returns the <code>ZipEntry</code> for the given entry name or 238 * <code>null</code> if not found. 239 * 240 * @param name the jar file entry name 241 * @return the <code>ZipEntry</code> for the given entry name or 242 * <code>null</code> if not found 243 * 244 * @throws IllegalStateException 245 * may be thrown if the jar file has been closed 246 * 247 * @see java.util.zip.ZipEntry 248 */ getEntry(String name)249 public ZipEntry getEntry(String name) { 250 ZipEntry ze = super.getEntry(name); 251 if (ze != null) { 252 return new JarFileEntry(ze); 253 } 254 return null; 255 } 256 257 private class JarEntryIterator implements Enumeration<JarEntry>, 258 Iterator<JarEntry> 259 { 260 final Enumeration<? extends ZipEntry> e = JarFile.super.entries(); 261 hasNext()262 public boolean hasNext() { 263 return e.hasMoreElements(); 264 } 265 next()266 public JarEntry next() { 267 ZipEntry ze = e.nextElement(); 268 return new JarFileEntry(ze); 269 } 270 hasMoreElements()271 public boolean hasMoreElements() { 272 return hasNext(); 273 } 274 nextElement()275 public JarEntry nextElement() { 276 return next(); 277 } 278 } 279 280 /** 281 * Returns an enumeration of the zip file entries. 282 */ entries()283 public Enumeration<JarEntry> entries() { 284 return new JarEntryIterator(); 285 } 286 287 @Override stream()288 public Stream<JarEntry> stream() { 289 return StreamSupport.stream(Spliterators.spliterator( 290 new JarEntryIterator(), size(), 291 Spliterator.ORDERED | Spliterator.DISTINCT | 292 Spliterator.IMMUTABLE | Spliterator.NONNULL), false); 293 } 294 295 private class JarFileEntry extends JarEntry { JarFileEntry(ZipEntry ze)296 JarFileEntry(ZipEntry ze) { 297 super(ze); 298 } getAttributes()299 public Attributes getAttributes() throws IOException { 300 Manifest man = JarFile.this.getManifest(); 301 if (man != null) { 302 return man.getAttributes(getName()); 303 } else { 304 return null; 305 } 306 } getCertificates()307 public Certificate[] getCertificates() { 308 try { 309 maybeInstantiateVerifier(); 310 } catch (IOException e) { 311 throw new RuntimeException(e); 312 } 313 if (certs == null && jv != null) { 314 certs = jv.getCerts(JarFile.this, this); 315 } 316 return certs == null ? null : certs.clone(); 317 } getCodeSigners()318 public CodeSigner[] getCodeSigners() { 319 try { 320 maybeInstantiateVerifier(); 321 } catch (IOException e) { 322 throw new RuntimeException(e); 323 } 324 if (signers == null && jv != null) { 325 signers = jv.getCodeSigners(JarFile.this, this); 326 } 327 return signers == null ? null : signers.clone(); 328 } 329 } 330 331 /* 332 * Ensures that the JarVerifier has been created if one is 333 * necessary (i.e., the jar appears to be signed.) This is done as 334 * a quick check to avoid processing of the manifest for unsigned 335 * jars. 336 */ maybeInstantiateVerifier()337 private void maybeInstantiateVerifier() throws IOException { 338 if (jv != null) { 339 return; 340 } 341 342 if (verify) { 343 String[] names = getMetaInfEntryNames(); 344 if (names != null) { 345 for (int i = 0; i < names.length; i++) { 346 String name = names[i].toUpperCase(Locale.ENGLISH); 347 if (name.endsWith(".DSA") || 348 name.endsWith(".RSA") || 349 name.endsWith(".EC") || 350 name.endsWith(".SF")) { 351 // Assume since we found a signature-related file 352 // that the jar is signed and that we therefore 353 // need a JarVerifier and Manifest 354 getManifest(); 355 return; 356 } 357 } 358 } 359 // No signature-related files; don't instantiate a 360 // verifier 361 verify = false; 362 } 363 } 364 365 366 /* 367 * Initializes the verifier object by reading all the manifest 368 * entries and passing them to the verifier. 369 */ initializeVerifier()370 private void initializeVerifier() { 371 ManifestEntryVerifier mev = null; 372 373 // Verify "META-INF/" entries... 374 try { 375 String[] names = getMetaInfEntryNames(); 376 if (names != null) { 377 for (int i = 0; i < names.length; i++) { 378 String uname = names[i].toUpperCase(Locale.ENGLISH); 379 if (MANIFEST_NAME.equals(uname) 380 || SignatureFileVerifier.isBlockOrSF(uname)) { 381 JarEntry e = getJarEntry(names[i]); 382 if (e == null) { 383 throw new JarException("corrupted jar file"); 384 } 385 if (mev == null) { 386 mev = new ManifestEntryVerifier 387 (getManifestFromReference()); 388 } 389 byte[] b = getBytes(e); 390 if (b != null && b.length > 0) { 391 jv.beginEntry(e, mev); 392 jv.update(b.length, b, 0, b.length, mev); 393 jv.update(-1, null, 0, 0, mev); 394 } 395 } 396 } 397 } 398 } catch (IOException ex) { 399 // if we had an error parsing any blocks, just 400 // treat the jar file as being unsigned 401 jv = null; 402 verify = false; 403 if (JarVerifier.debug != null) { 404 JarVerifier.debug.println("jarfile parsing error!"); 405 ex.printStackTrace(); 406 } 407 } 408 409 // if after initializing the verifier we have nothing 410 // signed, we null it out. 411 412 if (jv != null) { 413 414 jv.doneWithMeta(); 415 if (JarVerifier.debug != null) { 416 JarVerifier.debug.println("done with meta!"); 417 } 418 419 if (jv.nothingToVerify()) { 420 if (JarVerifier.debug != null) { 421 JarVerifier.debug.println("nothing to verify!"); 422 } 423 jv = null; 424 verify = false; 425 } 426 } 427 } 428 429 /* 430 * Reads all the bytes for a given entry. Used to process the 431 * META-INF files. 432 */ getBytes(ZipEntry ze)433 private byte[] getBytes(ZipEntry ze) throws IOException { 434 try (InputStream is = super.getInputStream(ze)) { 435 return IOUtils.readFully(is, (int)ze.getSize(), true); 436 } 437 } 438 439 /** 440 * Returns an input stream for reading the contents of the specified 441 * zip file entry. 442 * @param ze the zip file entry 443 * @return an input stream for reading the contents of the specified 444 * zip file entry 445 * @throws ZipException if a zip file format error has occurred 446 * @throws IOException if an I/O error has occurred 447 * @throws SecurityException if any of the jar file entries 448 * are incorrectly signed. 449 * @throws IllegalStateException 450 * may be thrown if the jar file has been closed 451 */ getInputStream(ZipEntry ze)452 public synchronized InputStream getInputStream(ZipEntry ze) 453 throws IOException 454 { 455 maybeInstantiateVerifier(); 456 if (jv == null) { 457 return super.getInputStream(ze); 458 } 459 if (!jvInitialized) { 460 initializeVerifier(); 461 jvInitialized = true; 462 // could be set to null after a call to 463 // initializeVerifier if we have nothing to 464 // verify 465 if (jv == null) 466 return super.getInputStream(ze); 467 } 468 469 // wrap a verifier stream around the real stream 470 return new JarVerifier.VerifierStream( 471 getManifestFromReference(), 472 ze instanceof JarFileEntry ? 473 (JarEntry) ze : getJarEntry(ze.getName()), 474 super.getInputStream(ze), 475 jv); 476 } 477 478 // Statics for hand-coded Boyer-Moore search 479 private static final char[] CLASSPATH_CHARS = {'c','l','a','s','s','-','p','a','t','h'}; 480 // The bad character shift for "class-path" 481 private static final int[] CLASSPATH_LASTOCC; 482 // The good suffix shift for "class-path" 483 private static final int[] CLASSPATH_OPTOSFT; 484 485 static { 486 CLASSPATH_LASTOCC = new int[128]; 487 CLASSPATH_OPTOSFT = new int[10]; 488 CLASSPATH_LASTOCC[(int)'c'] = 1; 489 CLASSPATH_LASTOCC[(int)'l'] = 2; 490 CLASSPATH_LASTOCC[(int)'s'] = 5; 491 CLASSPATH_LASTOCC[(int)'-'] = 6; 492 CLASSPATH_LASTOCC[(int)'p'] = 7; 493 CLASSPATH_LASTOCC[(int)'a'] = 8; 494 CLASSPATH_LASTOCC[(int)'t'] = 9; 495 CLASSPATH_LASTOCC[(int)'h'] = 10; 496 for (int i=0; i<9; i++) 497 CLASSPATH_OPTOSFT[i] = 10; 498 CLASSPATH_OPTOSFT[9]=1; 499 } 500 501 // BEGIN Android-changed: Fix JarFile to be thread safe. http://b/27826114 502 // A volatile field might also work instead of synchronized. http://b/81505612 503 // private JarEntry getManEntry() { getManEntry()504 private synchronized JarEntry getManEntry() { 505 // END Android-changed: Fix JarFile to be thread safe. http://b/27826114 506 if (manEntry == null) { 507 // First look up manifest entry using standard name 508 manEntry = getJarEntry(MANIFEST_NAME); 509 if (manEntry == null) { 510 // If not found, then iterate through all the "META-INF/" 511 // entries to find a match. 512 String[] names = getMetaInfEntryNames(); 513 if (names != null) { 514 for (int i = 0; i < names.length; i++) { 515 if (MANIFEST_NAME.equals( 516 names[i].toUpperCase(Locale.ENGLISH))) { 517 manEntry = getJarEntry(names[i]); 518 break; 519 } 520 } 521 } 522 } 523 } 524 return manEntry; 525 } 526 527 /** 528 * Returns {@code true} iff this JAR file has a manifest with the 529 * Class-Path attribute 530 * @hide 531 */ 532 // Android-changed: Make hasClassPathAttribute() @hide public, for internal use. 533 // Used by URLClassPath.JarLoader. 534 // boolean hasClassPathAttribute() throws IOException { hasClassPathAttribute()535 public boolean hasClassPathAttribute() throws IOException { 536 checkForSpecialAttributes(); 537 return hasClassPathAttribute; 538 } 539 540 /** 541 * Returns true if the pattern {@code src} is found in {@code b}. 542 * The {@code lastOcc} and {@code optoSft} arrays are the precomputed 543 * bad character and good suffix shifts. 544 */ match(char[] src, byte[] b, int[] lastOcc, int[] optoSft)545 private boolean match(char[] src, byte[] b, int[] lastOcc, int[] optoSft) { 546 int len = src.length; 547 int last = b.length - len; 548 int i = 0; 549 next: 550 while (i<=last) { 551 for (int j=(len-1); j>=0; j--) { 552 char c = (char) b[i+j]; 553 c = (((c-'A')|('Z'-c)) >= 0) ? (char)(c + 32) : c; 554 if (c != src[j]) { 555 i += Math.max(j + 1 - lastOcc[c&0x7F], optoSft[j]); 556 continue next; 557 } 558 } 559 return true; 560 } 561 return false; 562 } 563 564 /** 565 * On first invocation, check if the JAR file has the Class-Path 566 * attribute. A no-op on subsequent calls. 567 */ checkForSpecialAttributes()568 private void checkForSpecialAttributes() throws IOException { 569 if (hasCheckedSpecialAttributes) return; 570 // Android-changed: Special handling of well-known .jar files specific to OpenJDK. 571 // if (!isKnownNotToHaveSpecialAttributes()) { 572 { 573 JarEntry manEntry = getManEntry(); 574 if (manEntry != null) { 575 byte[] b = getBytes(manEntry); 576 if (match(CLASSPATH_CHARS, b, CLASSPATH_LASTOCC, CLASSPATH_OPTOSFT)) 577 hasClassPathAttribute = true; 578 } 579 } 580 hasCheckedSpecialAttributes = true; 581 } 582 583 584 // Android-removed: Special handling of well-known .jar files specific to OpenJDK. 585 /* 586 private static String javaHome; 587 private static volatile String[] jarNames; 588 private boolean isKnownNotToHaveSpecialAttributes() { 589 // Optimize away even scanning of manifest for jar files we 590 // deliver which don't have a class-path attribute. If one of 591 // these jars is changed to include such an attribute this code 592 // must be changed. 593 if (javaHome == null) { 594 javaHome = AccessController.doPrivileged( 595 new GetPropertyAction("java.home")); 596 } 597 if (jarNames == null) { 598 String[] names = new String[11]; 599 String fileSep = File.separator; 600 int i = 0; 601 names[i++] = fileSep + "rt.jar"; 602 names[i++] = fileSep + "jsse.jar"; 603 names[i++] = fileSep + "jce.jar"; 604 names[i++] = fileSep + "charsets.jar"; 605 names[i++] = fileSep + "dnsns.jar"; 606 names[i++] = fileSep + "zipfs.jar"; 607 names[i++] = fileSep + "localedata.jar"; 608 names[i++] = fileSep = "cldrdata.jar"; 609 names[i++] = fileSep + "sunjce_provider.jar"; 610 names[i++] = fileSep + "sunpkcs11.jar"; 611 names[i++] = fileSep + "sunec.jar"; 612 jarNames = names; 613 } 614 615 String name = getName(); 616 String localJavaHome = javaHome; 617 if (name.startsWith(localJavaHome)) { 618 String[] names = jarNames; 619 for (int i = 0; i < names.length; i++) { 620 if (name.endsWith(names[i])) { 621 return true; 622 } 623 } 624 } 625 return false; 626 } 627 */ 628 629 // Android-removed: Unused method ensureInitialization(). 630 /* 631 private synchronized void ensureInitialization() { 632 try { 633 maybeInstantiateVerifier(); 634 } catch (IOException e) { 635 throw new RuntimeException(e); 636 } 637 if (jv != null && !jvInitialized) { 638 initializeVerifier(); 639 jvInitialized = true; 640 } 641 } 642 */ 643 newEntry(ZipEntry ze)644 JarEntry newEntry(ZipEntry ze) { 645 return new JarFileEntry(ze); 646 } 647 648 // Android-removed: Unused methods entryNames(), entries2(). 649 /* 650 Enumeration<String> entryNames(CodeSource[] cs) { 651 ensureInitialization(); 652 if (jv != null) { 653 return jv.entryNames(this, cs); 654 } 655 656 /* 657 * JAR file has no signed content. Is there a non-signing 658 * code source? 659 * 660 boolean includeUnsigned = false; 661 for (int i = 0; i < cs.length; i++) { 662 if (cs[i].getCodeSigners() == null) { 663 includeUnsigned = true; 664 break; 665 } 666 } 667 if (includeUnsigned) { 668 return unsignedEntryNames(); 669 } else { 670 return new Enumeration<String>() { 671 672 public boolean hasMoreElements() { 673 return false; 674 } 675 676 public String nextElement() { 677 throw new NoSuchElementException(); 678 } 679 }; 680 } 681 } 682 683 /** 684 * Returns an enumeration of the zip file entries 685 * excluding internal JAR mechanism entries and including 686 * signed entries missing from the ZIP directory. 687 * 688 Enumeration<JarEntry> entries2() { 689 ensureInitialization(); 690 if (jv != null) { 691 return jv.entries2(this, super.entries()); 692 } 693 694 // screen out entries which are never signed 695 final Enumeration<? extends ZipEntry> enum_ = super.entries(); 696 return new Enumeration<JarEntry>() { 697 698 ZipEntry entry; 699 700 public boolean hasMoreElements() { 701 if (entry != null) { 702 return true; 703 } 704 while (enum_.hasMoreElements()) { 705 ZipEntry ze = enum_.nextElement(); 706 if (JarVerifier.isSigningRelated(ze.getName())) { 707 continue; 708 } 709 entry = ze; 710 return true; 711 } 712 return false; 713 } 714 715 public JarFileEntry nextElement() { 716 if (hasMoreElements()) { 717 ZipEntry ze = entry; 718 entry = null; 719 return new JarFileEntry(ze); 720 } 721 throw new NoSuchElementException(); 722 } 723 }; 724 } 725 726 CodeSource[] getCodeSources(URL url) { 727 ensureInitialization(); 728 if (jv != null) { 729 return jv.getCodeSources(this, url); 730 } 731 732 /* 733 * JAR file has no signed content. Is there a non-signing 734 * code source? 735 * 736 Enumeration<String> unsigned = unsignedEntryNames(); 737 if (unsigned.hasMoreElements()) { 738 return new CodeSource[]{JarVerifier.getUnsignedCS(url)}; 739 } else { 740 return null; 741 } 742 } 743 744 private Enumeration<String> unsignedEntryNames() { 745 final Enumeration<JarEntry> entries = entries(); 746 return new Enumeration<String>() { 747 748 String name; 749 750 /* 751 * Grab entries from ZIP directory but screen out 752 * metadata. 753 * 754 public boolean hasMoreElements() { 755 if (name != null) { 756 return true; 757 } 758 while (entries.hasMoreElements()) { 759 String value; 760 ZipEntry e = entries.nextElement(); 761 value = e.getName(); 762 if (e.isDirectory() || JarVerifier.isSigningRelated(value)) { 763 continue; 764 } 765 name = value; 766 return true; 767 } 768 return false; 769 } 770 771 public String nextElement() { 772 if (hasMoreElements()) { 773 String value = name; 774 name = null; 775 return value; 776 } 777 throw new NoSuchElementException(); 778 } 779 }; 780 } 781 782 CodeSource getCodeSource(URL url, String name) { 783 ensureInitialization(); 784 if (jv != null) { 785 if (jv.eagerValidation) { 786 CodeSource cs = null; 787 JarEntry je = getJarEntry(name); 788 if (je != null) { 789 cs = jv.getCodeSource(url, this, je); 790 } else { 791 cs = jv.getCodeSource(url, name); 792 } 793 return cs; 794 } else { 795 return jv.getCodeSource(url, name); 796 } 797 } 798 799 return JarVerifier.getUnsignedCS(url); 800 } 801 802 void setEagerValidation(boolean eager) { 803 try { 804 maybeInstantiateVerifier(); 805 } catch (IOException e) { 806 throw new RuntimeException(e); 807 } 808 if (jv != null) { 809 jv.setEagerValidation(eager); 810 } 811 } 812 813 List<Object> getManifestDigests() { 814 ensureInitialization(); 815 if (jv != null) { 816 return jv.getManifestDigests(); 817 } 818 return new ArrayList<Object>(); 819 } 820 */ 821 } 822