1 /* 2 * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.net.www.protocol.jar; 27 28 import java.io.*; 29 import java.net.*; 30 import java.nio.file.Files; 31 import java.nio.file.Path; 32 import java.nio.file.StandardCopyOption; 33 import java.util.*; 34 import java.util.jar.*; 35 import java.util.zip.ZipFile; 36 import java.util.zip.ZipEntry; 37 import java.security.CodeSigner; 38 import java.security.cert.Certificate; 39 import java.security.AccessController; 40 import java.security.PrivilegedAction; 41 import java.security.PrivilegedExceptionAction; 42 import java.security.PrivilegedActionException; 43 import sun.net.www.ParseUtil; 44 45 /* URL jar file is a common JarFile subtype used for JarURLConnection */ 46 public class URLJarFile extends JarFile { 47 48 /* 49 * Interface to be able to call retrieve() in plugin if 50 * this variable is set. 51 */ 52 private static URLJarFileCallBack callback = null; 53 54 /* Controller of the Jar File's closing */ 55 private URLJarFileCloseController closeController = null; 56 57 private static int BUF_SIZE = 2048; 58 59 private Manifest superMan; 60 private Attributes superAttr; 61 private Map<String, Attributes> superEntries; 62 63 static JarFile getJarFile(URL url) throws IOException { 64 return getJarFile(url, null); 65 } 66 67 static JarFile getJarFile(URL url, URLJarFileCloseController closeController) throws IOException { 68 if (isFileURL(url)) 69 return new URLJarFile(url, closeController); 70 else { 71 return retrieve(url, closeController); 72 } 73 } 74 75 /* 76 * Changed modifier from private to public in order to be able 77 * to instantiate URLJarFile from sun.plugin package. 78 */ 79 public URLJarFile(File file) throws IOException { 80 this(file, null); 81 } 82 83 /* 84 * Changed modifier from private to public in order to be able 85 * to instantiate URLJarFile from sun.plugin package. 86 */ 87 public URLJarFile(File file, URLJarFileCloseController closeController) throws IOException { 88 super(file, true, ZipFile.OPEN_READ | ZipFile.OPEN_DELETE); 89 this.closeController = closeController; 90 } 91 92 private URLJarFile(URL url, URLJarFileCloseController closeController) throws IOException { 93 super(ParseUtil.decode(url.getFile())); 94 this.closeController = closeController; 95 } 96 97 private static boolean isFileURL(URL url) { 98 if (url.getProtocol().equalsIgnoreCase("file")) { 99 /* 100 * Consider this a 'file' only if it's a LOCAL file, because 101 * 'file:' URLs can be accessible through ftp. 102 */ 103 String host = url.getHost(); 104 if (host == null || host.equals("") || host.equals("~") || 105 host.equalsIgnoreCase("localhost")) 106 return true; 107 } 108 return false; 109 } 110 111 /* 112 * close the jar file. 113 */ 114 protected void finalize() throws IOException { 115 close(); 116 } 117 118 /** 119 * Returns the <code>ZipEntry</code> for the given entry name or 120 * <code>null</code> if not found. 121 * 122 * @param name the JAR file entry name 123 * @return the <code>ZipEntry</code> for the given entry name or 124 * <code>null</code> if not found 125 * @see java.util.zip.ZipEntry 126 */ 127 public ZipEntry getEntry(String name) { 128 ZipEntry ze = super.getEntry(name); 129 if (ze != null) { 130 if (ze instanceof JarEntry) 131 return new URLJarFileEntry((JarEntry)ze); 132 else 133 throw new InternalError(super.getClass() + 134 " returned unexpected entry type " + 135 ze.getClass()); 136 } 137 return null; 138 } 139 140 public Manifest getManifest() throws IOException { 141 142 if (!isSuperMan()) { 143 return null; 144 } 145 146 Manifest man = new Manifest(); 147 Attributes attr = man.getMainAttributes(); 148 attr.putAll((Map)superAttr.clone()); 149 150 // now deep copy the manifest entries 151 if (superEntries != null) { 152 Map<String, Attributes> entries = man.getEntries(); 153 for (String key : superEntries.keySet()) { 154 Attributes at = superEntries.get(key); 155 entries.put(key, (Attributes) at.clone()); 156 } 157 } 158 159 return man; 160 } 161 162 /* If close controller is set the notify the controller about the pending close */ 163 public void close() throws IOException { 164 if (closeController != null) { 165 closeController.close(this); 166 } 167 super.close(); 168 } 169 170 // optimal side-effects 171 private synchronized boolean isSuperMan() throws IOException { 172 173 if (superMan == null) { 174 superMan = super.getManifest(); 175 } 176 177 if (superMan != null) { 178 superAttr = superMan.getMainAttributes(); 179 superEntries = superMan.getEntries(); 180 return true; 181 } else 182 return false; 183 } 184 185 /** 186 * Given a URL, retrieves a JAR file, caches it to disk, and creates a 187 * cached JAR file object. 188 */ 189 private static JarFile retrieve(final URL url) throws IOException { 190 return retrieve(url, null); 191 } 192 193 /** 194 * Given a URL, retrieves a JAR file, caches it to disk, and creates a 195 * cached JAR file object. 196 */ 197 private static JarFile retrieve(final URL url, final URLJarFileCloseController closeController) throws IOException { 198 /* 199 * See if interface is set, then call retrieve function of the class 200 * that implements URLJarFileCallBack interface (sun.plugin - to 201 * handle the cache failure for JARJAR file.) 202 */ 203 if (callback != null) 204 { 205 return callback.retrieve(url); 206 } 207 208 else 209 { 210 211 JarFile result = null; 212 213 /* get the stream before asserting privileges */ 214 try (final InputStream in = url.openConnection().getInputStream()) { 215 result = AccessController.doPrivileged( 216 new PrivilegedExceptionAction<JarFile>() { 217 public JarFile run() throws IOException { 218 Path tmpFile = Files.createTempFile("jar_cache", null); 219 try { 220 Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING); 221 JarFile jarFile = new URLJarFile(tmpFile.toFile(), closeController); 222 tmpFile.toFile().deleteOnExit(); 223 return jarFile; 224 } catch (Throwable thr) { 225 try { 226 Files.delete(tmpFile); 227 } catch (IOException ioe) { 228 thr.addSuppressed(ioe); 229 } 230 throw thr; 231 } 232 } 233 }); 234 } catch (PrivilegedActionException pae) { 235 throw (IOException) pae.getException(); 236 } 237 238 return result; 239 } 240 } 241 242 /* 243 * Set the call back interface to call retrive function in sun.plugin 244 * package if plugin is running. 245 */ 246 public static void setCallBack(URLJarFileCallBack cb) 247 { 248 callback = cb; 249 } 250 251 252 private class URLJarFileEntry extends JarEntry { 253 private JarEntry je; 254 255 URLJarFileEntry(JarEntry je) { 256 super(je); 257 this.je=je; 258 } 259 260 public Attributes getAttributes() throws IOException { 261 if (URLJarFile.this.isSuperMan()) { 262 Map<String, Attributes> e = URLJarFile.this.superEntries; 263 if (e != null) { 264 Attributes a = e.get(getName()); 265 if (a != null) 266 return (Attributes)a.clone(); 267 } 268 } 269 return null; 270 } 271 272 public java.security.cert.Certificate[] getCertificates() { 273 Certificate[] certs = je.getCertificates(); 274 return certs == null? null: certs.clone(); 275 } 276 277 public CodeSigner[] getCodeSigners() { 278 CodeSigner[] csg = je.getCodeSigners(); 279 return csg == null? null: csg.clone(); 280 } 281 } 282 283 public interface URLJarFileCloseController { 284 public void close(JarFile jarFile); 285 } 286 }