Blob Blame History Raw
--- java/org/apache/catalina/Context.java.orig	2014-07-21 17:24:05.516400000 -0400
+++ java/org/apache/catalina/Context.java	2014-07-21 17:46:59.386109000 -0400
@@ -40,6 +40,7 @@
 import org.apache.catalina.deploy.NamingResources;
 import org.apache.catalina.deploy.SecurityConstraint;
 import org.apache.catalina.util.CharsetMapper;
+import org.apache.tomcat.InstanceManager;
 import org.apache.tomcat.JarScanner;
 import org.apache.tomcat.util.http.mapper.Mapper;
 
@@ -61,7 +62,7 @@
  * <p>
  *
  * @author Craig R. McClanahan
- * @version $Id: Context.java 1492415 2013-06-12 20:41:33Z markt $
+ * @version $Id: Context.java 1552258 2013-12-19 09:25:18Z markt $
  */
 
 public interface Context extends Container {
@@ -616,70 +617,99 @@
 
 
     /**
-     * Get the server.xml <context> attribute's xmlNamespaceAware.
-     * @return true if namespace awareness is enabled.
+     * Will the parsing of web.xml and web-fragment.xml files for this Context
+     * be performed by a namespace aware parser?
      *
+     * @return true if namespace awareness is enabled.
      */
     public boolean getXmlNamespaceAware();
 
+    
+    /**
+     * Controls whether the parsing of web.xml and web-fragment.xml files for
+     * this Context will be performed by a namespace aware parser.
+     *
+     * @param xmlNamespaceAware true to enable namespace awareness
+     */
+    public void setXmlNamespaceAware(boolean xmlNamespaceAware);
 
+    
     /**
-     * Get the server.xml <context> attribute's xmlValidation.
-     * @return true if validation is enabled.
+     * Will the parsing of web.xml and web-fragment.xml files for this Context
+     * be performed by a validating parser?
      *
+     * @return true if validation is enabled.
      */
     public boolean getXmlValidation();
 
 
     /**
-     * Set the validation feature of the XML parser used when
-     * parsing xml instances.
-     * @param xmlValidation true to enable xml instance validation
+     * Controls whether the parsing of web.xml and web-fragment.xml files
+     * for this Context will be performed by a validating parser.
+     *
+     * @param xmlValidation true to enable xml validation
      */
     public void setXmlValidation(boolean xmlValidation);
 
 
-   /**
-     * Set the namespace aware feature of the XML parser used when
-     * parsing xml instances.
-     * @param xmlNamespaceAware true to enable namespace awareness
+    /**
+     * *.tld files are always parsed using a namespace aware parser.
+     *
+     * @return Always <code>true</code>
+     * 
+     * @deprecated This option will be removed in 8.0.x.
      */
-    public void setXmlNamespaceAware(boolean xmlNamespaceAware);
+    @Deprecated
+    public boolean getTldNamespaceAware();
+
+
     /**
-     * Get the server.xml <context> attribute's xmlValidation.
-     * @return true if validation is enabled.
+     * *.tld files are always parsed using a namespace aware parser.
+     *
+     * @param tldNamespaceAware ignored
+     * 
+     * @deprecated This option will be removed in 8.0.x.
      */
+    @Deprecated
+    public void setTldNamespaceAware(boolean tldNamespaceAware);
 
 
     /**
-     * Set the validation feature of the XML parser used when
-     * parsing tlds files.
-     * @param tldValidation true to enable xml instance validation
+     * Will the parsing of web.xml, web-fragment.xml, *.tld, *.jspx, *.tagx and
+     * tagplugin.xml files for this Context block the use of external entities?
+     *
+     * @return true if access to external entities is blocked
      */
-    public void setTldValidation(boolean tldValidation);
+    public boolean getXmlBlockExternal();
 
 
     /**
-     * Get the server.xml <context> attribute's webXmlValidation.
-     * @return true if validation is enabled.
+     * Controls whether the parsing of web.xml, web-fragment.xml, *.tld, *.jspx,
+     * *.tagx and tagplugin.xml files for this Context will block the use of
+     * external entities.
      *
+     * @param xmlBlockExternal true to block external entities
      */
-    public boolean getTldValidation();
+    public void setXmlBlockExternal(boolean xmlBlockExternal);
 
 
     /**
-     * Get the server.xml &lt;host&gt; attribute's xmlNamespaceAware.
-     * @return true if namespace awareness is enabled.
+     * Will the parsing of *.tld files for this Context be performed by a
+     * validating parser?
+     *
+     * @return true if validation is enabled.
      */
-    public boolean getTldNamespaceAware();
+    public boolean getTldValidation();
 
 
     /**
-     * Set the namespace aware feature of the XML parser used when
-     * parsing xml instances.
-     * @param tldNamespaceAware true to enable namespace awareness
+     * Controls whether the parsing of *.tld files for this Context will be
+     * performed by a validating parser.
+     *
+     * @param tldValidation true to enable xml validation
      */
-    public void setTldNamespaceAware(boolean tldNamespaceAware);
+    public void setTldValidation(boolean tldValidation);
+
 
     /**
      * Get the Jar Scanner to be used to scan for JAR resources for this
@@ -712,6 +742,16 @@
      */
     public boolean getLogEffectiveWebXml();
 
+    /**
+     * Get the instance manager associated with this context.
+     */
+    public InstanceManager getInstanceManager();
+
+    /**
+     * Set the instance manager associated with this context.
+     */
+    public void setInstanceManager(InstanceManager instanceManager);
+
     // --------------------------------------------------------- Public Methods
 
 
--- java/org/apache/catalina/Globals.java.orig	2014-07-21 17:24:05.521402000 -0400
+++ java/org/apache/catalina/Globals.java	2014-07-21 17:46:59.394034000 -0400
@@ -14,18 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-
 package org.apache.catalina;
 
-
 /**
  * Global constants that are applicable to multiple packages within Catalina.
  *
  * @author Craig R. McClanahan
- * @version $Id: Globals.java 1301255 2012-03-15 22:47:40Z markt $
+ * @version $Id: Globals.java 1549529 2013-12-09 10:05:56Z markt $
  */
-
 public final class Globals {
 
     /**
@@ -310,4 +306,25 @@
      * the tomcat instance installation path
      */
     public static final String CATALINA_BASE_PROP = "catalina.base";
+
+
+    /**
+     * Name of the ServletContext init-param that determines if the JSP engine
+     * should validate *.tld files when parsing them.
+     * <p>
+     * This must be kept in sync with org.apache.jasper.Constants
+     */
+    public static final String JASPER_XML_VALIDATION_TLD_INIT_PARAM =
+            "org.apache.jasper.XML_VALIDATE_TLD";
+
+
+    /**
+     * Name of the ServletContext init-param that determines if the JSP engine
+     * will block external entities from being used in *.tld, *.jspx, *.tagx and
+     * tagplugin.xml files.
+     * <p>
+     * This must be kept in sync with org.apache.jasper.Constants
+     */
+    public static final String JASPER_XML_BLOCK_EXTERNAL_INIT_PARAM =
+            "org.apache.jasper.XML_BLOCK_EXTERNAL";
 }
--- java/org/apache/catalina/ant/ValidatorTask.java.orig	2014-07-21 17:24:05.537406000 -0400
+++ java/org/apache/catalina/ant/ValidatorTask.java	2014-07-21 17:46:59.401034000 -0400
@@ -14,18 +14,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-
 package org.apache.catalina.ant;
 
-
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.InputStream;
 
+import org.apache.catalina.Globals;
 import org.apache.catalina.startup.Constants;
-import org.apache.catalina.startup.DigesterFactory;
+import org.apache.tomcat.util.descriptor.DigesterFactory;
 import org.apache.tomcat.util.digester.Digester;
 import org.apache.tools.ant.BuildException;
 import org.xml.sax.InputSource;
@@ -36,7 +34,7 @@
  * schema validation.
  *
  * @author Remy Maucherat
- * @version $Id: ValidatorTask.java 1001899 2010-09-27 20:24:18Z markt $
+ * @version $Id: ValidatorTask.java 1549529 2013-12-09 10:05:56Z markt $
  * @since 5.0
  */
 
@@ -90,7 +88,10 @@
         Thread.currentThread().setContextClassLoader
             (ValidatorTask.class.getClassLoader());
 
-        Digester digester = DigesterFactory.newDigester(true, true, null);
+        // Called through trusted manager interface. If running under a
+        // SecurityManager assume that untrusted applications may be deployed.
+        Digester digester = DigesterFactory.newDigester(
+                true, true, null, Globals.IS_SECURITY_ENABLED);
         try {
             file = file.getCanonicalFile();
             InputStream stream = 
--- java/org/apache/catalina/core/ApplicationContext.java.orig	2014-07-21 17:24:05.543399000 -0400
+++ java/org/apache/catalina/core/ApplicationContext.java	2014-07-22 16:07:21.579832000 -0400
@@ -5,9 +5,9 @@
  * The ASF licenses this file to You under the Apache License, Version 2.0
  * (the "License"); you may not use this file except in compliance with
  * the License.  You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -84,7 +84,7 @@
  *
  * @author Craig R. McClanahan
  * @author Remy Maucherat
- * @version $Id: ApplicationContext.java 1493015 2013-06-14 10:00:57Z markt $
+ * @version $Id: ApplicationContext.java 1549529 2013-12-09 10:05:56Z markt $
  */
 
 public class ApplicationContext
@@ -97,7 +97,7 @@
 
     static {
         STRICT_SERVLET_COMPLIANCE = Globals.STRICT_SERVLET_COMPLIANCE;
-        
+
         String requireSlash = System.getProperty(
                 "org.apache.catalina.core.ApplicationContext.GET_RESOURCE_REQUIRE_SLASH");
         if (requireSlash == null) {
@@ -302,7 +302,7 @@
         }
     }
 
-    
+
     /**
      * Return the main path associated with this context.
      */
@@ -310,7 +310,7 @@
     public String getContextPath() {
         return context.getPath();
     }
-    
+
 
     /**
      * Return the value of the specified initialization parameter, or
@@ -320,6 +320,20 @@
      */
     @Override
     public String getInitParameter(final String name) {
+        // Special handling for XML settings as the context setting must
+        // always override anything that might have been set by an application.
+        if (Globals.JASPER_XML_VALIDATION_TLD_INIT_PARAM.equals(name) &&
+                context.getTldValidation()) {
+            return "true";
+        }
+        if (Globals.JASPER_XML_BLOCK_EXTERNAL_INIT_PARAM.equals(name)) {
+            if (context.getXmlBlockExternal()) {
+                return "true";
+            } else if (Globals.IS_SECURITY_ENABLED) {
+                // System admin has explicitly changed the default
+                return "false";
+            }
+        }
         return parameters.get(name);
     }
 
@@ -330,7 +344,17 @@
      */
     @Override
     public Enumeration<String> getInitParameterNames() {
-        return Collections.enumeration(parameters.keySet());
+        Set<String> names = new HashSet<String>();
+        names.addAll(parameters.keySet());
+        // Special handling for XML settings as these attributes will always be
+        // available if they have been set on the context
+        if (context.getTldValidation()) {
+            names.add(Globals.JASPER_XML_VALIDATION_TLD_INIT_PARAM);
+        }
+        if (context.getXmlBlockExternal() || Globals.IS_SECURITY_ENABLED) {
+            names.add(Globals.JASPER_XML_BLOCK_EXTERNAL_INIT_PARAM);
+        }
+        return Collections.enumeration(names);
     }
 
 
@@ -395,7 +419,7 @@
         Wrapper wrapper = (Wrapper) context.findChild(name);
         if (wrapper == null)
             return (null);
-        
+
         return new ApplicationDispatcher(wrapper, null, null, null, null, name);
 
     }
@@ -444,7 +468,7 @@
         if (normalizedPath == null)
             return (null);
 
-        pos = normalizedPath.length(); 
+        pos = normalizedPath.length();
 
         // Use the thread local URI and mapping data
         DispatchData dd = dispatchData.get();
@@ -495,10 +519,10 @@
         String pathInfo = mappingData.pathInfo.toString();
 
         mappingData.recycle();
-        
+
         // Construct a RequestDispatcher to process this request
         return new ApplicationDispatcher
-            (wrapper, uriCC.toString(), wrapperPath, pathInfo, 
+            (wrapper, uriCC.toString(), wrapperPath, pathInfo,
              queryString, null);
 
     }
@@ -523,7 +547,7 @@
                 !path.startsWith("/") && GET_RESOURCE_REQUIRE_SLASH)
             throw new MalformedURLException(sm.getString(
                     "applicationContext.requestDispatcher.iae", path));
-        
+
         String normPath = RequestUtil.normalize(path);
         if (normPath == null)
             return (null);
@@ -724,7 +748,7 @@
     @Override
     @Deprecated
     public void log(Exception exception, String message) {
-        
+
         context.getLogger().error(message, exception);
 
     }
@@ -738,7 +762,7 @@
      */
     @Override
     public void log(String message, Throwable throwable) {
-        
+
         context.getLogger().error(message, throwable);
 
     }
@@ -893,11 +917,11 @@
     @Override
     public FilterRegistration.Dynamic addFilter(String filterName,
             String filterClass) throws IllegalStateException {
-        
+
         return addFilter(filterName, filterClass, null);
     }
 
