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.org.apache.xml.internal.resolver.Catalog; 30 import com.sun.org.apache.xml.internal.resolver.CatalogManager; 31 import com.sun.org.apache.xml.internal.resolver.tools.CatalogResolver; 32 import com.sun.xml.internal.ws.server.ServerRtException; 33 import com.sun.xml.internal.ws.util.ByteArrayBuffer; 34 import org.w3c.dom.Attr; 35 import org.w3c.dom.Element; 36 import org.w3c.dom.EntityReference; 37 import org.w3c.dom.Node; 38 import org.w3c.dom.NodeList; 39 import org.w3c.dom.Text; 40 import org.xml.sax.EntityResolver; 41 import org.xml.sax.ErrorHandler; 42 import org.xml.sax.SAXException; 43 import org.xml.sax.SAXNotRecognizedException; 44 import org.xml.sax.SAXNotSupportedException; 45 import org.xml.sax.SAXParseException; 46 import org.xml.sax.XMLReader; 47 import org.xml.sax.InputSource; 48 49 import javax.xml.XMLConstants; 50 import javax.xml.namespace.QName; 51 import javax.xml.parsers.DocumentBuilderFactory; 52 import javax.xml.parsers.ParserConfigurationException; 53 import javax.xml.parsers.SAXParserFactory; 54 import javax.xml.stream.XMLInputFactory; 55 import javax.xml.transform.Result; 56 import javax.xml.transform.Source; 57 import javax.xml.transform.Transformer; 58 import javax.xml.transform.TransformerConfigurationException; 59 import javax.xml.transform.TransformerException; 60 import javax.xml.transform.TransformerFactory; 61 import javax.xml.transform.sax.SAXTransformerFactory; 62 import javax.xml.transform.sax.TransformerHandler; 63 import javax.xml.transform.stream.StreamSource; 64 import javax.xml.ws.WebServiceException; 65 import javax.xml.xpath.XPathFactory; 66 import javax.xml.xpath.XPathFactoryConfigurationException; 67 import java.io.IOException; 68 import java.io.InputStream; 69 import java.io.OutputStreamWriter; 70 import java.io.Writer; 71 import java.net.URL; 72 import java.security.AccessController; 73 import java.security.PrivilegedAction; 74 import java.util.ArrayList; 75 import java.util.Enumeration; 76 import java.util.Iterator; 77 import java.util.List; 78 import java.util.StringTokenizer; 79 import java.util.logging.Level; 80 import java.util.logging.Logger; 81 82 /** 83 * @author WS Development Team 84 */ 85 public class XmlUtil { 86 private final static String LEXICAL_HANDLER_PROPERTY = 87 "http://xml.org/sax/properties/lexical-handler"; 88 89 private static final String DISALLOW_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl"; 90 91 private static final String EXTERNAL_GE = "http://xml.org/sax/features/external-general-entities"; 92 93 private static final String EXTERNAL_PE = "http://xml.org/sax/features/external-parameter-entities"; 94 95 private static final String LOAD_EXTERNAL_DTD = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; 96 97 private static final Logger LOGGER = Logger.getLogger(XmlUtil.class.getName()); 98 99 private static final String DISABLE_XML_SECURITY = "com.sun.xml.internal.ws.disableXmlSecurity"; 100 101 private static boolean XML_SECURITY_DISABLED = AccessController.doPrivileged( 102 new PrivilegedAction<Boolean>() { 103 @Override 104 public Boolean run() { 105 return Boolean.getBoolean(DISABLE_XML_SECURITY); 106 } 107 } 108 ); 109 110 public static String getPrefix(String s) { 111 int i = s.indexOf(':'); 112 if (i == -1) 113 return null; 114 return s.substring(0, i); 115 } 116 117 public static String getLocalPart(String s) { 118 int i = s.indexOf(':'); 119 if (i == -1) 120 return s; 121 return s.substring(i + 1); 122 } 123 124 125 126 public static String getAttributeOrNull(Element e, String name) { 127 Attr a = e.getAttributeNode(name); 128 if (a == null) 129 return null; 130 return a.getValue(); 131 } 132 133 public static String getAttributeNSOrNull( 134 Element e, 135 String name, 136 String nsURI) { 137 Attr a = e.getAttributeNodeNS(nsURI, name); 138 if (a == null) 139 return null; 140 return a.getValue(); 141 } 142 143 public static String getAttributeNSOrNull( 144 Element e, 145 QName name) { 146 Attr a = e.getAttributeNodeNS(name.getNamespaceURI(), name.getLocalPart()); 147 if (a == null) 148 return null; 149 return a.getValue(); 150 } 151 152 /* public static boolean matchesTagNS(Element e, String tag, String nsURI) { 153 try { 154 return e.getLocalName().equals(tag) 155 && e.getNamespaceURI().equals(nsURI); 156 } catch (NullPointerException npe) { 157 158 // localname not null since parsing would fail before here 159 throw new WSDLParseException( 160 "null.namespace.found", 161 e.getLocalName()); 162 } 163 } 164 165 public static boolean matchesTagNS( 166 Element e, 167 javax.xml.namespace.QName name) { 168 try { 169 return e.getLocalName().equals(name.getLocalPart()) 170 && e.getNamespaceURI().equals(name.getNamespaceURI()); 171 } catch (NullPointerException npe) { 172 173 // localname not null since parsing would fail before here 174 throw new WSDLParseException( 175 "null.namespace.found", 176 e.getLocalName()); 177 } 178 }*/ 179 180 public static Iterator getAllChildren(Element element) { 181 return new NodeListIterator(element.getChildNodes()); 182 } 183 184 public static Iterator getAllAttributes(Element element) { 185 return new NamedNodeMapIterator(element.getAttributes()); 186 } 187 188 public static List<String> parseTokenList(String tokenList) { 189 List<String> result = new ArrayList<String>(); 190 StringTokenizer tokenizer = new StringTokenizer(tokenList, " "); 191 while (tokenizer.hasMoreTokens()) { 192 result.add(tokenizer.nextToken()); 193 } 194 return result; 195 } 196 197 public static String getTextForNode(Node node) { 198 StringBuilder sb = new StringBuilder(); 199 200 NodeList children = node.getChildNodes(); 201 if (children.getLength() == 0) 202 return null; 203 204 for (int i = 0; i < children.getLength(); ++i) { 205 Node n = children.item(i); 206 207 if (n instanceof Text) 208 sb.append(n.getNodeValue()); 209 else if (n instanceof EntityReference) { 210 String s = getTextForNode(n); 211 if (s == null) 212 return null; 213 else 214 sb.append(s); 215 } else 216 return null; 217 } 218 219 return sb.toString(); 220 } 221 222 public static InputStream getUTF8Stream(String s) { 223 try { 224 ByteArrayBuffer bab = new ByteArrayBuffer(); 225 Writer w = new OutputStreamWriter(bab, "utf-8"); 226 w.write(s); 227 w.close(); 228 return bab.newInputStream(); 229 } catch (IOException e) { 230 throw new RuntimeException("should not happen"); 231 } 232 } 233 234 static final ContextClassloaderLocal<TransformerFactory> transformerFactory = new ContextClassloaderLocal<TransformerFactory>() { 235 @Override 236 protected TransformerFactory initialValue() throws Exception { 237 return TransformerFactory.newInstance(); 238 } 239 }; 240 241 static final ContextClassloaderLocal<SAXParserFactory> saxParserFactory = new ContextClassloaderLocal<SAXParserFactory>() { 242 @Override 243 protected SAXParserFactory initialValue() throws Exception { 244 SAXParserFactory factory = SAXParserFactory.newInstance(); 245 factory.setNamespaceAware(true); 246 return factory; 247 } 248 }; 249 250 /** 251 * Creates a new identity transformer. 252 */ 253 public static Transformer newTransformer() { 254 try { 255 return transformerFactory.get().newTransformer(); 256 } catch (TransformerConfigurationException tex) { 257 throw new IllegalStateException("Unable to create a JAXP transformer"); 258 } 259 } 260 261 /** 262 * Performs identity transformation. 263 */ 264 public static <T extends Result> 265 T identityTransform(Source src, T result) throws TransformerException, SAXException, ParserConfigurationException, IOException { 266 if (src instanceof StreamSource) { 267 // work around a bug in JAXP in JDK6u4 and earlier where the namespace processing 268 // is not turned on by default 269 StreamSource ssrc = (StreamSource) src; 270 TransformerHandler th = ((SAXTransformerFactory) transformerFactory.get()).newTransformerHandler(); 271 th.setResult(result); 272 XMLReader reader = saxParserFactory.get().newSAXParser().getXMLReader(); 273 reader.setContentHandler(th); 274 reader.setProperty(LEXICAL_HANDLER_PROPERTY, th); 275 reader.parse(toInputSource(ssrc)); 276 } else { 277 newTransformer().transform(src, result); 278 } 279 return result; 280 } 281 282 private static InputSource toInputSource(StreamSource src) { 283 InputSource is = new InputSource(); 284 is.setByteStream(src.getInputStream()); 285 is.setCharacterStream(src.getReader()); 286 is.setPublicId(src.getPublicId()); 287 is.setSystemId(src.getSystemId()); 288 return is; 289 } 290 291 /* 292 * Gets an EntityResolver using XML catalog 293 */ 294 public static EntityResolver createEntityResolver(@Nullable URL catalogUrl) { 295 // set up a manager 296 CatalogManager manager = new CatalogManager(); 297 manager.setIgnoreMissingProperties(true); 298 // Using static catalog may result in to sharing of the catalog by multiple apps running in a container 299 manager.setUseStaticCatalog(false); 300 Catalog catalog = manager.getCatalog(); 301 try { 302 if (catalogUrl != null) { 303 catalog.parseCatalog(catalogUrl); 304 } 305 } catch (IOException e) { 306 throw new ServerRtException("server.rt.err",e); 307 } 308 return workaroundCatalogResolver(catalog); 309 } 310 311 /** 312 * Gets a default EntityResolver for catalog at META-INF/jaxws-catalog.xml 313 */ 314 public static EntityResolver createDefaultCatalogResolver() { 315 316 // set up a manager 317 CatalogManager manager = new CatalogManager(); 318 manager.setIgnoreMissingProperties(true); 319 // Using static catalog may result in to sharing of the catalog by multiple apps running in a container 320 manager.setUseStaticCatalog(false); 321 // parse the catalog 322 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 323 Enumeration<URL> catalogEnum; 324 Catalog catalog = manager.getCatalog(); 325 try { 326 if (cl == null) { 327 catalogEnum = ClassLoader.getSystemResources("META-INF/jax-ws-catalog.xml"); 328 } else { 329 catalogEnum = cl.getResources("META-INF/jax-ws-catalog.xml"); 330 } 331 332 while(catalogEnum.hasMoreElements()) { 333 URL url = catalogEnum.nextElement(); 334 catalog.parseCatalog(url); 335 } 336 } catch (IOException e) { 337 throw new WebServiceException(e); 338 } 339 340 return workaroundCatalogResolver(catalog); 341 } 342 343 /** 344 * Default CatalogResolver implementation is broken as it depends on CatalogManager.getCatalog() which will always create a new one when 345 * useStaticCatalog is false. 346 * This returns a CatalogResolver that uses the catalog passed as parameter. 347 * @param catalog 348 * @return CatalogResolver 349 */ 350 private static CatalogResolver workaroundCatalogResolver(final Catalog catalog) { 351 // set up a manager 352 CatalogManager manager = new CatalogManager() { 353 @Override 354 public Catalog getCatalog() { 355 return catalog; 356 } 357 }; 358 manager.setIgnoreMissingProperties(true); 359 // Using static catalog may result in to sharing of the catalog by multiple apps running in a container 360 manager.setUseStaticCatalog(false); 361 362 return new CatalogResolver(manager); 363 } 364 365 /** 366 * {@link ErrorHandler} that always treat the error as fatal. 367 */ 368 public static final ErrorHandler DRACONIAN_ERROR_HANDLER = new ErrorHandler() { 369 @Override 370 public void warning(SAXParseException exception) { 371 } 372 373 @Override 374 public void error(SAXParseException exception) throws SAXException { 375 throw exception; 376 } 377 378 @Override 379 public void fatalError(SAXParseException exception) throws SAXException { 380 throw exception; 381 } 382 }; 383 384 public static DocumentBuilderFactory newDocumentBuilderFactory() { 385 return newDocumentBuilderFactory(false); 386 } 387 388 public static DocumentBuilderFactory newDocumentBuilderFactory(boolean disableSecurity) { 389 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 390 String featureToSet = XMLConstants.FEATURE_SECURE_PROCESSING; 391 try { 392 boolean securityOn = !isXMLSecurityDisabled(disableSecurity); 393 factory.setFeature(featureToSet, securityOn); 394 factory.setNamespaceAware(true); 395 if (securityOn) { 396 factory.setExpandEntityReferences(false); 397 featureToSet = DISALLOW_DOCTYPE_DECL; 398 factory.setFeature(featureToSet, true); 399 featureToSet = EXTERNAL_GE; 400 factory.setFeature(featureToSet, false); 401 featureToSet = EXTERNAL_PE; 402 factory.setFeature(featureToSet, false); 403 featureToSet = LOAD_EXTERNAL_DTD; 404 factory.setFeature(featureToSet, false); 405 } 406 } catch (ParserConfigurationException e) { 407 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support "+featureToSet+" feature!", new Object[] {factory.getClass().getName()} ); 408 } 409 return factory; 410 } 411 412 public static TransformerFactory newTransformerFactory(boolean secureXmlProcessingEnabled) { 413 TransformerFactory factory = TransformerFactory.newInstance(); 414 try { 415 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, isXMLSecurityDisabled(secureXmlProcessingEnabled)); 416 } catch (TransformerConfigurationException e) { 417 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[]{factory.getClass().getName()}); 418 } 419 return factory; 420 } 421 422 public static TransformerFactory newTransformerFactory() { 423 return newTransformerFactory(true); 424 } 425 426 public static SAXParserFactory newSAXParserFactory(boolean disableSecurity) { 427 SAXParserFactory factory = SAXParserFactory.newInstance(); 428 String featureToSet = XMLConstants.FEATURE_SECURE_PROCESSING; 429 try { 430 boolean securityOn = !isXMLSecurityDisabled(disableSecurity); 431 factory.setFeature(featureToSet, securityOn); 432 factory.setNamespaceAware(true); 433 if (securityOn) { 434 featureToSet = DISALLOW_DOCTYPE_DECL; 435 factory.setFeature(featureToSet, true); 436 featureToSet = EXTERNAL_GE; 437 factory.setFeature(featureToSet, false); 438 featureToSet = EXTERNAL_PE; 439 factory.setFeature(featureToSet, false); 440 featureToSet = LOAD_EXTERNAL_DTD; 441 factory.setFeature(featureToSet, false); 442 } 443 } catch (ParserConfigurationException | SAXNotRecognizedException | SAXNotSupportedException e) { 444 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support "+featureToSet+" feature!", new Object[]{factory.getClass().getName()}); 445 } 446 return factory; 447 } 448 449 public static XPathFactory newXPathFactory(boolean secureXmlProcessingEnabled) { 450 XPathFactory factory = XPathFactory.newInstance(); 451 try { 452 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, isXMLSecurityDisabled(secureXmlProcessingEnabled)); 453 } catch (XPathFactoryConfigurationException e) { 454 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[] { factory.getClass().getName() } ); 455 } 456 return factory; 457 } 458 459 public static XMLInputFactory newXMLInputFactory(boolean secureXmlProcessingEnabled) { 460 XMLInputFactory factory = XMLInputFactory.newInstance(); 461 if (isXMLSecurityDisabled(secureXmlProcessingEnabled)) { 462 // TODO-Miran: are those apppropriate defaults? 463 factory.setProperty(XMLInputFactory.SUPPORT_DTD, false); 464 factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); 465 } 466 return factory; 467 } 468 469 private static boolean isXMLSecurityDisabled(boolean runtimeDisabled) { 470 return XML_SECURITY_DISABLED || runtimeDisabled; 471 } 472 473 }