1 /*
   2  * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * @LastModified: Sep 2017
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *     http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.xml.internal.utils;
  23 
  24 import com.sun.org.apache.xalan.internal.XalanConstants;
  25 import com.sun.org.apache.xalan.internal.utils.FactoryImpl;
  26 import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager;
  27 import java.util.HashMap;
  28 import javax.xml.XMLConstants;
  29 import javax.xml.catalog.CatalogFeatures;
  30 import javax.xml.parsers.FactoryConfigurationError;
  31 import javax.xml.parsers.ParserConfigurationException;
  32 import javax.xml.parsers.SAXParserFactory;
  33 import jdk.xml.internal.JdkXmlFeatures;
  34 import jdk.xml.internal.JdkXmlUtils;
  35 import jdk.xml.internal.SecuritySupport;
  36 import org.xml.sax.SAXException;
  37 import org.xml.sax.SAXNotRecognizedException;
  38 import org.xml.sax.SAXNotSupportedException;
  39 import org.xml.sax.XMLReader;
  40 import org.xml.sax.helpers.XMLReaderFactory;
  41 
  42 /**
  43  * Creates XMLReader objects and caches them for re-use.
  44  * This class follows the singleton pattern.
  45  */
  46 @SuppressWarnings("deprecation") //org.xml.sax.helpers.XMLReaderFactory
  47 public class XMLReaderManager {
  48 
  49     private static final String NAMESPACES_FEATURE =
  50                              "http://xml.org/sax/features/namespaces";
  51     private static final String NAMESPACE_PREFIXES_FEATURE =
  52                              "http://xml.org/sax/features/namespace-prefixes";
  53     private static final XMLReaderManager m_singletonManager =
  54                                                      new XMLReaderManager();
  55     private static final String property = "org.xml.sax.driver";
  56     /**
  57      * Parser factory to be used to construct XMLReader objects
  58      */
  59     private static SAXParserFactory m_parserFactory;
  60 
  61     /**
  62      * Cache of XMLReader objects
  63      */
  64     private ThreadLocal<XMLReader> m_readers;
  65 
  66     /**
  67      * Keeps track of whether an XMLReader object is in use.
  68      */
  69     private HashMap<XMLReader, Boolean> m_inUse;
  70 
  71     private boolean m_useServicesMechanism = true;
  72 
  73     private boolean _secureProcessing;
  74      /**
  75      * protocols allowed for external DTD references in source file and/or stylesheet.
  76      */
  77     private String _accessExternalDTD = XalanConstants.EXTERNAL_ACCESS_DEFAULT;
  78 
  79     private XMLSecurityManager _xmlSecurityManager;
  80 
  81     //Catalog Feature
  82     private boolean _useCatalog;
  83     private CatalogFeatures _catalogFeatures;
  84 
  85     private int _cdataChunkSize;
  86 
  87     /**
  88      * Hidden constructor
  89      */
  90     private XMLReaderManager() {
  91     }
  92 
  93     /**
  94      * Retrieves the singleton reader manager
  95      */
  96     public static XMLReaderManager getInstance(boolean useServicesMechanism) {
  97         m_singletonManager.setServicesMechnism(useServicesMechanism);
  98         return m_singletonManager;
  99     }
 100 
 101     /**
 102      * Retrieves a cached XMLReader for this thread, or creates a new
 103      * XMLReader, if the existing reader is in use.  When the caller no
 104      * longer needs the reader, it must release it with a call to
 105      * {@link #releaseXMLReader}.
 106      */
 107     public synchronized XMLReader getXMLReader() throws SAXException {
 108         XMLReader reader;
 109 
 110         if (m_readers == null) {
 111             // When the m_readers.get() method is called for the first time
 112             // on a thread, a new XMLReader will automatically be created.
 113             m_readers = new ThreadLocal<>();
 114         }
 115 
 116         if (m_inUse == null) {
 117             m_inUse = new HashMap<>();
 118         }
 119 
 120         // If the cached reader for this thread is in use, construct a new
 121         // one; otherwise, return the cached reader unless it isn't an
 122         // instance of the class set in the 'org.xml.sax.driver' property
 123         reader = m_readers.get();
 124         boolean threadHasReader = (reader != null);
 125         String factory = SecuritySupport.getSystemProperty(property);
 126         if (threadHasReader && m_inUse.get(reader) != Boolean.TRUE &&
 127                 ( factory == null || reader.getClass().getName().equals(factory))) {
 128             m_inUse.put(reader, Boolean.TRUE);
 129         } else {
 130             try {
 131                 try {
 132                     // According to JAXP 1.2 specification, if a SAXSource
 133                     // is created using a SAX InputSource the Transformer or
 134                     // TransformerFactory creates a reader via the
 135                     // XMLReaderFactory if setXMLReader is not used
 136                     reader = XMLReaderFactory.createXMLReader();
 137                     try {
 138                         reader.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, _secureProcessing);
 139                     } catch (SAXNotRecognizedException e) {
 140                         XMLSecurityManager.printWarning(reader.getClass().getName(),
 141                                 XMLConstants.FEATURE_SECURE_PROCESSING, e);
 142                     }
 143                 } catch (SAXException e) {
 144                    try {
 145                         // If unable to create an instance, let's try to use
 146                         // the XMLReader from JAXP
 147                         if (m_parserFactory == null) {
 148                             m_parserFactory = FactoryImpl.getSAXFactory(m_useServicesMechanism);
 149                             m_parserFactory.setNamespaceAware(true);
 150                         }
 151 
 152                         reader = m_parserFactory.newSAXParser().getXMLReader();
 153                    } catch (ParserConfigurationException pce) {
 154                        throw pce;   // pass along pce
 155                    }
 156                 }
 157                 try {
 158                     reader.setFeature(NAMESPACES_FEATURE, true);
 159                     reader.setFeature(NAMESPACE_PREFIXES_FEATURE, false);
 160                 } catch (SAXException se) {
 161                     // Try to carry on if we've got a parser that
 162                     // doesn't know about namespace prefixes.
 163                 }
 164             } catch (ParserConfigurationException ex) {
 165                 throw new SAXException(ex);
 166             } catch (FactoryConfigurationError ex1) {
 167                 throw new SAXException(ex1.toString());
 168             } catch (NoSuchMethodError | AbstractMethodError ex2) {
 169             }
 170 
 171             // Cache the XMLReader if this is the first time we've created
 172             // a reader for this thread.
 173             if (!threadHasReader) {
 174                 m_readers.set(reader);
 175                 m_inUse.put(reader, Boolean.TRUE);
 176             }
 177         }
 178 
 179         //reader is cached, but this property might have been reset
 180         JdkXmlUtils.setXMLReaderPropertyIfSupport(reader, XMLConstants.ACCESS_EXTERNAL_DTD,
 181                 _accessExternalDTD, true);
 182 
 183         JdkXmlUtils.setXMLReaderPropertyIfSupport(reader, JdkXmlUtils.CDATA_CHUNK_SIZE,
 184                 _cdataChunkSize, false);
 185 
 186         String lastProperty = "";
 187         try {
 188             if (_xmlSecurityManager != null) {
 189                 for (XMLSecurityManager.Limit limit : XMLSecurityManager.Limit.values()) {
 190                     lastProperty = limit.apiProperty();
 191                     reader.setProperty(lastProperty,
 192                             _xmlSecurityManager.getLimitValueAsString(limit));
 193                 }
 194                 if (_xmlSecurityManager.printEntityCountInfo()) {
 195                     lastProperty = XalanConstants.JDK_ENTITY_COUNT_INFO;
 196                     reader.setProperty(XalanConstants.JDK_ENTITY_COUNT_INFO, XalanConstants.JDK_YES);
 197                 }
 198             }
 199         } catch (SAXException se) {
 200             XMLSecurityManager.printWarning(reader.getClass().getName(), lastProperty, se);
 201         }
 202 
 203         boolean supportCatalog = true;
 204         try {
 205             reader.setFeature(JdkXmlUtils.USE_CATALOG, _useCatalog);
 206         }
 207         catch (SAXNotRecognizedException | SAXNotSupportedException e) {
 208             supportCatalog = false;
 209         }
 210 
 211         if (supportCatalog && _useCatalog && _catalogFeatures != null) {
 212             try {
 213                 for (CatalogFeatures.Feature f : CatalogFeatures.Feature.values()) {
 214                     reader.setProperty(f.getPropertyName(), _catalogFeatures.get(f));
 215                 }
 216             } catch (SAXNotRecognizedException e) {
 217                 //shall not happen for internal settings
 218             }
 219         }
 220         return reader;
 221     }
 222 
 223     /**
 224      * Mark the cached XMLReader as available.  If the reader was not
 225      * actually in the cache, do nothing.
 226      *
 227      * @param reader The XMLReader that's being released.
 228      */
 229     public synchronized void releaseXMLReader(XMLReader reader) {
 230         // If the reader that's being released is the cached reader
 231         // for this thread, remove it from the m_isUse list.
 232         if (m_readers.get() == reader && reader != null) {
 233             m_inUse.remove(reader);
 234         }
 235     }
 236     /**
 237      * Return the state of the services mechanism feature.
 238      */
 239     public boolean useServicesMechnism() {
 240         return m_useServicesMechanism;
 241     }
 242 
 243     /**
 244      * Set the state of the services mechanism feature.
 245      */
 246     public void setServicesMechnism(boolean flag) {
 247         m_useServicesMechanism = flag;
 248     }
 249 
 250     /**
 251      * Set feature
 252      */
 253     public void setFeature(String name, boolean value) {
 254         if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) {
 255             _secureProcessing = value;
 256         } else if (XMLConstants.USE_CATALOG.equals(name)) {
 257             _useCatalog = value;
 258         }
 259     }
 260 
 261     /**
 262      * Get property value
 263      */
 264     public Object getProperty(String name) {
 265         if (name.equals(XMLConstants.ACCESS_EXTERNAL_DTD)) {
 266             return _accessExternalDTD;
 267         } else if (name.equals(XalanConstants.SECURITY_MANAGER)) {
 268             return _xmlSecurityManager;
 269         }
 270         return null;
 271     }
 272 
 273     /**
 274      * Set property.
 275      */
 276     public void setProperty(String name, Object value) {
 277         if (name.equals(XMLConstants.ACCESS_EXTERNAL_DTD)) {
 278             _accessExternalDTD = (String)value;
 279         } else if (name.equals(XalanConstants.SECURITY_MANAGER)) {
 280             _xmlSecurityManager = (XMLSecurityManager)value;
 281         } else if (JdkXmlFeatures.CATALOG_FEATURES.equals(name)) {
 282             _catalogFeatures = (CatalogFeatures)value;
 283         } else if (JdkXmlUtils.CDATA_CHUNK_SIZE.equals(name)) {
 284             _cdataChunkSize = JdkXmlUtils.getValue(value, _cdataChunkSize);
 285         }
 286     }
 287 }