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