-    
+
     /**
      * Add filter to context.
      * @param   filterName  Name of filter to add
@@ -915,11 +939,11 @@
     @Override
     public FilterRegistration.Dynamic addFilter(String filterName,
             Filter filter) throws IllegalStateException {
-        
+
         return addFilter(filterName, null, filter);
     }
 
-    
+
     /**
      * Add filter to context.
      * @param   filterName  Name of filter to add
@@ -937,13 +961,13 @@
     @Override
     public FilterRegistration.Dynamic addFilter(String filterName,
             Class<? extends Filter> filterClass) throws IllegalStateException {
-        
+
         return addFilter(filterName, filterClass.getName(), null);
     }
 
     private FilterRegistration.Dynamic addFilter(String filterName,
             String filterClass, Filter filter) throws IllegalStateException {
-        
+
         if (filterName == null || filterName.equals("")) {
             throw new IllegalArgumentException(sm.getString(
                     "applicationContext.invalidFilterName", filterName));
@@ -957,7 +981,7 @@
         }
 
         FilterDef filterDef = context.findFilterDef(filterName);
-        
+
         // Assume a 'complete' FilterRegistration is one that has a class and
         // a name
         if (filterDef == null) {
@@ -977,14 +1001,15 @@
             filterDef.setFilterClass(filter.getClass().getName());
             filterDef.setFilter(filter);
         }
-        
+
         return new ApplicationFilterRegistration(filterDef, context);
-    } 
-    
+    }
+
     @Override
     public <T extends Filter> T createFilter(Class<T> c)
     throws ServletException {
         try {
+            @SuppressWarnings("unchecked")
             T filter = (T) context.getInstanceManager().newInstance(c.getName());
             return filter;
         } catch (IllegalAccessException e) {
@@ -1011,7 +1036,7 @@
         return new ApplicationFilterRegistration(filterDef, context);
     }
 
-    
+
     /**
      * Add servlet to context.
      * @param   servletName  Name of servlet to add
@@ -1029,7 +1054,7 @@
     @Override
     public ServletRegistration.Dynamic addServlet(String servletName,
             String servletClass) throws IllegalStateException {
-        
+
         return addServlet(servletName, servletClass, null);
     }
 
@@ -1055,7 +1080,7 @@
         return addServlet(servletName, null, servlet);
     }
 
-    
+
     /**
      * Add servlet to context.
      * @param   servletName  Name of servlet to add
@@ -1080,7 +1105,7 @@
 
     private ServletRegistration.Dynamic addServlet(String servletName,
             String servletClass, Servlet servlet) throws IllegalStateException {
-        
+
         if (servletName == null || servletName.equals("")) {
             throw new IllegalArgumentException(sm.getString(
                     "applicationContext.invalidServletName", servletName));
@@ -1092,9 +1117,9 @@
                     sm.getString("applicationContext.addServlet.ise",
                             getContextPath()));
         }
-        
+
         Wrapper wrapper = (Wrapper) context.findChild(servletName);
-        
+
         // Assume a 'complete' ServletRegistration is one that has a class and
         // a name
         if (wrapper == null) {
@@ -1127,6 +1152,7 @@
     public <T extends Servlet> T createServlet(Class<T> c)
     throws ServletException {
         try {
+            @SuppressWarnings("unchecked")
             T servlet = (T) context.getInstanceManager().newInstance(c.getName());
             context.dynamicServletCreated(servlet);
             return servlet;
@@ -1151,10 +1177,10 @@
         if (wrapper == null) {
             return null;
         }
-        
+
         return new ApplicationServletRegistration(wrapper, context);
     }
-    
+
 
     /**
      * By default {@link SessionTrackingMode#URL} is always supported, {@link
@@ -1171,15 +1197,15 @@
 
     private void populateSessionTrackingModes() {
         // URL re-writing is always enabled by default
-        defaultSessionTrackingModes = EnumSet.of(SessionTrackingMode.URL); 
+        defaultSessionTrackingModes = EnumSet.of(SessionTrackingMode.URL);
         supportedSessionTrackingModes = EnumSet.of(SessionTrackingMode.URL);
-        
+
         if (context.getCookies()) {
             defaultSessionTrackingModes.add(SessionTrackingMode.COOKIE);
             supportedSessionTrackingModes.add(SessionTrackingMode.COOKIE);
         }
 
-        // SSL not enabled by default as it can only used on its own 
+        // SSL not enabled by default as it can only used on its own
         // Context > Host > Engine > Service
         Service s = ((Engine) context.getParent().getParent()).getService();
         Connector[] connectors = s.findConnectors();
@@ -1189,7 +1215,7 @@
                 supportedSessionTrackingModes.add(SessionTrackingMode.SSL);
                 break;
             }
-        } 
+        }
     }
 
     /**
@@ -1226,7 +1252,7 @@
                     sm.getString("applicationContext.setSessionTracking.ise",
                             getContextPath()));
         }
-        
+
         // Check that only supported tracking modes have been requested
         for (SessionTrackingMode sessionTrackingMode : sessionTrackingModes) {
             if (!supportedSessionTrackingModes.contains(sessionTrackingMode)) {
@@ -1244,7 +1270,7 @@
                         getContextPath()));
             }
         }
-        
+
         this.sessionTrackingModes = sessionTrackingModes;
     }
 
@@ -1253,8 +1279,8 @@
     public boolean setInitParameter(String name, String value) {
         return parameters.putIfAbsent(name, value) == null;
     }
-    
-    
+
+
     @Override
     public void addListener(Class<? extends EventListener> listenerClass) {
         EventListener listener;
@@ -1271,7 +1297,7 @@
 
     @Override
     public void addListener(String className) {
-        
+
         try {
             Object obj = context.getInstanceManager().newInstance(className);
 
@@ -1305,7 +1331,7 @@
                     "applicationContext.addListener.iae.cnfe", className),
                     e);
         }
-        
+
     }
 
 
@@ -1325,7 +1351,7 @@
             context.addApplicationEventListener(t);
             match = true;
         }
-        
+
         if (t instanceof HttpSessionListener
                 || (t instanceof ServletContextListener &&
                         newServletContextListenerAllowed)) {
@@ -1334,9 +1360,9 @@
             context.addApplicationLifecycleListener(t);
             match = true;
         }
-        
+
         if (match) return;
-        
+
         if (t instanceof ServletContextListener) {
             throw new IllegalArgumentException(sm.getString(
                     "applicationContext.addListener.iae.sclNotAllowed",
@@ -1353,8 +1379,9 @@
     public <T extends EventListener> T createListener(Class<T> c)
             throws ServletException {
         try {
+            @SuppressWarnings("unchecked")
             T listener =
-                (T) context.getInstanceManager().newInstance(c.getName());
+                (T) context.getInstanceManager().newInstance(c);
             if (listener instanceof ServletContextListener ||
                     listener instanceof ServletContextAttributeListener ||
                     listener instanceof ServletRequestListener ||
@@ -1375,27 +1402,26 @@
             throw new ServletException(e);
         } catch (InstantiationException e) {
             throw new ServletException(e);
-        } catch (ClassNotFoundException e) {
-            throw new ServletException(e);
-        }    }
+        }
+    }
 
 
     @Override
     public void declareRoles(String... roleNames) {
-        
+
         if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
             //TODO Spec breaking enhancement to ignore this restriction
             throw new IllegalStateException(
                     sm.getString("applicationContext.addRole.ise",
                             getContextPath()));
         }
-        
+
         if (roleNames == null) {
             throw new IllegalArgumentException(
                     sm.getString("applicationContext.roles.iae",
                             getContextPath()));
         }
-        
+
         for (String role : roleNames) {
             if (role == null || "".equals(role)) {
                 throw new IllegalArgumentException(
@@ -1424,7 +1450,7 @@
                         new RuntimePermission("getClassLoader"));
             }
         }
-        
+
         return result;
     }
 
@@ -1445,7 +1471,7 @@
     public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
         Map<String, ApplicationFilterRegistration> result =
             new HashMap<String, ApplicationFilterRegistration>();
-        
+
         FilterDef[] filterDefs = context.findFilterDefs();
         for (FilterDef filterDef : filterDefs) {
             result.put(filterDef.getFilterName(),
@@ -1473,7 +1499,7 @@
     public Map<String, ? extends ServletRegistration> getServletRegistrations() {
         Map<String, ApplicationServletRegistration> result =
             new HashMap<String, ApplicationServletRegistration>();
-        
+
         Container[] wrappers = context.findChildren();
         for (Container wrapper : wrappers) {
             result.put(((Wrapper) wrapper).getName(),
@@ -1484,12 +1510,12 @@
         return result;
     }
 
-    
+
     // -------------------------------------------------------- Package Methods
     protected StandardContext getContext() {
         return this.context;
     }
-    
+
     @Deprecated
     protected Map<String,String> getReadonlyAttributes() {
         return this.readOnlyAttributes;
@@ -1513,10 +1539,10 @@
             String key = keys.next();
             removeAttribute(key);
         }
-        
+
     }
-    
-    
+
+
     /**
      * Return the facade associated with this ApplicationContext.
      */
@@ -1541,7 +1567,7 @@
     protected void setNewServletContextListenerAllowed(boolean allowed) {
         this.newServletContextListenerAllowed = allowed;
     }
-    
+
     /**
      * List resource paths (recursively), and store all of them in the given
      * Set.
@@ -1572,13 +1598,13 @@
      */
     private static String getJNDIUri(String hostName, String path) {
         String result;
-        
+
         if (path.startsWith("/")) {
             result = "/" + hostName + path;
         } else {
             result = "/" + hostName + "/" + path;
         }
-        
+
         return result;
     }
 
--- java/org/apache/catalina/core/StandardContext.java.orig	2014-07-21 17:24:05.568403000 -0400
+++ java/org/apache/catalina/core/StandardContext.java	2014-07-22 16:08:44.199113000 -0400
@@ -126,6 +126,7 @@
 import org.apache.tomcat.InstanceManager;
 import org.apache.tomcat.JarScanner;
 import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.descriptor.XmlIdentifiers;
 import org.apache.tomcat.util.modeler.Registry;
 import org.apache.tomcat.util.scan.StandardJarScanner;
 
@@ -136,7 +137,7 @@
  *
  * @author Craig R. McClanahan
  * @author Remy Maucherat
- * @version $Id: StandardContext.java 1493073 2013-06-14 13:51:13Z markt $
+ * @version $Id: StandardContext.java 1549529 2013-12-09 10:05:56Z markt $
  */
 
 public class StandardContext extends ContainerBase
@@ -524,6 +525,12 @@
 
 
     /**
+     * Context level override for default {@link StandardHost#isCopyXML()}.
+     */
+    private boolean copyXML = false;
+
+
+    /**
      * The default context override flag for this web application.
      */
     private boolean override = false;
@@ -691,6 +698,13 @@
     protected int cacheMaxSize = 10240; // 10 MB
 
 
+
+    /**
+     * Attribute used to turn on/off the use of external entities.
+     */
+    private boolean xmlBlockExternal = Globals.IS_SECURITY_ENABLED;
+
+
     /**
      * Cache object max size in KB.
      */
@@ -726,7 +740,8 @@
 
 
     /**
-     * Attribute value used to turn on/off XML validation
+     * Attribute value used to turn on/off XML validation for web.xml and
+     * web-fragment.xml files.
      */
     private boolean webXmlValidation = Globals.STRICT_SERVLET_COMPLIANCE;
 
@@ -748,12 +763,6 @@
 
 
     /**
-     * Attribute value used to turn on/off TLD XML namespace validation
-     */
-    private boolean tldNamespaceAware = Globals.STRICT_SERVLET_COMPLIANCE;
-
-
-    /**
      * Should we save the configuration.
      */
     private boolean saveConfig = true;
@@ -1055,11 +1064,13 @@
     }
 
      
+    @Override
     public InstanceManager getInstanceManager() {
        return instanceManager;
     }
 
 
+    @Override
     public void setInstanceManager(InstanceManager instanceManager) {
        this.instanceManager = instanceManager;
     }
@@ -2463,6 +2474,17 @@
 
     }
 
+
+    public boolean getCopyXML() {
+        return copyXML;
+    }
+
+
+    public void setCopyXML(boolean copyXML) {
+        this.copyXML = copyXML;
+    }
+
+
     /**
      * Return the Java class name of the Wrapper implementation used
      * for servlets registered in this Context.
@@ -4861,7 +4883,7 @@
                 ExceptionUtils.handleThrowable(t);
                 getLogger().error
                     (sm.getString("standardContext.applicationListener",
-                                  listeners[i]), t);
+                                  listeners[i].getClassName()), t);
                 ok = false;
             }
         }
@@ -5233,8 +5255,7 @@
         }
         if (ok) {
             if (!resourcesStart()) {
-                log.error( "Error in resourceStart()");
-                ok = false;
+                throw new LifecycleException("Error in resourceStart()");
             }
         }
 
@@ -5822,17 +5843,10 @@
      */
     @Override
     public boolean isServlet22() {
-
-        if (this.publicId == null)
-            return (false);
-        if (this.publicId.equals
-            (org.apache.catalina.startup.Constants.WebDtdPublicId_22))
-            return (true);
-        else
-            return (false);
-
+        return XmlIdentifiers.WEB_22_PUBLIC.equals(publicId);
     }
 
