Adding a property 'jdk.xml.resolveExternalEntities' that can be used to globally disable the resolution of external general/parameters entities and the external DTD.

git-svn-id: https://svn.apache.org/repos/asf/xerces/java/trunk@1564073 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/org/apache/xerces/parsers/SecureProcessingConfiguration.java b/src/org/apache/xerces/parsers/SecureProcessingConfiguration.java
index 0ac4537..489417a 100644
--- a/src/org/apache/xerces/parsers/SecureProcessingConfiguration.java
+++ b/src/org/apache/xerces/parsers/SecureProcessingConfiguration.java
@@ -67,6 +67,18 @@
     private static final String ENTITY_RESOLVER_PROPERTY = 
         Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY;
     
+    /** Feature identifier: external general entities. */
+    private static final String EXTERNAL_GENERAL_ENTITIES =
+        Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE;
+
+    /** Feature identifier: external parameter entities. */
+    private static final String EXTERNAL_PARAMETER_ENTITIES =
+        Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE;
+    
+    /** Feature identifier: load external DTD. */
+    private static final String LOAD_EXTERNAL_DTD =
+        Constants.XERCES_FEATURE_PREFIX + Constants.LOAD_EXTERNAL_DTD_FEATURE;
+    
     /** Set to true for debugging */
     private static final boolean DEBUG = isDebugEnabled();
     
@@ -87,18 +99,21 @@
     private static final String TOTAL_ENTITY_SIZE_LIMIT_PROPERTY_NAME = "jdk.xml.totalEntitySizeLimit";
     private static final String MAX_GENERAL_ENTITY_SIZE_LIMIT_PROPERTY_NAME = "jdk.xml.maxGeneralEntitySizeLimit";
     private static final String MAX_PARAMETER_ENTITY_SIZE_LIMIT_PROPERTY_NAME = "jdk.xml.maxParameterEntitySizeLimit";
+    private static final String RESOLVE_EXTERNAL_ENTITIES_PROPERTY_NAME = "jdk.xml.resolveExternalEntities";
     
     private static final int ENTITY_EXPANSION_LIMIT_DEFAULT_VALUE = 64000;
     private static final int MAX_OCCUR_LIMIT_DEFAULT_VALUE = 5000;
     private static final int TOTAL_ENTITY_SIZE_LIMIT_DEFAULT_VALUE = 50000000;
     private static final int MAX_GENERAL_ENTITY_SIZE_LIMIT_DEFAULT_VALUE = Integer.MAX_VALUE;
     private static final int MAX_PARAMETER_ENTITY_SIZE_LIMIT_DEFAULT_VALUE = Integer.MAX_VALUE;
+    private static final boolean RESOLVE_EXTERNAL_ENTITIES_DEFAULT_VALUE = true;
     
     protected final int ENTITY_EXPANSION_LIMIT_SYSTEM_VALUE;
     protected final int MAX_OCCUR_LIMIT_SYSTEM_VALUE;
     protected final int TOTAL_ENTITY_SIZE_LIMIT_SYSTEM_VALUE;
     protected final int MAX_GENERAL_ENTITY_SIZE_LIMIT_SYSTEM_VALUE;
     protected final int MAX_PARAMETER_ENTITY_SIZE_LIMIT_SYSTEM_VALUE;
+    protected final boolean RESOLVE_EXTERNAL_ENTITIES_SYSTEM_VALUE;
     
     //
     // Fields
@@ -159,11 +174,17 @@
         TOTAL_ENTITY_SIZE_LIMIT_SYSTEM_VALUE = getPropertyValue(TOTAL_ENTITY_SIZE_LIMIT_PROPERTY_NAME, TOTAL_ENTITY_SIZE_LIMIT_DEFAULT_VALUE);
         MAX_GENERAL_ENTITY_SIZE_LIMIT_SYSTEM_VALUE = getPropertyValue(MAX_GENERAL_ENTITY_SIZE_LIMIT_PROPERTY_NAME, MAX_GENERAL_ENTITY_SIZE_LIMIT_DEFAULT_VALUE);
         MAX_PARAMETER_ENTITY_SIZE_LIMIT_SYSTEM_VALUE = getPropertyValue(MAX_PARAMETER_ENTITY_SIZE_LIMIT_PROPERTY_NAME, MAX_PARAMETER_ENTITY_SIZE_LIMIT_DEFAULT_VALUE);
+        RESOLVE_EXTERNAL_ENTITIES_SYSTEM_VALUE = getPropertyValue(RESOLVE_EXTERNAL_ENTITIES_PROPERTY_NAME, RESOLVE_EXTERNAL_ENTITIES_DEFAULT_VALUE);
         if (fJavaSecurityManagerEnabled || fLimitSpecified) {
+            if (!RESOLVE_EXTERNAL_ENTITIES_SYSTEM_VALUE) {
+                super.setFeature(EXTERNAL_GENERAL_ENTITIES, false);
+                super.setFeature(EXTERNAL_PARAMETER_ENTITIES, false);
+                super.setFeature(LOAD_EXTERNAL_DTD, false);
+            }
             fSecurityManager = new org.apache.xerces.util.SecurityManager();
             fSecurityManager.setEntityExpansionLimit(ENTITY_EXPANSION_LIMIT_SYSTEM_VALUE);
             fSecurityManager.setMaxOccurNodeLimit(MAX_OCCUR_LIMIT_SYSTEM_VALUE);
-            super.setProperty(SECURITY_MANAGER_PROPERTY, fSecurityManager);
+            super.setProperty(SECURITY_MANAGER_PROPERTY, fSecurityManager);   
         }
         fExternalEntityMonitor = new ExternalEntityMonitor();
         super.setProperty(ENTITY_RESOLVER_PROPERTY, fExternalEntityMonitor);
