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