Home | History | Annotate | Download | only in jar
      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 
     64     static JarFile getJarFile(URL url) throws IOException {
     65         return getJarFile(url, null);
     66     }
     67 
     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      */
     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      */
     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 
     93     private URLJarFile(URL url, URLJarFileCloseController closeController) throws IOException {
     94         super(ParseUtil.decode(url.getFile()));
     95         this.closeController = closeController;
     96     }
     97 
     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.
    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      */
    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) 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      */
    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 
    259         URLJarFileEntry(JarEntry je) {
    260             super(je);
    261             this.je=je;
    262         }
    263 
    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 
    276         public java.security.cert.Certificate[] getCertificates() {
    277             Certificate[] certs = je.getCertificates();
    278             return certs == null? null: certs.clone();
    279         }
    280 
    281         public CodeSigner[] getCodeSigners() {
    282             CodeSigner[] csg = je.getCodeSigners();
    283             return csg == null? null: csg.clone();
    284         }
    285     }
    286 
    287     public interface URLJarFileCloseController {
    288         public void close(JarFile jarFile);
    289     }
    290 }
    291