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     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      */
    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 
    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 */
    164     public void close() throws IOException {
    165         if (closeController != null) {
    166                 closeController.close(this);
    167         }
    168         super.close();
    169     }
    170 
    171     // optimal side-effects
    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      */
    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      */
    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 
    257         URLJarFileEntry(JarEntry je) {
    258             super(je);
    259             this.je=je;
    260         }
    261 
    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 
    274         public java.security.cert.Certificate[] getCertificates() {
    275             Certificate[] certs = je.getCertificates();
    276             return certs == null? null: certs.clone();
    277         }
    278 
    279         public CodeSigner[] getCodeSigners() {
    280             CodeSigner[] csg = je.getCodeSigners();
    281             return csg == null? null: csg.clone();
    282         }
    283     }
    284 
    285     public interface URLJarFileCloseController {
    286         public void close(JarFile jarFile);
    287     }
    288 }
    289