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 */ 115 // Android-note: All important methods here and in superclasses should synchronize on this 116 // to avoid premature finalization. The actual close implementation in ZipFile does. finalize()117 protected void finalize() throws IOException { 118 close(); 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 */ getEntry(String name)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 getManifest()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 */ 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 isSuperMan()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 */ retrieve(final URL url)192 private static JarFile retrieve(final URL url) throws IOException { 193 return retrieve(url, null); 194 } 195 196 /** 197 * Given a URL, retrieves a JAR file, caches it to disk, and creates a 198 * cached JAR file object. 199 */ retrieve(final URL url, final URLJarFileCloseController closeController)200 private static JarFile retrieve(final URL url, final URLJarFileCloseController closeController) throws IOException { 201 // Android-changed: Removed setCallBack(URLJarFileCallBack) and field callback (dead code). 202 // /* 203 // * See if interface is set, then call retrieve function of the class 204 // * that implements URLJarFileCallBack interface (sun.plugin - to 205 // * handle the cache failure for JARJAR file.) 206 // */ 207 // if (callback != null) 208 // { 209 // return callback.retrieve(url); 210 // } 211 // 212 // else 213 { 214 215 JarFile result = null; 216 217 /* get the stream before asserting privileges */ 218 try (final InputStream in = url.openConnection().getInputStream()) { 219 result = AccessController.doPrivileged( 220 new PrivilegedExceptionAction<JarFile>() { 221 public JarFile run() throws IOException { 222 Path tmpFile = Files.createTempFile("jar_cache", null); 223 try { 224 Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING); 225 JarFile jarFile = new URLJarFile(tmpFile.toFile(), closeController); 226 tmpFile.toFile().deleteOnExit(); 227 return jarFile; 228 } catch (Throwable thr) { 229 try { 230 Files.delete(tmpFile); 231 } catch (IOException ioe) { 232 thr.addSuppressed(ioe); 233 } 234 throw thr; 235 } 236 } 237 }); 238 } catch (PrivilegedActionException pae) { 239 throw (IOException) pae.getException(); 240 } 241 242 return result; 243 } 244 } 245 246 // Android-changed: Removed setCallBack(URLJarFileCallBack) and field callback (dead code). 247 // /* 248 // * Set the call back interface to call retrive function in sun.plugin 249 // * package if plugin is running. 250 // */ 251 // public static void setCallBack(URLJarFileCallBack cb) 252 // { 253 // callback = cb; 254 // } 255 256 private class URLJarFileEntry extends JarEntry { 257 private JarEntry je; 258 URLJarFileEntry(JarEntry je)259 URLJarFileEntry(JarEntry je) { 260 super(je); 261 this.je=je; 262 } 263 getAttributes()264 public Attributes getAttributes() throws IOException { 265 if (URLJarFile.this.isSuperMan()) { 266 Map<String, Attributes> e = URLJarFile.this.superEntries; 267 if (e != null) { 268 Attributes a = e.get(getName()); 269 if (a != null) 270 return (Attributes)a.clone(); 271 } 272 } 273 return null; 274 } 275 getCertificates()276 public java.security.cert.Certificate[] getCertificates() { 277 Certificate[] certs = je.getCertificates(); 278 return certs == null? null: certs.clone(); 279 } 280 getCodeSigners()281 public CodeSigner[] getCodeSigners() { 282 CodeSigner[] csg = je.getCodeSigners(); 283 return csg == null? null: csg.clone(); 284 } 285 } 286 287 public interface URLJarFileCloseController { close(JarFile jarFile)288 public void close(JarFile jarFile); 289 } 290 } 291