001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.tools; 003 004import java.io.IOException; 005import java.io.InputStream; 006 007import javax.xml.XMLConstants; 008import javax.xml.parsers.DocumentBuilder; 009import javax.xml.parsers.DocumentBuilderFactory; 010import javax.xml.parsers.ParserConfigurationException; 011import javax.xml.parsers.SAXParser; 012import javax.xml.parsers.SAXParserFactory; 013import javax.xml.stream.XMLInputFactory; 014import javax.xml.transform.TransformerConfigurationException; 015import javax.xml.transform.TransformerFactory; 016import javax.xml.validation.SchemaFactory; 017import javax.xml.validation.SchemaFactoryConfigurationError; 018 019import org.w3c.dom.Document; 020import org.xml.sax.InputSource; 021import org.xml.sax.SAXException; 022import org.xml.sax.helpers.DefaultHandler; 023 024/** 025 * XML utils, mainly used to construct safe factories. 026 * @since 13901 027 */ 028public final class XmlUtils { 029 030 private XmlUtils() { 031 // Hide default constructor for utils classes 032 } 033 034 /** 035 * Returns the W3C XML Schema factory implementation. Robust method dealing with ContextClassLoader problems. 036 * @return the W3C XML Schema factory implementation 037 */ 038 public static SchemaFactory newXmlSchemaFactory() { 039 try { 040 return SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 041 } catch (SchemaFactoryConfigurationError e) { 042 Logging.debug(e); 043 // Can happen with icedtea-web. Use workaround from https://issues.apache.org/jira/browse/GERONIMO-6185 044 Thread currentThread = Thread.currentThread(); 045 ClassLoader old = currentThread.getContextClassLoader(); 046 currentThread.setContextClassLoader(null); 047 try { 048 return SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 049 } finally { 050 currentThread.setContextClassLoader(old); 051 } 052 } 053 } 054 055 /** 056 * Returns a new secure DOM builder, supporting XML namespaces. 057 * @return a new secure DOM builder, supporting XML namespaces 058 * @throws ParserConfigurationException if a parser cannot be created which satisfies the requested configuration. 059 */ 060 public static DocumentBuilder newSafeDOMBuilder() throws ParserConfigurationException { 061 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); 062 builderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); 063 builderFactory.setNamespaceAware(true); 064 builderFactory.setValidating(false); 065 return builderFactory.newDocumentBuilder(); 066 } 067 068 /** 069 * Parse the content given {@link InputStream} as XML. 070 * This method uses a secure DOM builder, supporting XML namespaces. 071 * 072 * @param is The InputStream containing the content to be parsed. 073 * @return the result DOM document 074 * @throws ParserConfigurationException if a parser cannot be created which satisfies the requested configuration. 075 * @throws IOException if any IO errors occur. 076 * @throws SAXException for SAX errors. 077 */ 078 public static Document parseSafeDOM(InputStream is) throws ParserConfigurationException, IOException, SAXException { 079 long start = System.currentTimeMillis(); 080 Logging.debug("Starting DOM parsing of {0}", is); 081 Document result = newSafeDOMBuilder().parse(is); 082 if (Logging.isDebugEnabled()) { 083 Logging.debug("DOM parsing done in {0}", Utils.getDurationString(System.currentTimeMillis() - start)); 084 } 085 return result; 086 } 087 088 /** 089 * Returns a new secure SAX parser, supporting XML namespaces. 090 * @return a new secure SAX parser, supporting XML namespaces 091 * @throws ParserConfigurationException if a parser cannot be created which satisfies the requested configuration. 092 * @throws SAXException for SAX errors. 093 */ 094 public static SAXParser newSafeSAXParser() throws ParserConfigurationException, SAXException { 095 SAXParserFactory parserFactory = SAXParserFactory.newInstance(); 096 parserFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); 097 parserFactory.setNamespaceAware(true); 098 return parserFactory.newSAXParser(); 099 } 100 101 /** 102 * Parse the content given {@link org.xml.sax.InputSource} as XML using the specified {@link org.xml.sax.helpers.DefaultHandler}. 103 * This method uses a secure SAX parser, supporting XML namespaces. 104 * 105 * @param is The InputSource containing the content to be parsed. 106 * @param dh The SAX DefaultHandler to use. 107 * @throws ParserConfigurationException if a parser cannot be created which satisfies the requested configuration. 108 * @throws SAXException for SAX errors. 109 * @throws IOException if any IO errors occur. 110 */ 111 public static void parseSafeSAX(InputSource is, DefaultHandler dh) throws ParserConfigurationException, SAXException, IOException { 112 long start = System.currentTimeMillis(); 113 Logging.debug("Starting SAX parsing of {0} using {1}", is, dh); 114 newSafeSAXParser().parse(is, dh); 115 if (Logging.isDebugEnabled()) { 116 Logging.debug("SAX parsing done in {0}", Utils.getDurationString(System.currentTimeMillis() - start)); 117 } 118 } 119 120 /** 121 * Returns a new secure {@link XMLInputFactory}. 122 * @return a new secure {@code XMLInputFactory}, for which external entities are not loaded 123 */ 124 public static XMLInputFactory newSafeXMLInputFactory() { 125 XMLInputFactory factory = XMLInputFactory.newInstance(); 126 // do not try to load external entities, nor validate the XML 127 factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE); 128 factory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE); 129 factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE); 130 return factory; 131 } 132 133 /** 134 * Returns a new secure {@link TransformerFactory}. 135 * @return a new secure {@link TransformerFactory} 136 * @throws TransformerConfigurationException if the factory or the Transformers or Templates it creates cannot support this feature. 137 */ 138 public static TransformerFactory newSafeTransformerFactory() throws TransformerConfigurationException { 139 TransformerFactory factory = TransformerFactory.newInstance(); 140 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); 141 return factory; 142 } 143}