+
     @Override
     public Set<String> addServletSecurity(
             ApplicationServletRegistration registration,
@@ -5928,7 +5942,7 @@
         if (getResources() == null)
             return oldContextClassLoader;
 
-        if (getLoader().getClassLoader() != null) {
+        if (getLoader() != null && getLoader().getClassLoader() != null) {
             Thread.currentThread().setContextClassLoader
                 (getLoader().getClassLoader());
         }
@@ -6580,72 +6594,67 @@
 
     }
 
-     /**
-     * Set the validation feature of the XML parser used when
-     * parsing xml instances.
-     * @param webXmlValidation true to enable xml instance validation
-     */
+    
+    @Override
+    public boolean getXmlNamespaceAware(){
+        return webXmlNamespaceAware;
+    }
+
+
+    @Override
+    public void setXmlNamespaceAware(boolean webXmlNamespaceAware){
+        this.webXmlNamespaceAware = webXmlNamespaceAware;
+    }    
+
+
     @Override
     public void setXmlValidation(boolean webXmlValidation){
-        
         this.webXmlValidation = webXmlValidation;
-
     }
 
-    /**
-     * Get the server.xml <context> attribute's xmlValidation.
-     * @return true if validation is enabled.
-     *
-     */
+
     @Override
     public boolean getXmlValidation(){
         return webXmlValidation;
     }
 
 
-    /**
-     * Get the server.xml <context> attribute's xmlNamespaceAware.
-     * @return true if namespace awarenes is enabled.
-     */
     @Override
-    public boolean getXmlNamespaceAware(){
-        return webXmlNamespaceAware;
+    public boolean getTldNamespaceAware(){
+        return true;
     }
 
 
-    /**
-     * Set the namespace aware feature of the XML parser used when
-     * parsing xml instances.
-     * @param webXmlNamespaceAware true to enable namespace awareness
-     */
     @Override
-    public void setXmlNamespaceAware(boolean webXmlNamespaceAware){
-        this.webXmlNamespaceAware= webXmlNamespaceAware;
+    public void setTldNamespaceAware(boolean tldNamespaceAware){
+        // NO-OP;
     }    
 
 
-    /**
-     * Set the validation feature of the XML parser used when
-     * parsing tlds files. 
-     * @param tldValidation true to enable xml instance validation
-     */
+    @Override
+    public void setXmlBlockExternal(boolean xmlBlockExternal) {
+        this.xmlBlockExternal = xmlBlockExternal;
+    }
+
+
+    @Override
+    public boolean getXmlBlockExternal() {
+        return xmlBlockExternal;
+    }
+
+
     @Override
     public void setTldValidation(boolean tldValidation){
-        
         this.tldValidation = tldValidation;
-
     }
 
-    /**
-     * Get the server.xml <context> attribute's webXmlValidation.
-     * @return true if validation is enabled.
-     *
-     */
+
     @Override
     public boolean getTldValidation(){
         return tldValidation;
     }
 
+
     /**
      * Sets the process TLDs attribute.
      *
@@ -6655,6 +6664,7 @@
         processTlds = newProcessTlds;
     }
 
+
     /**
      * Returns the processTlds attribute value.
      */
@@ -6662,26 +6672,6 @@
         return processTlds;
     }
 
-    /**
-     * Get the server.xml &lt;host&gt; attribute's xmlNamespaceAware.
-     * @return true if namespace awarenes is enabled.
-     */
-    @Override
-    public boolean getTldNamespaceAware(){
-        return tldNamespaceAware;
-    }
-
-
-    /**
-     * Set the namespace aware feature of the XML parser used when
-     * parsing xml instances.
-     * @param tldNamespaceAware true to enable namespace awareness
-     */
-    @Override
-    public void setTldNamespaceAware(boolean tldNamespaceAware){
-        this.tldNamespaceAware= tldNamespaceAware;
-    }    
-
 
     /** 
      * Support for "stateManageable" JSR77 
--- java/org/apache/catalina/startup/ContextConfig.java.orig	2014-07-21 17:24:05.574403000 -0400
+++ java/org/apache/catalina/startup/ContextConfig.java	2014-07-21 17:46:59.422037000 -0400
@@ -95,6 +95,8 @@
 import org.apache.tomcat.util.bcel.classfile.ElementValue;
 import org.apache.tomcat.util.bcel.classfile.ElementValuePair;
 import org.apache.tomcat.util.bcel.classfile.JavaClass;
+import org.apache.tomcat.util.descriptor.DigesterFactory;
+import org.apache.tomcat.util.descriptor.XmlErrorHandler;
 import org.apache.tomcat.util.digester.Digester;
 import org.apache.tomcat.util.digester.RuleSet;
 import org.apache.tomcat.util.res.StringManager;
@@ -109,7 +111,7 @@
  *
  * @author Craig R. McClanahan
  * @author Jean-Francois Arcand
- * @version $Id: ContextConfig.java 1488152 2013-05-31 11:07:18Z kkolinko $
+ * @version $Id: ContextConfig.java 1549529 2013-12-09 10:05:56Z markt $
  */
 public class ContextConfig implements LifecycleListener {
 
@@ -514,14 +516,16 @@
     public void createWebXmlDigester(boolean namespaceAware,
             boolean validation) {
 
+        boolean blockExternal = context.getXmlBlockExternal();
+        
         webRuleSet = new WebRuleSet(false);
         webDigester = DigesterFactory.newDigester(validation,
-                namespaceAware, webRuleSet);
+                namespaceAware, webRuleSet, blockExternal);
         webDigester.getParser();
 
         webFragmentRuleSet = new WebRuleSet(true);
         webFragmentDigester = DigesterFactory.newDigester(validation,
-                namespaceAware, webFragmentRuleSet);
+                namespaceAware, webFragmentRuleSet, blockExternal);
         webFragmentDigester.getParser();
     }
 
@@ -710,11 +714,11 @@
         String pathName = cn.getBaseName();
 
         boolean unpackWARs = true;
-        if (host instanceof StandardHost &&
-                context instanceof StandardContext) {
-            unpackWARs = ((StandardHost) host).isUnpackWARs() &&
-                    ((StandardContext) context).getUnpackWAR() &&
-                    (docBase.startsWith(canonicalAppBase.getPath()));
+        if (host instanceof StandardHost) {
+            unpackWARs = ((StandardHost) host).isUnpackWARs();
+            if (unpackWARs && context instanceof StandardContext) {
+                unpackWARs =  ((StandardContext) context).getUnpackWAR();
+            }
         }
 
         if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory() && unpackWARs) {
@@ -838,14 +842,6 @@
 
         createWebXmlDigester(context.getXmlNamespaceAware(),
                 context.getXmlValidation());
-
-        try {
-            fixDocBase();
-        } catch (IOException e) {
-            log.error(sm.getString(
-                    "contextConfig.fixDocBase", context.getName()), e);
-        }
-
     }
 
 
@@ -854,8 +850,14 @@
      */
     protected synchronized void beforeStart() {
 
-        antiLocking();
+        try {
+            fixDocBase();
+        } catch (IOException e) {
+            log.error(sm.getString(
+                    "contextConfig.fixDocBase", context.getName()), e);
+        }
 
+        antiLocking();
     }
 
 
@@ -1131,7 +1133,7 @@
             for (int j = 0; j < roles.length; j++) {
                 if (!"*".equals(roles[j]) &&
                     !context.findSecurityRole(roles[j])) {
-                    log.info(sm.getString("contextConfig.role.auth", roles[j]));
+                    log.warn(sm.getString("contextConfig.role.auth", roles[j]));
                     context.addSecurityRole(roles[j]);
                 }
             }
@@ -1143,14 +1145,14 @@
             Wrapper wrapper = (Wrapper) wrappers[i];
             String runAs = wrapper.getRunAs();
             if ((runAs != null) && !context.findSecurityRole(runAs)) {
-                log.info(sm.getString("contextConfig.role.runas", runAs));
+                log.warn(sm.getString("contextConfig.role.runas", runAs));
                 context.addSecurityRole(runAs);
             }
             String names[] = wrapper.findSecurityReferences();
             for (int j = 0; j < names.length; j++) {
                 String link = wrapper.findSecurityReference(names[j]);
                 if ((link != null) && !context.findSecurityRole(link)) {
-                    log.info(sm.getString("contextConfig.role.link", link));
+                    log.warn(sm.getString("contextConfig.role.link", link));
                     context.addSecurityRole(link);
                 }
             }
@@ -1258,7 +1260,7 @@
         // Step 1. Identify all the JARs packaged with the application
         // If the JARs have a web-fragment.xml it will be parsed at this
         // point.
-        Map<String,WebXml> fragments = processJarsForWebFragments();
+        Map<String,WebXml> fragments = processJarsForWebFragments(webXml);
 
         // Step 2. Order the fragments.
         Set<WebXml> orderedFragments = null;
@@ -1546,7 +1548,7 @@
             URL url = fragment.getURL();
             Jar jar = null;
             InputStream is = null;
-            ServletContainerInitializer sci = null;
+            List<ServletContainerInitializer> detectedScis = null;
             try {
                 if ("jar".equals(url.getProtocol())) {
                     jar = JarFactory.newInstance(url);
@@ -1559,7 +1561,7 @@
                     }
                 }
                 if (is != null) {
-                    sci = getServletContainerInitializer(is);
+                    detectedScis = getServletContainerInitializers(is);
                 }
             } catch (IOException ioe) {
                 log.error(sm.getString(
@@ -1580,42 +1582,44 @@
                 }
             }
 
-            if (sci == null) {
+            if (detectedScis == null) {
                 continue;
             }
 
-            initializerClassMap.put(sci, new HashSet<Class<?>>());
+            for (ServletContainerInitializer sci : detectedScis) {
+                initializerClassMap.put(sci, new HashSet<Class<?>>());
 
-            HandlesTypes ht = null;
-            try {
-                ht = sci.getClass().getAnnotation(HandlesTypes.class);
-            } catch (Exception e) {
-                if (log.isDebugEnabled()) {
-                    log.info(sm.getString("contextConfig.sci.debug", url), e);
-                } else {
-                    log.info(sm.getString("contextConfig.sci.info", url));
+                HandlesTypes ht = null;
+                try {
+                    ht = sci.getClass().getAnnotation(HandlesTypes.class);
+                } catch (Exception e) {
+                    if (log.isDebugEnabled()) {
+                        log.info(sm.getString("contextConfig.sci.debug", url),
+                                e);
+                    } else {
+                        log.info(sm.getString("contextConfig.sci.info", url));
+                    }
                 }
-            }
-            if (ht != null) {
-                Class<?>[] types = ht.value();
-                if (types != null) {
-                    for (Class<?> type : types) {
-                        if (type.isAnnotation()) {
-                            handlesTypesAnnotations = true;
-                        } else {
-                            handlesTypesNonAnnotations = true;
-                        }
-                        Set<ServletContainerInitializer> scis =
-                            typeInitializerMap.get(type);
-                        if (scis == null) {
-                            scis = new HashSet<ServletContainerInitializer>();
-                            typeInitializerMap.put(type, scis);
+                if (ht != null) {
+                    Class<?>[] types = ht.value();
+                    if (types != null) {
+                        for (Class<?> type : types) {
+                            if (type.isAnnotation()) {
+                                handlesTypesAnnotations = true;
+                            } else {
+                                handlesTypesNonAnnotations = true;
+                            }
+                            Set<ServletContainerInitializer> scis = typeInitializerMap
+                                    .get(type);
+                            if (scis == null) {
+                                scis = new HashSet<ServletContainerInitializer>();
+                                typeInitializerMap.put(type, scis);
+                            }
+                            scis.add(sci);
                         }
-                        scis.add(sci);
                     }
                 }
             }
-
         }
     }
 
@@ -1627,19 +1631,28 @@
      * @return      The class name
      * @throws IOException
      */
-    protected ServletContainerInitializer getServletContainerInitializer(
+    protected List<ServletContainerInitializer> getServletContainerInitializers(
             InputStream is) throws IOException {
 
-        String className = null;
+        List<ServletContainerInitializer> initializers = new ArrayList<ServletContainerInitializer>();
 
         if (is != null) {
             String line = null;
             try {
-                BufferedReader br =
-                    new BufferedReader(new InputStreamReader(is, "UTF-8"));
-                line = br.readLine();
-                if (line != null && line.trim().length() > 0) {
-                    className = line.trim();
+                BufferedReader br = new BufferedReader(new InputStreamReader(
+                        is, "UTF-8"));
+                while ((line = br.readLine()) != null) {
+                    line = line.trim();
+                    if (line.length() > 0) {
+                        int i = line.indexOf('#');
+                        if (i > -1) {
+                            if (i == 0) {
+                                continue;
+                            }
+                            line = line.substring(0, i).trim();
+                        }
+                        initializers.add(getServletContainerInitializer(line));
+                    }
                 }
             } catch (UnsupportedEncodingException e) {
                 // Should never happen with UTF-8
@@ -1647,11 +1660,16 @@
             }
         }
 
+        return initializers;
+    }
+
+    protected ServletContainerInitializer getServletContainerInitializer(
+            String className) throws IOException {
         ServletContainerInitializer sci = null;
         try {
-            Class<?> clazz = Class.forName(className,true,
-                    context.getLoader().getClassLoader());
-             sci = (ServletContainerInitializer) clazz.newInstance();
+            Class<?> clazz = Class.forName(className, true, context.getLoader()
+                    .getClassLoader());
+            sci = (ServletContainerInitializer) clazz.newInstance();
         } catch (ClassNotFoundException e) {
             log.error(sm.getString("contextConfig.invalidSci", className), e);
             throw new IOException(e);
@@ -1923,10 +1941,21 @@
      *
      * @return A map of JAR name to processed web fragment (if any)
      */
-    protected Map<String,WebXml> processJarsForWebFragments() {
+    protected Map<String,WebXml> processJarsForWebFragments(WebXml application) {
 
         JarScanner jarScanner = context.getJarScanner();
-        FragmentJarScannerCallback callback = new FragmentJarScannerCallback();
+
+        boolean parseRequired = true;
+        Set<String> absoluteOrder = application.getAbsoluteOrdering();
+        if (absoluteOrder != null && absoluteOrder.isEmpty() &&
+                !context.getXmlValidation()) {
+            // Skip parsing when there is an empty absolute ordering and
+            // validation is not enabled
+            parseRequired = false;
+        }
+
+        FragmentJarScannerCallback callback =
+                new FragmentJarScannerCallback(parseRequired);
 
         jarScanner.scan(context.getServletContext(),
                 context.getLoader().getClassLoader(), callback,
@@ -2282,6 +2311,12 @@
             } catch (IOException e) {
                 log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
                         className), e);
+            } finally {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    // ignore
+                }
             }
         }
     }
@@ -2650,7 +2685,12 @@
         private static final String FRAGMENT_LOCATION =
             "META-INF/web-fragment.xml";
         private Map<String,WebXml> fragments = new HashMap<String,WebXml>();
+        private final boolean parseRequired;
 
+        public FragmentJarScannerCallback(boolean parseRequired) {
+            this.parseRequired = parseRequired;
+        }
+        
         @Override
         public void scan(JarURLConnection jarConn) throws IOException {
 
@@ -2662,15 +2702,18 @@
 
             try {
                 jar = JarFactory.newInstance(url);
-                is = jar.getInputStream(FRAGMENT_LOCATION);
+                if (parseRequired || context.getXmlValidation()) {
+                    is = jar.getInputStream(FRAGMENT_LOCATION);
+                }
 
                 if (is == null) {
-                    // If there is no web.xml, normal JAR no impact on
-                    // distributable
+                    // If there is no web-fragment.xml to process there is no
+                    // impact on distributable
                     fragment.setDistributable(true);
                 } else {
                     InputSource source = new InputSource(
-                            resourceURL.toString() + "!/" + FRAGMENT_LOCATION);
+                            "jar:" + resourceURL.toString() + "!/" +
+                            FRAGMENT_LOCATION);
                     source.setByteStream(is);
                     parseWebXml(source, fragment, true);
                 }
--- java/org/apache/catalina/startup/FailedContext.java.orig	2014-07-21 17:24:05.579400000 -0400
+++ java/org/apache/catalina/startup/FailedContext.java	2014-07-21 17:46:59.429034000 -0400
@@ -59,6 +59,7 @@
 import org.apache.catalina.util.CharsetMapper;
 import org.apache.catalina.util.LifecycleMBeanBase;
 import org.apache.juli.logging.Log;
+import org.apache.tomcat.InstanceManager;
 import org.apache.tomcat.JarScanner;
 import org.apache.tomcat.util.http.mapper.Mapper;
 import org.apache.tomcat.util.res.StringManager;
@@ -427,10 +428,15 @@
     @Override
     public void setTldValidation(boolean tldValidation) { /* NO-OP */ }
     @Override
+    public boolean getXmlBlockExternal() { return true; }
+    @Override
+    public void setXmlBlockExternal(boolean xmlBlockExternal) { /* NO-OP */ }
+
+    @Override
     public boolean getTldValidation() { return false; }
 
     @Override
-    public boolean getTldNamespaceAware() { return false; }
+    public boolean getTldNamespaceAware() { return true; }
     @Override
     public void setTldNamespaceAware(boolean tldNamespaceAware) { /* NO-OP */ }
 
@@ -685,4 +691,10 @@
 
     @Override
     public Map<String, String> findPreDestroyMethods() { return null; }
+
+    @Override
+    public InstanceManager getInstanceManager() { return null; }
+
+    @Override
+    public void setInstanceManager(InstanceManager instanceManager) { /* NO-OP */ }
 }
\ No newline at end of file
--- java/org/apache/catalina/startup/TldConfig.java.orig	2014-07-21 17:24:05.583404000 -0400
+++ java/org/apache/catalina/startup/TldConfig.java	2014-07-24 12:20:22.130289000 -0400
@@ -40,6 +40,8 @@
 import org.apache.tomcat.JarScanner;
 import org.apache.tomcat.JarScannerCallback;
 import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.descriptor.DigesterFactory;
+import org.apache.tomcat.util.descriptor.XmlErrorHandler;
 import org.apache.tomcat.util.digester.Digester;
 import org.apache.tomcat.util.res.StringManager;
 import org.apache.tomcat.util.scan.Jar;
@@ -83,38 +85,23 @@
      * Create (if necessary) and return a Digester configured to process the
      * tld.
      */
-    private static Digester createTldDigester(boolean namespaceAware,
-            boolean validation) {
-        
-        Digester digester = null;
-        if (!namespaceAware && !validation) {
-            if (tldDigesters[0] == null) {
-                tldDigesters[0] = DigesterFactory.newDigester(validation,
-                        namespaceAware, new TldRuleSet());
-                tldDigesters[0].getParser();
-            }
-            digester = tldDigesters[0];
-        } else if (!namespaceAware && validation) {
-            if (tldDigesters[1] == null) {
-                tldDigesters[1] = DigesterFactory.newDigester(validation,
-                        namespaceAware, new TldRuleSet());
-                tldDigesters[1].getParser();
-            }
-            digester = tldDigesters[1];
-        } else if (namespaceAware && !validation) {
-            if (tldDigesters[2] == null) {
-                tldDigesters[2] = DigesterFactory.newDigester(validation,
-                        namespaceAware, new TldRuleSet());
-                tldDigesters[2].getParser();
-            }
-            digester = tldDigesters[2];
-        } else {
-            if (tldDigesters[3] == null) {
-                tldDigesters[3] = DigesterFactory.newDigester(validation,
-                        namespaceAware, new TldRuleSet());
-                tldDigesters[3].getParser();
-            }
-            digester = tldDigesters[3];
+    private static synchronized Digester createTldDigester(boolean validation,
+            boolean blockExternal) {
+
+        Digester digester;
+        int cacheIndex = 0;
+        if (validation) {
+            cacheIndex += 1;
+        }
+        if (blockExternal) {
+            cacheIndex += 2;
+        }
+        digester = tldDigesters[cacheIndex];
+        if (digester == null) {
+            digester = DigesterFactory.newDigester(validation,
+                    true, new TldRuleSet(), blockExternal);
+            digester.getParser();
+            tldDigesters[cacheIndex] = digester;
         }
         return digester;
     }
@@ -155,7 +142,10 @@
             }
             StringTokenizer tokenizer = new StringTokenizer(jarNames, ",");
             while (tokenizer.hasMoreElements()) {
-                noTldJars.add(tokenizer.nextToken());
+                String token = tokenizer.nextToken().trim();
+                if (token.length() > 0) {
+                    noTldJars.add(token);
+                }
             }
         }
     }
@@ -354,10 +344,15 @@
                 try {
                     stream = context.getServletContext().getResourceAsStream(
                             resourcePath);
-                    XmlErrorHandler handler = tldScanStream(stream);
-                    handler.logFindings(log, resourcePath);
-                    taglibUris.add(descriptor.getTaglibURI());
-                    webxmlTaglibUris.add(descriptor.getTaglibURI());
+                    if (stream != null) {
+                        XmlErrorHandler handler = tldScanStream(stream);
+                        handler.logFindings(log, resourcePath);
+                        taglibUris.add(descriptor.getTaglibURI());
+                        webxmlTaglibUris.add(descriptor.getTaglibURI());
+                    } else {
+                        log.warn(sm.getString("tldConfig.webxmlFailPathDoesNotExist", resourcePath,
+                                descriptor.getTaglibURI()));
+                    }
                 } catch (IOException ioe) {
                     log.warn(sm.getString("tldConfig.webxmlFail", resourcePath,
                             descriptor.getTaglibURI()), ioe);
@@ -580,8 +575,8 @@
     
     private void init() {
         if (tldDigester == null){
-            tldDigester = createTldDigester(context.getTldNamespaceAware(),
-                    context.getTldValidation());
+            tldDigester = createTldDigester(context.getTldValidation(),
+                    context.getXmlBlockExternal());
         }
     }
 
--- java/org/apache/jasper/Constants.java.orig	2014-07-21 17:24:05.588400000 -0400
+++ java/org/apache/jasper/Constants.java	2014-07-21 17:46:59.443034000 -0400
@@ -14,14 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.jasper;
 
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
-
 /**
  * Some constants and other global data that are used by the compiler and the runtime.
  *
@@ -233,4 +231,23 @@
      */
     public static final String TLD_JAR_SKIP_PROP=
             "org.apache.catalina.startup.TldConfig.jarsToSkip";
+
+
+    /**
+     * Name of the ServletContext init-param that determines if the XML parsers
+     * used for *.tld files will be validating or not.
+     * <p>
+     * This must be kept in sync with org.apache.catalina.Globals
+     */
+    public static final String XML_VALIDATION_TLD_INIT_PARAM =
+            "org.apache.jasper.XML_VALIDATE_TLD";
+
+    /**
+     * Name of the ServletContext init-param that determines if the XML parsers
+     * will block the resolution of external entities.
+     * <p>
+     * This must be kept in sync with org.apache.catalina.Globals
+     */
+    public static final String XML_BLOCK_EXTERNAL_INIT_PARAM =
+            "org.apache.jasper.XML_BLOCK_EXTERNAL";
 }
--- java/org/apache/jasper/JspC.java.orig	2014-07-21 17:24:05.593406000 -0400
+++ java/org/apache/jasper/JspC.java	2014-07-21 17:46:59.450034000 -0400
@@ -126,6 +126,8 @@
     protected static final String SWITCH_ENCODING = "-javaEncoding";
     protected static final String SWITCH_SMAP = "-smap";
     protected static final String SWITCH_DUMP_SMAP = "-dumpsmap";
+    protected static final String SWITCH_VALIDATE_TLD = "-validateTld";
+    protected static final String SWITCH_BLOCK_EXTERNAL = "-blockExternal";
     protected static final String SHOW_SUCCESS ="-s";
     protected static final String LIST_ERRORS = "-l";
     protected static final int INC_WEBXML = 10;
@@ -156,6 +158,8 @@
     protected URLClassLoader loader = null;
     protected boolean trimSpaces = false;
     protected boolean genStringAsCharArray = false;
+    protected boolean validateTld;
+    protected boolean blockExternal;
     protected boolean xpoweredBy;
     protected boolean mappedFile = false;
     protected boolean poolingEnabled = true;
@@ -363,6 +367,10 @@
                 smapSuppressed = false;
             } else if (tok.equals(SWITCH_DUMP_SMAP)) {
                 smapDumped = true;
+            } else if (tok.equals(SWITCH_VALIDATE_TLD)) {
+                setValidateTld(true);
+            } else if (tok.equals(SWITCH_BLOCK_EXTERNAL)) {
+                setBlockExternal(true);
             } else {
                 if (tok.startsWith("-")) {
                     throw new JasperException("Unrecognized option: " + tok +
@@ -842,8 +850,20 @@
         }
     }
 
-    public void setValidateXml( boolean b ) {
-        org.apache.jasper.xmlparser.ParserUtils.validating=b;
+    public void setValidateTld( boolean b ) {
+        this.validateTld = b;
+    }
+
+    public boolean isValidateTld() {
+        return validateTld;
+    }
+
+    public void setBlockExternal( boolean b ) {
+        this.blockExternal = b;
+    }
+
+    public boolean isBlockExternal() {
+        return blockExternal;
     }
 
     public void setListErrors( boolean b ) {
@@ -1257,8 +1277,6 @@
 
     /**
      * Executes the compilation.
-     *
-     * @throws JasperException If an error occurs
      */
     @Override
     public void execute() {
@@ -1426,6 +1444,13 @@
         } catch (MalformedURLException me) {
             System.out.println("**" + me);
         }
+        if (isValidateTld()) {
+            context.setInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM, "true");
+        }
+        if (isBlockExternal()) {
+            context.setInitParameter(Constants.XML_BLOCK_EXTERNAL_INIT_PARAM, "true");
+        }
+
         rctxt = new JspRuntimeContext(context, this);
         jspConfig = new JspConfig(context);
         tagPluginManager = new TagPluginManager(context);
--- java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java.orig	2014-07-21 17:24:05.597404000 -0400
+++ java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java	2014-07-21 17:46:59.457040000 -0400
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.jasper.compiler;
 
 import java.io.InputStream;
@@ -24,11 +23,13 @@
 import java.util.Set;
 import java.util.Vector;
 
+import javax.servlet.ServletContext;
 import javax.servlet.jsp.tagext.FunctionInfo;
 import javax.servlet.jsp.tagext.TagFileInfo;
 import javax.servlet.jsp.tagext.TagInfo;
 import javax.servlet.jsp.tagext.TagLibraryInfo;
 
+import org.apache.jasper.Constants;
 import org.apache.jasper.JasperException;
 import org.apache.jasper.JspCompilationContext;
 import org.apache.jasper.util.ExceptionUtils;
@@ -124,7 +125,21 @@
                                 pi.addDependant(path, ctxt.getLastModified(path));
                             }
                             
-                            ParserUtils pu = new ParserUtils();
+                            ServletContext servletContext = ctxt.getServletContext();
+                            boolean validate = Boolean.parseBoolean(
+                                    servletContext.getInitParameter(
+                                            Constants.XML_VALIDATION_TLD_INIT_PARAM));
+                            String blockExternalString =
+                                    servletContext.getInitParameter(
+                                            Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
+                            boolean blockExternal;
+                            if (blockExternalString == null) {
+                                blockExternal = Constants.IS_SECURITY_ENABLED;
+                            } else {
+                                blockExternal = Boolean.parseBoolean(blockExternalString);
+                            }
+                            
+                            ParserUtils pu = new ParserUtils(validate, blockExternal);
                             TreeNode tld = pu.parseXMLDocument(uri, in);
 
                             if (tld.findAttribute("version") != null) {
--- java/org/apache/jasper/compiler/JspConfig.java.orig	2014-07-21 17:24:05.605401000 -0400
+++ java/org/apache/jasper/compiler/JspConfig.java	2014-07-21 17:46:59.469055000 -0400
@@ -22,6 +22,7 @@
 
 import javax.servlet.ServletContext;
 
+import org.apache.jasper.Constants;
 import org.apache.jasper.JasperException;
 import org.apache.jasper.xmlparser.ParserUtils;
 import org.apache.jasper.xmlparser.TreeNode;
@@ -77,9 +78,20 @@
         try {
             webXml = new WebXml(ctxt);
             
+            boolean validate = Boolean.parseBoolean(
+                    ctxt.getInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM));
+            String blockExternalString =
+                    ctxt.getInitParameter(Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
+            boolean blockExternal;
+            if (blockExternalString == null) {
+                blockExternal = Constants.IS_SECURITY_ENABLED;
+            } else {
+                blockExternal = Boolean.parseBoolean(blockExternalString);
+            }
+
             TreeNode webApp = null;
             if (webXml.getInputSource() != null) {
-                ParserUtils pu = new ParserUtils();
+                ParserUtils pu = new ParserUtils(validate, blockExternal);
                 webApp = pu.parseXMLDocument(webXml.getSystemId(),
                         webXml.getInputSource());
             }
--- java/org/apache/jasper/compiler/JspDocumentParser.java.orig	2014-07-21 17:24:05.610399000 -0400
+++ java/org/apache/jasper/compiler/JspDocumentParser.java	2014-07-21 17:46:59.476041000 -0400
@@ -5,9 +5,9 @@
  * The ASF licenses this file to You under the Apache License, Version 2.0
  * (the "License"); you may not use this file except in compliance with
  * the License.  You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -30,17 +30,20 @@
 import javax.xml.parsers.SAXParser;
 import javax.xml.parsers.SAXParserFactory;
 
+import org.apache.jasper.Constants;
 import org.apache.jasper.JasperException;
 import org.apache.jasper.JspCompilationContext;
+import org.apache.tomcat.util.descriptor.DigesterFactory;
+import org.apache.tomcat.util.descriptor.LocalResolver;
 import org.xml.sax.Attributes;
 import org.xml.sax.InputSource;
 import org.xml.sax.Locator;
 import org.xml.sax.SAXException;
 import org.xml.sax.SAXParseException;
 import org.xml.sax.XMLReader;
-import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.ext.DefaultHandler2;
+import org.xml.sax.ext.EntityResolver2;
 import org.xml.sax.helpers.AttributesImpl;
-import org.xml.sax.helpers.DefaultHandler;
 
 /**
  * Class implementing a parser for a JSP document, that is, a JSP page in XML
@@ -51,8 +54,8 @@
  */
 
 class JspDocumentParser
-    extends DefaultHandler
-    implements LexicalHandler, TagConstants {
+    extends DefaultHandler2
+    implements TagConstants {
 
     private static final String LEXICAL_HANDLER_PROPERTY =
         "http://xml.org/sax/properties/lexical-handler";
@@ -71,7 +74,7 @@
      * Outermost (in the nesting hierarchy) node whose body is declared to be
      * scriptless. If a node's body is declared to be scriptless, all its
      * nested nodes must be scriptless, too.
-     */ 
+     */
     private Node scriptlessBodyNode;
 
     private Locator locator;
@@ -92,6 +95,7 @@
     private boolean inDTD;
 
     private boolean isValidating;
+    private final EntityResolver2 entityResolver;
 
     private ErrorDispatcher err;
     private boolean isTagFile;
@@ -120,6 +124,20 @@
         this.isTagFile = isTagFile;
         this.directivesOnly = directivesOnly;
         this.isTop = true;
+
+        String blockExternalString = ctxt.getServletContext().getInitParameter(
+                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
+        boolean blockExternal;
+        if (blockExternalString == null) {
+            blockExternal = Constants.IS_SECURITY_ENABLED;
+        } else {
+            blockExternal = Boolean.parseBoolean(blockExternalString);
+        }
+
+        this.entityResolver = new LocalResolver(
+                DigesterFactory.SERVLET_API_PUBLIC_IDS,
+                DigesterFactory.SERVLET_API_SYSTEM_IDS,
+                blockExternal);
     }
 
     /*
@@ -162,6 +180,8 @@
                 jspDocParser.isTop = false;
             }
 
+            jspDocParser.isValidating = false;
+
             // Parse the input
             SAXParser saxParser = getSAXParser(false, jspDocParser);
             InputStream inStream = null;
@@ -238,11 +258,34 @@
         }
     }
 
+    
+    @Override
+    public InputSource getExternalSubset(String name, String baseURI)
+            throws SAXException, IOException {
+        return entityResolver.getExternalSubset(name, baseURI);
+    }
+
+    
+    
+    @Override
+    public InputSource resolveEntity(String publicId, String systemId)
+            throws SAXException, IOException {
+        return entityResolver.resolveEntity(publicId, systemId);
+    }
+
+    
+    @Override
+    public InputSource resolveEntity(String name, String publicId, String baseURI, String systemId)
+            throws SAXException, IOException {
+        return entityResolver.resolveEntity(name, publicId, baseURI, systemId);
+    }
+
+    
     /*
      * Receives notification of the start of an element.
      *
      * This method assigns the given tag attributes to one of 3 buckets:
-     * 
+     *
      * - "xmlns" attributes that represent (standard or custom) tag libraries.
      * - "xmlns" attributes that do not represent tag libraries.
      * - all remaining attributes.
@@ -272,11 +315,8 @@
             return;
         }
 
-        String currentPrefix = getPrefix(current.getQName());
-        
         // jsp:text must not have any subelements
-        if (JSP_URI.equals(uri) && TEXT_ACTION.equals(current.getLocalName())
-                && "jsp".equals(currentPrefix)) {
+        if (current instanceof Node.JspText) {
             throw new SAXParseException(
                 Localizer.getMessage("jsp.error.text.has_subelement"),
                 locator);
@@ -288,7 +328,7 @@
         if (attrs != null) {
             /*
              * Notice that due to a bug in the underlying SAX parser, the
-             * attributes must be enumerated in descending order. 
+             * attributes must be enumerated in descending order.
              */
             boolean isTaglib = false;
             for (int i = attrs.getLength() - 1; i >= 0; i--) {
@@ -437,7 +477,7 @@
      * invoke this method with chunks of it.  This is a problem when we try
      * to determine if the text contains only whitespaces, or when we are
      * looking for an EL expression string.  Therefore it is necessary to
-     * buffer and concatenate the chunks and process the concatenated text 
+     * buffer and concatenate the chunks and process the concatenated text
      * later (at beginTag and endTag)
      *
      * @param buf The characters
@@ -670,7 +710,7 @@
                         if (!(child instanceof Node.NamedAttribute)) {
                             throw new SAXParseException(Localizer.getMessage(
                                     "jasper.error.emptybodycontent.nonempty",
-                                    current.qName), locator); 
+                                    current.qName), locator);
                         }
                     }
                 }
@@ -785,7 +825,7 @@
     }
 
     /*
-     * Receives notification of the start of a Namespace mapping. 
+     * Receives notification of the start of a Namespace mapping.
      */
     @Override
     public void startPrefixMapping(String prefix, String uri)
@@ -795,7 +835,7 @@
         if (directivesOnly && !(JSP_URI.equals(uri))) {
             return;
         }
-        
+
         try {
             taglibInfo = getTaglibInfo(prefix, uri);
         } catch (JasperException je) {
@@ -816,7 +856,7 @@
     }
 
     /*
-     * Receives notification of the end of a Namespace mapping. 
+     * Receives notification of the end of a Namespace mapping.
      */
     @Override
     public void endPrefixMapping(String prefix) throws SAXException {
@@ -1425,17 +1465,25 @@
         throws Exception {
 
         SAXParserFactory factory = SAXParserFactory.newInstance();
-        factory.setNamespaceAware(true);
 
+        factory.setNamespaceAware(true);
         // Preserve xmlns attributes
         factory.setFeature(
             "http://xml.org/sax/features/namespace-prefixes",
             true);
+
         factory.setValidating(validating);
-        //factory.setFeature(
-        //    "http://xml.org/sax/features/validation",
-        //    validating);
-        
+        if (validating) {
+            // Enable DTD validation
+            factory.setFeature(
+                    "http://xml.org/sax/features/validation",
+                    true);
+            // Enable schema validation
+            factory.setFeature(
+                    "http://apache.org/xml/features/validation/schema",
+                    true);
+        }
+
         // Configure the parser
         SAXParser saxParser = factory.newSAXParser();
         XMLReader xmlReader = saxParser.getXMLReader();
--- java/org/apache/jasper/compiler/TagLibraryInfoImpl.java.orig	2014-07-21 17:24:05.614400000 -0400
+++ java/org/apache/jasper/compiler/TagLibraryInfoImpl.java	2014-07-21 17:46:59.483050000 -0400
@@ -31,6 +31,7 @@
 import java.util.Map;
 import java.util.Vector;
 
+import javax.servlet.ServletContext;
 import javax.servlet.jsp.tagext.FunctionInfo;
 import javax.servlet.jsp.tagext.PageData;
 import javax.servlet.jsp.tagext.TagAttributeInfo;
@@ -43,6 +44,7 @@
 import javax.servlet.jsp.tagext.ValidationMessage;
 import javax.servlet.jsp.tagext.VariableInfo;
 
+import org.apache.jasper.Constants;
 import org.apache.jasper.JasperException;
 import org.apache.jasper.JspCompilationContext;
 import org.apache.jasper.util.ExceptionUtils;
@@ -212,8 +214,20 @@
         Vector<TagFileInfo> tagFileVector = new Vector<TagFileInfo>();
         Hashtable<String, FunctionInfo> functionTable = new Hashtable<String, FunctionInfo>();
 
+        ServletContext servletContext = ctxt.getServletContext();
+        boolean validate = Boolean.parseBoolean(servletContext.getInitParameter(
+                Constants.XML_VALIDATION_TLD_INIT_PARAM));
+        String blockExternalString = servletContext.getInitParameter(
+                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
+        boolean blockExternal;
+        if (blockExternalString == null) {
+            blockExternal = Constants.IS_SECURITY_ENABLED;
+        } else {
+            blockExternal = Boolean.parseBoolean(blockExternalString);
+        }
+
         // Create an iterator over the child elements of our <taglib> element
-        ParserUtils pu = new ParserUtils();
+        ParserUtils pu = new ParserUtils(validate, blockExternal);
         TreeNode tld = pu.parseXMLDocument(uri, in);
 
         // Check to see if the <taglib> root element contains a 'version'
--- java/org/apache/jasper/compiler/TagPluginManager.java.orig	2014-07-21 17:24:05.619399000 -0400
+++ java/org/apache/jasper/compiler/TagPluginManager.java	2014-07-21 17:46:59.492036000 -0400
@@ -5,9 +5,9 @@
  * The ASF licenses this file to You under the Apache License, Version 2.0
  * (the "License"); you may not use this file except in compliance with
  * the License.  You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -25,6 +25,7 @@
 
 import javax.servlet.ServletContext;
 
+import org.apache.jasper.Constants;
 import org.apache.jasper.JasperException;
 import org.apache.jasper.compiler.tagplugin.TagPlugin;
 import org.apache.jasper.compiler.tagplugin.TagPluginContext;
@@ -45,7 +46,6 @@
     private boolean initialized = false;
     private HashMap<String, TagPlugin> tagPlugins = null;
     private ServletContext ctxt;
-    private PageInfo pageInfo;
 
     public TagPluginManager(ServletContext ctxt) {
         this.ctxt = ctxt;
@@ -59,19 +59,9 @@
             return;
         }
 
-        this.pageInfo = pageInfo;
-
-        page.visit(new Node.Visitor() {
-            @Override
-            public void visit(Node.CustomTag n)
-                    throws JasperException {
-                invokePlugin(n);
-                visitBody(n);
-            }
-        });
-
+        page.visit(new NodeVisitor(this, pageInfo));
     }
- 
+
     private void init(ErrorDispatcher err) throws JasperException {
         if (initialized)
             return;
@@ -130,8 +120,18 @@
     private void loadTagPlugins(ErrorDispatcher err, InputStream is)
             throws JasperException {
 
-        TreeNode root =
-                (new ParserUtils()).parseXMLDocument(TAG_PLUGINS_XML, is);
+        String blockExternalString = ctxt.getInitParameter(
+                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
+        boolean blockExternal;
+        if (blockExternalString == null) {
+            blockExternal = Constants.IS_SECURITY_ENABLED;
+        } else {
+            blockExternal = Boolean.parseBoolean(blockExternalString);
+        }
+        
+        ParserUtils pu = new ParserUtils(false, blockExternal);
+        
+        TreeNode root = pu.parseXMLDocument(TAG_PLUGINS_XML, is);
         if (root == null) {
             return;
         }
@@ -175,12 +175,12 @@
     }
 
     /**
-     * Invoke tag plugin for the given custom tag, if a plugin exists for 
+     * Invoke tag plugin for the given custom tag, if a plugin exists for
      * the custom tag's tag handler.
      *
      * The given custom tag node will be manipulated by the plugin.
      */
-    private void invokePlugin(Node.CustomTag n) {
+    private void invokePlugin(Node.CustomTag n, PageInfo pageInfo) {
         TagPlugin tagPlugin = tagPlugins.get(n.getTagHandlerClass().getName());
         if (tagPlugin == null) {
             return;
@@ -191,8 +191,24 @@
         tagPlugin.doTag(tagPluginContext);
     }
 
-    static class TagPluginContextImpl implements TagPluginContext {
-        private Node.CustomTag node;
+    private static class NodeVisitor extends Node.Visitor {
+        private final TagPluginManager manager;
+        private final PageInfo pageInfo;
+
+        public NodeVisitor(TagPluginManager manager, PageInfo pageInfo) {
+            this.manager = manager;
+            this.pageInfo = pageInfo;
+        }
+
+        @Override
+        public void visit(Node.CustomTag n) throws JasperException {
+            manager.invokePlugin(n, pageInfo);
+            visitBody(n);
+        }
+    }
+
+    private static class TagPluginContextImpl implements TagPluginContext {
+        private final Node.CustomTag node;
         private Node.Nodes curNodes;
         private PageInfo pageInfo;
         private HashMap<String, Object> pluginAttributes;
@@ -291,7 +307,7 @@
 
         @Override
         public void generateBody() {
-            // Since we'll generate the body anyway, this is really a nop, 
+            // Since we'll generate the body anyway, this is really a nop,
             // except for the fact that it lets us put the Java sources the
             // plugins produce in the correct order (w.r.t the body).
             curNodes = node.getAtETag();
--- java/org/apache/jasper/compiler/TldLocationsCache.java.orig	2014-07-21 17:24:05.623404000 -0400
+++ java/org/apache/jasper/compiler/TldLocationsCache.java	2014-07-21 17:46:59.500034000 -0400
@@ -286,9 +286,23 @@
                 return;
             }
 
+            boolean validate = Boolean.parseBoolean(
+                    ctxt.getInitParameter(
+                            Constants.XML_VALIDATION_TLD_INIT_PARAM));
+            String blockExternalString = ctxt.getInitParameter(
+                    Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
+            boolean blockExternal;
+            if (blockExternalString == null) {
+                blockExternal = Constants.IS_SECURITY_ENABLED;
+            } else {
+                blockExternal = Boolean.parseBoolean(blockExternalString);
+            }
+            
             // Parse the web application deployment descriptor
+            ParserUtils pu = new ParserUtils(validate, blockExternal);
+            
             TreeNode webtld = null;
-            webtld = new ParserUtils().parseXMLDocument(webXml.getSystemId(),
+            webtld = pu.parseXMLDocument(webXml.getSystemId(),
                     webXml.getInputSource());
 
             // Allow taglib to be an element of the root or jsp-config (JSP2.0)
@@ -491,8 +505,20 @@
             // Parse the tag library descriptor at the specified resource path
             String uri = null;
 
-            TreeNode tld =
-                new ParserUtils().parseXMLDocument(resourcePath, stream);
+            boolean validate = Boolean.parseBoolean(
+                    ctxt.getInitParameter(
+                            Constants.XML_VALIDATION_TLD_INIT_PARAM));
+            String blockExternalString = ctxt.getInitParameter(
+                    Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
+            boolean blockExternal;
+            if (blockExternalString == null) {
+                blockExternal = Constants.IS_SECURITY_ENABLED;
+            } else {
+                blockExternal = Boolean.parseBoolean(blockExternalString);
+            }
+            
+            ParserUtils pu = new ParserUtils(validate, blockExternal); 
+            TreeNode tld = pu.parseXMLDocument(resourcePath, stream);
             TreeNode uriNode = tld.findChild("uri");
             if (uriNode != null) {
                 String body = uriNode.getBody();
--- java/org/apache/jasper/xmlparser/ParserUtils.java.orig	2014-07-21 17:24:05.628407000 -0400
+++ java/org/apache/jasper/xmlparser/ParserUtils.java	2014-07-21 17:46:59.508034000 -0400
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.jasper.xmlparser;
 
 import java.io.IOException;
@@ -27,8 +26,9 @@
 import org.apache.jasper.Constants;
 import org.apache.jasper.JasperException;
 import org.apache.jasper.compiler.Localizer;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.descriptor.DigesterFactory;
+import org.apache.tomcat.util.descriptor.LocalResolver;
+import org.apache.tomcat.util.descriptor.XmlErrorHandler;
 import org.w3c.dom.Comment;
 import org.w3c.dom.Document;
 import org.w3c.dom.NamedNodeMap;
@@ -48,24 +48,38 @@
  * use a separate class loader for the parser to be used.
  *
  * @author Craig R. McClanahan
- * @version $Id: ParserUtils.java 1301227 2012-03-15 21:59:06Z markt $
+ * @version $Id: ParserUtils.java 1549529 2013-12-09 10:05:56Z markt $
  */
-
 public class ParserUtils {
 
     /**
      * An error handler for use when parsing XML documents.
      */
-    static ErrorHandler errorHandler = new MyErrorHandler();
+    static ErrorHandler errorHandler = new XmlErrorHandler();
 
     /**
      * An entity resolver for use when parsing XML documents.
      */
-    static EntityResolver entityResolver = new MyEntityResolver();
-
-    // Turn off for JSP 2.0 until switch over to using xschema.
-    public static boolean validating = false;
-
+    static EntityResolver entityResolver;
+    
+    private final EntityResolver entityResolverInstance;
+    
+    private final boolean validating;
+
+    public ParserUtils(boolean validating) {
+        this(validating, Constants.IS_SECURITY_ENABLED);
+    }
+
+    public ParserUtils(boolean validating, boolean blockExternal) {
+        this.validating = validating;
+        if (entityResolver == null) {
+            this.entityResolverInstance = new LocalResolver(
+                    DigesterFactory.SERVLET_API_PUBLIC_IDS,
+                    DigesterFactory.SERVLET_API_SYSTEM_IDS, blockExternal);
+        } else {
+            this.entityResolverInstance = entityResolver;
+        }
+    }
 
     // --------------------------------------------------------- Public Methods
 
@@ -91,7 +105,7 @@
             factory.setNamespaceAware(true);
             factory.setValidating(validating);
             DocumentBuilder builder = factory.newDocumentBuilder();
-            builder.setEntityResolver(entityResolver);
+            builder.setEntityResolver(entityResolverInstance);
             builder.setErrorHandler(errorHandler);
             document = builder.parse(is);
         } catch (ParserConfigurationException ex) {
@@ -196,56 +210,3 @@
         return (treeNode);
     }
 }
-
-
-// ------------------------------------------------------------ Private Classes
-
-class MyEntityResolver implements EntityResolver {
-
-    @Override
-    public InputSource resolveEntity(String publicId, String systemId)
-            throws SAXException {
-        for (int i = 0; i < Constants.CACHED_DTD_PUBLIC_IDS.size(); i++) {
-            String cachedDtdPublicId = Constants.CACHED_DTD_PUBLIC_IDS.get(i);
-            if (cachedDtdPublicId.equals(publicId)) {
-                String resourcePath =
-                    Constants.CACHED_DTD_RESOURCE_PATHS.get(i);
-                InputStream input = this.getClass().getResourceAsStream(
-                        resourcePath);
-                if (input == null) {
-                    throw new SAXException(Localizer.getMessage(
-                            "jsp.error.internal.filenotfound", resourcePath));
-                }
-                InputSource isrc = new InputSource(input);
-                return isrc;
-            }
-        }
-        Log log = LogFactory.getLog(MyEntityResolver.class);
-        if (log.isDebugEnabled())
-            log.debug("Resolve entity failed" + publicId + " " + systemId);
-        log.error(Localizer.getMessage("jsp.error.parse.xml.invalidPublicId",
-                publicId));
-        return null;
-    }
-}
-
-class MyErrorHandler implements ErrorHandler {
-
-    @Override
-    public void warning(SAXParseException ex) throws SAXException {
-        Log log = LogFactory.getLog(MyErrorHandler.class);
-        if (log.isDebugEnabled())
-            log.debug("ParserUtils: warning ", ex);
-        // We ignore warnings
-    }
-
-    @Override
-    public void error(SAXParseException ex) throws SAXException {
-        throw ex;
-    }
-
-    @Override
-    public void fatalError(SAXParseException ex) throws SAXException {
-        throw ex;
-    }
-}
--- webapps/docs/changelog.xml.orig	2014-07-22 17:42:18.576034000 -0400
+++ webapps/docs/changelog.xml	2014-07-22 18:48:34.775453000 -0400
@@ -58,6 +58,12 @@
 <section name="Tomcat 7.0.42 (markt)">
   <subsection name="Catalina">
     <changelog>
+      <add>
+        Add an option to the Context to control the blocking of XML external
+        entities when parsing XML configuration files and enable this blocking
+        by default when a security manager is used. The block is implemented via
+        a custom resolver to enable the logging of any blocked entities. (markt)
+      </add>
       <fix>
         CVE-2014-0099, Fix overflow when parsing long values from
         byte array. (markt) Patch applied by Red Hat Jun 16 2014
--- webapps/docs/config/context.xml.orig	2014-07-21 17:24:05.643402000 -0400
+++ webapps/docs/config/context.xml	2014-07-21 17:46:59.530037000 -0400
@@ -448,6 +448,14 @@
         useful for portlet specification implementations) set this attribute to
         <code>/</code> in the global <code>CATALINA_BASE/conf/context.xml</code>
         file.</p>
+        <p>Note: Once one web application using
+        <code>sessionCookiePath=&quot;/&quot;</code> obtains a session, all
+        subsequent sessions for any other web application in the same host also
+        configured with <code>sessionCookiePath=&quot;/&quot;</code> will always
+        use the same session ID. This holds even if the session is invalidated
+        and a new one created. This makes session fixation protection more
+        difficult and requires custom, Tomcat specific code to change the
+        session ID shared by the multiple applications.</p>
       </attribute>
 
       <attribute name="sessionCookiePathUsesTrailingSlash" required="false">
@@ -493,18 +501,6 @@
         of the flag is <code>false</code>.</p>
       </attribute>
 
-      <attribute name="tldNamespaceAware" required="false">
-        <p>If the value of this flag is <code>true</code>, the TLD files
-        XML validation will be namespace-aware.  If you turn this flag on,
-        you should probably also turn <code>tldValidation</code> on. If the
-        <code>org.apache.catalina.STRICT_SERVLET_COMPLIANCE</code>
-        <a href="systemprops.html">system property</a> is set to
-        <code>true</code>, the default value of this attribute will be
-        <code>true</code>, else the default value will be <code>false</code>.
-        Setting this attribute to <code>true</code> will incur a performance
-        penalty.</p>
-      </attribute>
-
       <attribute name="tldValidation" required="false">
         <p>If the value of this flag is <code>true</code>, the TLD files
         will be XML validated on context startup. If the
@@ -528,11 +524,26 @@
         Context.  If not specified, a standard default value will be used.</p>
       </attribute>
 
+      <attribute name="xmlBlockExternal" required="false">
+        <p>If the value of this flag is <code>true</code>, the parsing of
+        <code>web.xml</code>, <code>web-fragment.xml</code>, <code>*.tld</code>,
+        <code>*.jspx</code>, <code>*.tagx</code> and <code>tagPlugins.xml</code>
+        files for this web application will not permit external entities to be
+        loaded. If a <code>SecurityManager</code> is configured then the default
+        value of this attribute will be <code>true</code>, else the default
+        value will be <code>false</code>.</p>
+      </attribute>
+
       <attribute name="xmlNamespaceAware" required="false">
-        <p>If the value of this flag is <code>true</code>, the validation of
-        XML files will be namespace-aware.  If you turn this flag on,
-        you should probably also turn <code>xmlValidation</code> on. If
-        the <code>org.apache.catalina.STRICT_SERVLET_COMPLIANCE</code>
+        <p>If the value of this flag is <code>true</code>, the parsing of
+        <code>web.xml</code> and <code>web-fragment.xml</code> files for this
+        web application will be namespace-aware. Note that <code>*.tld</code>,
+        <code>*.jspx</code> and <code>*.tagx</code> files are always parsed
+        using a namespace-aware parser and that the <code>tagPlugins.xml</code>
+        file (if any) is never parsed using a namespace-aware parser. Note also
+        that if you turn this flag on, you should probably also turn
+        <code>xmlValidation</code> on. If the
+        <code>org.apache.catalina.STRICT_SERVLET_COMPLIANCE</code>
         <a href="systemprops.html">system property</a> is set to
         <code>true</code>, the default value of this attribute will be
         <code>true</code>, else the default value will be <code>false</code>.
@@ -541,8 +552,9 @@
       </attribute>
 
       <attribute name="xmlValidation" required="false">
-        <p>If the value of this flag is <code>true</code>, the XML files
-        will be validated on context startup. If the
+        <p>If the value of this flag is <code>true</code>, the parsing of
+        <code>web.xml</code> and <code>web-fragment.xml</code> files for this
+        web application will use a validating parser. If the
         <code>org.apache.catalina.STRICT_SERVLET_COMPLIANCE</code>
         <a href="systemprops.html">system property</a> is set to
         <code>true</code>, the default value of this attribute will be
@@ -706,7 +718,10 @@
         is likely to result in instability. As such, enabling this should be
         viewed as an option of last resort in a development environment and is
         not recommended in a production environment. If not specified, the
-        default value of <code>false</code> will be used.</p>
+        default value of <code>false</code> will be used. If this feature is
+        enabled, web applications may take up to two seconds longer to stop as
+        executor threads are given up to two seconds to stop gracefully before
+        <code>Thread.stop()</code> is called on any remaining threads.</p>
       </attribute>
 
       <attribute name="clearReferencesStopTimerThreads" required = "false">
@@ -717,6 +732,22 @@
         not specified, the default value of <code>false</code> will be used.</p>
       </attribute>
 
+      <attribute name="copyXML" required="false">
+        <p>Set to <code>true</code> if you want a context XML descriptor
+        embedded inside the application (located at
+        <code>/META-INF/context.xml</code>) to be copied to the owning
+        <a href="host.html">Host</a>'s <code>xmlBase</code> when the application
+        is deployed. On subsequent starts, the copied context XML descriptor
+        will be used in preference to any context XML descriptor embedded inside
+        the application even if the descriptor embedded inside the application
+        is more recent. The flag's value defaults to <code>false</code>. Note if
+        the <strong>deployXML</strong> attribute of the owning
+        <a href="host.html">Host</a> is <code>false</code> or if the
+        <strong>copyXML</strong> attribute of the owning
+        <a href="host.html">Host</a> is <code>true</code>, this attribute will
+        have no effect.</p>
+      </attribute>
+
       <attribute name="jndiExceptionOnFailedWrite" required="false">
         <p>If <code>true</code>, any attempt by an application to modify the
         provided JNDI context with a call to bind(), unbind(),
--- webapps/docs/security-howto.xml.orig	2014-07-21 17:24:05.649404000 -0400
+++ webapps/docs/security-howto.xml	2014-07-21 17:46:59.538035000 -0400
@@ -72,10 +72,74 @@
   </section>
 
   <section name="Default web applications">
-    <p>Tomcat ships with a number of web applications by default.
-    Vulnerabilities have been discovered in these applications in the past.
-    Applications that are not required should be removed so the system will not
-    be at risk if another vulnerability is discovered.</p>
+
+    <subsection name="General">
+      <p>Tomcat ships with a number of web applications that are enabled by
+      default. Vulnerabilities have been discovered in these applications in the
+      past. Applications that are not required should be removed so the system
+      will not be at risk if another vulnerability is discovered.</p>
+    </subsection>
+
+    <subsection name="ROOT">
+      <p>The ROOT web application presents a very low security risk but it does
+      include the version of Tomcat that is being used. The ROOT web application
+      should normally be removed from a publicly accessible Tomcat instance, not
+      for security reasons, but so that a more appropriate default page is shown
+      to users.</p>
+    </subsection>
+
+    <subsection name="Documentation">
+      <p>The documentation web application presents a very low security risk but
+      it does identify the version of Tomcat that is being used. It should
+      normally be removed from a publicly accessible Tomcat instance.</p>
+    </subsection>
+
+    <subsection name="Examples">
+      <p>The examples web application should always be removed from any security
+      sensitive installation. While the examples web application does not
+      contain any known vulnerabilities, it is known to contain features
+      (particularly the cookie examples that display the contents of all
+      received and allow new cookies to be set) that may be used by an attacker
+      in conjunction with a vulnerability in another application deployed on the
+      Tomcat instance to obtain additional information that would otherwise be
+      unavailable.</p>
+    </subsection>
+
+    <subsection name="Manager">
+      <p>The Manager application allows the remote deployment of web
+      applications and is frequently targeted by attackers due to the widespread
+      use of weak passwords and publicly accessible Tomcat instances with the
+      Manager application enabled. The Manager application is not accessible by
+      default as no users are configured with the necessary access. If the
+      Manager application is enabled then guidance in the section
+      <strong>Securing Management Applications</strong> section should be
+      followed.</p>
+    </subsection>
+
+    <subsection name="Host Manager">
+      <p>The Host Manager application allows the creation and management of
+      virtual hosts - including the enabling of the Manager application for a
+      virtual host. The Host Manager application is not accessible by default
+      as no users are configured with the necessary access. If the Host Manager
+      application is enabled then guidance in the section <strong>Securing
+      Management Applications</strong> section should be followed.</p>
+    </subsection>
+
+   <subsection name="Securing Management Applications">
+     <p>When deploying a web application that provides management functions for
+     the Tomcat instance, the following guidelines should be followed:</p>
+     <ul>
+       <ol>Ensure that any users permitted to access the management application
+           have strong passwords.</ol>
+       <ol>Do not remove the use of the <a
+           href="config/realm.html#LockOut_Realm_-_org.apache.catalina.realm.LockOutRealm">LockOutRealm</a>
+           which prevents brute force attacks against user passwords.</ol>
+       <ol>Uncomment the <a href="config/valve.html#Remote_Address_Filter">RemoteAddrValve</a>
+           in <code>/META-INF/context.xml</code> which limits access to
+           localhost. If remote access is required, limit it to specific IP
+           addresses using this valve.</ol>
+     </ul>
+   </subsection>
   </section>
 
   <section name="Security manager">
@@ -109,6 +173,16 @@
     manager should be introduced at the start of the development cycle as it can
     be time-consuming to track down and fix issues caused by enabling a security
     manager for a mature application.</p>
+
+    <p>Enabling the security manager changes the defaults for the following
+    settings:</p>
+    <ul>
+      <li>The default value for the <strong>deployXML</strong> attribute of the
+      <strong>Host</strong> element is changed to <code>false</code>.</li>
+      <li>The default value for the <strong>xmlBlockExternal</strong> attribute
+      of the <strong>Context</strong> element is changed to <code>true</code>.
+      </li>
+    </ul>
   </section>
 
   <section name="server.xml">
@@ -229,9 +303,11 @@
       </p>
 
       <p>In a hosted environment where web applications may not be trusted, set
-      the <strong>deployXML</strong> attribute to <code>false</code> to ignore any
-      context.xml packaged with the web application that may try to assign
-      increased privileges to the web application. </p>
+      the <strong>deployXML</strong> attribute to <code>false</code> to ignore
+      any context.xml packaged with the web application that may try to assign
+      increased privileges to the web application. Note that if the security
+      manager is enabled that the <strong>deployXML</strong> attribute will
+      default to <code>false</code>.</p>
     </subsection>
 
     <subsection name="Context">
--- java/org/apache/catalina/core/DefaultInstanceManager.java.orig	2014-07-22 16:56:51.460868000 -0400
+++ java/org/apache/catalina/core/DefaultInstanceManager.java	2014-07-22 16:59:03.496296000 -0400
@@ -55,7 +55,7 @@
 import org.apache.tomcat.util.res.StringManager;
 
 /**
- * @version $Id: DefaultInstanceManager.java 1437338 2013-01-23 11:02:35Z markt $
+ * @version $Id: DefaultInstanceManager.java 1514663 2013-08-16 11:51:28Z markt $
  */
 public class DefaultInstanceManager implements InstanceManager {
 
@@ -133,6 +133,11 @@
     }
 
     @Override
+    public Object newInstance(Class<?> clazz) throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException {
+        return newInstance(clazz.newInstance(), clazz);
+    }
+
+    @Override
     public Object newInstance(String className) throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException, ClassNotFoundException {
         Class<?> clazz = loadClassMaybePrivileged(className, classLoader);
         return newInstance(clazz.newInstance(), clazz);
--- java/org/apache/tomcat/InstanceManager.java.orig	2014-07-22 16:57:21.347937000 -0400
+++ java/org/apache/tomcat/InstanceManager.java	2014-07-22 17:00:47.400650000 -0400
@@ -21,10 +21,14 @@
 import javax.naming.NamingException;
 
 /**
- * @version $Id: InstanceManager.java 1200164 2011-11-10 05:46:02Z kkolinko $
+ * @version $Id: InstanceManager.java 1514663 2013-08-16 11:51:28Z markt $
  */
 public interface InstanceManager {
 
+    public Object newInstance(Class<?> clazz)
+            throws IllegalAccessException, InvocationTargetException, NamingException,
+                InstantiationException;
+
     public Object newInstance(String className)
         throws IllegalAccessException, InvocationTargetException, NamingException,
             InstantiationException, ClassNotFoundException;
--- java/org/apache/jasper/EmbeddedServletOptions.java.orig	2014-07-22 17:08:09.862127000 -0400
+++ java/org/apache/jasper/EmbeddedServletOptions.java	2014-07-22 17:08:43.749244000 -0400
@@ -30,7 +30,6 @@
 import org.apache.jasper.compiler.Localizer;
 import org.apache.jasper.compiler.TagPluginManager;
 import org.apache.jasper.compiler.TldLocationsCache;
-import org.apache.jasper.xmlparser.ParserUtils;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 
@@ -456,11 +455,7 @@
             String v=config.getInitParameter( k );
             setProperty( k, v);
         }
-        
-        // quick hack
-        String validating=config.getInitParameter( "validating");
-        if( "false".equals( validating )) ParserUtils.validating=false;
-        
+                
         String keepgen = config.getInitParameter("keepgenerated");
         if (keepgen != null) {
             if (keepgen.equalsIgnoreCase("true")) {
--- java/org/apache/tomcat/util/descriptor/DigesterFactory.java.orig	2014-07-21 17:24:05.654400000 -0400
+++ java/org/apache/tomcat/util/descriptor/DigesterFactory.java	2014-07-21 17:46:59.547034000 -0400
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.descriptor;
+
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.RuleSet;
+import org.xml.sax.ext.EntityResolver2;
+
+/**
+ * Wrapper class around the Digester that hide Digester's initialization
+ * details.
+ */
+public class DigesterFactory {
+
+    /**
+     * Mapping of well-known public IDs used by the Servlet API to the matching
+     * local resource.
+     */
+    public static final Map<String,String> SERVLET_API_PUBLIC_IDS;
+
+    /**
+     * Mapping of well-known system IDs used by the Servlet API to the matching
+     * local resource.
+     */
+    public static final Map<String,String> SERVLET_API_SYSTEM_IDS;
+
+    static {
+        Map<String, String> publicIds = new HashMap<String, String>();
+        Map<String, String> systemIds = new HashMap<String, String>();
+
+        // W3C
+        publicIds.put(XmlIdentifiers.XSD_10_PUBLIC, idFor("XMLSchema.dtd"));
+        publicIds.put(XmlIdentifiers.DATATYPES_PUBLIC, idFor("datatypes.dtd"));
+        systemIds.put(XmlIdentifiers.XML_2001_XSD, idFor("xml.xsd"));
+
+        // from J2EE 1.2
+        publicIds.put(XmlIdentifiers.WEB_22_PUBLIC, idFor("web-app_2_2.dtd"));
+        publicIds.put(XmlIdentifiers.TLD_11_PUBLIC, idFor("web-jsptaglibrary_1_1.dtd"));
+
+        // from J2EE 1.3
+        publicIds.put(XmlIdentifiers.WEB_23_PUBLIC, idFor("web-app_2_3.dtd"));
+        publicIds.put(XmlIdentifiers.TLD_12_PUBLIC, idFor("web-jsptaglibrary_1_2.dtd"));
+
+        // from J2EE 1.4
+        systemIds.put("http://www.ibm.com/webservices/xsd/j2ee_web_services_1_1.xsd",
+                idFor("j2ee_web_services_1_1.xsd"));
+        systemIds.put("http://www.ibm.com/webservices/xsd/j2ee_web_services_client_1_1.xsd",
+                idFor("j2ee_web_services_client_1_1.xsd"));
+        systemIds.put(XmlIdentifiers.WEB_24_XSD, idFor("web-app_2_4.xsd"));
+        systemIds.put(XmlIdentifiers.TLD_20_XSD, idFor("web-jsptaglibrary_2_0.xsd"));
+        addSelf(systemIds, "j2ee_1_4.xsd");
+        addSelf(systemIds, "jsp_2_0.xsd");
+
+        // from JavaEE 5
+        systemIds.put(XmlIdentifiers.WEB_25_XSD, idFor("web-app_2_5.xsd"));
+        systemIds.put(XmlIdentifiers.TLD_21_XSD, idFor("web-jsptaglibrary_2_1.xsd"));
+        addSelf(systemIds, "javaee_5.xsd");
+        addSelf(systemIds, "jsp_2_1.xsd");
+        addSelf(systemIds, "javaee_web_services_1_2.xsd");
+        addSelf(systemIds, "javaee_web_services_client_1_2.xsd");
+
+        // from JavaEE 6
+        systemIds.put(XmlIdentifiers.WEB_30_XSD, idFor("web-app_3_0.xsd"));
+        systemIds.put(XmlIdentifiers.WEB_FRAGMENT_30_XSD, idFor("web-fragment_3_0.xsd"));
+        addSelf(systemIds, "web-common_3_0.xsd");
+        addSelf(systemIds, "javaee_6.xsd");
+        addSelf(systemIds, "jsp_2_2.xsd");
+        addSelf(systemIds, "javaee_web_services_1_3.xsd");
+        addSelf(systemIds, "javaee_web_services_client_1_3.xsd");
+
+        SERVLET_API_PUBLIC_IDS = Collections.unmodifiableMap(publicIds);
+        SERVLET_API_SYSTEM_IDS = Collections.unmodifiableMap(systemIds);
+    }
+
+    private static void addSelf(Map<String, String> ids, String id) {
+        String systemId = idFor(id);
+        ids.put(systemId, systemId);
+        ids.put(id, systemId);
+    }
+
+    private static String idFor(String url) {
+        URL id = ServletContext.class.getResource("resources/" + url);
+        if (id == null) {
+            id = ServletContext.class.getResource("jsp/resources/" + url);
+        }
+        return id.toExternalForm();
+    }
+
+
+    /**
+     * Create a <code>Digester</code> parser.
+     * @param xmlValidation turn on/off xml validation
+     * @param xmlNamespaceAware turn on/off namespace validation
+     * @param rule an instance of <code>RuleSet</code> used for parsing the xml.
+     * @param blockExternal turn on/off the blocking of external resources
+     */
+    public static Digester newDigester(boolean xmlValidation,
+                                       boolean xmlNamespaceAware,
+                                       RuleSet rule,
+                                       boolean blockExternal) {
+        Digester digester = new Digester();
+        digester.setNamespaceAware(xmlNamespaceAware);
+        digester.setValidating(xmlValidation);
+        digester.setUseContextClassLoader(true);
+        EntityResolver2 resolver = new LocalResolver(SERVLET_API_PUBLIC_IDS,
+                SERVLET_API_SYSTEM_IDS, blockExternal);
+        digester.setEntityResolver(resolver);
+        if (rule != null) {
+            digester.addRuleSet(rule);
+        }
+
+        return digester;
+    }
+}
--- java/org/apache/tomcat/util/descriptor/LocalResolver.java.orig	2014-07-21 17:24:05.658412000 -0400
+++ java/org/apache/tomcat/util/descriptor/LocalResolver.java	2014-07-21 17:46:59.555034000 -0400
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.descriptor;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Map;
+
+import org.apache.tomcat.util.res.StringManager;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.EntityResolver2;
+
+/**
+ * A resolver for locally cached XML resources.
+ */
+public class LocalResolver implements EntityResolver2 {
+
+    private static final StringManager sm =
+            StringManager.getManager(Constants.PACKAGE_NAME);
+
+    private final Map<String,String> publicIds;
+    private final Map<String,String> systemIds;
+    private final boolean blockExternal;
+
+    /**
+     * Constructor providing mappings of public and system identifiers to local
+     * resources. Each map contains a mapping from a well-known identifier to a
+     * URL for a local resource path.
+     *
+     * @param publicIds mapping of well-known public identifiers to local
+     *                  resources
+     * @param systemIds mapping of well-known system identifiers to local
+     *                  resources
+     * @param blockExternal are external resources blocked that are not
+     *                      well-known
+     */
+    public LocalResolver(Map<String,String> publicIds,
+            Map<String,String> systemIds, boolean blockExternal) {
+        this.publicIds = publicIds;
+        this.systemIds = systemIds;
+        this.blockExternal = blockExternal;
+    }
+
+
+    @Override
+    public InputSource resolveEntity(String publicId, String systemId)
+            throws SAXException, IOException {
+        return resolveEntity(null, publicId, null, systemId);
+    }
+
+
+    @Override
+    public InputSource resolveEntity(String name, String publicId,
+            String base, String systemId) throws SAXException, IOException {
+
+        // First try resolving using the publicId
+        String resolved = publicIds.get(publicId);
+        if (resolved != null) {
+            InputSource is = new InputSource(resolved);
+            is.setPublicId(publicId);
+            return is;
+        }
+
+        // If there is no systemId, can't try anything else
+        if (systemId == null) {
+            throw new FileNotFoundException(sm.getString("localResolver.unresolvedEntity",
+                    name, publicId, systemId, base));
+        }
+
+        // Try resolving with the supplied systemId
+        resolved = systemIds.get(systemId);
+        if (resolved != null) {
+            InputSource is = new InputSource(resolved);
+            is.setPublicId(publicId);
+            return is;
+        }
+
+        // Resolve the supplied systemId against the base
+        URI systemUri;
+        try {
+            if (base == null) {
+                systemUri = new URI(systemId);
+            } else {
+                // Can't use URI.resolve() because "jar:..." URLs are not valid
+                // hierarchical URIs so resolve() does not work. new URL()
+                // delegates to the jar: stream handler and it manages to figure
+                // it out.
+                URI baseUri = new URI(base);
+                systemUri = new URL(baseUri.toURL(), systemId).toURI();
+            }
+            systemUri = systemUri.normalize();
+        } catch (URISyntaxException e) {
+            // May be caused by a | being used instead of a : in an absolute
+            // file URI on Windows.
+            if (blockExternal) {
+                // Absolute paths aren't allowed so block it
+                throw new MalformedURLException(e.getMessage());
+            } else {
+                // See if the URLHandler can resolve it
+                return new InputSource(systemId);
+            }
+        }
+        if (systemUri.isAbsolute()) {
+            // Try the resolved systemId
+            resolved = systemIds.get(systemUri.toString());
+            if (resolved != null) {
+                InputSource is = new InputSource(resolved);
+                is.setPublicId(publicId);
+                return is;
+            }
+            if (!blockExternal) {
+                InputSource is = new InputSource(systemUri.toString());
+                is.setPublicId(publicId);
+                return is;
+            }
+        }
+        throw new FileNotFoundException(sm.getString("localResolver.unresolvedEntity",
+                name, publicId, systemId, base));
+    }
+
+
+    @Override
+    public InputSource getExternalSubset(String name, String baseURI)
+            throws SAXException, IOException {
+        return null;
+    }
+}
--- java/org/apache/tomcat/util/descriptor/LocalStrings.properties.orig	2014-07-21 17:24:05.663406000 -0400
+++ java/org/apache/tomcat/util/descriptor/LocalStrings.properties	2014-07-21 17:46:59.562036000 -0400
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+localResolver.unresolvedEntity=Could not resolve XML resource [{0}] with public ID [{1}], system ID [{2}] and base URI [{3}] to a known, local entity.
+
+xmlErrorHandler.error=Non-fatal error [{0}] reported processing [{1}].
+xmlErrorHandler.warning=Warning [{0}] reported processing [{1}].
--- java/org/apache/tomcat/util/descriptor/Constants.java.orig	2014-07-21 17:24:05.668398000 -0400
+++ java/org/apache/tomcat/util/descriptor/Constants.java	2014-07-21 17:46:59.569034000 -0400
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.descriptor;
+
+public class Constants {
+
+    public static final String PACKAGE_NAME =
+            Constants.class.getPackage().getName();
+
+}
--- java/org/apache/tomcat/util/descriptor/LocalStrings_es.properties.orig	2014-07-21 17:24:05.672404000 -0400
+++ java/org/apache/tomcat/util/descriptor/LocalStrings_es.properties	2014-07-21 17:46:59.574035000 -0400
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+xmlErrorHandler.error = Error no fatal [{0}] reportado por el proceso [{1}].
+xmlErrorHandler.warning = Aviso [{0}] reportado por el proceso [{1}].
--- java/org/apache/tomcat/util/descriptor/XmlIdentifiers.java.orig	2014-07-21 17:24:05.677400000 -0400
+++ java/org/apache/tomcat/util/descriptor/XmlIdentifiers.java	2014-07-21 17:46:59.580036000 -0400
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.descriptor;
+
+/**
+ * Defines constants for well-known Public and System identifiers documented by
+ * the Servlet and JSP specifications.
+ */
+public final class XmlIdentifiers {
+
+    // from W3C
+    public static final String XML_2001_XSD = "http://www.w3.org/2001/xml.xsd";
+    public static final String DATATYPES_PUBLIC = "datatypes";
+    public static final String XSD_10_PUBLIC =
+            "-//W3C//DTD XMLSCHEMA 200102//EN";
+
+    // from J2EE 1.2
+    public static final String WEB_22_PUBLIC =
+            "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN";
+    public static final String WEB_22_SYSTEM =
+            "http://java.sun.com/dtd/web-app_2_2.dtd";
+    public static final String TLD_11_PUBLIC =
+            "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN";
+    public static final String TLD_11_SYSTEM =
+            "http://java.sun.com/dtd/web-jsptaglibrary_1_1.dtd";
+
+    // from J2EE 1.3
+    public static final String WEB_23_PUBLIC =
+            "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN";
+    public static final String WEB_23_SYSTEM =
+            "http://java.sun.com/dtd/web-app_2_3.dtd";
+    public static final String TLD_12_PUBLIC =
+            "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN";
+    public static final String TLD_12_SYSTEM =
+            "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd";
+
+    // from J2EE 1.4
+    public static final String WEB_24_XSD =
+            "http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd";
+    public static final String TLD_20_XSD =
+            "http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd";
+    public static final String WEBSERVICES_11_XSD =
+            "http://www.ibm.com/webservices/xsd/j2ee_web_services_1_1.xsd";
+
+    // from JavaEE 5
+    public static final String WEB_25_XSD =
+            "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd";
+    public static final String TLD_21_XSD =
+            "http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd";
+    public static final String WEBSERVICES_12_XSD =
+            "http://java.sun.com/xml/ns/javaee/javaee_web_services_1_2.xsd";
+
+    // from JavaEE 6
+    public static final String WEB_30_XSD =
+            "http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd";
+    public static final String WEB_FRAGMENT_30_XSD =
+            "http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd";
+    public static final String WEBSERVICES_13_XSD =
+            "http://java.sun.com/xml/ns/javaee/javaee_web_services_1_3.xsd";
+
+    private XmlIdentifiers() {
+    }
+}
\ No newline at end of file
--- java/org/apache/tomcat/util/descriptor/XmlErrorHandler.java.orig	2014-07-21 17:24:05.683398000 -0400
+++ java/org/apache/tomcat/util/descriptor/XmlErrorHandler.java	2014-07-21 17:46:59.586038000 -0400
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.descriptor;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.juli.logging.Log;
+import org.apache.tomcat.util.res.StringManager;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+public class XmlErrorHandler implements ErrorHandler {
+
+    private static final StringManager sm =
+        StringManager.getManager(Constants.PACKAGE_NAME);
+
+    private final Set<SAXParseException> errors = new HashSet<SAXParseException>();
+
+    private final Set<SAXParseException> warnings = new HashSet<SAXParseException>();
+
+    @Override
+    public void error(SAXParseException exception) throws SAXException {
+        // Collect non-fatal errors
+        errors.add(exception);
+    }
+
+    @Override
+    public void fatalError(SAXParseException exception) throws SAXException {
+        // Re-throw fatal errors
+        throw exception;
+    }
+
+    @Override
+    public void warning(SAXParseException exception) throws SAXException {
+        // Collect warnings
+        warnings.add(exception);
+    }
+
+    public Set<SAXParseException> getErrors() {
+        // Internal use only - don't worry about immutability
+        return errors;
+    }
+
+    public Set<SAXParseException> getWarnings() {
+        // Internal use only - don't worry about immutability
+        return warnings;
+    }
+
+    public void logFindings(Log log, String source) {
+        for (SAXParseException e : getWarnings()) {
+            log.warn(sm.getString(
+                    "xmlErrorHandler.warning", e.getMessage(), source));
+        }
+        for (SAXParseException e : getErrors()) {
+            log.warn(sm.getString(
+                    "xmlErrorHandler.error", e.getMessage(), source));
+        }
+    }
+}