@@ -420,6 +441,144 @@
         return defaultValue;
     }
     
+    private boolean getPropertyValue(String propertyName, boolean defaultValue) {
+        
+        // Step #1: Use the system property first
+        try {
+            String propertyValue = SecuritySupport.getSystemProperty(propertyName);
+            if (propertyValue != null && propertyValue.length() > 0) {
+                if (DEBUG) {
+                    debugPrintln("found system property \"" + propertyName + "\", value=" + propertyValue);
+                }
+                final boolean booleanValue = Boolean.valueOf(propertyValue).booleanValue();
+                fLimitSpecified = true;
+                return booleanValue;
+            }
+        }
+        // The VM ran out of memory or there was some other serious problem. Re-throw.
+        catch (VirtualMachineError vme) {
+            throw vme;
+        }
+        // ThreadDeath should always be re-thrown
+        catch (ThreadDeath td) {
+            throw td;
+        }
+        catch (Throwable e) {
+            // Ignore all other exceptions/errors and continue w/ next location
+            if (DEBUG) {
+                debugPrintln(e.getClass().getName() + ": " + e.getMessage());
+                e.printStackTrace();
+            }
+        }
+        
+        // Step #2: Use $java.home/lib/jaxp.properties
+        try {
+            boolean fExists = false;
+            File f = null;
+            try {               
+                String javah = SecuritySupport.getSystemProperty("java.home");
+                String configFile = javah + File.separator +
+                        "lib" + File.separator + "jaxp.properties";
+
+                f = new File(configFile);
+                fExists = SecuritySupport.getFileExists(f);
+
+            }
+            catch (SecurityException se) {
+                // If there is a security exception, move on to next location.
+                lastModified = -1;
+                jaxpProperties = null;            
+            }
+
+            synchronized (SecureProcessingConfiguration.class) {    
+
+                boolean runBlock = false;
+                FileInputStream fis = null;
+
+                try {
+                    if (lastModified >= 0) {
+                        // File has been modified, or didn't previously exist. 
+                        // Need to reload properties    
+                        if ((fExists) &&
+                            (lastModified < (lastModified = SecuritySupport.getLastModified(f)))) {  
+                            runBlock = true;
+                        } 
+                        else {
+                            if (!fExists) {
+                                // file existed, but it's been deleted.
+                                lastModified = -1;
+                                jaxpProperties = null;
+                            }
+                        }        
+                    } 
+                    else {
+                        if (fExists) { 
+                            // File didn't exist, but it does now.
+                            runBlock = true;
+                            lastModified = SecuritySupport.getLastModified(f);
+                        }    
+                    }
+
+                    if (runBlock == true) {
+                        // Try to read from $java.home/lib/jaxp.properties
+                        jaxpProperties = new Properties();
+
+                        fis = SecuritySupport.getFileInputStream(f);
+                        jaxpProperties.load(fis);
+                    }       
+
+                }
+                catch (Exception x) {
+                    lastModified = -1;
+                    jaxpProperties = null;
+                    // assert(x instanceof FileNotFoundException
+                    //        || x instanceof SecurityException)
+                    // In both cases, ignore and return the default value
+                }
+                finally {
+                    // try to close the input stream if one was opened.
+                    if (fis != null) {
+                        try {
+                            fis.close();
+                        }
+                        // Ignore the exception.
+                        catch (IOException exc) {}
+                    }
+                }
+            }
+
+            if (jaxpProperties != null) {            
+                String propertyValue = jaxpProperties.getProperty(propertyName);
+                if (propertyValue != null && propertyValue.length() > 0) {
+                    if (DEBUG) {
+                        debugPrintln("found \"" + propertyName + "\" in jaxp.properties, value=" + propertyValue);
+                    }
+                    final boolean booleanValue = Boolean.valueOf(propertyValue).booleanValue();
+                    fLimitSpecified = true;
+                    return booleanValue;
+                }
+            }
+        }
+        // The VM ran out of memory or there was some other serious problem. Re-throw.
+        catch (VirtualMachineError vme) {
+            throw vme;
+        }
+        // ThreadDeath should always be re-thrown
+        catch (ThreadDeath td) {
+            throw td;
+        }
+        catch (Throwable e) {
+            // Ignore all other exceptions/errors and return the default value.
+            if (DEBUG) {
+                debugPrintln(e.getClass().getName() + ": " + e.getMessage());
+                e.printStackTrace();
+            }
+        }
+        
+        // Step #3: Return the default value.
+        return defaultValue;
+    }
+    
     //
     // Private static methods
     //