1 /*
2  * Copyright (c) 2016, 2021, 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 jdk.xml.internal;
26 
27 import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager;
28 import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
29 import com.sun.org.apache.xerces.internal.impl.Constants;
30 import com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl;
31 import com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl;
32 import com.sun.org.apache.xerces.internal.util.ParserConfigurationSettings;
33 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
34 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
35 import javax.xml.XMLConstants;
36 import javax.xml.catalog.CatalogFeatures;
37 import javax.xml.catalog.CatalogFeatures.Feature;
38 import javax.xml.parsers.DocumentBuilderFactory;
39 import javax.xml.parsers.ParserConfigurationException;
40 import javax.xml.parsers.SAXParserFactory;
41 import javax.xml.transform.TransformerConfigurationException;
42 import javax.xml.transform.sax.SAXTransformerFactory;
43 import org.w3c.dom.Document;
44 import org.xml.sax.SAXException;
45 import org.xml.sax.SAXNotRecognizedException;
46 import org.xml.sax.SAXNotSupportedException;
47 import org.xml.sax.XMLReader;
48 
49 /**
50  * Constants for use across JAXP processors.
51  */
52 public class JdkXmlUtils {
53     private static final String DOM_FACTORY_ID = "javax.xml.parsers.DocumentBuilderFactory";
54     private static final String SAX_FACTORY_ID = "javax.xml.parsers.SAXParserFactory";
55     private static final String SAX_DRIVER = "org.xml.sax.driver";
56 
57     /**
58      * Xerces features
59      */
60     public static final String NAMESPACES_FEATURE =
61         Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;
62     public static final String NAMESPACE_PREFIXES_FEATURE =
63         Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACE_PREFIXES_FEATURE;
64 
65 
66     /**
67      * Catalog features
68      */
69     public final static String USE_CATALOG = XMLConstants.USE_CATALOG;
70     public final static String SP_USE_CATALOG = "javax.xml.useCatalog";
71     public final static String CATALOG_FILES = CatalogFeatures.Feature.FILES.getPropertyName();
72     public final static String CATALOG_DEFER = CatalogFeatures.Feature.DEFER.getPropertyName();
73     public final static String CATALOG_PREFER = CatalogFeatures.Feature.PREFER.getPropertyName();
74     public final static String CATALOG_RESOLVE = CatalogFeatures.Feature.RESOLVE.getPropertyName();
75 
76     /**
77      * Reset SymbolTable feature System property name is identical to feature
78      * name
79      */
80     public final static String RESET_SYMBOL_TABLE = "jdk.xml.resetSymbolTable";
81 
82     /**
83      * jdk.xml.overrideDefaultParser: enables the use of a 3rd party's parser
84      * implementation to override the system-default parser.
85      */
86     public static final String OVERRIDE_PARSER = "jdk.xml.overrideDefaultParser";
87     public static final boolean OVERRIDE_PARSER_DEFAULT = SecuritySupport.getJAXPSystemProperty(
88                     Boolean.class, OVERRIDE_PARSER, "false");
89 
90     /**
91      * Values for a feature
92      */
93     public static final String FEATURE_TRUE = "true";
94     public static final String FEATURE_FALSE = "false";
95 
96     /**
97      * Default value of USE_CATALOG. This will read the System property
98      */
99     public static final boolean USE_CATALOG_DEFAULT
100             = SecuritySupport.getJAXPSystemProperty(Boolean.class, SP_USE_CATALOG, "true");
101 
102     /**
103      * Default value of RESET_SYMBOL_TABLE. This will read the System property
104      */
105     public static final boolean RESET_SYMBOL_TABLE_DEFAULT
106             = SecuritySupport.getJAXPSystemProperty(Boolean.class, RESET_SYMBOL_TABLE, "false");
107 
108     /**
109      * JDK features (will be consolidated in the next major feature revamp
110      */
111     public final static String CDATA_CHUNK_SIZE = "jdk.xml.cdataChunkSize";
112     public static final int CDATA_CHUNK_SIZE_DEFAULT
113             = SecuritySupport.getJAXPSystemProperty(Integer.class, CDATA_CHUNK_SIZE, "0");
114 
115     /**
116      * The system-default factory
117      */
118     private static final SAXParserFactory defaultSAXFactory = getSAXFactory(false);
119 
120     /**
121      * Returns the value.
122      *
123      * @param value the specified value
124      * @param defValue the default value
125      * @return the value, or the default value if the value is null
126      */
getValue(Object value, int defValue)127     public static int getValue(Object value, int defValue) {
128         if (value == null) {
129             return defValue;
130         }
131 
132         if (value instanceof Number) {
133             return ((Number) value).intValue();
134         } else if (value instanceof String) {
135             return Integer.parseInt(String.valueOf(value));
136         } else {
137             throw new IllegalArgumentException("Unexpected class: "
138                     + value.getClass());
139         }
140     }
141 
142     /**
143      * Sets the XMLReader instance with the specified property if the the
144      * property is supported, ignores error if not, issues a warning if so
145      * requested.
146      *
147      * @param reader an XMLReader instance
148      * @param property the name of the property
149      * @param value the value of the property
150      * @param warn a flag indicating whether a warning should be issued
151      */
setXMLReaderPropertyIfSupport(XMLReader reader, String property, Object value, boolean warn)152     public static void setXMLReaderPropertyIfSupport(XMLReader reader, String property,
153             Object value, boolean warn) {
154         try {
155             reader.setProperty(property, value);
156         } catch (SAXNotRecognizedException | SAXNotSupportedException e) {
157             if (warn) {
158                 XMLSecurityManager.printWarning(reader.getClass().getName(),
159                         property, e);
160             }
161         }
162     }
163 
164     /**
165      * Returns the value of a Catalog feature by the property name.
166      *
167      * @param features a CatalogFeatures instance
168      * @param name the name of a Catalog feature
169      * @return the value of a Catalog feature, null if the name does not match
170      * any feature supported by the Catalog.
171      */
getCatalogFeature(CatalogFeatures features, String name)172     public static String getCatalogFeature(CatalogFeatures features, String name) {
173         for (Feature feature : Feature.values()) {
174             if (feature.getPropertyName().equals(name)) {
175                 return features.get(feature);
176             }
177         }
178         return null;
179     }
180 
181     /**
182      * Creates an instance of a CatalogFeatures.
183      *
184      * @param defer the defer property defined in CatalogFeatures
185      * @param file the file path to a catalog
186      * @param prefer the prefer property defined in CatalogFeatures
187      * @param resolve the resolve property defined in CatalogFeatures
188      * @return a {@link javax.xml.transform.Source} object
189      */
getCatalogFeatures(String defer, String file, String prefer, String resolve)190     public static CatalogFeatures getCatalogFeatures(String defer, String file,
191             String prefer, String resolve) {
192 
193         CatalogFeatures.Builder builder = CatalogFeatures.builder();
194         if (file != null) {
195             builder = builder.with(CatalogFeatures.Feature.FILES, file);
196         }
197         if (prefer != null) {
198             builder = builder.with(CatalogFeatures.Feature.PREFER, prefer);
199         }
200         if (defer != null) {
201             builder = builder.with(CatalogFeatures.Feature.DEFER, defer);
202         }
203         if (resolve != null) {
204             builder = builder.with(CatalogFeatures.Feature.RESOLVE, resolve);
205         }
206 
207         return builder.build();
208     }
209 
210     /**
211      * Passing on the CatalogFeatures settings from one Xerces configuration
212      * object to another.
213      *
214      * @param config1 a Xerces configuration object
215      * @param config2 a Xerces configuration object
216      */
catalogFeaturesConfig2Config(XMLComponentManager config1, ParserConfigurationSettings config2)217     public static void catalogFeaturesConfig2Config(XMLComponentManager config1,
218             ParserConfigurationSettings config2) {
219         boolean supportCatalog = true;
220         boolean useCatalog = config1.getFeature(XMLConstants.USE_CATALOG);
221         try {
222             config2.setFeature(JdkXmlUtils.USE_CATALOG, useCatalog);
223         } catch (XMLConfigurationException e) {
224             supportCatalog = false;
225         }
226 
227         if (supportCatalog && useCatalog) {
228             try {
229                 for (CatalogFeatures.Feature f : CatalogFeatures.Feature.values()) {
230                     config2.setProperty(f.getPropertyName(), config1.getProperty(f.getPropertyName()));
231                 }
232             } catch (XMLConfigurationException e) {
233                 //shall not happen for internal settings
234             }
235         }
236     }
237 
238     /**
239      * Passing on the CatalogFeatures settings from a Xerces configuration
240      * object to an XMLReader.
241      *
242      * @param config a Xerces configuration object
243      * @param reader an XMLReader
244      */
catalogFeaturesConfig2Reader(XMLComponentManager config, XMLReader reader)245     public static void catalogFeaturesConfig2Reader(XMLComponentManager config, XMLReader reader) {
246         boolean supportCatalog = true;
247         boolean useCatalog = config.getFeature(XMLConstants.USE_CATALOG);
248         try {
249             reader.setFeature(JdkXmlUtils.USE_CATALOG, useCatalog);
250         } catch (SAXNotRecognizedException | SAXNotSupportedException e) {
251             supportCatalog = false;
252         }
253 
254         if (supportCatalog && useCatalog) {
255             try {
256                 for (CatalogFeatures.Feature f : CatalogFeatures.Feature.values()) {
257                     reader.setProperty(f.getPropertyName(), config.getProperty(f.getPropertyName()));
258                 }
259             } catch (SAXNotRecognizedException | SAXNotSupportedException e) {
260                 //shall not happen for internal settings
261             }
262         }
263     }
264 
265     /**
266      * Returns an XMLReader instance. If overrideDefaultParser is requested, use
267      * SAXParserFactory or XMLReaderFactory, otherwise use the system-default
268      * SAXParserFactory to locate an XMLReader.
269      *
270      * @param overrideDefaultParser a flag indicating whether a 3rd party's
271      * parser implementation may be used to override the system-default one
272      * @param secureProcessing a flag indicating whether secure processing is
273      * requested
274      * @param useXMLReaderFactory a flag indicating when the XMLReader should be
275      * created using XMLReaderFactory. True is a compatibility mode that honors
276      * the property org.xml.sax.driver (see JDK-6490921).
277      * @return an XMLReader instance
278      */
getXMLReader(boolean overrideDefaultParser, boolean secureProcessing)279     public static XMLReader getXMLReader(boolean overrideDefaultParser,
280             boolean secureProcessing) {
281         SAXParserFactory saxFactory;
282         XMLReader reader = null;
283         String spSAXDriver = SecuritySupport.getSystemProperty(SAX_DRIVER);
284         if (spSAXDriver != null) {
285             reader = getXMLReaderWXMLReaderFactory();
286         } else if (overrideDefaultParser) {
287             reader = getXMLReaderWSAXFactory(overrideDefaultParser);
288         }
289 
290         if (reader != null) {
291             if (secureProcessing) {
292                 try {
293                     reader.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, secureProcessing);
294                 } catch (SAXException e) {
295                     XMLSecurityManager.printWarning(reader.getClass().getName(),
296                             XMLConstants.FEATURE_SECURE_PROCESSING, e);
297                 }
298             }
299             try {
300                 reader.setFeature(NAMESPACES_FEATURE, true);
301                 reader.setFeature(NAMESPACE_PREFIXES_FEATURE, false);
302             } catch (SAXException se) {
303                 // older version of a parser
304             }
305             return reader;
306         }
307 
308         // use the system-default
309         saxFactory = defaultSAXFactory;
310 
311         try {
312             reader = saxFactory.newSAXParser().getXMLReader();
313         } catch (ParserConfigurationException | SAXException ex) {
314             // shall not happen with the system-default reader
315         }
316         return reader;
317     }
318 
319     /**
320      * Creates a system-default DOM Document.
321      *
322      * @return a DOM Document instance
323      */
getDOMDocument()324     public static Document getDOMDocument() {
325         try {
326             DocumentBuilderFactory dbf = JdkXmlUtils.getDOMFactory(false);
327             return dbf.newDocumentBuilder().newDocument();
328         } catch (ParserConfigurationException pce) {
329             // can never happen with the system-default configuration
330         }
331         return null;
332     }
333 
334     /**
335      * Returns a DocumentBuilderFactory instance.
336      *
337      * @param overrideDefaultParser a flag indicating whether the system-default
338      * implementation may be overridden. If the system property of the
339      * DOM factory ID is set, override is always allowed.
340      *
341      * @return a DocumentBuilderFactory instance.
342      */
getDOMFactory(boolean overrideDefaultParser)343     public static DocumentBuilderFactory getDOMFactory(boolean overrideDefaultParser) {
344         boolean override = overrideDefaultParser;
345         String spDOMFactory = SecuritySupport.getJAXPSystemProperty(DOM_FACTORY_ID);
346 
347         if (spDOMFactory != null && System.getSecurityManager() == null) {
348             override = true;
349         }
350         DocumentBuilderFactory dbf
351                 = !override
352                         ? new DocumentBuilderFactoryImpl()
353                         : DocumentBuilderFactory.newInstance();
354         dbf.setNamespaceAware(true);
355         // false is the default setting. This step here is for compatibility
356         dbf.setValidating(false);
357         return dbf;
358     }
359 
360     /**
361      * Returns a SAXParserFactory instance.
362      *
363      * @param overrideDefaultParser a flag indicating whether the system-default
364      * implementation may be overridden. If the system property of the
365      * DOM factory ID is set, override is always allowed.
366      *
367      * @return a SAXParserFactory instance.
368      */
getSAXFactory(boolean overrideDefaultParser)369     public static SAXParserFactory getSAXFactory(boolean overrideDefaultParser) {
370         boolean override = overrideDefaultParser;
371         String spSAXFactory = SecuritySupport.getJAXPSystemProperty(SAX_FACTORY_ID);
372         if (spSAXFactory != null && System.getSecurityManager() == null) {
373             override = true;
374         }
375 
376         SAXParserFactory factory
377                 = !override
378                         ? new SAXParserFactoryImpl()
379                         : SAXParserFactory.newInstance();
380         factory.setNamespaceAware(true);
381         return factory;
382     }
383 
getSAXTransformFactory(boolean overrideDefaultParser)384     public static SAXTransformerFactory getSAXTransformFactory(boolean overrideDefaultParser) {
385         SAXTransformerFactory tf = overrideDefaultParser
386                 ? (SAXTransformerFactory) SAXTransformerFactory.newInstance()
387                 : (SAXTransformerFactory) new TransformerFactoryImpl();
388         try {
389             tf.setFeature(OVERRIDE_PARSER, overrideDefaultParser);
390         } catch (TransformerConfigurationException ex) {
391             // ignore since it'd never happen with the JDK impl.
392         }
393         return tf;
394     }
395 
396     /**
397      * Returns the character to be used to quote the input content. Between
398      * single and double quotes, this method returns the one that is not found
399      * in the input. Returns double quote by default.
400      *
401      * @param s the input string
402      * @return returns the quote not found in the input
403      */
getQuoteChar(String s)404     public static char getQuoteChar(String s) {
405         if (s != null && s.indexOf('"') > -1) {
406             return '\'';
407         } else {
408             return '"';
409         }
410     }
411 
getXMLReaderWSAXFactory(boolean overrideDefaultParser)412     private static XMLReader getXMLReaderWSAXFactory(boolean overrideDefaultParser) {
413         SAXParserFactory saxFactory = getSAXFactory(overrideDefaultParser);
414         try {
415             return saxFactory.newSAXParser().getXMLReader();
416         } catch (ParserConfigurationException | SAXException ex) {
417             return getXMLReaderWXMLReaderFactory();
418         }
419     }
420 
421     @SuppressWarnings("deprecation")
getXMLReaderWXMLReaderFactory()422     private static XMLReader getXMLReaderWXMLReaderFactory() {
423         try {
424             return org.xml.sax.helpers.XMLReaderFactory.createXMLReader();
425         } catch (SAXException ex1) {
426         }
427         return null;
428     }
429 }
430