1 /* 2 * Copyright (c) 2001, 2016, 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 Runtime.Version version = "runtime".equals(url.getRef()) 70 ? JarFile.runtimeVersion() 71 : JarFile.baseVersion(); 72 return new URLJarFile(url, closeController, version); 73 } else { 74 return retrieve(url, closeController); 75 } 76 } 77 78 /* 79 * Changed modifier from private to public in order to be able 80 * to instantiate URLJarFile from sun.plugin package. 81 */ 82 public URLJarFile(File file) throws IOException { 83 this(file, null); 84 } 85 86 /* 87 * Changed modifier from private to public in order to be able 88 * to instantiate URLJarFile from sun.plugin package. 89 */ 90 public URLJarFile(File file, URLJarFileCloseController closeController) throws IOException { 91 super(file, true, ZipFile.OPEN_READ | ZipFile.OPEN_DELETE); 92 this.closeController = closeController; 93 } 94 95 private URLJarFile(File file, URLJarFileCloseController closeController, Runtime.Version version) 96 throws IOException { 97 super(file, true, ZipFile.OPEN_READ | ZipFile.OPEN_DELETE, version); 98 this.closeController = closeController; 99 } 100 101 private URLJarFile(URL url, URLJarFileCloseController closeController, Runtime.Version version) 102 throws IOException { 103 super(new File(ParseUtil.decode(url.getFile())), true, ZipFile.OPEN_READ, version); 104 this.closeController = closeController; 105 } 106 107 private static boolean isFileURL(URL url) { 108 if (url.getProtocol().equalsIgnoreCase("file")) { 109 /* 110 * Consider this a 'file' only if it's a LOCAL file, because 111 * 'file:' URLs can be accessible through ftp. 112 */ 113 String host = url.getHost(); 114 if (host == null || host.equals("") || host.equals("~") || 115 host.equalsIgnoreCase("localhost")) 116 return true; 117 } 118 return false; 119 } 120 121 /** 122 * Returns the <code>ZipEntry</code> for the given entry name or 123 * <code>null</code> if not found. 124 * 125 * @param name the JAR file entry name 126 * @return the <code>ZipEntry</code> for the given entry name or 127 * <code>null</code> if not found 128 * @see java.util.zip.ZipEntry 129 */ 130 public ZipEntry getEntry(String name) { 131 ZipEntry ze = super.getEntry(name); 132 if (ze != null) { 133 if (ze instanceof JarEntry) 134 return new URLJarFileEntry((JarEntry)ze); 135 else 136 throw new InternalError(super.getClass() + 137 " returned unexpected entry type " + 138 ze.getClass()); 139 } 140 return null; 141 } 142 143 public Manifest getManifest() throws IOException { 144 145 if (!isSuperMan()) { 146 return null; 147 } 148 149 Manifest man = new Manifest(); 150 Attributes attr = man.getMainAttributes(); 151 attr.putAll((Map)superAttr.clone()); 152 153 // now deep copy the manifest entries 154 if (superEntries != null) { 155 Map<String, Attributes> entries = man.getEntries(); 156 for (String key : superEntries.keySet()) { 157 Attributes at = superEntries.get(key); 158 entries.put(key, (Attributes) at.clone()); 159 } 160 } 161 162 return man; 163 } 164 165 /* If close controller is set the notify the controller about the pending close */ 166 public void close() throws IOException { 167 if (closeController != null) { 168 closeController.close(this); 169 } 170 super.close(); 171 } 172 173 // optimal side-effects 174 private synchronized boolean isSuperMan() throws IOException { 175 176 if (superMan == null) { 177 superMan = super.getManifest(); 178 } 179 180 if (superMan != null) { 181 superAttr = superMan.getMainAttributes(); 182 superEntries = superMan.getEntries(); 183 return true; 184 } else 185 return false; 186 } 187 188 /** 189 * Given a URL, retrieves a JAR file, caches it to disk, and creates a 190 * cached JAR file object. 191 */ 192 private static JarFile retrieve(final URL url, final URLJarFileCloseController closeController) throws IOException { 193 /* 194 * See if interface is set, then call retrieve function of the class 195 * that implements URLJarFileCallBack interface (sun.plugin - to 196 * handle the cache failure for JARJAR file.) 197 */ 198 if (callback != null) 199 { 200 return callback.retrieve(url); 201 } 202 203 else 204 { 205 206 JarFile result = null; 207 Runtime.Version version = "runtime".equals(url.getRef()) 208 ? JarFile.runtimeVersion() 209 : JarFile.baseVersion(); 210 211 /* get the stream before asserting privileges */ 212 try (final InputStream in = url.openConnection().getInputStream()) { 213 result = AccessController.doPrivileged( 214 new PrivilegedExceptionAction<>() { 215 public JarFile run() throws IOException { 216 Path tmpFile = Files.createTempFile("jar_cache", null); 217 try { 218 Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING); 219 JarFile jarFile = new URLJarFile(tmpFile.toFile(), closeController, version); 220 tmpFile.toFile().deleteOnExit(); 221 return jarFile; 222 } catch (Throwable thr) { 223 try { 224 Files.delete(tmpFile); 225 } catch (IOException ioe) { 226 thr.addSuppressed(ioe); 227 } 228 throw thr; 229 } 230 } 231 }); 232 } catch (PrivilegedActionException pae) { 233 throw (IOException) pae.getException(); 234 } 235 236 return result; 237 } 238 } 239 240 /* 241 * Set the call back interface to call retrive function in sun.plugin 242 * package if plugin is running. 243 */ 244 public static void setCallBack(URLJarFileCallBack cb) 245 { 246 callback = cb; 247 } 248 249 250 private class URLJarFileEntry extends JarEntry { 251 private JarEntry je; 252 253 URLJarFileEntry(JarEntry je) { 254 super(je); 255 this.je=je; 256 } 257 258 public Attributes getAttributes() throws IOException { 259 if (URLJarFile.this.isSuperMan()) { 260 Map<String, Attributes> e = URLJarFile.this.superEntries; 261 if (e != null) { 262 Attributes a = e.get(getName()); 263 if (a != null) 264 return (Attributes)a.clone(); 265 } 266 } 267 return null; 268 } 269 270 public java.security.cert.Certificate[] getCertificates() { 271 Certificate[] certs = je.getCertificates(); 272 return certs == null? null: certs.clone(); 273 } 274 275 public CodeSigner[] getCodeSigners() { 276 CodeSigner[] csg = je.getCodeSigners(); 277 return csg == null? null: csg.clone(); 278 } 279 } 280 281 public interface URLJarFileCloseController { 282 public void close(JarFile jarFile); 283 } 284 }