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