1 /*
   2  * Copyright (c) 2015, 2017, 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 package javax.xml.catalog;
  26 
  27 import java.io.File;
  28 import java.io.IOException;
  29 import java.net.MalformedURLException;
  30 import java.net.URI;
  31 import java.util.Iterator;
  32 import java.util.jar.JarEntry;
  33 import java.util.jar.JarFile;
  34 import static javax.xml.catalog.CatalogFeatures.DEFER_FALSE;
  35 import static javax.xml.catalog.CatalogFeatures.DEFER_TRUE;
  36 import javax.xml.catalog.CatalogFeatures.Feature;
  37 import static javax.xml.catalog.CatalogFeatures.PREFER_PUBLIC;
  38 import static javax.xml.catalog.CatalogFeatures.PREFER_SYSTEM;
  39 import static javax.xml.catalog.CatalogFeatures.RESOLVE_CONTINUE;
  40 import static javax.xml.catalog.CatalogFeatures.RESOLVE_IGNORE;
  41 import static javax.xml.catalog.CatalogFeatures.RESOLVE_STRICT;
  42 import jdk.xml.internal.SecuritySupport;
  43 
  44 /**
  45  *
  46  * @since 9
  47  */
  48 class Util {
  49 
  50     final static String URN = "urn:publicid:";
  51     final static String PUBLICID_PREFIX = "-//";
  52     final static String PUBLICID_PREFIX_ALT = "+//";
  53     final static String SCHEME_FILE = "file";
  54     final static String SCHEME_JAR = "jar";
  55     final static String SCHEME_JARFILE = "jar:file:";
  56 
  57     /**
  58      * Finds an entry in the catalog that matches with the publicId or systemId.
  59      *
  60      * The resolution follows the following rules determined by the prefer
  61      * setting:
  62      *
  63      * prefer "system": attempts to resolve with a system entry; attempts to
  64      * resolve with a public entry when only publicId is specified.
  65      *
  66      * prefer "public": attempts to resolve with a system entry; attempts to
  67      * resolve with a public entry if no matching system entry is found.
  68      *
  69      * If no match is found, continue searching uri entries
  70      *
  71      * @param catalog the catalog
  72      * @param publicId the publicId
  73      * @param systemId the systemId
  74      * @return the resolved systemId if a match is found, null otherwise
  75      */
  76     static String resolve(CatalogImpl catalog, String publicId, String systemId) {
  77         String resolvedSystemId = null;
  78 
  79         //search the current catalog
  80         catalog.reset();
  81         if (systemId != null) {
  82             /*
  83              If a system identifier is specified, it is used no matter how
  84              prefer is set.
  85              */
  86             resolvedSystemId = catalog.matchSystem(systemId);
  87         }
  88 
  89         if (resolvedSystemId == null && publicId != null) {
  90             resolvedSystemId = catalog.matchPublic(publicId);
  91         }
  92 
  93         if (resolvedSystemId == null && systemId != null) {
  94             resolvedSystemId = catalog.matchURI(systemId);
  95         }
  96 
  97         //mark the catalog as having been searched before trying alternatives
  98         catalog.markAsSearched();
  99 
 100         //search alternative catalogs
 101         if (resolvedSystemId == null) {
 102             Iterator<Catalog> iter = catalog.catalogs().iterator();
 103             while (iter.hasNext()) {
 104                 resolvedSystemId = resolve((CatalogImpl) iter.next(), publicId, systemId);
 105                 if (resolvedSystemId != null) {
 106                     break;
 107                 }
 108 
 109             }
 110         }
 111 
 112         return resolvedSystemId;
 113     }
 114 
 115     static void validateUrisSyntax(URI... uris) {
 116         for (URI uri : uris) {
 117             validateUriSyntax(uri);
 118         }
 119     }
 120 
 121     static void validateUrisSyntax(String... uris) {
 122         for (String uri : uris) {
 123             validateUriSyntax(URI.create(uri));
 124         }
 125     }
 126 
 127     /**
 128      * Validate that the URI must be absolute and a valid URL.
 129      *
 130      * Note that this method does not verify the existence of the resource. The
 131      * Catalog standard requires that such resources be ignored.
 132      *
 133      * @param uri
 134      * @throws IllegalArgumentException if the uri is not absolute and a valid
 135      * URL
 136      */
 137     static void validateUriSyntax(URI uri) {
 138         CatalogMessages.reportNPEOnNull("URI input", uri);
 139 
 140         if (!uri.isAbsolute()) {
 141             CatalogMessages.reportIAE(CatalogMessages.ERR_URI_NOTABSOLUTE,
 142                     new Object[]{uri}, null);
 143         }
 144 
 145         try {
 146             // check if the scheme was valid
 147             uri.toURL();
 148         } catch (MalformedURLException ex) {
 149             CatalogMessages.reportIAE(CatalogMessages.ERR_URI_NOTVALIDURL,
 150                     new Object[]{uri}, null);
 151         }
 152     }
 153 
 154     /**
 155      * Checks whether the URI is a file URI, including JAR file.
 156      *
 157      * @param uri the specified URI.
 158      * @return true if it is a file or JAR file URI, false otherwise
 159      */
 160     static boolean isFileUri(URI uri) {
 161         if (SCHEME_FILE.equals(uri.getScheme())
 162                 || SCHEME_JAR.equals(uri.getScheme())) {
 163             return true;
 164         }
 165         return false;
 166     }
 167 
 168     /**
 169      * Verifies whether the file resource exists.
 170      *
 171      * @param uri the URI to locate the resource
 172      * @param openJarFile a flag to indicate whether a JAR file should be
 173      * opened. This operation may be expensive.
 174      * @return true if the resource exists, false otherwise.
 175      */
 176     static boolean isFileUriExist(URI uri, boolean openJarFile) {
 177         if (uri != null && uri.isAbsolute()) {
 178             if (null != uri.getScheme()) {
 179                 switch (uri.getScheme()) {
 180                     case SCHEME_FILE:
 181                         String path = uri.getPath();
 182                         File f1 = new File(path);
 183                         if (f1.isFile()) {
 184                             return true;
 185                         }
 186                         break;
 187                     case SCHEME_JAR:
 188                         String tempUri = uri.toString();
 189                         int pos = tempUri.indexOf("!");
 190                         if (pos < 0) {
 191                             return false;
 192                         }
 193                         if (openJarFile) {
 194                             String jarFile = tempUri.substring(SCHEME_JARFILE.length(), pos);
 195                             String entryName = tempUri.substring(pos + 2);
 196                             try {
 197                                 JarFile jf = new JarFile(jarFile);
 198                                 JarEntry je = jf.getJarEntry(entryName);
 199                                 if (je != null) {
 200                                     return true;
 201                                 }
 202                             } catch (IOException ex) {
 203                                 return false;
 204                             }
 205                         } else {
 206                             return true;
 207                         }
 208                         break;
 209                 }
 210             }
 211         }
 212         return false;
 213     }
 214 
 215     /**
 216      * Find catalog file paths by reading the system property, and then
 217      * jaxp.properties if the system property is not specified.
 218      *
 219      * @param sysPropertyName the name of system property
 220      * @return the catalog file paths, or null if not found.
 221      */
 222     static String[] getCatalogFiles(String sysPropertyName) {
 223         String value = SecuritySupport.getJAXPSystemProperty(sysPropertyName);
 224         if (value != null && !value.isEmpty()) {
 225             return value.split(";");
 226         }
 227         return null;
 228     }
 229 
 230     /**
 231      * Checks whether the specified string is null or empty, returns the
 232      * original string with leading and trailing spaces removed if not.
 233      *
 234      * @param test the string to be tested
 235      * @return the original string with leading and trailing spaces removed, or
 236      * null if it is null or empty
 237      *
 238      */
 239     static String getNotNullOrEmpty(String test) {
 240         if (test == null) {
 241             return test;
 242         } else {
 243             String temp = test.trim();
 244             if (temp.length() == 0) {
 245                 return null;
 246             } else {
 247                 return temp;
 248             }
 249         }
 250     }
 251 
 252     /**
 253      * Validates the input for features.
 254      *
 255      * @param f the feature
 256      * @param value the value
 257      * @throws IllegalArgumentException if the value is invalid for the feature
 258      */
 259     static void validateFeatureInput(Feature f, String value) {
 260         CatalogMessages.reportNPEOnNull(f.name(), value);
 261         if (value.length() == 0) {
 262             CatalogMessages.reportIAE(CatalogMessages.ERR_INVALID_ARGUMENT,
 263                     new Object[]{value, f.name()}, null);
 264         }
 265 
 266         if (f == Feature.PREFER) {
 267             if (!value.equals(PREFER_SYSTEM) && !value.equals(PREFER_PUBLIC)) {
 268                 CatalogMessages.reportIAE(CatalogMessages.ERR_INVALID_ARGUMENT,
 269                         new Object[]{value, Feature.PREFER.name()}, null);
 270             }
 271         } else if (f == Feature.DEFER) {
 272             if (!value.equals(DEFER_TRUE) && !value.equals(DEFER_FALSE)) {
 273                 CatalogMessages.reportIAE(CatalogMessages.ERR_INVALID_ARGUMENT,
 274                         new Object[]{value, Feature.DEFER.name()}, null);
 275             }
 276         } else if (f == Feature.RESOLVE) {
 277             if (!value.equals(RESOLVE_STRICT) && !value.equals(RESOLVE_CONTINUE)
 278                     && !value.equals(RESOLVE_IGNORE)) {
 279                 CatalogMessages.reportIAE(CatalogMessages.ERR_INVALID_ARGUMENT,
 280                         new Object[]{value, Feature.RESOLVE.name()}, null);
 281             }
 282         } else if (f == Feature.FILES) {
 283             Util.validateUrisSyntax(value.split(";"));
 284         }
 285     }
 286 }