1 /* 2 * Copyright (c) 1997, 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 26 package com.sun.xml.internal.ws.util.xml; 27 28 import com.sun.istack.internal.Nullable; 29 import com.sun.xml.internal.ws.util.ByteArrayBuffer; 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.io.OutputStreamWriter; 33 import java.io.Writer; 34 import java.net.URL; 35 import java.security.AccessController; 36 import java.security.PrivilegedAction; 37 import java.util.ArrayList; 38 import java.util.Iterator; 39 import java.util.List; 40 import java.util.StringTokenizer; 41 import java.util.logging.Level; 42 import java.util.logging.Logger; 43 import javax.xml.XMLConstants; 44 import javax.xml.namespace.QName; 45 import javax.xml.parsers.DocumentBuilderFactory; 46 import javax.xml.parsers.ParserConfigurationException; 47 import javax.xml.parsers.SAXParserFactory; 48 import javax.xml.stream.XMLInputFactory; 49 import javax.xml.transform.Result; 50 import javax.xml.transform.Source; 51 import javax.xml.transform.Transformer; 52 import javax.xml.transform.TransformerConfigurationException; 53 import javax.xml.transform.TransformerException; 54 import javax.xml.transform.TransformerFactory; 55 import javax.xml.transform.sax.SAXTransformerFactory; 56 import javax.xml.transform.sax.TransformerHandler; 57 import javax.xml.transform.stream.StreamSource; 58 import javax.xml.validation.SchemaFactory; 59 import javax.xml.xpath.XPathFactory; 60 import javax.xml.xpath.XPathFactoryConfigurationException; 61 import org.w3c.dom.Attr; 62 import org.w3c.dom.Element; 63 import org.w3c.dom.EntityReference; 64 import org.w3c.dom.Node; 65 import org.w3c.dom.NodeList; 66 import org.w3c.dom.Text; 67 import org.xml.sax.EntityResolver; 68 import org.xml.sax.ErrorHandler; 69 import org.xml.sax.InputSource; 70 import org.xml.sax.SAXException; 71 import org.xml.sax.SAXNotRecognizedException; 72 import org.xml.sax.SAXNotSupportedException; 73 import org.xml.sax.SAXParseException; 74 import org.xml.sax.XMLReader; 75 76 /** 77 * @author WS Development Team 78 */ 79 public class XmlUtil { 80 81 // not in older JDK, so must be duplicated here, otherwise javax.xml.XMLConstants should be used 82 private static final String ACCESS_EXTERNAL_SCHEMA = "http://javax.xml.XMLConstants/property/accessExternalSchema"; 83 84 private final static String LEXICAL_HANDLER_PROPERTY = 85 "http://xml.org/sax/properties/lexical-handler"; 86 87 private static final Logger LOGGER = Logger.getLogger(XmlUtil.class.getName()); 88 89 private static final String DISABLE_XML_SECURITY = "com.sun.xml.internal.ws.disableXmlSecurity"; 90 91 private static boolean XML_SECURITY_DISABLED = AccessController.doPrivileged( 92 new PrivilegedAction<Boolean>() { 93 @Override 94 public Boolean run() { 95 return Boolean.getBoolean(DISABLE_XML_SECURITY); 96 } 97 } 98 ); 99 100 public static String getPrefix(String s) { 101 int i = s.indexOf(':'); 102 if (i == -1) 103 return null; 104 return s.substring(0, i); 105 } 106 107 public static String getLocalPart(String s) { 108 int i = s.indexOf(':'); 109 if (i == -1) 110 return s; 111 return s.substring(i + 1); 112 } 113 114 115 116 public static String getAttributeOrNull(Element e, String name) { 117 Attr a = e.getAttributeNode(name); 118 if (a == null) 119 return null; 120 return a.getValue(); 121 } 122 123 public static String getAttributeNSOrNull( 124 Element e, 125 String name, 126 String nsURI) { 127 Attr a = e.getAttributeNodeNS(nsURI, name); 128 if (a == null) 129 return null; 130 return a.getValue(); 131 } 132 133 public static String getAttributeNSOrNull( 134 Element e, 135 QName name) { 136 Attr a = e.getAttributeNodeNS(name.getNamespaceURI(), name.getLocalPart()); 137 if (a == null) 138 return null; 139 return a.getValue(); 140 } 141 142 /* public static boolean matchesTagNS(Element e, String tag, String nsURI) { 143 try { 144 return e.getLocalName().equals(tag) 145 && e.getNamespaceURI().equals(nsURI); 146 } catch (NullPointerException npe) { 147 148 // localname not null since parsing would fail before here 149 throw new WSDLParseException( 150 "null.namespace.found", 151 e.getLocalName()); 152 } 153 } 154 155 public static boolean matchesTagNS( 156 Element e, 157 javax.xml.namespace.QName name) { 158 try { 159 return e.getLocalName().equals(name.getLocalPart()) 160 && e.getNamespaceURI().equals(name.getNamespaceURI()); 161 } catch (NullPointerException npe) { 162 163 // localname not null since parsing would fail before here 164 throw new WSDLParseException( 165 "null.namespace.found", 166 e.getLocalName()); 167 } 168 }*/ 169 170 public static Iterator getAllChildren(Element element) { 171 return new NodeListIterator(element.getChildNodes()); 172 } 173 174 public static Iterator getAllAttributes(Element element) { 175 return new NamedNodeMapIterator(element.getAttributes()); 176 } 177 178 public static List<String> parseTokenList(String tokenList) { 179 List<String> result = new ArrayList<>(); 180 StringTokenizer tokenizer = new StringTokenizer(tokenList, " "); 181 while (tokenizer.hasMoreTokens()) { 182 result.add(tokenizer.nextToken()); 183 } 184 return result; 185 } 186 187 public static String getTextForNode(Node node) { 188 StringBuilder sb = new StringBuilder(); 189 190 NodeList children = node.getChildNodes(); 191 if (children.getLength() == 0) 192 return null; 193 194 for (int i = 0; i < children.getLength(); ++i) { 195 Node n = children.item(i); 196 197 if (n instanceof Text) 198 sb.append(n.getNodeValue()); 199 else if (n instanceof EntityReference) { 200 String s = getTextForNode(n); 201 if (s == null) 202 return null; 203 else 204 sb.append(s); 205 } else 206 return null; 207 } 208 209 return sb.toString(); 210 } 211 212 public static InputStream getUTF8Stream(String s) { 213 try { 214 ByteArrayBuffer bab = new ByteArrayBuffer(); 215 Writer w = new OutputStreamWriter(bab, "utf-8"); 216 w.write(s); 217 w.close(); 218 return bab.newInputStream(); 219 } catch (IOException e) { 220 throw new RuntimeException("should not happen"); 221 } 222 } 223 224 static final ContextClassloaderLocal<TransformerFactory> transformerFactory = new ContextClassloaderLocal<TransformerFactory>() { 225 @Override 226 protected TransformerFactory initialValue() throws Exception { 227 return TransformerFactory.newInstance(); 228 } 229 }; 230 231 static final ContextClassloaderLocal<SAXParserFactory> saxParserFactory = new ContextClassloaderLocal<SAXParserFactory>() { 232 @Override 233 protected SAXParserFactory initialValue() throws Exception { 234 SAXParserFactory factory = newSAXParserFactory(true); 235 factory.setNamespaceAware(true); 236 return factory; 237 } 238 }; 239 240 /** 241 * Creates a new identity transformer. 242 * @return 243 */ 244 public static Transformer newTransformer() { 245 try { 246 return transformerFactory.get().newTransformer(); 247 } catch (TransformerConfigurationException tex) { 248 throw new IllegalStateException("Unable to create a JAXP transformer"); 249 } 250 } 251 252 /** 253 * Performs identity transformation. 254 * @param <T> 255 * @param src 256 * @param result 257 * @return 258 * @throws javax.xml.transform.TransformerException 259 * @throws java.io.IOException 260 * @throws org.xml.sax.SAXException 261 * @throws javax.xml.parsers.ParserConfigurationException 262 */ 263 public static <T extends Result> T identityTransform(Source src, T result) 264 throws TransformerException, SAXException, ParserConfigurationException, IOException { 265 if (src instanceof StreamSource) { 266 // work around a bug in JAXP in JDK6u4 and earlier where the namespace processing 267 // is not turned on by default 268 StreamSource ssrc = (StreamSource) src; 269 TransformerHandler th = ((SAXTransformerFactory) transformerFactory.get()).newTransformerHandler(); 270 th.setResult(result); 271 XMLReader reader = saxParserFactory.get().newSAXParser().getXMLReader(); 272 reader.setContentHandler(th); 273 reader.setProperty(LEXICAL_HANDLER_PROPERTY, th); 274 reader.parse(toInputSource(ssrc)); 275 } else { 276 newTransformer().transform(src, result); 277 } 278 return result; 279 } 280 281 private static InputSource toInputSource(StreamSource src) { 282 InputSource is = new InputSource(); 283 is.setByteStream(src.getInputStream()); 284 is.setCharacterStream(src.getReader()); 285 is.setPublicId(src.getPublicId()); 286 is.setSystemId(src.getSystemId()); 287 return is; 288 } 289 290 /** 291 * Gets an EntityResolver using XML catalog 292 * 293 * @param catalogUrl 294 * @return 295 */ 296 public static EntityResolver createEntityResolver(@Nullable URL catalogUrl) { 297 return XmlCatalogUtil.createEntityResolver(catalogUrl); 298 } 299 300 /** 301 * Gets a default EntityResolver for catalog at META-INF/jaxws-catalog.xml 302 * 303 * @return 304 */ 305 public static EntityResolver createDefaultCatalogResolver() { 306 return XmlCatalogUtil.createDefaultCatalogResolver(); 307 } 308 309 /** 310 * {@link ErrorHandler} that always treat the error as fatal. 311 */ 312 public static final ErrorHandler DRACONIAN_ERROR_HANDLER = new ErrorHandler() { 313 @Override 314 public void warning(SAXParseException exception) { 315 } 316 317 @Override 318 public void error(SAXParseException exception) throws SAXException { 319 throw exception; 320 } 321 322 @Override 323 public void fatalError(SAXParseException exception) throws SAXException { 324 throw exception; 325 } 326 }; 327 328 public static DocumentBuilderFactory newDocumentBuilderFactory(boolean disableSecurity) { 329 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 330 try { 331 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, !xmlSecurityDisabled(disableSecurity)); 332 } catch (ParserConfigurationException e) { 333 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[] { factory.getClass().getName() } ); 334 } 335 return factory; 336 } 337 338 public static TransformerFactory newTransformerFactory(boolean disableSecurity) { 339 TransformerFactory factory = TransformerFactory.newInstance(); 340 try { 341 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, !xmlSecurityDisabled(disableSecurity)); 342 } catch (TransformerConfigurationException e) { 343 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[]{factory.getClass().getName()}); 344 } 345 return factory; 346 } 347 348 public static SAXParserFactory newSAXParserFactory(boolean disableSecurity) { 349 SAXParserFactory factory = SAXParserFactory.newInstance(); 350 try { 351 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, !xmlSecurityDisabled(disableSecurity)); 352 } catch (ParserConfigurationException | SAXNotRecognizedException | SAXNotSupportedException e) { 353 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[]{factory.getClass().getName()}); 354 } 355 return factory; 356 } 357 358 public static XPathFactory newXPathFactory(boolean disableSecurity) { 359 XPathFactory factory = XPathFactory.newInstance(); 360 try { 361 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, !xmlSecurityDisabled(disableSecurity)); 362 } catch (XPathFactoryConfigurationException e) { 363 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[] { factory.getClass().getName() } ); 364 } 365 return factory; 366 } 367 368 public static XMLInputFactory newXMLInputFactory(boolean disableSecurity) { 369 XMLInputFactory factory = XMLInputFactory.newInstance(); 370 if (xmlSecurityDisabled(disableSecurity)) { 371 // TODO-Miran: are those apppropriate defaults? 372 factory.setProperty(XMLInputFactory.SUPPORT_DTD, false); 373 factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); 374 } 375 return factory; 376 } 377 378 private static boolean xmlSecurityDisabled(boolean runtimeDisabled) { 379 return XML_SECURITY_DISABLED || runtimeDisabled; 380 } 381 382 public static SchemaFactory allowExternalAccess(SchemaFactory sf, String value, boolean disableSecurity) { 383 384 // if xml security (feature secure processing) disabled, nothing to do, no restrictions applied 385 if (xmlSecurityDisabled(disableSecurity)) { 386 if (LOGGER.isLoggable(Level.FINE)) { 387 LOGGER.log(Level.FINE, "Xml Security disabled, no JAXP xsd external access configuration necessary."); 388 } 389 return sf; 390 } 391 392 if (System.getProperty("javax.xml.accessExternalSchema") != null) { 393 if (LOGGER.isLoggable(Level.FINE)) { 394 LOGGER.log(Level.FINE, "Detected explicitly JAXP configuration, no JAXP xsd external access configuration necessary."); 395 } 396 return sf; 397 } 398 399 try { 400 sf.setProperty(ACCESS_EXTERNAL_SCHEMA, value); 401 if (LOGGER.isLoggable(Level.FINE)) { 402 LOGGER.log(Level.FINE, "Property \"{0}\" is supported and has been successfully set by used JAXP implementation.", new Object[]{ACCESS_EXTERNAL_SCHEMA}); 403 } 404 } catch (SAXException ignored) { 405 // nothing to do; support depends on version JDK or SAX implementation 406 if (LOGGER.isLoggable(Level.CONFIG)) { 407 LOGGER.log(Level.CONFIG, "Property \"{0}\" is not supported by used JAXP implementation.", new Object[]{ACCESS_EXTERNAL_SCHEMA}); 408 } 409 } 410 return sf; 411 } 412 413 }