1 /*
   2  * Copyright (c) 1999, 2013, 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.misc;
  27 
  28 import java.io.File;
  29 import java.io.FilenameFilter;
  30 import java.io.IOException;
  31 import java.io.FileNotFoundException;
  32 import java.util.StringTokenizer;
  33 import java.util.Vector;
  34 import java.util.Enumeration;
  35 import java.util.jar.JarFile;
  36 import java.util.jar.Manifest;
  37 import java.util.jar.Attributes;
  38 import java.util.jar.Attributes.Name;
  39 import java.security.AccessController;
  40 import java.security.PrivilegedAction;
  41 import java.security.PrivilegedExceptionAction;
  42 import java.security.PrivilegedActionException;
  43 import java.net.URL;
  44 import java.net.MalformedURLException;
  45 import sun.net.www.ParseUtil;
  46 
  47 /**
  48  * This class checks dependent extensions a particular jar file may have
  49  * declared through its manifest attributes.
  50  * <p>
  51  * Jar file declared dependent extensions through the extension-list
  52  * attribute. The extension-list contains a list of keys used to
  53  * fetch the other attributes describing the required extension.
  54  * If key is the extension key declared in the extension-list
  55  * attribute, the following describing attribute can be found in
  56  * the manifest:
  57  * <ul>
  58  * <li>key-Extension-Name:  (Specification package name)</li>
  59  * <li>key-Specification-Version: (Specification-Version)</li>
  60  * <li>key-Implementation-Version: (Implementation-Version)</li>
  61  * <li>key-Implementation-Vendor-Id: (Imlementation-Vendor-Id)</li>
  62  * <li>key-Implementation-Version: (Implementation version)</li>
  63  * <li>key-Implementation-URL: (URL to download the requested extension)</li>
  64  * </ul>
  65  * <p>
  66  * This class also maintain versioning consistency of installed
  67  * extensions dependencies declared in jar file manifest.
  68  *
  69  * @deprecated this class will be removed in a future release.
  70  * @author  Jerome Dochez
  71  */
  72 @Deprecated
  73 public class ExtensionDependency {
  74 
  75     /* Callbak interfaces to delegate installation of missing extensions */
  76     private static Vector<ExtensionInstallationProvider> providers;
  77 
  78     /**
  79      * Register an ExtensionInstallationProvider. The provider is responsible
  80      * for handling the installation (upgrade) of any missing extensions.
  81      *
  82      * @param eip ExtensionInstallationProvider implementation
  83      */
  84     public synchronized static void addExtensionInstallationProvider
  85         (ExtensionInstallationProvider eip)
  86     {
  87         if (providers == null) {
  88             providers = new Vector<>();
  89         }
  90         providers.add(eip);
  91     }
  92 
  93     /**
  94      * Unregister a previously installed installation provider
  95      */
  96     public synchronized static void removeExtensionInstallationProvider
  97         (ExtensionInstallationProvider eip)
  98     {
  99         providers.remove(eip);
 100     }
 101 
 102     /**
 103      * Checks the dependencies of the jar file on installed extension.
 104      *
 105      * @param jar containing the attributes declaring the dependencies
 106      */
 107     public static boolean checkExtensionsDependencies(JarFile jar)
 108     {
 109         if (providers == null) {
 110             // no need to bother, nobody is registered to install missing
 111             // extensions
 112             return true;
 113         }
 114 
 115         try {
 116             ExtensionDependency extDep = new ExtensionDependency();
 117             return extDep.checkExtensions(jar);
 118         } catch (ExtensionInstallationException e) {
 119             debug(e.getMessage());
 120         }
 121         return false;
 122     }
 123 
 124     /*
 125      * Check for all declared required extensions in the jar file
 126      * manifest.
 127      */
 128     protected boolean checkExtensions(JarFile jar)
 129         throws ExtensionInstallationException
 130     {
 131         Manifest man;
 132         try {
 133             man = jar.getManifest();
 134         } catch (IOException e) {
 135             return false;
 136         }
 137 
 138         if (man == null) {
 139             // The applet does not define a manifest file, so
 140             // we just assume all dependencies are satisfied.
 141             return true;
 142         }
 143 
 144         boolean result = true;
 145         Attributes attr = man.getMainAttributes();
 146         if (attr != null) {
 147             // Let's get the list of declared dependencies
 148             String value = attr.getValue(Name.EXTENSION_LIST);
 149             if (value != null) {
 150                 StringTokenizer st = new StringTokenizer(value);
 151                 // Iterate over all declared dependencies
 152                 while (st.hasMoreTokens()) {
 153                     String extensionName = st.nextToken();
 154                     debug("The file " + jar.getName() +
 155                           " appears to depend on " + extensionName);
 156                     // Sanity Check
 157                     String extName = extensionName + "-" +
 158                         Name.EXTENSION_NAME.toString();
 159                     if (attr.getValue(extName) == null) {
 160                         debug("The jar file " + jar.getName() +
 161                               " appers to depend on "
 162                               + extensionName + " but does not define the " +
 163                               extName + " attribute in its manifest ");
 164 
 165                     } else {
 166                         if (!checkExtension(extensionName, attr)) {
 167                             debug("Failed installing " + extensionName);
 168                             result = false;
 169                         }
 170                     }
 171                 }
 172             } else {
 173                 debug("No dependencies for " + jar.getName());
 174             }
 175         }
 176         return result;
 177     }
 178 
 179 
 180     /*
 181      * Check that a particular dependency on an extension is satisfied.
 182      *
 183      * @param extensionName is the key used for the attributes in the manifest
 184      * @param attr is the attributes of the manifest file
 185      *
 186      * @return true if the dependency is satisfied by the installed extensions
 187      */
 188     protected synchronized boolean checkExtension(final String extensionName,
 189                                      final Attributes attr)
 190         throws ExtensionInstallationException
 191     {
 192         debug("Checking extension " + extensionName);
 193         if (checkExtensionAgainstInstalled(extensionName, attr))
 194             return true;
 195 
 196         debug("Extension not currently installed ");
 197         ExtensionInfo reqInfo = new ExtensionInfo(extensionName, attr);
 198         return installExtension(reqInfo, null);
 199     }
 200 
 201     /*
 202      * Check if a particular extension is part of the currently installed
 203      * extensions.
 204      *
 205      * @param extensionName is the key for the attributes in the manifest
 206      * @param attr is the attributes of the manifest
 207      *
 208      * @return true if the requested extension is already installed
 209      */
 210     boolean checkExtensionAgainstInstalled(String extensionName,
 211                                            Attributes attr)
 212         throws ExtensionInstallationException
 213     {
 214         File fExtension = checkExtensionExists(extensionName);
 215 
 216         if (fExtension != null) {
 217         // Extension already installed, just check against this one
 218             try {
 219                 if (checkExtensionAgainst(extensionName, attr, fExtension))
 220                     return true;
 221             } catch (FileNotFoundException e) {
 222                 debugException(e);
 223             } catch (IOException e) {
 224                 debugException(e);
 225             }
 226             return false;
 227 
 228         } else {
 229         // Not sure if extension is already installed, so check all the
 230         // installed extension jar files to see if we get a match
 231 
 232             File[] installedExts;
 233 
 234             try {
 235             // Get the list of installed extension jar files so we can
 236             // compare the installed versus the requested extension
 237                 installedExts = getInstalledExtensions();
 238             } catch(IOException e) {
 239                 debugException(e);
 240                 return false;
 241             }
 242 
 243             for (int i=0;i<installedExts.length;i++) {
 244                 try {
 245                     if (checkExtensionAgainst(extensionName, attr, installedExts[i]))
 246                         return true;
 247                 } catch (FileNotFoundException e) {
 248                     debugException(e);
 249                 } catch (IOException e) {
 250                     debugException(e);
 251                     // let's continue with the next installed extension
 252                 }
 253             }
 254         }
 255         return false;
 256     }
 257 
 258     /*
 259      * Check if the requested extension described by the attributes
 260      * in the manifest under the key extensionName is compatible with
 261      * the jar file.
 262      *
 263      * @param extensionName key in the attribute list
 264      * @param attr manifest file attributes
 265      * @param file installed extension jar file to compare the requested
 266      * extension against.
 267      */
 268     protected boolean checkExtensionAgainst(String extensionName,
 269                                             Attributes attr,
 270                                             final File file)
 271         throws IOException,
 272                FileNotFoundException,
 273                ExtensionInstallationException
 274     {
 275 
 276         debug("Checking extension " + extensionName +
 277               " against " + file.getName());
 278 
 279         // Load the jar file ...
 280         Manifest man;
 281         try {
 282             man = AccessController.doPrivileged(
 283                 new PrivilegedExceptionAction<Manifest>() {
 284                     public Manifest run()
 285                             throws IOException, FileNotFoundException {
 286                          if (!file.exists())
 287                              throw new FileNotFoundException(file.getName());
 288                          JarFile jarFile =  new JarFile(file);
 289                          return jarFile.getManifest();
 290                      }
 291                  });
 292         } catch(PrivilegedActionException e) {
 293             if (e.getException() instanceof FileNotFoundException)
 294                 throw (FileNotFoundException) e.getException();
 295             throw (IOException) e.getException();
 296         }
 297 
 298         // Construct the extension information object
 299         ExtensionInfo reqInfo = new ExtensionInfo(extensionName, attr);
 300         debug("Requested Extension : " + reqInfo);
 301 
 302         int isCompatible = ExtensionInfo.INCOMPATIBLE;
 303         ExtensionInfo instInfo = null;
 304 
 305         if (man != null) {
 306             Attributes instAttr = man.getMainAttributes();
 307             if (instAttr != null) {
 308                 instInfo = new ExtensionInfo(null, instAttr);
 309                 debug("Extension Installed " + instInfo);
 310                 isCompatible = instInfo.isCompatibleWith(reqInfo);
 311                 switch(isCompatible) {
 312                 case ExtensionInfo.COMPATIBLE:
 313                     debug("Extensions are compatible");
 314                     return true;
 315 
 316                 case ExtensionInfo.INCOMPATIBLE:
 317                     debug("Extensions are incompatible");
 318                     return false;
 319 
 320                 default:
 321                     // everything else
 322                     debug("Extensions require an upgrade or vendor switch");
 323                     return installExtension(reqInfo, instInfo);
 324 
 325                 }
 326             }
 327         }
 328         return false;
 329     }
 330 
 331     /*
 332      * An required extension is missing, if an ExtensionInstallationProvider is
 333      * registered, delegate the installation of that particular extension to it.
 334      *
 335      * @param reqInfo Missing extension information
 336      * @param instInfo Older installed version information
 337      *
 338      * @return true if the installation is successful
 339      */
 340     protected boolean installExtension(ExtensionInfo reqInfo,
 341                                        ExtensionInfo instInfo)
 342         throws ExtensionInstallationException
 343     {
 344         Vector<ExtensionInstallationProvider> currentProviders;
 345         synchronized(providers) {
 346             @SuppressWarnings("unchecked")
 347             Vector<ExtensionInstallationProvider> tmp =
 348                 (Vector<ExtensionInstallationProvider>) providers.clone();
 349             currentProviders = tmp;
 350         }
 351         for (Enumeration<ExtensionInstallationProvider> e = currentProviders.elements();
 352                 e.hasMoreElements();) {
 353             ExtensionInstallationProvider eip = e.nextElement();
 354 
 355             if (eip!=null) {
 356                 // delegate the installation to the provider
 357                 if (eip.installExtension(reqInfo, instInfo)) {
 358                     debug(reqInfo.name + " installation successful");
 359                     Launcher.ExtClassLoader cl = (Launcher.ExtClassLoader)
 360                         Launcher.getLauncher().getClassLoader().getParent();
 361                     addNewExtensionsToClassLoader(cl);
 362                     return true;
 363                 }
 364             }
 365         }
 366         // We have tried all of our providers, noone could install this
 367         // extension, we just return failure at this point
 368         debug(reqInfo.name + " installation failed");
 369         return false;
 370     }
 371 
 372     /**
 373      * Checks if the extension, that is specified in the extension-list in
 374      * the applet jar manifest, is already installed (i.e. exists in the
 375      * extension directory).
 376      *
 377      * @param extensionName extension name in the extension-list
 378      *
 379      * @return the extension if it exists in the extension directory
 380      */
 381     private File checkExtensionExists(String extensionName) {
 382         // Function added to fix bug 4504166
 383         final String extName = extensionName;
 384         final String[] fileExt = {".jar", ".zip"};
 385 
 386         return AccessController.doPrivileged(
 387             new PrivilegedAction<File>() {
 388                 public File run() {
 389                     try {
 390                         File fExtension;
 391                         File[] dirs = getExtDirs();
 392 
 393                         // Search the extension directories for the extension that is specified
 394                         // in the attribute extension-list in the applet jar manifest
 395                         for (int i=0;i<dirs.length;i++) {
 396                             for (int j=0;j<fileExt.length;j++) {
 397                                 if (extName.toLowerCase().endsWith(fileExt[j])) {
 398                                     fExtension = new File(dirs[i], extName);
 399                                 } else {
 400                                     fExtension = new File(dirs[i], extName+fileExt[j]);
 401                                 }
 402                                 debug("checkExtensionExists:fileName " + fExtension.getName());
 403                                 if (fExtension.exists()) {
 404                                     return fExtension;
 405                                 }
 406                             }
 407                         }
 408                         return null;
 409 
 410                     } catch(Exception e) {
 411                          debugException(e);
 412                          return null;
 413                     }
 414                 }
 415             });
 416     }
 417 
 418     /**
 419      * @return the java.ext.dirs property as a list of directory
 420      */
 421     private static File[] getExtDirs() {
 422         String s = java.security.AccessController.doPrivileged(
 423                 new sun.security.action.GetPropertyAction("java.ext.dirs"));
 424 
 425         File[] dirs;
 426         if (s != null) {
 427             StringTokenizer st =
 428                 new StringTokenizer(s, File.pathSeparator);
 429             int count = st.countTokens();
 430             debug("getExtDirs count " + count);
 431             dirs = new File[count];
 432             for (int i = 0; i < count; i++) {
 433                 dirs[i] = new File(st.nextToken());
 434                 debug("getExtDirs dirs["+i+"] "+ dirs[i]);
 435             }
 436         } else {
 437             dirs = new File[0];
 438             debug("getExtDirs dirs " + dirs);
 439         }
 440         debug("getExtDirs dirs.length " + dirs.length);
 441         return dirs;
 442     }
 443 
 444     /*
 445      * Scan the directories and return all files installed in those
 446      *
 447      * @param dirs list of directories to scan
 448      *
 449      * @return the list of files installed in all the directories
 450      */
 451     private static File[] getExtFiles(File[] dirs) throws IOException {
 452         Vector<File> urls = new Vector<File>();
 453         for (int i = 0; i < dirs.length; i++) {
 454             String[] files = dirs[i].list(new JarFilter());
 455             if (files != null) {
 456                 debug("getExtFiles files.length " + files.length);
 457                 for (int j = 0; j < files.length; j++) {
 458                     File f = new File(dirs[i], files[j]);
 459                     urls.add(f);
 460                     debug("getExtFiles f["+j+"] "+ f);
 461                 }
 462             }
 463         }
 464         File[] ua = new File[urls.size()];
 465         urls.copyInto(ua);
 466         debug("getExtFiles ua.length " + ua.length);
 467         return ua;
 468     }
 469 
 470     /*
 471      * @return the list of installed extensions jar files
 472      */
 473     private File[] getInstalledExtensions() throws IOException {
 474         return AccessController.doPrivileged(
 475             new PrivilegedAction<File[]>() {
 476                 public File[] run() {
 477                      try {
 478                          return getExtFiles(getExtDirs());
 479                      } catch(IOException e) {
 480                          debug("Cannot get list of installed extensions");
 481                          debugException(e);
 482                         return new File[0];
 483                      }
 484                  }
 485             });
 486     }
 487 
 488     /*
 489      * Add the newly installed jar file to the extension class loader.
 490      *
 491      * @param cl the current installed extension class loader
 492      *
 493      * @return true if successful
 494      */
 495     private Boolean addNewExtensionsToClassLoader(Launcher.ExtClassLoader cl) {
 496         try {
 497             File[] installedExts = getInstalledExtensions();
 498             for (int i=0;i<installedExts.length;i++) {
 499                 final File instFile = installedExts[i];
 500                 URL instURL = AccessController.doPrivileged(
 501                     new PrivilegedAction<URL>() {
 502                         public URL run() {
 503                             try {
 504                                 return ParseUtil.fileToEncodedURL(instFile);
 505                             } catch (MalformedURLException e) {
 506                                 debugException(e);
 507                                 return null;
 508                             }
 509                         }
 510                     });
 511                 if (instURL != null) {
 512                     URL[] urls = cl.getURLs();
 513                     boolean found=false;
 514                     for (int j = 0; j<urls.length; j++) {
 515                         debug("URL["+j+"] is " + urls[j] + " looking for "+
 516                                            instURL);
 517                         if (urls[j].toString().compareToIgnoreCase(
 518                                     instURL.toString())==0) {
 519                             found=true;
 520                             debug("Found !");
 521                         }
 522                     }
 523                     if (!found) {
 524                         debug("Not Found ! adding to the classloader " +
 525                               instURL);
 526                         cl.addExtURL(instURL);
 527                     }
 528                 }
 529             }
 530         } catch (MalformedURLException e) {
 531             e.printStackTrace();
 532         } catch (IOException e) {
 533             e.printStackTrace();
 534             // let's continue with the next installed extension
 535         }
 536         return Boolean.TRUE;
 537     }
 538 
 539     // True to display all debug and trace messages
 540     static final boolean DEBUG = false;
 541 
 542     private static void debug(String s) {
 543         if (DEBUG) {
 544             System.err.println(s);
 545         }
 546     }
 547 
 548     private void debugException(Throwable e) {
 549         if (DEBUG) {
 550             e.printStackTrace();
 551         }
 552     }
 553 
 554 }