diff -Nru tomcat7-7.0.70/build.properties.default tomcat7-7.0.72/build.properties.default --- tomcat7-7.0.70/build.properties.default 2016-06-15 14:24:03.000000000 +0000 +++ tomcat7-7.0.72/build.properties.default 2016-09-14 12:02:51.000000000 +0000 @@ -25,7 +25,7 @@ # ----- Version Control Flags ----- version.major=7 version.minor=0 -version.build=70 +version.build=72 version.patch=0 version.suffix= @@ -67,7 +67,7 @@ compile.target=1.6 compile.debug=true -base-apache.loc.1=http://www.apache.org/dist +base-apache.loc.1=http://www.apache.org/dyn/closer.lua?action=download&filename= base-apache.loc.2=http://archive.apache.org/dist base-commons.loc.1=${base-apache.loc.1}/commons base-commons.loc.2=${base-apache.loc.2}/commons @@ -140,7 +140,7 @@ jdt.loc.2=http://download.eclipse.org/eclipse/downloads/drops4/${jdt.release}/ecj-${jdt.version}.jar # ----- Tomcat native library ----- -tomcat-native.version=1.2.7 +tomcat-native.version=1.2.8 tomcat-native.home=${base.path}/tomcat-native-${tomcat-native.version} tomcat-native.tar.gz=${tomcat-native.home}/tomcat-native.tar.gz tomcat-native.loc.1=${base-tomcat.loc.1}/tomcat-connectors/native/${tomcat-native.version}/source/tomcat-native-${tomcat-native.version}-src.tar.gz @@ -211,7 +211,7 @@ # ----- objenesis, used by EasyMock, version 1.2 or later ----- objenesis.version=1.2 objenesis.home=${base.path}/objenesis-${objenesis.version} -objenesis.loc=https://objenesis.googlecode.com/files/objenesis-${objenesis.version}-bin.zip +objenesis.loc=https://bintray.com/easymock/distributions/download_file?file_path=objenesis-${objenesis.version}-bin.zip objenesis.jar=${objenesis.home}/objenesis-${objenesis.version}.jar # ----- Checkstyle, version 6.0 or later ----- diff -Nru tomcat7-7.0.70/conf/web.xml tomcat7-7.0.72/conf/web.xml --- tomcat7-7.0.70/conf/web.xml 2015-11-25 13:47:58.000000000 +0000 +++ tomcat7-7.0.72/conf/web.xml 2016-08-30 22:45:07.000000000 +0000 @@ -163,6 +163,8 @@ + + @@ -224,6 +226,8 @@ + + @@ -326,26 +330,18 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + @@ -362,14 +358,10 @@ cgi org.apache.catalina.servlets.CGIServlet - debug - 0 - - cgiPathPrefix WEB-INF/cgi - 5 + 5 --> diff -Nru tomcat7-7.0.70/debian/changelog tomcat7-7.0.72/debian/changelog --- tomcat7-7.0.70/debian/changelog 2016-09-14 08:56:45.000000000 +0000 +++ tomcat7-7.0.72/debian/changelog 2016-09-20 11:28:54.000000000 +0000 @@ -1,3 +1,9 @@ +tomcat7 (7.0.72-1) unstable; urgency=medium + + * New upstream release + + -- Emmanuel Bourg Tue, 20 Sep 2016 13:28:54 +0200 + tomcat7 (7.0.70-3) unstable; urgency=high * Team upload. diff -Nru tomcat7-7.0.70/java/org/apache/catalina/authenticator/mbeans-descriptors.xml tomcat7-7.0.72/java/org/apache/catalina/authenticator/mbeans-descriptors.xml --- tomcat7-7.0.70/java/org/apache/catalina/authenticator/mbeans-descriptors.xml 2014-01-27 14:53:14.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/authenticator/mbeans-descriptors.xml 2016-08-10 07:25:25.000000000 +0000 @@ -235,7 +235,7 @@ type="boolean"/> = 0) { - relative = requestPath.substring(0, pos + 1) + path; + if (context.getDispatchersUseEncodedPaths()) { + if (pos >= 0) { + relative = URLEncoder.DEFAULT.encode( + requestPath.substring(0, pos + 1), "UTF-8") + path; + } else { + relative = URLEncoder.DEFAULT.encode(requestPath, "UTF-8") + path; + } } else { - relative = requestPath + path; + if (pos >= 0) { + relative = requestPath.substring(0, pos + 1) + path; + } else { + relative = requestPath + path; + } } return context.getServletContext().getRequestDispatcher(relative); - } diff -Nru tomcat7-7.0.70/java/org/apache/catalina/Context.java tomcat7-7.0.72/java/org/apache/catalina/Context.java --- tomcat7-7.0.70/java/org/apache/catalina/Context.java 2015-12-01 13:07:02.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/Context.java 2016-07-27 15:58:11.000000000 +0000 @@ -1714,4 +1714,26 @@ * @see #setUseRelativeRedirects(boolean) */ public boolean getUseRelativeRedirects(); + + /** + * Are paths used in calls to obtain a request dispatcher expected to be + * encoded? This affects both how Tomcat handles calls to obtain a request + * dispatcher as well as how Tomcat generates paths used to obtain request + * dispatchers internally. + * + * @param dispatchersUseEncodedPaths {@code true} to use encoded paths, + * otherwise {@code false} + */ + public void setDispatchersUseEncodedPaths(boolean dispatchersUseEncodedPaths); + + /** + * Are paths used in calls to obtain a request dispatcher expected to be + * encoded? This applys to both how Tomcat handles calls to obtain a request + * dispatcher as well as how Tomcat generates paths used to obtain request + * dispatchers internally. + * + * @return {@code true} if encoded paths will be used, otherwise + * {@code false} + */ + public boolean getDispatchersUseEncodedPaths(); } diff -Nru tomcat7-7.0.70/java/org/apache/catalina/core/ApplicationContext.java tomcat7-7.0.72/java/org/apache/catalina/core/ApplicationContext.java --- tomcat7-7.0.70/java/org/apache/catalina/core/ApplicationContext.java 2016-04-27 09:27:29.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/core/ApplicationContext.java 2016-07-27 15:58:11.000000000 +0000 @@ -18,10 +18,12 @@ import java.io.File; import java.io.InputStream; +import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; +import java.net.URLDecoder; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; @@ -490,6 +492,26 @@ if (normalizedPath == null) return (null); + if (getContext().getDispatchersUseEncodedPaths()) { + // Decode + String decodedPath; + try { + decodedPath = URLDecoder.decode(normalizedPath, "UTF-8"); + } catch (UnsupportedEncodingException e) { + // Impossible + return null; + } + + // Security check to catch attempts to encode /../ sequences + normalizedPath = RequestUtil.normalize(decodedPath); + if (!decodedPath.equals(normalizedPath)) { + getContext().getLogger().warn( + sm.getString("applicationContext.illegalDispatchPath", path), + new IllegalArgumentException()); + return null; + } + } + pos = normalizedPath.length(); // Use the thread local URI and mapping data @@ -542,7 +564,7 @@ mappingData.recycle(); - String encodedUri = URLEncoder.DEFAULT.encode(uriCC.toString()); + String encodedUri = URLEncoder.DEFAULT.encode(uriCC.toString(), "UTF-8"); // Construct a RequestDispatcher to process this request return new ApplicationDispatcher(wrapper, encodedUri, wrapperPath, pathInfo, diff -Nru tomcat7-7.0.70/java/org/apache/catalina/core/ApplicationFilterConfig.java tomcat7-7.0.72/java/org/apache/catalina/core/ApplicationFilterConfig.java --- tomcat7-7.0.70/java/org/apache/catalina/core/ApplicationFilterConfig.java 2016-01-07 09:09:47.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/core/ApplicationFilterConfig.java 2016-07-01 20:15:43.000000000 +0000 @@ -300,8 +300,7 @@ unregisterJMX(); - if (this.filter != null) - { + if (this.filter != null) { try { if (Globals.IS_SECURITY_ENABLED) { try { diff -Nru tomcat7-7.0.70/java/org/apache/catalina/core/AprLifecycleListener.java tomcat7-7.0.72/java/org/apache/catalina/core/AprLifecycleListener.java --- tomcat7-7.0.70/java/org/apache/catalina/core/AprLifecycleListener.java 2016-04-11 07:27:16.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/core/AprLifecycleListener.java 2016-07-02 21:16:48.000000000 +0000 @@ -65,12 +65,11 @@ // ---------------------------------------------- Constants - protected static final int TCN_REQUIRED_MAJOR = 1; protected static final int TCN_REQUIRED_MINOR = 1; protected static final int TCN_REQUIRED_PATCH = 32; - protected static final int TCN_RECOMMENDED_MINOR = 1; - protected static final int TCN_RECOMMENDED_PV = 33; + protected static final int TCN_RECOMMENDED_MINOR = 2; + protected static final int TCN_RECOMMENDED_PV = 8; // ---------------------------------------------- Properties diff -Nru tomcat7-7.0.70/java/org/apache/catalina/core/LocalStrings.properties tomcat7-7.0.72/java/org/apache/catalina/core/LocalStrings.properties --- tomcat7-7.0.70/java/org/apache/catalina/core/LocalStrings.properties 2016-06-07 13:00:50.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/core/LocalStrings.properties 2016-07-27 15:58:11.000000000 +0000 @@ -22,6 +22,7 @@ applicationContext.addRole.ise=Roles can not be added to context {0} as the context has been initialised applicationContext.addServlet.ise=Servlets can not be added to context {0} as the context has been initialised applicationContext.attributeEvent=Exception thrown by attributes event listener +applicationContext.illegalDispatchPath=An application attempted to obtain a request dispatcher with an illegal path [{0}] that was rejected because it contained an encoded directory traversal attempt applicationContext.invalidFilterName=Unable to add filter definition due to invalid filter name [{0}]. applicationContext.invalidServletName=Unable to add servlet definition due to invalid servlet name [{0}]. applicationContext.mapping.error=Error during mapping @@ -264,6 +265,7 @@ standardWrapper.createFilters=Create filters exception for servlet {0} standardWrapper.deallocateException=Deallocate exception for servlet {0} standardWrapper.destroyException=Servlet.destroy() for servlet {0} threw exception +standardWrapper.destroyInstance=InstanceManager.destroy() for servlet [{0}] threw exception standardWrapper.exception0=Tomcat Exception Report standardWrapper.exception1=A Servlet Exception Has Occurred standardWrapper.exception2=Exception Report: diff -Nru tomcat7-7.0.70/java/org/apache/catalina/core/mbeans-descriptors.xml tomcat7-7.0.72/java/org/apache/catalina/core/mbeans-descriptors.xml --- tomcat7-7.0.70/java/org/apache/catalina/core/mbeans-descriptors.xml 2016-05-20 11:10:18.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/core/mbeans-descriptors.xml 2016-08-10 07:25:25.000000000 +0000 @@ -34,7 +34,7 @@ writeable="false"/> @@ -833,7 +833,7 @@ @@ -1718,7 +1718,7 @@ @@ -1864,7 +1864,7 @@ impact="ACTION" returnType="void"> diff -Nru tomcat7-7.0.70/java/org/apache/catalina/core/NamingContextListener.java tomcat7-7.0.72/java/org/apache/catalina/core/NamingContextListener.java --- tomcat7-7.0.70/java/org/apache/catalina/core/NamingContextListener.java 2015-12-17 13:16:37.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/core/NamingContextListener.java 2016-08-22 21:04:21.000000000 +0000 @@ -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. @@ -41,6 +41,7 @@ import org.apache.catalina.ContainerEvent; import org.apache.catalina.ContainerListener; import org.apache.catalina.Context; +import org.apache.catalina.Engine; import org.apache.catalina.Host; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleEvent; @@ -68,6 +69,7 @@ import org.apache.naming.ResourceRef; import org.apache.naming.ServiceRef; import org.apache.naming.TransactionRef; +import org.apache.naming.factory.ResourceLinkFactory; import org.apache.tomcat.util.modeler.Registry; import org.apache.tomcat.util.res.StringManager; @@ -88,8 +90,8 @@ protected Log logger = log; - - + + /** * Name of the associated naming context. */ @@ -131,13 +133,13 @@ */ protected javax.naming.Context envCtx = null; - + /** * Objectnames hashtable. */ protected HashMap objectNames = new HashMap(); - + /** * Determines if an attempt to write to a read-only context results in an @@ -192,7 +194,7 @@ this.name = name; } - + /** * Return the comp context. */ @@ -200,7 +202,7 @@ public javax.naming.Context getCompContext() { return this.compCtx; } - + /** * Return the env context. @@ -208,7 +210,7 @@ public javax.naming.Context getEnvContext() { return this.envCtx; } - + /** * Return the associated naming context. @@ -282,7 +284,7 @@ ContextAccessController.setReadOnly(getName()); try { ContextBindings.bindClassLoader - (container, container, + (container, container, ((Container) container).getLoader().getClassLoader()); } catch (NamingException e) { logger.error(sm.getString("naming.bindFailed", e)); @@ -294,7 +296,7 @@ (namingContext); try { ContextBindings.bindClassLoader - (container, container, + (container, container, this.getClass().getClassLoader()); } catch (NamingException e) { logger.error(sm.getString("naming.bindFailed", e)); @@ -322,14 +324,14 @@ if (container instanceof Context) { ContextBindings.unbindClassLoader - (container, container, + (container, container, ((Container) container).getLoader().getClassLoader()); } if (container instanceof Server) { namingResources.removePropertyChangeListener(this); ContextBindings.unbindClassLoader - (container, container, + (container, container, this.getClass().getClassLoader()); } @@ -344,6 +346,11 @@ registry.unregisterComponent(objectName); } } + + javax.naming.Context global = getGlobalNamingContext(); + if (global != null) { + ResourceLinkFactory.deregisterGlobalResourceAccess(global); + } } finally { objectNames.clear(); @@ -391,7 +398,7 @@ String environmentName = (String) event.getData(); if (environmentName != null) { - ContextEnvironment env = + ContextEnvironment env = namingResources.findEnvironment(environmentName); addEnvironment(env); } @@ -400,7 +407,7 @@ String localEjbName = (String) event.getData(); if (localEjbName != null) { - ContextLocalEjb localEjb = + ContextLocalEjb localEjb = namingResources.findLocalEjb(localEjbName); addLocalEjb(localEjb); } @@ -409,7 +416,7 @@ String resourceName = (String) event.getData(); if (resourceName != null) { - ContextResource resource = + ContextResource resource = namingResources.findResource(resourceName); addResource(resource); } @@ -418,7 +425,7 @@ String resourceLinkName = (String) event.getData(); if (resourceLinkName != null) { - ContextResourceLink resourceLink = + ContextResourceLink resourceLink = namingResources.findResourceLink(resourceLinkName); addResourceLink(resourceLink); } @@ -427,7 +434,7 @@ String resourceEnvRefName = (String) event.getData(); if (resourceEnvRefName != null) { - ContextResourceEnvRef resourceEnvRef = + ContextResourceEnvRef resourceEnvRef = namingResources.findResourceEnvRef(resourceEnvRefName); addResourceEnvRef(resourceEnvRef); } @@ -436,7 +443,7 @@ String serviceName = (String) event.getData(); if (serviceName != null) { - ContextService service = + ContextService service = namingResources.findService(serviceName); addService(service); } @@ -599,14 +606,14 @@ } } else if (name.equals("resourceEnvRef")) { if (oldValue != null) { - ContextResourceEnvRef resourceEnvRef = + ContextResourceEnvRef resourceEnvRef = (ContextResourceEnvRef) oldValue; if (resourceEnvRef.getName() != null) { removeResourceEnvRef(resourceEnvRef.getName()); } } if (newValue != null) { - ContextResourceEnvRef resourceEnvRef = + ContextResourceEnvRef resourceEnvRef = (ContextResourceEnvRef) newValue; if (resourceEnvRef.getName() != null) { addResourceEnvRef(resourceEnvRef); @@ -670,7 +677,7 @@ } // Resource links - ContextResourceLink[] resourceLinks = + ContextResourceLink[] resourceLinks = namingResources.findResourceLinks(); for (i = 0; i < resourceLinks.length; i++) { addResourceLink(resourceLinks[i]); @@ -689,7 +696,7 @@ } // Environment entries - ContextEnvironment[] contextEnvironments = + ContextEnvironment[] contextEnvironments = namingResources.findEnvironments(); for (i = 0; i < contextEnvironments.length; i++) { addEnvironment(contextEnvironments[i]); @@ -723,7 +730,7 @@ } } } catch (NameAlreadyBoundException e) { - // Ignore because UserTransaction was obviously + // Ignore because UserTransaction was obviously // added via ResourceLink } catch (NamingException e) { logger.error(sm.getString("naming.bindFailed", e)); @@ -733,7 +740,7 @@ // Binding the resources directory context if (container instanceof Context) { try { - compCtx.bind("Resources", + compCtx.bind("Resources", ((Container) container).getResources()); } catch (NamingException e) { logger.error(sm.getString("naming.bindFailed", e)); @@ -763,30 +770,30 @@ if (domain == null) { domain = "Catalina"; } - + ObjectName name = null; String quotedResourceName = ObjectName.quote(resource.getName()); - if (container instanceof Server) { + if (container instanceof Server) { name = new ObjectName(domain + ":type=DataSource" + - ",class=" + resource.getType() + + ",class=" + resource.getType() + ",name=" + quotedResourceName); - } else if (container instanceof Context) { + } else if (container instanceof Context) { String contextName = ((Context)container).getName(); if (!contextName.startsWith("/")) contextName = "/" + contextName; Host host = (Host) ((Context)container).getParent(); name = new ObjectName(domain + ":type=DataSource" + - ",context=" + contextName + + ",context=" + contextName + ",host=" + host.getName() + ",class=" + resource.getType() + ",name=" + quotedResourceName); } - + return (name); } - + /** * Set the specified EJBs in the naming context. */ @@ -964,7 +971,7 @@ wsdlURL = ((Context) container). getServletContext(). getResource("/" + service.getWsdlfile()); - logger.debug(" Changing service ref wsdl file for /" + logger.debug(" Changing service ref wsdl file for /" + service.getWsdlfile()); } catch (MalformedURLException e) { logger.error(sm.getString("naming.wsdlFailed", e)); @@ -998,7 +1005,7 @@ jaxrpcURL = ((Context) container). getServletContext(). getResource("/" + service.getJaxrpcmappingfile()); - logger.debug(" Changing service ref jaxrpc file for /" + logger.debug(" Changing service ref jaxrpc file for /" + service.getJaxrpcmappingfile()); } catch (MalformedURLException e) { logger.error(sm.getString("naming.wsdlFailed", e)); @@ -1055,7 +1062,7 @@ try { if (logger.isDebugEnabled()) { - logger.debug(" Adding service ref " + logger.debug(" Adding service ref " + service.getName() + " " + ref); } createSubcontexts(envCtx, service.getName()); @@ -1087,7 +1094,7 @@ } try { if (logger.isDebugEnabled()) { - logger.debug(" Adding resource ref " + logger.debug(" Adding resource ref " + resource.getName() + " " + ref); } createSubcontexts(envCtx, resource.getName()); @@ -1107,7 +1114,7 @@ logger.warn(sm.getString("naming.jmxRegistrationFailed", e)); } } - + } @@ -1155,8 +1162,8 @@ ref.add(refAddr); } } - javax.naming.Context ctx = - "UserTransaction".equals(resourceLink.getName()) + javax.naming.Context ctx = + "UserTransaction".equals(resourceLink.getName()) ? compCtx : envCtx; try { if (logger.isDebugEnabled()) @@ -1167,6 +1174,17 @@ logger.error(sm.getString("naming.bindFailed", e)); } + ResourceLinkFactory.registerGlobalResourceAccess( + getGlobalNamingContext(), resourceLink.getName(), resourceLink.getGlobal()); + } + + + private javax.naming.Context getGlobalNamingContext() { + if (container instanceof Context) { + Engine e = (Engine) ((Context) container).getParent().getParent(); + return e.getService().getServer().getGlobalNamingContext(); + } + return null; } @@ -1270,6 +1288,7 @@ logger.error(sm.getString("naming.unbindFailed", e)); } + ResourceLinkFactory.deregisterGlobalResourceAccess(getGlobalNamingContext(), name); } diff -Nru tomcat7-7.0.70/java/org/apache/catalina/core/StandardContext.java tomcat7-7.0.72/java/org/apache/catalina/core/StandardContext.java --- tomcat7-7.0.70/java/org/apache/catalina/core/StandardContext.java 2016-05-20 11:10:18.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/core/StandardContext.java 2016-07-27 15:58:11.000000000 +0000 @@ -814,7 +814,7 @@ * particularly IE, don't send a session cookie for context /foo with * requests intended for context /foobar. */ - private boolean sessionCookiePathUsesTrailingSlash = true; + private boolean sessionCookiePathUsesTrailingSlash = false; /** @@ -929,10 +929,29 @@ private boolean useRelativeRedirects = !Globals.STRICT_SERVLET_COMPLIANCE; + private boolean dispatchersUseEncodedPaths = true; + // ----------------------------------------------------- Context Properties @Override + public void setDispatchersUseEncodedPaths(boolean dispatchersUseEncodedPaths) { + this.dispatchersUseEncodedPaths = dispatchersUseEncodedPaths; + } + + + /** + * {@inheritDoc} + *

+ * The default value for this implementation is {@code true}. + */ + @Override + public boolean getDispatchersUseEncodedPaths() { + return dispatchersUseEncodedPaths; + } + + + @Override public void setUseRelativeRedirects(boolean useRelativeRedirects) { this.useRelativeRedirects = useRelativeRedirects; } @@ -2261,7 +2280,7 @@ log.warn(sm.getString( "standardContext.pathInvalid", path, this.path)); } - encodedPath = urlEncoder.encode(this.path); + encodedPath = urlEncoder.encode(this.path, "UTF-8"); if (getName() == null) { setName(this.path); } diff -Nru tomcat7-7.0.70/java/org/apache/catalina/core/StandardWrapper.java tomcat7-7.0.72/java/org/apache/catalina/core/StandardWrapper.java --- tomcat7-7.0.70/java/org/apache/catalina/core/StandardWrapper.java 2016-01-01 21:36:36.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/core/StandardWrapper.java 2016-07-14 20:07:54.000000000 +0000 @@ -573,35 +573,22 @@ /** - * Return true if the servlet class represented by this - * component implements the SingleThreadModel interface. - */ - public boolean isSingleThreadModel() { - - // Short-cuts - // If singleThreadModel is true, must have already checked this - // If instance != null, must have already loaded + * Does the servlet class represented by this component implement the + * SingleThreadModel interface? This can only be determined + * once the class is loaded. Calling this method will not trigger loading + * the class since that may cause the application to behave unexpectedly. + * + * @return {@code null} if the class has not been loaded, otherwise {@code + * true} if the servlet does implement {@code SingleThreadModel} and + * {@code false} if it does not. + */ + public Boolean isSingleThreadModel() { + // If the servlet has been loaded either singleThreadModel will be true + // or instance will be non-null if (singleThreadModel || instance != null) { - return singleThreadModel; - } - - // The logic to determine this safely is more complex than one might - // expect. allocate() already has the necessary logic so re-use it. - // Make sure the Servlet is loaded with the right class loader - ClassLoader old = Thread.currentThread().getContextClassLoader(); - ClassLoader webappClassLoader = - ((Context) getParent()).getLoader().getClassLoader(); - try { - Thread.currentThread().setContextClassLoader(webappClassLoader); - Servlet s = allocate(); - deallocate(s); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - } finally { - Thread.currentThread().setContextClassLoader(old); + return Boolean.valueOf(singleThreadModel); } - return singleThreadModel; - + return null; } @@ -1486,12 +1473,7 @@ instanceSupport.fireInstanceEvent (InstanceEvent.AFTER_DESTROY_EVENT, instance); - - // Annotation processing - if (!((Context) getParent()).getIgnoreAnnotations()) { - ((StandardContext)getParent()).getInstanceManager().destroyInstance(instance); - } - + } catch (Throwable t) { t = ExceptionUtils.unwrapInvocationTargetException(t); ExceptionUtils.handleThrowable(t); @@ -1506,6 +1488,15 @@ (sm.getString("standardWrapper.destroyException", getName()), t); } finally { + // Annotation processing + if (!((Context) getParent()).getIgnoreAnnotations()) { + try { + ((Context)getParent()).getInstanceManager().destroyInstance(instance); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error(sm.getString("standardWrapper.destroyInstance", getName()), t); + } + } // Write captured output if (swallowOutput) { String log = SystemLogHandler.stopCapture(); diff -Nru tomcat7-7.0.70/java/org/apache/catalina/deploy/ErrorPage.java tomcat7-7.0.72/java/org/apache/catalina/deploy/ErrorPage.java --- tomcat7-7.0.70/java/org/apache/catalina/deploy/ErrorPage.java 2014-01-27 14:53:14.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/deploy/ErrorPage.java 2016-08-11 20:17:20.000000000 +0000 @@ -92,9 +92,8 @@ try { this.errorCode = Integer.parseInt(errorCode); } catch (NumberFormatException nfe) { - this.errorCode = 0; + throw new IllegalArgumentException(nfe); } - } diff -Nru tomcat7-7.0.70/java/org/apache/catalina/filters/CorsFilter.java tomcat7-7.0.72/java/org/apache/catalina/filters/CorsFilter.java --- tomcat7-7.0.70/java/org/apache/catalina/filters/CorsFilter.java 2015-05-18 11:35:00.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/filters/CorsFilter.java 2016-08-23 13:24:51.000000000 +0000 @@ -863,8 +863,14 @@ return true; } - URI originURI; + // RFC6454, section 4. "If uri-scheme is file, the implementation MAY + // return an implementation-defined value.". No limits are placed on + // that value so treat all file URIs as valid origins. + if (origin.startsWith("file://")) { + return true; + } + URI originURI; try { originURI = new URI(origin); } catch (URISyntaxException e) { diff -Nru tomcat7-7.0.70/java/org/apache/catalina/filters/LocalStrings.properties tomcat7-7.0.72/java/org/apache/catalina/filters/LocalStrings.properties --- tomcat7-7.0.70/java/org/apache/catalina/filters/LocalStrings.properties 2015-11-30 16:29:01.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/filters/LocalStrings.properties 2016-06-16 13:02:31.000000000 +0000 @@ -47,4 +47,6 @@ httpHeaderSecurityFilter.committed=Unable to add HTTP headers since response is already committed on entry to the HTTP header security Filter httpHeaderSecurityFilter.clickjack.invalid=An invalid value [{0}] was specified for the anti click-jacking header +requestFilter.deny=Denied request for [{0}] based on property [{1}] + restCsrfPreventionFilter.invalidNonce=CSRF nonce validation failed \ No newline at end of file diff -Nru tomcat7-7.0.70/java/org/apache/catalina/filters/RequestFilter.java tomcat7-7.0.72/java/org/apache/catalina/filters/RequestFilter.java --- tomcat7-7.0.70/java/org/apache/catalina/filters/RequestFilter.java 2014-01-27 14:53:14.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/filters/RequestFilter.java 2016-06-16 13:02:31.000000000 +0000 @@ -24,6 +24,7 @@ import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.catalina.comet.CometEvent; @@ -205,6 +206,10 @@ chain.doFilter(request, response); } else { if (response instanceof HttpServletResponse) { + if (getLogger().isDebugEnabled()) { + getLogger().debug(sm.getString("requestFilter.deny", + ((HttpServletRequest) request).getRequestURI(), property)); + } ((HttpServletResponse) response).sendError(denyStatus); } else { sendErrorWhenNotHttp(response); diff -Nru tomcat7-7.0.70/java/org/apache/catalina/ha/authenticator/mbeans-descriptors.xml tomcat7-7.0.72/java/org/apache/catalina/ha/authenticator/mbeans-descriptors.xml --- tomcat7-7.0.70/java/org/apache/catalina/ha/authenticator/mbeans-descriptors.xml 2015-04-22 10:32:06.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/ha/authenticator/mbeans-descriptors.xml 2016-08-10 07:25:25.000000000 +0000 @@ -38,7 +38,7 @@ type="boolean"/> { @@ -962,7 +963,7 @@ } - + protected void copyStateWithoutTransformers(WebappClassLoaderBase base) { base.antiJARLocking = this.antiJARLocking; base.resources = this.resources; @@ -992,7 +993,7 @@ base.searchExternalFirst = this.searchExternalFirst; } - + /** * Add a new repository to the set of places this ClassLoader can look for * classes to be loaded. @@ -1442,7 +1443,7 @@ URL url = null; String path = nameToPath(name); - + if (hasExternalRepositories && searchExternalFirst) url = super.findResource(name); @@ -1769,7 +1770,7 @@ if (log.isDebugEnabled()) log.debug("loadClass(" + name + ", " + resolve + ")"); Class clazz = null; - + // Log access to stopped classloader if (!started) { try { @@ -1778,7 +1779,7 @@ log.info(sm.getString("webappClassLoader.stopped", name), e); } } - + // (0) Check our previously loaded local class cache clazz = findLoadedClass0(name); if (clazz != null) { @@ -1788,7 +1789,7 @@ resolveClass(clazz); return (clazz); } - + // (0.1) Check our previously loaded class cache clazz = findLoadedClass(name); if (clazz != null) { @@ -1798,7 +1799,7 @@ resolveClass(clazz); return (clazz); } - + // (0.2) Try loading the class with the system class loader, to prevent // the webapp from overriding J2SE classes try { @@ -1811,7 +1812,7 @@ } catch (ClassNotFoundException e) { // Ignore } - + // (0.5) Permission to access this class when using a SecurityManager if (securityManager != null) { int i = name.lastIndexOf('.'); @@ -1832,9 +1833,9 @@ } } } - + boolean delegateLoad = delegate || filter(name); - + // (1) Delegate to our parent if requested if (delegateLoad) { if (log.isDebugEnabled()) @@ -1852,7 +1853,7 @@ // Ignore } } - + // (2) Search local repositories if (log.isDebugEnabled()) log.debug(" Searching local repositories"); @@ -1868,7 +1869,7 @@ } catch (ClassNotFoundException e) { // Ignore } - + // (3) Delegate to parent unconditionally if (!delegateLoad) { if (log.isDebugEnabled()) @@ -1887,7 +1888,7 @@ } } } - + throw new ClassNotFoundException(name); } @@ -1902,8 +1903,8 @@ } return this; } - - + + /** * Get the Permissions for a CodeSource. If this instance * of WebappClassLoaderBase is for a web application context, @@ -1935,6 +1936,27 @@ } + @Override + public boolean check(Permission permission) { + if (!Globals.IS_SECURITY_ENABLED) { + return true; + } + Policy currentPolicy = Policy.getPolicy(); + if (currentPolicy != null) { + ResourceEntry entry = findResourceInternal("/", "/", false); + if (entry != null) { + CodeSource cs = new CodeSource( + entry.codeBase, (java.security.cert.Certificate[]) null); + PermissionCollection pc = currentPolicy.getPermissions(cs); + if (pc.implies(permission)) { + return true; + } + } + } + return false; + } + + /** * Returns the search path of URLs for loading classes and resources. * This includes the original list of URLs specified to the constructor, @@ -2080,7 +2102,7 @@ break; } } - + } @@ -3076,9 +3098,8 @@ try { jarFiles[i] = new JarFile(jarRealFiles[i]); } catch (IOException e) { - if (log.isDebugEnabled()) { - log.debug("Failed to open JAR", e); - } + log.warn(sm.getString("webappClassLoader.jarOpenFail", jarFiles[i]), e); + closeJARs(true); return false; } } @@ -3591,8 +3612,8 @@ path.append(name); return path.toString(); } - - + + /** * Returns true if the specified package name is sealed according to the * given manifest. diff -Nru tomcat7-7.0.70/java/org/apache/catalina/manager/HTMLManagerServlet.java tomcat7-7.0.72/java/org/apache/catalina/manager/HTMLManagerServlet.java --- tomcat7-7.0.70/java/org/apache/catalina/manager/HTMLManagerServlet.java 2016-01-26 23:40:33.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/manager/HTMLManagerServlet.java 2016-08-02 15:34:58.000000000 +0000 @@ -456,10 +456,10 @@ StringBuilder tmp = new StringBuilder(); tmp.append("path="); - tmp.append(URL_ENCODER.encode(displayPath)); + tmp.append(URL_ENCODER.encode(displayPath, "UTF-8")); if (ctxt.getWebappVersion().length() > 0) { tmp.append("&version="); - tmp.append(URL_ENCODER.encode(ctxt.getWebappVersion())); + tmp.append(URL_ENCODER.encode(ctxt.getWebappVersion(), "UTF-8")); } String pathVersion = tmp.toString(); @@ -471,7 +471,7 @@ } args = new Object[7]; - args[0] = "" + RequestUtil.filter(displayPath) + ""; if ("".equals(ctxt.getWebappVersion())) { args[1] = noVersion; @@ -583,8 +583,8 @@ args[4] = smClient.getString("htmlManagerServlet.serverOSName"); args[5] = smClient.getString("htmlManagerServlet.serverOSVersion"); args[6] = smClient.getString("htmlManagerServlet.serverOSArch"); - args[7] = sm.getString("htmlManagerServlet.serverHostname"); - args[8] = sm.getString("htmlManagerServlet.serverIPAddress"); + args[7] = smClient.getString("htmlManagerServlet.serverHostname"); + args[8] = smClient.getString("htmlManagerServlet.serverIPAddress"); writer.print(MessageFormat.format (Constants.SERVER_HEADER_SECTION, args)); diff -Nru tomcat7-7.0.70/java/org/apache/catalina/mbeans/mbeans-descriptors.xml tomcat7-7.0.72/java/org/apache/catalina/mbeans/mbeans-descriptors.xml --- tomcat7-7.0.70/java/org/apache/catalina/mbeans/mbeans-descriptors.xml 2014-01-27 14:53:14.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/mbeans/mbeans-descriptors.xml 2016-08-10 07:25:25.000000000 +0000 @@ -188,7 +188,7 @@ description="The context path for this Context" type="java.lang.String"/> diff -Nru tomcat7-7.0.70/java/org/apache/catalina/realm/DataSourceRealm.java tomcat7-7.0.72/java/org/apache/catalina/realm/DataSourceRealm.java --- tomcat7-7.0.70/java/org/apache/catalina/realm/DataSourceRealm.java 2016-01-21 01:56:04.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/realm/DataSourceRealm.java 2016-08-30 23:00:35.000000000 +0000 @@ -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. @@ -271,13 +271,13 @@ */ @Override public Principal authenticate(String username, String credentials) { - + // No user or no credentials // Can't possibly authenticate, don't bother the database then if (username == null || credentials == null) { return null; } - + Connection dbConnection = null; // Ensure that we have an open database connection @@ -286,7 +286,7 @@ // If the db connection open fails, return "not authenticated" return null; } - + try { // Acquire a Principal object for this user @@ -315,25 +315,43 @@ * authenticating this username */ protected Principal authenticate(Connection dbConnection, - String username, - String credentials) { + String username, + String credentials) { + // No user or no credentials + // Can't possibly authenticate, don't bother the database then + if (username == null || credentials == null) { + if (containerLog.isTraceEnabled()) + containerLog.trace(sm.getString("dataSourceRealm.authenticateFailure", + username)); + return null; + } + // Look up the user's credentials String dbCredentials = getPassword(dbConnection, username); + if(dbCredentials == null) { + // User was not found in the database. + // Waste a bit of time as not to reveal that the user does not exist. + compareCredentials(credentials, getClass().getName()); + + if (containerLog.isTraceEnabled()) + containerLog.trace(sm.getString("dataSourceRealm.authenticateFailure", + username)); + return null; + } + // Validate the user's credentials boolean validated = compareCredentials(credentials, dbCredentials); if (validated) { if (containerLog.isTraceEnabled()) - containerLog.trace( - sm.getString("dataSourceRealm.authenticateSuccess", - username)); + containerLog.trace(sm.getString("dataSourceRealm.authenticateSuccess", + username)); } else { if (containerLog.isTraceEnabled()) - containerLog.trace( - sm.getString("dataSourceRealm.authenticateFailure", - username)); - return (null); + containerLog.trace(sm.getString("dataSourceRealm.authenticateFailure", + username)); + return null; } ArrayList list = getRoles(dbConnection, username); @@ -358,7 +376,7 @@ try { if (!dbConnection.getAutoCommit()) { dbConnection.commit(); - } + } } catch (SQLException e) { containerLog.error("Exception committing connection before closing:", e); } @@ -392,7 +410,7 @@ } catch (Exception e) { // Log the problem for posterity containerLog.error(sm.getString("dataSourceRealm.exception"), e); - } + } return null; } @@ -421,18 +439,18 @@ } try { - return getPassword(dbConnection, username); + return getPassword(dbConnection, username); } finally { close(dbConnection); } } - + /** * Return the password associated with the given principal's user name. * @param dbConnection The database connection to be used * @param username Username for which password should be retrieved */ - protected String getPassword(Connection dbConnection, + protected String getPassword(Connection dbConnection, String username) { ResultSet rs = null; @@ -447,7 +465,7 @@ } return (dbCredentials != null) ? dbCredentials.trim() : null; - + } catch(SQLException e) { containerLog.error( sm.getString("dataSourceRealm.getPassword.exception", @@ -464,10 +482,10 @@ containerLog.error( sm.getString("dataSourceRealm.getPassword.exception", username), e); - + } } - + return null; } @@ -511,7 +529,7 @@ close(dbConnection); } } - + /** * Return the roles associated with the given user name * @param dbConnection The database connection to be used @@ -519,7 +537,7 @@ */ protected ArrayList getRoles(Connection dbConnection, String username) { - + if (allRolesMode != AllRolesMode.STRICT_MODE && !isRoleStoreDefined()) { // Using an authentication only configuration and no role store has // been defined so don't spend cycles looking @@ -529,12 +547,12 @@ ResultSet rs = null; PreparedStatement stmt = null; ArrayList list = null; - + try { stmt = roles(dbConnection, username); rs = stmt.executeQuery(); list = new ArrayList(); - + while (rs.next()) { String role = rs.getString(1); if (role != null) { @@ -560,7 +578,7 @@ username), e); } } - + return null; } @@ -584,7 +602,7 @@ return (credentials); } - + /** * Return a PreparedStatement configured to perform the SELECT required * to retrieve user roles for the specified username. @@ -597,7 +615,7 @@ private PreparedStatement roles(Connection dbConnection, String username) throws SQLException { - PreparedStatement roles = + PreparedStatement roles = dbConnection.prepareStatement(preparedRoles); roles.setString(1, username); @@ -643,7 +661,7 @@ temp.append(userNameCol); temp.append(" = ?"); preparedCredentials = temp.toString(); - + super.startInternal(); } } diff -Nru tomcat7-7.0.70/java/org/apache/catalina/realm/JDBCRealm.java tomcat7-7.0.72/java/org/apache/catalina/realm/JDBCRealm.java --- tomcat7-7.0.70/java/org/apache/catalina/realm/JDBCRealm.java 2014-01-27 14:53:14.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/realm/JDBCRealm.java 2016-08-30 23:00:35.000000000 +0000 @@ -395,16 +395,29 @@ public synchronized Principal authenticate(Connection dbConnection, String username, String credentials) { - // No user or no credentials // Can't possibly authenticate, don't bother the database then if (username == null || credentials == null) { + if (containerLog.isTraceEnabled()) + containerLog.trace(sm.getString("jdbcRealm.authenticateFailure", + username)); return null; } // Look up the user's credentials String dbCredentials = getPassword(username); + if (dbCredentials == null) { + // User was not found in the database. + // Waste a bit of time as not to reveal that the user does not exist. + compareCredentials(credentials, getClass().getName()); + + if (containerLog.isTraceEnabled()) + containerLog.trace(sm.getString("jdbcRealm.authenticateFailure", + username)); + return null; + } + // Validate the user's credentials boolean validated = compareCredentials(credentials, dbCredentials); @@ -416,14 +429,13 @@ if (containerLog.isTraceEnabled()) containerLog.trace(sm.getString("jdbcRealm.authenticateFailure", username)); - return (null); + return null; } ArrayList roles = getRoles(username); // Create and return a suitable Principal for this user return (new GenericPrincipal(username, credentials, roles)); - } diff -Nru tomcat7-7.0.70/java/org/apache/catalina/realm/JNDIRealm.java tomcat7-7.0.72/java/org/apache/catalina/realm/JNDIRealm.java --- tomcat7-7.0.70/java/org/apache/catalina/realm/JNDIRealm.java 2016-03-07 16:29:52.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/realm/JNDIRealm.java 2016-07-31 09:39:27.000000000 +0000 @@ -1998,18 +1998,8 @@ } // Perform the configured search and process the results - NamingEnumeration results = null; - boolean thisRoleSearchAsUser = isRoleSearchAsUser(); - try { - if (thisRoleSearchAsUser) { - userCredentialsAdd(context, dn, user.getPassword()); - } - results = context.search(base, filter, controls); - } finally { - if (thisRoleSearchAsUser) { - userCredentialsRemove(context); - } - } + NamingEnumeration results = searchAsUser(context, user, base, filter, controls, + isRoleSearchAsUser()); if (results == null) return list; // Should never happen, but just in case ... @@ -2060,7 +2050,8 @@ containerLog.trace("Perform a nested group search with base "+ roleBase + " and filter " + filter); } - results = context.search(roleBase, filter, controls); + results = searchAsUser(context, user, roleBase, filter, controls, + isRoleSearchAsUser()); try { while (results.hasMore()) { @@ -2096,6 +2087,45 @@ return list; } + /** + * Perform the search on the context as the {@code dn}, when + * {@code searchAsUser} is {@code true}, otherwise search the context with + * the default credentials. + * + * @param context + * context to search on + * @param user + * user to bind on + * @param base + * base to start the search from + * @param filter + * filter to use for the search + * @param controls + * controls to use for the search + * @param searchAsUser + * {@code true} when the search should be done as user, or + * {@code false} for using the default credentials + * @return enumeration with all found entries + * @throws NamingException + * if a directory server error occurs + */ + private NamingEnumeration searchAsUser(DirContext context, + User user, String base, String filter, + SearchControls controls, boolean searchAsUser) throws NamingException { + NamingEnumeration results; + try { + if (searchAsUser) { + userCredentialsAdd(context, user.getDN(), user.getPassword()); + } + results = context.search(base, filter, controls); + } finally { + if (searchAsUser) { + userCredentialsRemove(context); + } + } + return results; + } + /** * Return a String representing the value of the specified attribute. diff -Nru tomcat7-7.0.70/java/org/apache/catalina/realm/LockOutRealm.java tomcat7-7.0.72/java/org/apache/catalina/realm/LockOutRealm.java --- tomcat7-7.0.70/java/org/apache/catalina/realm/LockOutRealm.java 2014-01-27 14:53:14.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/realm/LockOutRealm.java 2016-06-20 18:36:29.000000000 +0000 @@ -139,23 +139,9 @@ String nonce, String nc, String cnonce, String qop, String realmName, String md5a2) { - if (isLocked(username)) { - // Trying to authenticate a locked user is an automatic failure - registerAuthFailure(username); - - log.warn(sm.getString("lockOutRealm.authLockedUser", username)); - return null; - } - - Principal authenticatedUser = super.authenticate(username, clientDigest, - nonce, nc, cnonce, qop, realmName, md5a2); - - if (authenticatedUser == null) { - registerAuthFailure(username); - } else { - registerAuthSuccess(username); - } - return authenticatedUser; + Principal authenticatedUser = super.authenticate(username, clientDigest, nonce, nc, cnonce, + qop, realmName, md5a2); + return filterLockedAccounts(username, authenticatedUser); } @@ -169,22 +155,8 @@ */ @Override public Principal authenticate(String username, String credentials) { - if (isLocked(username)) { - // Trying to authenticate a locked user is an automatic failure - registerAuthFailure(username); - - log.warn(sm.getString("lockOutRealm.authLockedUser", username)); - return null; - } - Principal authenticatedUser = super.authenticate(username, credentials); - - if (authenticatedUser == null) { - registerAuthFailure(username); - } else { - registerAuthSuccess(username); - } - return authenticatedUser; + return filterLockedAccounts(username, authenticatedUser); } @@ -202,22 +174,8 @@ username = certs[0].getSubjectDN().getName(); } - if (isLocked(username)) { - // Trying to authenticate a locked user is an automatic failure - registerAuthFailure(username); - - log.warn(sm.getString("lockOutRealm.authLockedUser", username)); - return null; - } - Principal authenticatedUser = super.authenticate(certs); - - if (authenticatedUser == null) { - registerAuthFailure(username); - } else { - registerAuthSuccess(username); - } - return authenticatedUser; + return filterLockedAccounts(username, authenticatedUser); } @@ -238,23 +196,9 @@ username = name.toString(); - if (isLocked(username)) { - // Trying to authenticate a locked user is an automatic failure - registerAuthFailure(username); + Principal authenticatedUser = super.authenticate(gssContext, storeCreds); - log.warn(sm.getString("lockOutRealm.authLockedUser", username)); - return null; - } - - Principal authenticatedUser = - super.authenticate(gssContext, storeCreds); - - if (authenticatedUser == null) { - registerAuthFailure(username); - } else { - registerAuthSuccess(username); - } - return authenticatedUser; + return filterLockedAccounts(username, authenticatedUser); } // Fail in all other cases @@ -262,6 +206,30 @@ } + /* + * Filters authenticated principals to ensure that null is + * returned for any user that is currently locked out. + */ + private Principal filterLockedAccounts(String username, Principal authenticatedUser) { + // Register all failed authentications + if (authenticatedUser == null) { + registerAuthFailure(username); + } + + if (isLocked(username)) { + // If the user is currently locked, authentication will always fail + log.warn(sm.getString("lockOutRealm.authLockedUser", username)); + return null; + } + + if (authenticatedUser != null) { + registerAuthSuccess(username); + } + + return authenticatedUser; + } + + /** * Unlock the specified username. This will remove all records of * authentication failures for this user. diff -Nru tomcat7-7.0.70/java/org/apache/catalina/realm/mbeans-descriptors.xml tomcat7-7.0.72/java/org/apache/catalina/realm/mbeans-descriptors.xml --- tomcat7-7.0.70/java/org/apache/catalina/realm/mbeans-descriptors.xml 2014-12-14 13:10:32.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/realm/mbeans-descriptors.xml 2016-08-10 07:25:25.000000000 +0000 @@ -91,7 +91,7 @@ diff -Nru tomcat7-7.0.70/java/org/apache/catalina/realm/MemoryRealm.java tomcat7-7.0.72/java/org/apache/catalina/realm/MemoryRealm.java --- tomcat7-7.0.70/java/org/apache/catalina/realm/MemoryRealm.java 2015-11-11 09:55:51.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/realm/MemoryRealm.java 2016-08-30 23:00:35.000000000 +0000 @@ -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. @@ -139,25 +139,37 @@ @Override public Principal authenticate(String username, String credentials) { + // No user or no credentials + // Can't possibly authenticate, don't bother the database then + if (username == null || credentials == null) { + if (log.isDebugEnabled()) + log.debug(sm.getString("memoryRealm.authenticateFailure", username)); + return null; + } + GenericPrincipal principal = principals.get(username); - boolean validated; - if (principal == null) { - validated = false; - } else { - validated = compareCredentials(credentials, principal.getPassword()); + if (principal == null || principal.getPassword() == null) { + // User was not found in the database or the password was null + // Waste a bit of time as not to reveal that the user does not exist. + compareCredentials(credentials, getClass().getName()); + + if (log.isDebugEnabled()) + log.debug(sm.getString("memoryRealm.authenticateFailure", username)); + return null; } + boolean validated = compareCredentials(credentials, principal.getPassword()); + if (validated) { if (log.isDebugEnabled()) log.debug(sm.getString("memoryRealm.authenticateSuccess", username)); - return (principal); + return principal; } else { if (log.isDebugEnabled()) log.debug(sm.getString("memoryRealm.authenticateFailure", username)); - return (null); + return null; } - } diff -Nru tomcat7-7.0.70/java/org/apache/catalina/realm/RealmBase.java tomcat7-7.0.72/java/org/apache/catalina/realm/RealmBase.java --- tomcat7-7.0.70/java/org/apache/catalina/realm/RealmBase.java 2016-06-15 11:10:48.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/realm/RealmBase.java 2016-08-30 23:00:35.000000000 +0000 @@ -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. @@ -161,7 +161,7 @@ * The all role mode. */ protected AllRolesMode allRolesMode = AllRolesMode.STRICT_MODE; - + /** * When processing users authenticated via the GSS-API, should any @@ -169,7 +169,7 @@ */ protected boolean stripRealmForGss = true; - + private int transportGuaranteeRedirectStatus = HttpServletResponse.SC_FOUND; @@ -401,23 +401,46 @@ */ @Override public Principal authenticate(String username, String credentials) { + // No user or no credentials + // Can't possibly authenticate, don't bother doing anything. + if(username == null || credentials == null) { + if (containerLog.isTraceEnabled()) { + containerLog.trace(sm.getString("realmBase.authenticateFailure", + username)); + } + return null; + } + // Look up the user's credentials String serverCredentials = getPassword(username); - boolean validated = compareCredentials(credentials, serverCredentials); - if (!validated) { + if (serverCredentials == null) { + // User was not found + // Waste a bit of time as not to reveal that the user does not exist. + compareCredentials(credentials, getClass().getName()); + if (containerLog.isTraceEnabled()) { containerLog.trace(sm.getString("realmBase.authenticateFailure", username)); } return null; } - if (containerLog.isTraceEnabled()) { - containerLog.trace(sm.getString("realmBase.authenticateSuccess", - username)); - } - return getPrincipal(username); + boolean validated = compareCredentials(credentials, serverCredentials); + + if (validated) { + if (containerLog.isTraceEnabled()) { + containerLog.trace(sm.getString("realmBase.authenticateSuccess", + username)); + } + return getPrincipal(username); + } else { + if (containerLog.isTraceEnabled()) { + containerLog.trace(sm.getString("realmBase.authenticateFailure", + username)); + } + return null; + } } @@ -468,13 +491,13 @@ } if (log.isDebugEnabled()) { - log.debug("Digest : " + clientDigest + " Username:" + username - + " ClientSigest:" + clientDigest + " nonce:" + nonce - + " nc:" + nc + " cnonce:" + cnonce + " qop:" + qop - + " realm:" + realm + "md5a2:" + md5a2 + log.debug("Digest : " + clientDigest + " Username:" + username + + " ClientSigest:" + clientDigest + " nonce:" + nonce + + " nc:" + nc + " cnonce:" + cnonce + " qop:" + qop + + " realm:" + realm + "md5a2:" + md5a2 + " Server digest:" + serverDigest); } - + if (serverDigest.equals(clientDigest)) { return getPrincipal(username); } @@ -519,7 +542,7 @@ } - + /** * {@inheritDoc} */ @@ -532,10 +555,10 @@ } catch (GSSException e) { log.warn(sm.getString("realmBase.gssNameFail"), e); } - + if (gssName!= null) { String name = gssName.toString(); - + if (isStripRealmForGss()) { int i = name.indexOf('@'); if (i > 0) { @@ -558,12 +581,12 @@ return getPrincipal(name, gssCredential); } } - + // Fail in all other cases return null; } - + protected boolean compareCredentials(String userCredentials, String serverCredentials) { @@ -668,13 +691,13 @@ if (uri == null) { uri = "/"; } - + String method = request.getMethod(); int i; boolean found = false; for (i = 0; i < constraints.length; i++) { SecurityCollection [] collection = constraints[i].findCollections(); - + // If collection is null, continue to avoid an NPE // See Bugzilla 30624 if ( collection == null) { @@ -689,7 +712,7 @@ for(int j=0; j < collection.length; j++){ String [] patterns = collection[j].findPatterns(); - + // If patterns is null, continue to avoid an NPE // See Bugzilla 30624 if ( patterns == null) { @@ -718,7 +741,7 @@ for (i = 0; i < constraints.length; i++) { SecurityCollection [] collection = constraints[i].findCollections(); - + // If collection is null, continue to avoid an NPE // See Bugzilla 30624 if ( collection == null) { @@ -744,9 +767,9 @@ int length = -1; for(int k=0; k < patterns.length; k++) { String pattern = patterns[k]; - if(pattern.startsWith("/") && pattern.endsWith("/*") && + if(pattern.startsWith("/") && pattern.endsWith("/*") && pattern.length() >= longest) { - + if(pattern.length() == 2) { matched = true; length = pattern.length(); @@ -791,7 +814,7 @@ if ( collection == null) { continue; } - + if (log.isDebugEnabled()) { log.debug(" Checking constraint '" + constraints[i] + "' against " + method + " " + uri + " --> " + @@ -842,7 +865,7 @@ for (i = 0; i < constraints.length; i++) { SecurityCollection [] collection = constraints[i].findCollections(); - + // If collection is null, continue to avoid an NPE // See Bugzilla 30624 if ( collection == null) { @@ -874,7 +897,7 @@ if(matched) { if(results == null) { results = new ArrayList(); - } + } results.add(constraints[i]); } } @@ -887,7 +910,7 @@ } return resultsToArray(results); } - + /** * Convert an ArrayList to a SecurityContraint []. */ @@ -901,7 +924,7 @@ return array; } - + /** * Perform access control based on the specified authorization constraint. * Return true if this constraint is satisfied and processing @@ -953,7 +976,7 @@ denyfromall = true; break; } - + if(log.isDebugEnabled()) log.debug("Passing all access"); status = true; @@ -991,7 +1014,7 @@ status = true; break; } - + // For AllRolesMode.STRICT_AUTH_ONLY_MODE there must be zero roles roles = request.getContext().findSecurityRoles(); if (roles.length == 0 && allRolesMode == AllRolesMode.STRICT_AUTH_ONLY_MODE) { @@ -1004,7 +1027,7 @@ } } } - + // Return a "Forbidden" message denying access to this resource if(!status) { response.sendError @@ -1014,8 +1037,8 @@ return status; } - - + + /** * Return true if the specified Principal has the specified * security role, within the context of this Realm; otherwise return @@ -1054,7 +1077,7 @@ } - + /** * Enforce any user data constraint required by the security constraint * guarding this request URI. Return true if this constraint @@ -1145,8 +1168,8 @@ return (false); } - - + + /** * Remove a property change listener from this component. * @@ -1172,7 +1195,7 @@ x509UsernameRetriever = createUsernameRetriever(x509UsernameRetrieverClassName); } - + /** * Prepare for the beginning of active use of the public methods of this * component and implement the requirements of @@ -1210,12 +1233,12 @@ protected void stopInternal() throws LifecycleException { setState(LifecycleState.STOPPING); - + // Clean up allocated resources md = null; } - - + + /** * Return a String representation of this component. */ @@ -1226,8 +1249,8 @@ sb.append(']'); return sb.toString(); } - - + + // ------------------------------------------------------ Protected Methods @@ -1249,7 +1272,7 @@ synchronized (this) { try { md.reset(); - + byte[] bytes = null; try { bytes = credentials.getBytes(getDigestCharset()); @@ -1289,7 +1312,7 @@ // Use pre-generated digest return getPassword(username); } - + String digestValue = username + ":" + realmName + ":" + getPassword(username); @@ -1335,7 +1358,7 @@ return(getPrincipal(username)); } - + /** * Return the Principal associated with the given user name. @@ -1346,11 +1369,11 @@ protected Principal getPrincipal(String username, GSSCredential gssCredential) { Principal p = getPrincipal(username); - + if (p instanceof GenericPrincipal) { ((GenericPrincipal) p).setGssCredential(gssCredential); } - + return p; } @@ -1377,7 +1400,7 @@ return null; } - + // --------------------------------------------------------- Static Methods @@ -1404,7 +1427,7 @@ if (encoding == null) { md.update(credentials.getBytes()); } else { - md.update(credentials.getBytes(encoding)); + md.update(credentials.getBytes(encoding)); } // Digest the credentials and return as hexadecimal @@ -1426,12 +1449,12 @@ String encoding = null; int firstCredentialArg = 2; - + if (args.length > 4 && args[2].equalsIgnoreCase("-e")) { encoding = args[3]; firstCredentialArg = 4; } - + if(args.length > firstCredentialArg && args[0].equalsIgnoreCase("-a")) { for(int i=firstCredentialArg; i < args.length ; i++){ System.out.print(args[i]+":"); @@ -1449,7 +1472,7 @@ @Override public String getObjectNameKeyProperties() { - + StringBuilder keyProperties = new StringBuilder("type=Realm"); keyProperties.append(getRealmSuffix()); keyProperties.append(MBeanUtils.getContainerKeyProperties(container)); @@ -1467,7 +1490,7 @@ public String getRealmPath() { return realmPath; } - + public void setRealmPath(String theRealmPath) { realmPath = theRealmPath; } @@ -1478,10 +1501,10 @@ protected static class AllRolesMode { - + private String name; /** Use the strict servlet spec interpretation which requires that the user - * have one of the web-app/security-role/role-name + * have one of the web-app/security-role/role-name */ public static final AllRolesMode STRICT_MODE = new AllRolesMode("strict"); /** Allow any authenticated user @@ -1490,7 +1513,7 @@ /** Allow any authenticated user only if there are no web-app/security-roles */ public static final AllRolesMode STRICT_AUTH_ONLY_MODE = new AllRolesMode("strictAuthOnly"); - + static AllRolesMode toMode(String name) { AllRolesMode mode; @@ -1504,12 +1527,12 @@ throw new IllegalStateException("Unknown mode, must be one of: strict, authOnly, strictAuthOnly"); return mode; } - + private AllRolesMode(String name) { this.name = name; } - + @Override public boolean equals(Object o) { diff -Nru tomcat7-7.0.70/java/org/apache/catalina/servlets/CGIServlet.java tomcat7-7.0.72/java/org/apache/catalina/servlets/CGIServlet.java --- tomcat7-7.0.70/java/org/apache/catalina/servlets/CGIServlet.java 2016-01-01 21:11:52.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/servlets/CGIServlet.java 2016-09-07 09:03:59.000000000 +0000 @@ -14,8 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - package org.apache.catalina.servlets; import java.io.BufferedOutputStream; @@ -34,8 +32,10 @@ import java.util.Hashtable; import java.util.List; import java.util.Locale; +import java.util.Map.Entry; import java.util.StringTokenizer; import java.util.Vector; +import java.util.regex.Pattern; import javax.servlet.RequestDispatcher; import javax.servlet.ServletConfig; @@ -49,6 +49,9 @@ import javax.servlet.http.HttpSession; import org.apache.catalina.util.IOTools; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.res.StringManager; /** @@ -117,7 +120,7 @@ * * CGI Specification:
derived from * http://cgi-spec.golux.com. - * A work-in-progress & expired Internet Draft. Note no actual RFC describing + * A work-in-progress & expired Internet Draft. Note no actual RFC describing * the CGI specification exists. Where the behavior of this servlet differs * from the specification cited above, it is either documented here, a bug, * or an instance where the specification cited differs from Best @@ -160,7 +163,6 @@ *

* [end excerpt] * - *

*

Implementation notes

*

* @@ -174,8 +176,8 @@ * and stderr (which should not be too hard). *
* If you find your cgi scripts are timing out receiving input, you can set - * the init parameter of your webapps' cgi-handling servlet - * to be + * the init parameter stderrTimeout of your webapps' cgi-handling + * servlet. *

*

* @@ -212,7 +214,6 @@ * http://cgi-spec.golux.com. * *

- *

*

TODO:

*
    *
  • Support for setting headers (for example, Location headers don't work) @@ -229,30 +230,22 @@ * not needed *
  • [add more to this TODO list] *
- *

* * @author Martin T Dengler [root@martindengler.com] * @author Amy Roh - * @since Tomcat 4.0 */ public final class CGIServlet extends HttpServlet { + private static final Log log = LogFactory.getLog(CGIServlet.class); + private static final StringManager sm = StringManager.getManager(CGIServlet.class); + + private static final String LINE_SEP = System.getProperty("line.separator"); + /* some vars below copied from Craig R. McClanahan's InvokerServlet */ private static final long serialVersionUID = 1L; /** - * The debugging detail level for this servlet. Useful values range from 0 - * to 5 where 0 means no logging and 5 means maximum logging. Values of 10 - * or more mean maximum logging and debug info added to the HTTP response. - * If an error occurs and debug is 10 or more the standard error page - * mechanism will be disabled and a response body with debug information - * will be produced. Note that any value of 10 or more has the same effect - * as a value of 10. - */ - private int debug = 0; - - /** * The CGI search path will start at * webAppRootDir + File.separator + cgiPathPrefix * (or webAppRootDir alone if cgiPathPrefix is @@ -276,11 +269,21 @@ */ private long stderrTimeout = 2000; + /** + * The regular expression used to select HTTP headers to be passed to the + * CGI process as environment variables. The name of the environment + * variable will be the name of the HTTP header converter to upper case, + * prefixed with HTTP_ and with all - characters + * converted to _. + */ + private Pattern envHttpHeadersPattern = Pattern.compile( + "ACCEPT[-0-9A-Z]*|CACHE-CONTROL|COOKIE|HOST|IF-[-0-9A-Z]*|REFERER|USER-AGENT"); + /** object used to ensure multiple threads don't try to expand same file */ - static Object expandFileLock = new Object(); + private static final Object expandFileLock = new Object(); /** the shell environment variables to be passed to the CGI script */ - Hashtable shellEnv = new Hashtable(); + private final Hashtable shellEnv = new Hashtable(); /** * Sets instance variables. @@ -303,8 +306,6 @@ super.init(config); // Set our properties from the initialization parameters - if (getServletConfig().getInitParameter("debug") != null) - debug = Integer.parseInt(getServletConfig().getInitParameter("debug")); cgiPathPrefix = getServletConfig().getInitParameter("cgiPathPrefix"); boolean passShellEnvironment = Boolean.parseBoolean(getServletConfig().getInitParameter("passShellEnvironment")); @@ -339,226 +340,174 @@ "stderrTimeout")); } + if (getServletConfig().getInitParameter("envHttpHeaders") != null) { + envHttpHeadersPattern = + Pattern.compile(getServletConfig().getInitParameter("envHttpHeaders")); + } } /** - * Prints out important Servlet API and container information + * Logs important Servlet API and container information. * *

* Copied from SnoopAllServlet by Craig R. McClanahan *

* - * @param out ServletOutputStream as target of the information + * @param out Unused * @param req HttpServletRequest object used as source of information - * @param res HttpServletResponse object currently not used but could - * provide future information + * @param res Unused * * @exception IOException if a write operation exception occurs * + * @deprecated Use {@link #printServletEnvironment(HttpServletRequest)}. + * This will be removed in Tomcat 8.5.X onwards */ + @Deprecated protected void printServletEnvironment(ServletOutputStream out, - HttpServletRequest req, HttpServletResponse res) - throws IOException { + HttpServletRequest req, HttpServletResponse res) throws IOException { + printServletEnvironment(req); + } + + /** + * Logs important Servlet API and container information. + * + *

+ * Based on SnoopAllServlet by Craig R. McClanahan + *

+ * + * @param req HttpServletRequest object used as source of information + * + * @exception IOException if a write operation exception occurs + */ + private void printServletEnvironment(HttpServletRequest req) throws IOException { // Document the properties from ServletRequest - out.println("

ServletRequest Properties

"); - out.println("
    "); + log.trace("ServletRequest Properties"); Enumeration attrs = req.getAttributeNames(); while (attrs.hasMoreElements()) { String attr = attrs.nextElement(); - out.println("
  • attribute " + attr + " = " + - req.getAttribute(attr)); + log.trace("Request Attribute: " + attr + ": [ " + req.getAttribute(attr) +"]"); } - out.println("
  • characterEncoding = " + - req.getCharacterEncoding()); - out.println("
  • contentLength = " + - req.getContentLength()); - out.println("
  • contentType = " + - req.getContentType()); + log.trace("Character Encoding: [" + req.getCharacterEncoding() + "]"); + log.trace("Content Length: [" + req.getContentLength() + "]"); + log.trace("Content Type: [" + req.getContentType() + "]"); Enumeration locales = req.getLocales(); while (locales.hasMoreElements()) { Locale locale = locales.nextElement(); - out.println("
  • locale = " + locale); + log.trace("Locale: [" +locale + "]"); } Enumeration params = req.getParameterNames(); while (params.hasMoreElements()) { String param = params.nextElement(); - String values[] = req.getParameterValues(param); - for (int i = 0; i < values.length; i++) - out.println("
  • parameter " + param + " = " + - values[i]); - } - out.println("
  • protocol = " + req.getProtocol()); - out.println("
  • remoteAddr = " + req.getRemoteAddr()); - out.println("
  • remoteHost = " + req.getRemoteHost()); - out.println("
  • scheme = " + req.getScheme()); - out.println("
  • secure = " + req.isSecure()); - out.println("
  • serverName = " + req.getServerName()); - out.println("
  • serverPort = " + req.getServerPort()); - out.println("
"); - out.println("
"); + for (String value : req.getParameterValues(param)) { + log.trace("Request Parameter: " + param + ": [" + value + "]"); + } + } + log.trace("Protocol: [" + req.getProtocol() + "]"); + log.trace("Remote Address: [" + req.getRemoteAddr() + "]"); + log.trace("Remote Host: [" + req.getRemoteHost() + "]"); + log.trace("Scheme: [" + req.getScheme() + "]"); + log.trace("Secure: [" + req.isSecure() + "]"); + log.trace("Server Name: [" + req.getServerName() + "]"); + log.trace("Server Port: [" + req.getServerPort() + "]"); // Document the properties from HttpServletRequest - out.println("

HttpServletRequest Properties

"); - out.println("
    "); - out.println("
  • authType = " + req.getAuthType()); - out.println("
  • contextPath = " + - req.getContextPath()); + log.trace("HttpServletRequest Properties"); + log.trace("Auth Type: [" + req.getAuthType() + "]"); + log.trace("Context Path: [" + req.getContextPath() + "]"); Cookie cookies[] = req.getCookies(); - if (cookies!=null) { - for (int i = 0; i < cookies.length; i++) - out.println("
  • cookie " + cookies[i].getName() +" = " +cookies[i].getValue()); + if (cookies != null) { + for (Cookie cookie : cookies) { + log.trace("Cookie: " + cookie.getName() + ": [" + cookie.getValue() + "]"); + } } Enumeration headers = req.getHeaderNames(); while (headers.hasMoreElements()) { String header = headers.nextElement(); - out.println("
  • header " + header + " = " + - req.getHeader(header)); + log.trace("HTTP Header: " + header + ": [" + req.getHeader(header) + "]"); } - out.println("
  • method = " + req.getMethod()); - out.println("
  • pathInfo = " - + req.getPathInfo()); - out.println("
  • pathTranslated = " + - req.getPathTranslated()); - out.println("
  • queryString = " + - req.getQueryString()); - out.println("
  • remoteUser = " + - req.getRemoteUser()); - out.println("
  • requestedSessionId = " + - req.getRequestedSessionId()); - out.println("
  • requestedSessionIdFromCookie = " + - req.isRequestedSessionIdFromCookie()); - out.println("
  • requestedSessionIdFromURL = " + - req.isRequestedSessionIdFromURL()); - out.println("
  • requestedSessionIdValid = " + - req.isRequestedSessionIdValid()); - out.println("
  • requestURI = " + - req.getRequestURI()); - out.println("
  • servletPath = " + - req.getServletPath()); - out.println("
  • userPrincipal = " + - req.getUserPrincipal()); - out.println("
"); - out.println("
"); - - // Document the servlet request attributes - out.println("

ServletRequest Attributes

"); - out.println("
    "); - attrs = req.getAttributeNames(); - while (attrs.hasMoreElements()) { - String attr = attrs.nextElement(); - out.println("
  • " + attr + " = " + - req.getAttribute(attr)); - } - out.println("
"); - out.println("
"); + log.trace("Method: [" + req.getMethod() + "]"); + log.trace("Path Info: [" + req.getPathInfo() + "]"); + log.trace("Path Translated: [" + req.getPathTranslated() + "]"); + log.trace("Query String: [" + req.getQueryString() + "]"); + log.trace("Remote User: [" + req.getRemoteUser() + "]"); + log.trace("Requested Session ID: [" + req.getRequestedSessionId() + "]"); + log.trace("Requested Session ID From Cookie: [" + + req.isRequestedSessionIdFromCookie() + "]"); + log.trace("Requested Session ID From URL: [" + req.isRequestedSessionIdFromURL() + "]"); + log.trace("Requested Session ID Valid: [" + req.isRequestedSessionIdValid() + "]"); + log.trace("Request URI: [" + req.getRequestURI() + "]"); + log.trace("Servlet Path: [" + req.getServletPath() + "]"); + log.trace("User Principal: [" + req.getUserPrincipal() + "]"); // Process the current session (if there is one) HttpSession session = req.getSession(false); if (session != null) { // Document the session properties - out.println("

HttpSession Properties

"); - out.println("
    "); - out.println("
  • id = " + - session.getId()); - out.println("
  • creationTime = " + - new Date(session.getCreationTime())); - out.println("
  • lastAccessedTime = " + - new Date(session.getLastAccessedTime())); - out.println("
  • maxInactiveInterval = " + - session.getMaxInactiveInterval()); - out.println("
"); - out.println("
"); + log.trace("HttpSession Properties"); + log.trace("ID: [" + session.getId() + "]"); + log.trace("Creation Time: [" + new Date(session.getCreationTime()) + "]"); + log.trace("Last Accessed Time: [" + new Date(session.getLastAccessedTime()) + "]"); + log.trace("Max Inactive Interval: [" + session.getMaxInactiveInterval() + "]"); // Document the session attributes - out.println("

HttpSession Attributes

"); - out.println("
    "); attrs = session.getAttributeNames(); while (attrs.hasMoreElements()) { String attr = attrs.nextElement(); - out.println("
  • " + attr + " = " + - session.getAttribute(attr)); + log.trace("Session Attribute: " + attr + ": [" + session.getAttribute(attr) + "]"); } - out.println("
"); - out.println("
"); - } // Document the servlet configuration properties - out.println("

ServletConfig Properties

"); - out.println("
    "); - out.println("
  • servletName = " + - getServletConfig().getServletName()); - out.println("
"); - out.println("
"); + log.trace("ServletConfig Properties"); + log.trace("Servlet Name: [" + getServletConfig().getServletName() + "]"); // Document the servlet configuration initialization parameters - out.println("

ServletConfig Initialization Parameters

"); - out.println("
    "); params = getServletConfig().getInitParameterNames(); while (params.hasMoreElements()) { String param = params.nextElement(); String value = getServletConfig().getInitParameter(param); - out.println("
  • " + param + " = " + value); + log.trace("Servlet Init Param: " + param + ": [" + value + "]"); } - out.println("
"); - out.println("
"); // Document the servlet context properties - out.println("

ServletContext Properties

"); - out.println("
    "); - out.println("
  • majorVersion = " + - getServletContext().getMajorVersion()); - out.println("
  • minorVersion = " + - getServletContext().getMinorVersion()); - out.println("
  • realPath('/') = " + - getServletContext().getRealPath("/")); - out.println("
  • serverInfo = " + - getServletContext().getServerInfo()); - out.println("
"); - out.println("
"); + log.trace("ServletContext Properties"); + log.trace("Major Version: [" + getServletContext().getMajorVersion() + "]"); + log.trace("Minor Version: [" + getServletContext().getMinorVersion() + "]"); + log.trace("Real Path for '/': [" + getServletContext().getRealPath("/") + "]"); + log.trace("Server Info: [" + getServletContext().getServerInfo() + "]"); // Document the servlet context initialization parameters - out.println("

ServletContext Initialization Parameters

"); - out.println("
    "); + log.trace("ServletContext Initialization Parameters"); params = getServletContext().getInitParameterNames(); while (params.hasMoreElements()) { String param = params.nextElement(); String value = getServletContext().getInitParameter(param); - out.println("
  • " + param + " = " + value); + log.trace("Servlet Context Init Param: " + param + ": [" + value + "]"); } - out.println("
"); - out.println("
"); // Document the servlet context attributes - out.println("

ServletContext Attributes

"); - out.println("
    "); + log.trace("ServletContext Attributes"); attrs = getServletContext().getAttributeNames(); while (attrs.hasMoreElements()) { String attr = attrs.nextElement(); - out.println("
  • " + attr + " = " + - getServletContext().getAttribute(attr)); + log.trace("Servlet Context Attribute: " + attr + + ": [" + getServletContext().getAttribute(attr) + "]"); } - out.println("
"); - out.println("
"); - - } /** - * Provides CGI Gateway service -- delegates to doGet + * Provides CGI Gateway service -- delegates to + * {@link #doGet(HttpServletRequest, HttpServletResponse)}. * * @param req HttpServletRequest passed in by servlet container * @param res HttpServletResponse passed in by servlet container * * @exception ServletException if a servlet-specific exception occurs * @exception IOException if a read/write exception occurs - * - * @see javax.servlet.http.HttpServlet - * */ @Override protected void doPost(HttpServletRequest req, HttpServletResponse res) @@ -568,16 +517,13 @@ /** - * Provides CGI Gateway service + * Provides CGI Gateway service. * * @param req HttpServletRequest passed in by servlet container * @param res HttpServletResponse passed in by servlet container * * @exception ServletException if a servlet-specific exception occurs * @exception IOException if a read/write exception occurs - * - * @see javax.servlet.http.HttpServlet - * */ @Override protected void doGet(HttpServletRequest req, HttpServletResponse res) @@ -590,75 +536,37 @@ cgiEnv.getEnvironment(), cgiEnv.getWorkingDirectory(), cgiEnv.getParameters()); - //if POST, we need to cgi.setInput - //REMIND: how does this interact with Servlet API 2.3's Filters?! + if ("POST".equals(req.getMethod())) { cgi.setInput(req.getInputStream()); } cgi.setResponse(res); cgi.run(); + } else { + res.sendError(404); } - if (!cgiEnv.isValid()) { - if (setStatus(res, 404)) { - return; - } - } - - if (debug >= 10) { - - ServletOutputStream out = res.getOutputStream(); - out.println("$Name$"); - out.println("$Header$

"); - - if (cgiEnv.isValid()) { - out.println(cgiEnv.toString()); - } else { - out.println("

"); - out.println("CGI script not found or not specified."); - out.println("

"); - out.println("

"); - out.println("Check the HttpServletRequest "); - out.println("pathInfo "); - out.println("property to see if it is what you meant "); - out.println("it to be. You must specify an existant "); - out.println("and executable file as part of the "); - out.println("path-info."); - out.println("

"); - out.println("

"); - out.println("For a good discussion of how CGI scripts "); - out.println("work and what their environment variables "); - out.println("mean, please visit the CGI "); - out.println("Specification page."); - out.println("

"); - + if (log.isTraceEnabled()) { + String[] cgiEnvLines = cgiEnv.toString().split(LINE_SEP); + for (String cgiEnvLine : cgiEnvLines) { + log.trace(cgiEnvLine); } - printServletEnvironment(out, req, res); - - out.println(""); - + printServletEnvironment(req); + } } - } //doGet - - - /* - * Behaviour depends on the status code and the value of debug. + /** + * Behaviour depends on the status code. * - * Status < 400 - Always calls setStatus. Returns false. CGI servlet will - * provide the response body. - * Status >= 400 - Depends on debug - * debug < 10 - Calls sendError(status), returns true. Standard error - * page mechanism will provide the response body. - * debug >= 10 - Calls setStatus(status), return false. CGI servlet will - * provide the response body. + * Status < 400 - Calls setStatus. Returns false. CGI servlet will provide + * the response body. + * Status >= 400 - Calls sendError(status), returns true. Standard error + * page mechanism will provide the response body. */ private boolean setStatus(HttpServletResponse response, int status) throws IOException { - - if (status >= HttpServletResponse.SC_BAD_REQUEST && debug < 10) { + if (status >= HttpServletResponse.SC_BAD_REQUEST) { response.sendError(status); return true; } else { @@ -671,11 +579,6 @@ /** * Encapsulates the CGI environment and rules to derive * that environment from the servlet container and request information. - * - *

- *

- * - * @since Tomcat 4.0 */ protected class CGIEnvironment { @@ -705,13 +608,13 @@ private String command = null; /** cgi command's desired working directory */ - private File workingDirectory = null; + private final File workingDirectory; /** cgi command's command line parameters */ - private ArrayList cmdLineParameters = new ArrayList(); + private final ArrayList cmdLineParameters = new ArrayList(); /** whether or not this object is valid or not */ - private boolean valid = false; + private final boolean valid; /** @@ -722,7 +625,7 @@ * the Servlet API * @param context ServletContext for information provided by the * Servlet API - * + * @throws IOException an IO error occurred */ protected CGIEnvironment(HttpServletRequest req, ServletContext context) throws IOException { @@ -734,6 +637,8 @@ if (this.valid) { workingDirectory = new File(command.substring(0, command.lastIndexOf(File.separator))); + } else { + workingDirectory = null; } } @@ -757,7 +662,7 @@ * * @param req HttpServletRequest for information provided by * the Servlet API - * @throws UnsupportedEncodingException + * @throws UnsupportedEncodingException Unknown encoding */ protected void setupFromRequest(HttpServletRequest req) throws UnsupportedEncodingException { @@ -817,6 +722,7 @@ * *

* Example URI: + *

*
 /servlet/cgigateway/dir1/realCGIscript/pathinfo1 
*
    *
  • path = $CATALINA_HOME/mywebapp/dir1/realCGIscript @@ -824,7 +730,6 @@ *
  • cgiName = /dir1/realCGIscript *
  • name = realCGIscript *
- *

*

* CGI search algorithm: search the real path below * <my-webapp-root> and find the first non-directory in @@ -874,8 +779,6 @@ * name - simple name (no directories) of the * cgi script, or null if no cgi was found * - * - * @since Tomcat 4.0 */ protected String[] findCGI(String pathInfo, String webAppRootDir, String contextPath, String servletPath, @@ -884,46 +787,41 @@ String name = null; String scriptname = null; - if ((webAppRootDir != null) - && (webAppRootDir.lastIndexOf(File.separator) == - (webAppRootDir.length() - 1))) { - //strip the trailing "/" from the webAppRootDir - webAppRootDir = - webAppRootDir.substring(0, (webAppRootDir.length() - 1)); + if (webAppRootDir != null && + webAppRootDir.lastIndexOf(File.separator) == (webAppRootDir.length() - 1)) { + //strip the trailing "/" from the webAppRootDir + webAppRootDir = webAppRootDir.substring(0, (webAppRootDir.length() - 1)); } if (cgiPathPrefix != null) { - webAppRootDir = webAppRootDir + File.separator - + cgiPathPrefix; + webAppRootDir = webAppRootDir + File.separator + cgiPathPrefix; } - if (debug >= 2) { - log("findCGI: path=" + pathInfo + ", " + webAppRootDir); + if (log.isDebugEnabled()) { + log.debug(sm.getString("cgiServlet.find.path", pathInfo, webAppRootDir)); } File currentLocation = new File(webAppRootDir); - StringTokenizer dirWalker = - new StringTokenizer(pathInfo, "/"); - if (debug >= 3) { - log("findCGI: currentLoc=" + currentLocation); + StringTokenizer dirWalker = new StringTokenizer(pathInfo, "/"); + if (log.isDebugEnabled()) { + log.debug(sm.getString("cgiServlet.find.location", + currentLocation.getAbsolutePath())); } StringBuilder cginameBuilder = new StringBuilder(); while (!currentLocation.isFile() && dirWalker.hasMoreElements()) { - if (debug >= 3) { - log("findCGI: currentLoc=" + currentLocation); - } String nextElement = (String) dirWalker.nextElement(); currentLocation = new File(currentLocation, nextElement); cginameBuilder.append('/').append(nextElement); + if (log.isDebugEnabled()) { + log.debug(sm.getString("cgiServlet.find.location", + currentLocation.getAbsolutePath())); + } } String cginame = cginameBuilder.toString(); if (!currentLocation.isFile()) { return new String[] { null, null, null, null }; } - if (debug >= 2) { - log("findCGI: FOUND cgi at " + currentLocation); - } path = currentLocation.getAbsolutePath(); name = currentLocation.getName(); @@ -936,9 +834,8 @@ scriptname = scriptname + cginame; } - if (debug >= 1) { - log("findCGI calc: name=" + name + ", path=" + path - + ", scriptname=" + scriptname + ", cginame=" + cginame); + if (log.isDebugEnabled()) { + log.debug(sm.getString("cgiServlet.find.found", name, path, scriptname, cginame)); } return new String[] { path, scriptname, cginame, name }; } @@ -952,6 +849,7 @@ * * @return true if environment was set OK, false if there * was a problem and no environment was set + * @throws IOException an IO error occurred */ protected boolean setCGIEnvironment(HttpServletRequest req) throws IOException { @@ -1106,12 +1004,8 @@ //REMIND: rewrite multiple headers as if received as single //REMIND: change character set //REMIND: I forgot what the previous REMIND means - if ("AUTHORIZATION".equalsIgnoreCase(header) || - "PROXY_AUTHORIZATION".equalsIgnoreCase(header)) { - //NOOP per CGI specification section 11.2 - } else { - envp.put("HTTP_" + header.replace('-', '_'), - req.getHeader(header)); + if (envHttpHeadersPattern.matcher(header).matches()) { + envp.put("HTTP_" + header.replace('-', '_'), req.getHeader(header)); } } @@ -1161,10 +1055,8 @@ if (is == null) { // didn't find anything, give up now - if (debug >= 2) { - log("expandCGIScript: source '" + srcPath + "' not found"); - } - return; + log.warn(sm.getString("cgiServlet.expandNotFound", srcPath)); + return; } File f = new File(destPath.toString()); @@ -1172,21 +1064,16 @@ try { is.close(); } catch (IOException e) { - log("Could not close is", e); + log.warn(sm.getString("cgiServlet.expandCloseFail", srcPath), e); } // Don't need to expand if it already exists return; } // create directories - String dirPath = destPath.toString().substring( - 0,destPath.toString().lastIndexOf('/')); - File dir = new File(dirPath); + File dir = f.getParentFile(); if (!dir.mkdirs() && !dir.isDirectory()) { - if (debug >= 2) { - log("expandCGIScript: failed to create directories for '" + - dir.getAbsolutePath() + "'"); - } + log.warn(sm.getString("cgiServlet.expandCreateDirFail", dir.getAbsolutePath())); return; } @@ -1210,20 +1097,20 @@ try { is.close(); } catch (IOException e) { - log("Could not close is.", e); + log.warn(sm.getString("cgiServlet.expandError"), e); } fos.close(); } - if (debug >= 2) { - log("expandCGIScript: expanded '" + srcPath + "' to '" + destPath + "'"); + if (log.isDebugEnabled()) { + log.debug(sm.getString("cgiServlet.expandOk", srcPath, destPath)); } } } catch (IOException ioe) { + log.warn(sm.getString("cgiServlet.expandFail", srcPath, destPath), ioe); // delete in case file is corrupted if (f.exists()) { - if (!f.delete() && debug >= 2) { - log("expandCGIScript: failed to delete '" + - f.getAbsolutePath() + "'"); + if (!f.delete()) { + log.warn(sm.getString("cgiServlet.expandDeleteFail", f.getAbsolutePath())); } } } @@ -1231,65 +1118,67 @@ /** - * Print important CGI environment information in a easy-to-read HTML - * table - * - * @return HTML string containing CGI environment info + * Returns important CGI environment information in a multi-line text + * format. * + * @return CGI environment info */ @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append(""); - - sb.append(""); - - sb.append(""); - - sb.append(""); + sb.append("CGIEnvironment Info:"); + sb.append(LINE_SEP); if (isValid()) { - Enumeration envk = env.keys(); - while (envk.hasMoreElements()) { - String s = envk.nextElement(); - sb.append(""); + sb.append("Validity: [true]"); + sb.append(LINE_SEP); + + sb.append("Environment values:"); + sb.append(LINE_SEP); + for (Entry entry : env.entrySet()) { + sb.append(" "); + sb.append(entry.getKey()); + sb.append(": ["); + sb.append(blanksToString(entry.getValue(), "will be set to blank")); + sb.append("]"); + sb.append(LINE_SEP); } - } - sb.append(""); + sb.append("Derived Command :["); + sb.append(nullsToBlanks(command)); + sb.append("]"); + sb.append(LINE_SEP); - sb.append(""); - sb.append(""); + sb.append("Working Directory: ["); + if (workingDirectory != null) { + sb.append(workingDirectory.toString()); + } + sb.append("]"); + sb.append(LINE_SEP); - sb.append(""); - - sb.append("
"); - sb.append("CGIEnvironment Info
Debug Level"); - sb.append(debug); - sb.append("
Validity:"); - sb.append(isValid()); - sb.append("
"); - sb.append(s); - sb.append(""); - sb.append(blanksToString(env.get(s), - "[will be set to blank]")); - sb.append("

Derived Command"); - sb.append(nullsToBlanks(command)); - sb.append("
Working Directory"); - if (workingDirectory != null) { - sb.append(workingDirectory.toString()); - } - sb.append("
Command Line Params"); - for (int i=0; i < cmdLineParameters.size(); i++) { - String param = cmdLineParameters.get(i); - sb.append("

"); - sb.append(param); - sb.append("

"); + sb.append("Command Line Params:"); + sb.append(LINE_SEP); + for (String param : cmdLineParameters) { + sb.append(" ["); + sb.append(param); + sb.append("]"); + sb.append(LINE_SEP); + } + } else { + sb.append("Validity: [false]"); + sb.append(LINE_SEP); + sb.append("CGI script not found or not specified."); + sb.append(LINE_SEP); + sb.append("Check the HttpServletRequest pathInfo property to see if it is what "); + sb.append(LINE_SEP); + sb.append("you meant it to be. You must specify an existant and executable file "); + sb.append(LINE_SEP); + sb.append("as part of the path-info."); + sb.append(LINE_SEP); } - sb.append("

end."); return sb.toString(); } @@ -1421,16 +1310,16 @@ protected class CGIRunner { /** script/command to be executed */ - private String command = null; + private final String command; /** environment used when invoking the cgi script */ - private Hashtable env = null; + private final Hashtable env; /** working directory used when invoking the cgi script */ - private File wd = null; + private final File wd; /** command line parameters to be passed to the invoked script */ - private ArrayList params = null; + private final ArrayList params; /** stdin to be passed to cgi script */ private InputStream stdin = null; @@ -1467,7 +1356,7 @@ /** - * Checks & sets ready status + * Checks and sets ready status */ protected void updateReadyStatus() { if (command != null @@ -1550,6 +1439,7 @@ * *

* This implements the following CGI specification recommedations: + *

*
    *
  • Servers SHOULD provide the "query" component of * the script-URI as command-line arguments to scripts if it @@ -1589,7 +1479,6 @@ * container's implementation of the Servlet API methods. *
* - *

* * @exception IOException if problems during reading/writing occur * @@ -1603,12 +1492,11 @@ */ if (!isReady()) { - throw new IOException(this.getClass().getName() - + ": not ready to run."); + throw new IOException(this.getClass().getName() + ": not ready to run."); } - if (debug >= 1 ) { - log("runCGI(envp=[" + env + "], command=" + command + ")"); + if (log.isDebugEnabled()) { + log.debug("envp: [" + env + "], command: [" + command + "]"); } if ((command.indexOf(File.separator + "." + File.separator) >= 0) @@ -1671,7 +1559,7 @@ errReaderThread = new Thread() { @Override public void run () { - sendToLog(stdErrRdr) ; + sendToLog(stdErrRdr); } }; errReaderThread.start(); @@ -1691,10 +1579,9 @@ try { //set headers String line = null; - while (((line = cgiHeaderReader.readLine()) != null) - && !("".equals(line))) { - if (debug >= 2) { - log("runCGI: addHeader(\"" + line + "\")"); + while (((line = cgiHeaderReader.readLine()) != null) && !("".equals(line))) { + if (log.isTraceEnabled()) { + log.trace("addHeader(\"" + line + "\")"); } if (line.startsWith("HTTP")) { skipBody = setStatus(response, getSCFromHttpStatusLine(line)); @@ -1709,7 +1596,7 @@ response.addHeader(header , value); } } else { - log("runCGI: bad header line \"" + line + "\""); + log.info(sm.getString("cgiServlet.runBadHeader", line)); } } @@ -1721,9 +1608,8 @@ try { while (!skipBody && (bufRead = cgiOutput.read(bBuf)) != -1) { - if (debug >= 4) { - log("runCGI: output " + bufRead + - " bytes of data"); + if (log.isTraceEnabled()) { + log.trace("output " + bufRead + " bytes of data"); } out.write(bBuf, 0, bufRead); } @@ -1751,18 +1637,16 @@ } } //replacement for Process.waitFor() - } - catch (IOException e){ - log ("Caught exception " + e); + } catch (IOException e){ + log.warn(sm.getString("cgiServlet.runFail"), e); throw e; - } - finally{ + } finally { // Close the header reader if (cgiHeaderReader != null) { try { cgiHeaderReader.close(); } catch (IOException ioe) { - log ("Exception closing header reader " + ioe); + log.warn(sm.getString("cgiServlet.runHeaderReaderFail"), ioe); } } // Close the output stream if used @@ -1770,7 +1654,7 @@ try { cgiOutput.close(); } catch (IOException ioe) { - log ("Exception closing output stream " + ioe); + log.warn(sm.getString("cgiServlet.runOutputStreamFail"), ioe); } } // Make sure the error stream reader has finished @@ -1778,11 +1662,7 @@ try { errReaderThread.join(stderrTimeout); } catch (InterruptedException e) { - log ("Interupted waiting for stderr reader thread"); - } - } - if (debug > 4) { - log ("Running finally block"); + log.warn(sm.getString("cgiServlet.runReaderInterupt")); } } if (proc != null){ proc.destroy(); @@ -1803,7 +1683,7 @@ if (statusStart < 1 || line.length() < statusStart + 3) { // Not a valid HTTP Status-Line - log ("runCGI: invalid HTTP Status-Line:" + line); + log.warn(sm.getString("cgiServlet.runInvalidStatus", line)); return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; } @@ -1814,7 +1694,7 @@ statusCode = Integer.parseInt(status); } catch (NumberFormatException nfe) { // Not a valid status code - log ("runCGI: invalid status code:" + status); + log.warn(sm.getString("cgiServlet.runInvalidStatus", status)); return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; } @@ -1832,7 +1712,7 @@ private int getSCFromCGIStatusHeader(String value) { if (value.length() < 3) { // Not a valid status value - log ("runCGI: invalid status value:" + value); + log.warn(sm.getString("cgiServlet.runInvalidStatus", value)); return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; } @@ -1843,7 +1723,7 @@ statusCode = Integer.parseInt(status); } catch (NumberFormatException nfe) { // Not a valid status code - log ("runCGI: invalid status code:" + status); + log.warn(sm.getString("cgiServlet.runInvalidStatus", status)); return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; } @@ -1855,20 +1735,20 @@ int lineCount = 0 ; try { while ((line = rdr.readLine()) != null) { - log("runCGI (stderr):" + line) ; + log.warn(sm.getString("cgiServlet.runStdErr", line)); lineCount++ ; } } catch (IOException e) { - log("sendToLog error", e) ; + log.warn(sm.getString("cgiServlet.runStdErrFail"), e); } finally { try { - rdr.close() ; - } catch (IOException ce) { - log("sendToLog error", ce) ; + rdr.close(); + } catch (IOException e) { + log.warn(sm.getString("cgiServlet.runStdErrFail"), e); } } - if ( lineCount > 0 && debug > 2) { - log("runCGI: " + lineCount + " lines received on stderr") ; + if (lineCount > 0) { + log.warn(sm.getString("cgiServlet.runStdErrCount", Integer.valueOf(lineCount))); } } } //class CGIRunner @@ -1885,7 +1765,7 @@ private static final int STATE_SECOND_CR = 3; private static final int STATE_HEADER_END = 4; - private InputStream input; + private final InputStream input; private int state; HTTPHeaderInputStream(InputStream theInput) { diff -Nru tomcat7-7.0.70/java/org/apache/catalina/servlets/DefaultServlet.java tomcat7-7.0.72/java/org/apache/catalina/servlets/DefaultServlet.java --- tomcat7-7.0.70/java/org/apache/catalina/servlets/DefaultServlet.java 2016-01-05 14:35:58.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/servlets/DefaultServlet.java 2016-07-26 15:42:37.000000000 +0000 @@ -763,7 +763,7 @@ * @param path Path which has to be rewritten */ protected String rewriteUrl(String path) { - return urlEncoder.encode( path ); + return urlEncoder.encode(path, "UTF-8"); } diff -Nru tomcat7-7.0.70/java/org/apache/catalina/servlets/LocalStrings.properties tomcat7-7.0.72/java/org/apache/catalina/servlets/LocalStrings.properties --- tomcat7-7.0.70/java/org/apache/catalina/servlets/LocalStrings.properties 2014-03-17 23:04:45.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/servlets/LocalStrings.properties 2016-08-15 20:24:31.000000000 +0000 @@ -13,6 +13,25 @@ # See the License for the specific language governing permissions and # limitations under the License. +cgiServlet.expandCloseFail=Failed to close input stream for script at path [{0}] +cgiServlet.expandCreateDirFail=Failed to create destination directory [{0}] for script expansion +cgiServlet.expandDeleteFail=Failed to delete file at [{0}] after IOException during expansion +cgiServlet.expandError=Failed to close input stream +cgiServlet.expandFail=Failed to expand script at path [{0}] to [{1}] +cgiServlet.expandNotFound=Unable to expand [{0}] as it could not be found +cgiServlet.expandOk=Expanded script at path [{0}] to [{1}] +cgiServlet.find.found=Found CGI: name [{0}], path [{1}], script name [{2}] and CGI name [{3}] +cgiServlet.find.location=Looking for a file at [{0}] +cgiServlet.find.path=CGI script requested at path [{0}] relative to CGI location [{1}] +cgiServlet.runBadHeader=Bad header line [{0}] +cgiServlet.runFail=I/O problems processing CGI +cgiServlet.runHeaderReaderFail=I/O problems closing header reader +cgiServlet.runInvalidStatus=Invalid status [{0}] +cgiServlet.runOutputStreamFail=I/O problems closing output stream +cgiServlet.runReaderInterupt=Interupted waiting for stderr reader thread +cgiServlet.runStdErr=stderr line: [{0}] +cgiServlet.runStdErrCount=Received [{0}] lines on stderr +cgiServlet.runStdErrFail=I/O problems with stderr defaultServlet.blockExternalEntity=Blocked access to external entity with publicId [{0}] and systemId [{0}] defaultServlet.blockExternalEntity2=Blocked access to external entity with name [{0}], publicId [{1}], baseURI [{2}] and systemId [{3}] defaultServlet.blockExternalSubset=Blocked access to external subset with name [{0}] and baseURI [{1}] diff -Nru tomcat7-7.0.70/java/org/apache/catalina/session/mbeans-descriptors.xml tomcat7-7.0.72/java/org/apache/catalina/session/mbeans-descriptors.xml --- tomcat7-7.0.70/java/org/apache/catalina/session/mbeans-descriptors.xml 2016-01-26 23:30:59.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/session/mbeans-descriptors.xml 2016-08-10 07:25:25.000000000 +0000 @@ -129,7 +129,7 @@ writeable="false"/> @@ -329,7 +329,7 @@ writeable="false"/> @@ -417,7 +417,7 @@ + returnType="boolean"> diff -Nru tomcat7-7.0.70/java/org/apache/catalina/ssi/SSIMediator.java tomcat7-7.0.72/java/org/apache/catalina/ssi/SSIMediator.java --- tomcat7-7.0.70/java/org/apache/catalina/ssi/SSIMediator.java 2014-01-27 14:53:14.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/ssi/SSIMediator.java 2016-07-26 15:42:37.000000000 +0000 @@ -296,7 +296,7 @@ protected String encode(String value, String encoding) { String retVal = null; if (encoding.equalsIgnoreCase("url")) { - retVal = urlEncoder.encode(value); + retVal = urlEncoder.encode(value, "UTF-8"); } else if (encoding.equalsIgnoreCase("none")) { retVal = value; } else if (encoding.equalsIgnoreCase("entity")) { diff -Nru tomcat7-7.0.70/java/org/apache/catalina/startup/ContextConfig.java tomcat7-7.0.72/java/org/apache/catalina/startup/ContextConfig.java --- tomcat7-7.0.70/java/org/apache/catalina/startup/ContextConfig.java 2016-04-04 08:24:24.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/startup/ContextConfig.java 2016-08-24 14:57:11.000000000 +0000 @@ -94,6 +94,7 @@ import org.apache.tomcat.util.bcel.classfile.JavaClass; import org.apache.tomcat.util.buf.UriUtil; import org.apache.tomcat.util.descriptor.DigesterFactory; +import org.apache.tomcat.util.descriptor.InputSourceUtil; import org.apache.tomcat.util.descriptor.XmlErrorHandler; import org.apache.tomcat.util.digester.Digester; import org.apache.tomcat.util.digester.RuleSet; @@ -526,7 +527,7 @@ boolean validation) { boolean blockExternal = context.getXmlBlockExternal(); - + webRuleSet = new WebRuleSet(false); webDigester = DigesterFactory.newDigester(validation, namespaceAware, webRuleSet, blockExternal); @@ -1301,6 +1302,9 @@ ((FileDirContext) binding.getObject()).getDocBase()); processAnnotationsFile(webInfClassDir, webXml, webXml.isMetadataComplete()); + } else if ("META-INF".equals(binding.getName())) { + // Skip the META-INF directory from any JARs that have been + // expanded in to WEB-INF/classes (sometimes IDEs do this). } else { String resource = "/WEB-INF/classes/" + binding.getName(); @@ -1463,6 +1467,8 @@ if (entry != null && entry.getGlobalTimeStamp() == globalTimeStamp && entry.getHostTimeStamp() == hostTimeStamp) { + InputSourceUtil.close(globalWebXml); + InputSourceUtil.close(hostWebXml); return entry.getWebXml(); } @@ -1874,15 +1880,7 @@ } finally { digester.reset(); ruleSet.recycle(); - - InputStream is = source.getByteStream(); - if (is != null) { - try { - is.close(); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - } - } + InputSourceUtil.close(source); } } @@ -2157,7 +2155,7 @@ return; if ((javaClass.getAccessFlags() & - org.apache.tomcat.util.bcel.Constants.ACC_ANNOTATION) > 0) { + org.apache.tomcat.util.bcel.Const.ACC_ANNOTATION) > 0) { // Skip annotations. return; } @@ -2665,7 +2663,7 @@ public FragmentJarScannerCallback(boolean parseRequired) { this.parseRequired = parseRequired; } - + @Override public void scan(JarURLConnection jarConn) throws IOException { diff -Nru tomcat7-7.0.70/java/org/apache/catalina/startup/FailedContext.java tomcat7-7.0.72/java/org/apache/catalina/startup/FailedContext.java --- tomcat7-7.0.70/java/org/apache/catalina/startup/FailedContext.java 2015-12-01 13:07:02.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/startup/FailedContext.java 2016-07-27 15:58:11.000000000 +0000 @@ -732,4 +732,9 @@ public void setUseRelativeRedirects(boolean useRelativeRedirects) { /* NO-OP */ } @Override public boolean getUseRelativeRedirects() { return true; } + + @Override + public void setDispatchersUseEncodedPaths(boolean dispatchersUseEncodedPaths) { /* NO-OP */ } + @Override + public boolean getDispatchersUseEncodedPaths() { return true; } } \ No newline at end of file diff -Nru tomcat7-7.0.70/java/org/apache/catalina/tribes/group/RpcChannel.java tomcat7-7.0.72/java/org/apache/catalina/tribes/group/RpcChannel.java --- tomcat7-7.0.70/java/org/apache/catalina/tribes/group/RpcChannel.java 2014-08-27 15:49:27.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/tribes/group/RpcChannel.java 2016-06-20 02:43:05.000000000 +0000 @@ -35,7 +35,7 @@ * A channel to handle RPC messaging * @author Filip Hanik */ -public class RpcChannel implements ChannelListener{ +public class RpcChannel implements ChannelListener { private static final Log log = LogFactory.getLog(RpcChannel.class); public static final int FIRST_REPLY = 1; @@ -98,7 +98,7 @@ } } catch ( InterruptedException ix ) { Thread.currentThread().interrupt(); - }finally { + } finally { responseMap.remove(key); } return collector.getResponses(); @@ -127,7 +127,7 @@ } }//synchronized }//end if - } else{ + } else { boolean finished = false; final ExtendedRpcCallback excallback = (callback instanceof ExtendedRpcCallback)?((ExtendedRpcCallback)callback) : null; boolean asyncReply = ((replyMessageOptions & Channel.SEND_OPTIONS_ASYNCHRONOUS) == Channel.SEND_OPTIONS_ASYNCHRONOUS); @@ -157,7 +157,7 @@ channel.send(new Member[] {sender}, rmsg,replyMessageOptions & ~Channel.SEND_OPTIONS_SYNCHRONIZED_ACK); } finished = true; - }catch ( Exception x ) { + } catch ( Exception x ) { if (excallback != null && !asyncReply) { excallback.replyFailed(rmsg.message, reply, sender, x); } else { @@ -185,7 +185,7 @@ if ( msg instanceof RpcMessage ) { RpcMessage rmsg = (RpcMessage)msg; return Arrays.equals(rmsg.rpcId,rpcId); - }else return false; + } else return false; } public Channel getChannel() { @@ -255,7 +255,7 @@ this(key, options, destcnt, 0); } - public void addResponse(Serializable message, Member sender){ + public void addResponse(Serializable message, Member sender) { Response resp = new Response(sender,message); responses.add(resp); } diff -Nru tomcat7-7.0.70/java/org/apache/catalina/tribes/membership/mbeans-descriptors.xml tomcat7-7.0.72/java/org/apache/catalina/tribes/membership/mbeans-descriptors.xml --- tomcat7-7.0.70/java/org/apache/catalina/tribes/membership/mbeans-descriptors.xml 2016-04-05 07:40:25.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/tribes/membership/mbeans-descriptors.xml 2016-08-10 07:25:25.000000000 +0000 @@ -35,7 +35,7 @@ type="java.lang.String"/> timeout ) { + log.warn("Member[" + member + "] in the Map[" + mapname + + "] has timed-out in the ping processing."); memberDisappeared(member); } } @@ -620,7 +622,8 @@ mapmsg.deserialize(getExternalLoaders()); if (mapmsg.getMsgType() == MapMessage.MSG_START) { mapMemberAdded(mapmsg.getPrimary()); - } else if (mapmsg.getMsgType() == MapMessage.MSG_INIT) { + } else if (mapmsg.getMsgType() == MapMessage.MSG_INIT + || mapmsg.getMsgType() == MapMessage.MSG_PING) { memberAlive(mapmsg.getPrimary()); } else { // other messages are ignored. diff -Nru tomcat7-7.0.70/java/org/apache/catalina/tribes/util/Arrays.java tomcat7-7.0.72/java/org/apache/catalina/tribes/util/Arrays.java --- tomcat7-7.0.70/java/org/apache/catalina/tribes/util/Arrays.java 2014-01-27 14:09:29.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/tribes/util/Arrays.java 2016-06-20 02:30:29.000000000 +0000 @@ -188,9 +188,6 @@ int idx = indexOf(member,members)+1; if (idx >= members.length ) idx = ((members.length>0)?0:-1); -//System.out.println("Next index:"+idx); -//System.out.println("Member:"+member.getName()); -//System.out.println("Members:"+toNameString(members)); return idx; } diff -Nru tomcat7-7.0.70/java/org/apache/catalina/util/LifecycleBase.java tomcat7-7.0.72/java/org/apache/catalina/util/LifecycleBase.java --- tomcat7-7.0.70/java/org/apache/catalina/util/LifecycleBase.java 2016-01-26 22:59:59.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/util/LifecycleBase.java 2016-08-23 19:39:46.000000000 +0000 @@ -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. @@ -35,7 +35,7 @@ public abstract class LifecycleBase implements Lifecycle { private static Log log = LogFactory.getLog(LifecycleBase.class); - + private static StringManager sm = StringManager.getManager("org.apache.catalina.util"); @@ -79,10 +79,10 @@ lifecycle.removeLifecycleListener(listener); } - + /** * Allow sub classes to fire {@link Lifecycle} events. - * + * * @param type Event type * @param data Data associated with event. */ @@ -90,48 +90,47 @@ lifecycle.fireLifecycleEvent(type, data); } - + @Override public final synchronized void init() throws LifecycleException { if (!state.equals(LifecycleState.NEW)) { invalidTransition(Lifecycle.BEFORE_INIT_EVENT); } - setStateInternal(LifecycleState.INITIALIZING, null, false); try { + setStateInternal(LifecycleState.INITIALIZING, null, false); initInternal(); + setStateInternal(LifecycleState.INITIALIZED, null, false); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); setStateInternal(LifecycleState.FAILED, null, false); throw new LifecycleException( sm.getString("lifecycleBase.initFail",toString()), t); } - - setStateInternal(LifecycleState.INITIALIZED, null, false); } - - + + protected abstract void initInternal() throws LifecycleException; - + /** * {@inheritDoc} */ @Override public final synchronized void start() throws LifecycleException { - + if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) || LifecycleState.STARTED.equals(state)) { - + if (log.isDebugEnabled()) { Exception e = new LifecycleException(); log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e); } else if (log.isInfoEnabled()) { log.info(sm.getString("lifecycleBase.alreadyStarted", toString())); } - + return; } - + if (state.equals(LifecycleState.NEW)) { init(); } else if (state.equals(LifecycleState.FAILED)) { @@ -141,10 +140,20 @@ invalidTransition(Lifecycle.BEFORE_START_EVENT); } - setStateInternal(LifecycleState.STARTING_PREP, null, false); - try { + setStateInternal(LifecycleState.STARTING_PREP, null, false); startInternal(); + if (state.equals(LifecycleState.FAILED)) { + // This is a 'controlled' failure. The component put itself into the + // FAILED state so call stop() to complete the clean-up. + stop(); + } else if (!state.equals(LifecycleState.STARTING)) { + // Shouldn't be necessary but acts as a check that sub-classes are + // doing what they are supposed to. + invalidTransition(Lifecycle.AFTER_START_EVENT); + } else { + setStateInternal(LifecycleState.STARTED, null, false); + } } catch (Throwable t) { // This is an 'uncontrolled' failure so put the component into the // FAILED state and throw an exception. @@ -152,18 +161,6 @@ setStateInternal(LifecycleState.FAILED, null, false); throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t); } - - if (state.equals(LifecycleState.FAILED)) { - // This is a 'controlled' failure. The component put itself into the - // FAILED state so call stop() to complete the clean-up. - stop(); - } else if (!state.equals(LifecycleState.STARTING)) { - // Shouldn't be necessary but acts as a check that sub-classes are - // doing what they are supposed to. - invalidTransition(Lifecycle.AFTER_START_EVENT); - } else { - setStateInternal(LifecycleState.STARTED, null, false); - } } @@ -171,13 +168,13 @@ * Sub-classes must ensure that the state is changed to * {@link LifecycleState#STARTING} during the execution of this method. * Changing state will trigger the {@link Lifecycle#START_EVENT} event. - * + * * If a component fails to start it may either throw a * {@link LifecycleException} which will cause it's parent to fail to start * or it can place itself in the error state in which case {@link #stop()} * will be called on the failed component but the parent component will * continue to start normally. - * + * * @throws LifecycleException */ protected abstract void startInternal() throws LifecycleException; @@ -198,10 +195,10 @@ } else if (log.isInfoEnabled()) { log.info(sm.getString("lifecycleBase.alreadyStopped", toString())); } - + return; } - + if (state.equals(LifecycleState.NEW)) { state = LifecycleState.STOPPED; return; @@ -210,18 +207,26 @@ if (!state.equals(LifecycleState.STARTED) && !state.equals(LifecycleState.FAILED)) { invalidTransition(Lifecycle.BEFORE_STOP_EVENT); } - - if (state.equals(LifecycleState.FAILED)) { - // Don't transition to STOPPING_PREP as that would briefly mark the - // component as available but do ensure the BEFORE_STOP_EVENT is - // fired - fireLifecycleEvent(BEFORE_STOP_EVENT, null); - } else { - setStateInternal(LifecycleState.STOPPING_PREP, null, false); - } try { + if (state.equals(LifecycleState.FAILED)) { + // Don't transition to STOPPING_PREP as that would briefly mark the + // component as available but do ensure the BEFORE_STOP_EVENT is + // fired + fireLifecycleEvent(BEFORE_STOP_EVENT, null); + } else { + setStateInternal(LifecycleState.STOPPING_PREP, null, false); + } + stopInternal(); + + // Shouldn't be necessary but acts as a check that sub-classes are + // doing what they are supposed to. + if (!state.equals(LifecycleState.STOPPING) && !state.equals(LifecycleState.FAILED)) { + invalidTransition(Lifecycle.AFTER_STOP_EVENT); + } + + setStateInternal(LifecycleState.STOPPED, null, false); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); setStateInternal(LifecycleState.FAILED, null, false); @@ -231,17 +236,8 @@ // Complete stop process first setStateInternal(LifecycleState.STOPPED, null, false); destroy(); - return; } } - - // Shouldn't be necessary but acts as a check that sub-classes are - // doing what they are supposed to. - if (!state.equals(LifecycleState.STOPPING) && !state.equals(LifecycleState.FAILED)) { - invalidTransition(Lifecycle.AFTER_STOP_EVENT); - } - - setStateInternal(LifecycleState.STOPPED, null, false); } @@ -249,7 +245,7 @@ * Sub-classes must ensure that the state is changed to * {@link LifecycleState#STOPPING} during the execution of this method. * Changing state will trigger the {@link Lifecycle#STOP_EVENT} event. - * + * * @throws LifecycleException */ protected abstract void stopInternal() throws LifecycleException; @@ -280,10 +276,10 @@ // multiple calls are made to destroy() log.info(sm.getString("lifecycleBase.alreadyDestroyed", toString())); } - + return; } - + if (!state.equals(LifecycleState.STOPPED) && !state.equals(LifecycleState.FAILED) && !state.equals(LifecycleState.NEW) && @@ -291,23 +287,21 @@ invalidTransition(Lifecycle.BEFORE_DESTROY_EVENT); } - setStateInternal(LifecycleState.DESTROYING, null, false); - try { + setStateInternal(LifecycleState.DESTROYING, null, false); destroyInternal(); + setStateInternal(LifecycleState.DESTROYED, null, false); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); setStateInternal(LifecycleState.FAILED, null, false); throw new LifecycleException( sm.getString("lifecycleBase.destroyFail",toString()), t); } - - setStateInternal(LifecycleState.DESTROYED, null, false); } - - + + protected abstract void destroyInternal() throws LifecycleException; - + /** * {@inheritDoc} */ @@ -331,21 +325,21 @@ * Calling this method will automatically fire any associated * {@link Lifecycle} event. It will also check that any attempted state * transition is valid for a sub-class. - * + * * @param state The new state for this component */ protected synchronized void setState(LifecycleState state) throws LifecycleException { setStateInternal(state, null, true); } - - + + /** * Provides a mechanism for sub-classes to update the component state. * Calling this method will automatically fire any associated * {@link Lifecycle} event. It will also check that any attempted state * transition is valid for a sub-class. - * + * * @param state The new state for this component * @param data The data to pass to the associated {@link Lifecycle} event */ @@ -356,11 +350,11 @@ private synchronized void setStateInternal(LifecycleState state, Object data, boolean check) throws LifecycleException { - + if (log.isDebugEnabled()) { log.debug(sm.getString("lifecycleBase.setState", this, state)); } - + if (check) { // Must have been triggered by one of the abstract methods (assume // code in this class is correct) @@ -371,7 +365,7 @@ // a possible NPE further down the method return; } - + // Any method can transition to failed // startInternal() permits STARTING_PREP to STARTING // stopInternal() permits STOPPING_PREP to STOPPING and FAILED to @@ -387,7 +381,7 @@ invalidTransition(state.name()); } } - + this.state = state; String lifecycleEvent = state.getLifecycleEvent(); if (lifecycleEvent != null) { diff -Nru tomcat7-7.0.70/java/org/apache/catalina/util/URLEncoder.java tomcat7-7.0.72/java/org/apache/catalina/util/URLEncoder.java --- tomcat7-7.0.70/java/org/apache/catalina/util/URLEncoder.java 2016-04-26 13:39:16.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/util/URLEncoder.java 2016-07-26 15:42:37.000000000 +0000 @@ -67,13 +67,37 @@ safeCharacters.set( c ); } - public String encode( String path ) { + + /** + * URL encodes the provided path using UTF-8. + * + * @param path The path to encode + * + * @return The encoded path + * + * @deprecated Use {@link #encode(String, String)} + */ + @Deprecated + public String encode(String path) { + return encode(path, "UTF-8"); + } + + + /** + * URL encodes the provided path using the given encoding. + * + * @param path The path to encode + * @param encoding The encoding to use to convert the path to bytes + * + * @return The encoded path + */ + public String encode(String path, String encoding) { int maxBytesPerChar = 10; StringBuilder rewrittenPath = new StringBuilder(path.length()); ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar); OutputStreamWriter writer = null; try { - writer = new OutputStreamWriter(buf, "UTF8"); + writer = new OutputStreamWriter(buf, encoding); } catch (Exception e) { e.printStackTrace(); writer = new OutputStreamWriter(buf); diff -Nru tomcat7-7.0.70/java/org/apache/catalina/valves/LocalStrings.properties tomcat7-7.0.72/java/org/apache/catalina/valves/LocalStrings.properties --- tomcat7-7.0.70/java/org/apache/catalina/valves/LocalStrings.properties 2015-03-30 09:01:48.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/valves/LocalStrings.properties 2016-06-16 13:02:31.000000000 +0000 @@ -51,6 +51,7 @@ # Request filter valve - RemoteAddrValve, RemoteHostValve requestFilterValve.configInvalid=One or more invalid configuration settings were provided for the Remote[Addr|Host]Valve which prevented the Valve and its parent containers from starting +requestFilterValve.deny=Denied request for [{0}] based on property [{1}] sslValve.certError=Failed to process certificate string [{0}] to create a java.security.cert.X509Certificate object sslValve.invalidProvider=The SSL provider specified on the connector associated with this request of [{0}] is invalid. The certificate data could not be processed. diff -Nru tomcat7-7.0.70/java/org/apache/catalina/valves/mbeans-descriptors.xml tomcat7-7.0.72/java/org/apache/catalina/valves/mbeans-descriptors.xml --- tomcat7-7.0.70/java/org/apache/catalina/valves/mbeans-descriptors.xml 2014-12-06 10:29:23.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/valves/mbeans-descriptors.xml 2016-08-10 07:25:25.000000000 +0000 @@ -348,7 +348,7 @@ writeable="false"/> process() method to perform the actual filtering. - * This method must be implemented by a concrete subclass. - * - * @param request The servlet request to be processed - * @param response The servlet response to be created - * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet error occurs - */ @Override - public void invoke(Request request, Response response) - throws IOException, ServletException { - + public void invoke(Request request, Response response) throws IOException, ServletException { String property; if (addConnectorPort) { property = request.getRequest().getRemoteAddr() + ";" + request.getConnector().getPort(); @@ -117,8 +104,12 @@ property = request.getRequest().getRemoteAddr(); } process(property, request, response); - } + + @Override + protected Log getLog() { + return log; + } } diff -Nru tomcat7-7.0.70/java/org/apache/catalina/valves/RemoteHostValve.java tomcat7-7.0.72/java/org/apache/catalina/valves/RemoteHostValve.java --- tomcat7-7.0.70/java/org/apache/catalina/valves/RemoteHostValve.java 2015-12-08 08:55:26.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/valves/RemoteHostValve.java 2016-06-16 13:02:31.000000000 +0000 @@ -16,14 +16,14 @@ */ package org.apache.catalina.valves; - import java.io.IOException; import javax.servlet.ServletException; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; - +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; /** * Concrete implementation of RequestFilterValve that filters @@ -32,14 +32,13 @@ * * @author Craig R. McClanahan */ +public final class RemoteHostValve extends RequestFilterValve { -public final class RemoteHostValve - extends RequestFilterValve { + private static final Log log = LogFactory.getLog(RemoteHostValve.class); // ----------------------------------------------------- Instance Variables - /** * The descriptive information related to this implementation. */ @@ -93,23 +92,8 @@ // --------------------------------------------------------- Public Methods - - /** - * Extract the desired request property, and pass it (along with the - * specified request and response objects) to the protected - * process() method to perform the actual filtering. - * This method must be implemented by a concrete subclass. - * - * @param request The servlet request to be processed - * @param response The servlet response to be created - * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet error occurs - */ @Override - public void invoke(Request request, Response response) - throws IOException, ServletException { - + public void invoke(Request request, Response response) throws IOException, ServletException { String property; if (addConnectorPort) { property = request.getRequest().getRemoteHost() + ";" + request.getConnector().getPort(); @@ -117,8 +101,11 @@ property = request.getRequest().getRemoteHost(); } process(property, request, response); - } + @Override + protected Log getLog() { + return log; + } } diff -Nru tomcat7-7.0.70/java/org/apache/catalina/valves/RequestFilterValve.java tomcat7-7.0.72/java/org/apache/catalina/valves/RequestFilterValve.java --- tomcat7-7.0.70/java/org/apache/catalina/valves/RequestFilterValve.java 2014-12-06 10:29:23.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/catalina/valves/RequestFilterValve.java 2016-06-16 13:02:31.000000000 +0000 @@ -27,6 +27,7 @@ import org.apache.catalina.LifecycleException; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; +import org.apache.juli.logging.Log; /** * Implementation of a Valve that performs filtering based on comparing the @@ -335,12 +336,19 @@ return; } + if (getLog().isDebugEnabled()) { + getLog().debug(sm.getString("requestFilterValve.deny", + request.getRequestURI(), property)); + } + // Deny this request denyRequest(request, response); - } + protected abstract Log getLog(); + + /** * Reject the request that was denied by this valve. *

If invalidAuthenticationWhenDeny is true diff -Nru tomcat7-7.0.70/java/org/apache/coyote/AbstractProcessor.java tomcat7-7.0.72/java/org/apache/coyote/AbstractProcessor.java --- tomcat7-7.0.70/java/org/apache/coyote/AbstractProcessor.java 2015-04-01 12:57:59.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/coyote/AbstractProcessor.java 2016-08-11 18:57:23.000000000 +0000 @@ -40,6 +40,8 @@ protected Request request; protected Response response; protected SocketWrapper socketWrapper = null; + private int maxCookieCount = 200; + /** * Error state for the request/response currently being processed. @@ -207,7 +209,18 @@ @Override public abstract SocketState upgradeDispatch() throws IOException; - /** + + public int getMaxCookieCount() { + return maxCookieCount; + } + + + public void setMaxCookieCount(int maxCookieCount) { + this.maxCookieCount = maxCookieCount; + } + + + /** * @deprecated Will be removed in Tomcat 8.0.x. */ @Deprecated diff -Nru tomcat7-7.0.70/java/org/apache/coyote/AbstractProtocol.java tomcat7-7.0.72/java/org/apache/coyote/AbstractProtocol.java --- tomcat7-7.0.70/java/org/apache/coyote/AbstractProtocol.java 2016-06-03 15:41:38.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/coyote/AbstractProtocol.java 2016-08-11 18:57:23.000000000 +0000 @@ -86,6 +86,13 @@ protected AbstractEndpoint endpoint = null; + /** + * The maximum number of cookies permitted for a request. Use a value less + * than zero for no limit. Defaults to 200. + */ + private int maxCookieCount = 200; + + // ----------------------------------------------- Generic property handling /** @@ -156,6 +163,16 @@ } + public int getMaxCookieCount() { + return maxCookieCount; + } + + + public void setMaxCookieCount(int maxCookieCount) { + this.maxCookieCount = maxCookieCount; + } + + // ---------------------- Properties that are passed through to the EndPoint @Override diff -Nru tomcat7-7.0.70/java/org/apache/coyote/ajp/AbstractAjpProcessor.java tomcat7-7.0.72/java/org/apache/coyote/ajp/AbstractAjpProcessor.java --- tomcat7-7.0.70/java/org/apache/coyote/ajp/AbstractAjpProcessor.java 2016-05-13 14:34:03.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/coyote/ajp/AbstractAjpProcessor.java 2016-08-11 18:57:23.000000000 +0000 @@ -766,6 +766,7 @@ // Set this every time in case limit has been changed via JMX headers.setLimit(endpoint.getMaxHeaderCount()); + request.getCookies().setLimit(getMaxCookieCount()); boolean contentLengthSet = false; int hCount = requestHeaderMessage.getInt(); diff -Nru tomcat7-7.0.70/java/org/apache/coyote/ajp/AjpAprProtocol.java tomcat7-7.0.72/java/org/apache/coyote/ajp/AjpAprProtocol.java --- tomcat7-7.0.70/java/org/apache/coyote/ajp/AjpAprProtocol.java 2016-01-27 11:42:26.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/coyote/ajp/AjpAprProtocol.java 2016-08-11 18:57:23.000000000 +0000 @@ -151,6 +151,7 @@ processor.setRequiredSecret(proto.requiredSecret); processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); processor.setClientCertProvider(proto.getClientCertProvider()); + processor.setMaxCookieCount(proto.getMaxCookieCount()); register(processor); return processor; } diff -Nru tomcat7-7.0.70/java/org/apache/coyote/ajp/AjpNioProtocol.java tomcat7-7.0.72/java/org/apache/coyote/ajp/AjpNioProtocol.java --- tomcat7-7.0.70/java/org/apache/coyote/ajp/AjpNioProtocol.java 2016-01-27 11:42:26.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/coyote/ajp/AjpNioProtocol.java 2016-08-11 18:57:23.000000000 +0000 @@ -181,6 +181,7 @@ processor.setRequiredSecret(proto.requiredSecret); processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); processor.setClientCertProvider(proto.getClientCertProvider()); + processor.setMaxCookieCount(proto.getMaxCookieCount()); register(processor); return processor; } diff -Nru tomcat7-7.0.70/java/org/apache/coyote/ajp/AjpProtocol.java tomcat7-7.0.72/java/org/apache/coyote/ajp/AjpProtocol.java --- tomcat7-7.0.70/java/org/apache/coyote/ajp/AjpProtocol.java 2016-01-27 11:42:26.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/coyote/ajp/AjpProtocol.java 2016-08-11 18:57:23.000000000 +0000 @@ -139,6 +139,7 @@ processor.setRequiredSecret(proto.requiredSecret); processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); processor.setClientCertProvider(proto.getClientCertProvider()); + processor.setMaxCookieCount(proto.getMaxCookieCount()); register(processor); return processor; } diff -Nru tomcat7-7.0.70/java/org/apache/coyote/AsyncStateMachine.java tomcat7-7.0.72/java/org/apache/coyote/AsyncStateMachine.java --- tomcat7-7.0.70/java/org/apache/coyote/AsyncStateMachine.java 2016-04-24 18:56:17.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/coyote/AsyncStateMachine.java 2016-09-05 14:33:45.000000000 +0000 @@ -26,32 +26,46 @@ /** * Manages the state transitions for async requests. - * + * *

  * The internal states that are used are:
- * DISPATCHED    - Standard request. Not in Async mode.
- * STARTING      - ServletRequest.startAsync() has been called but the
- *                 request in which that call was made has not finished
- *                 processing.
- * STARTED       - ServletRequest.startAsync() has been called and the
- *                 request in which that call was made has finished
- *                 processing.
- * MUST_COMPLETE - complete() has been called before the request in which
- *                 ServletRequest.startAsync() has finished. As soon as that
- *                 request finishes, the complete() will be processed.
- * COMPLETING    - The call to complete() was made once the request was in
- *                 the STARTED state. May or may not be triggered by a
- *                 container thread - depends if start(Runnable) was used
- * TIMING_OUT    - The async request has timed out and is waiting for a call
- *                 to complete(). If that isn't made, the error state will
- *                 entered.
- * MUST_DISPATCH - dispatch() has been called from a container thread. The
- *                 dispatch will be processed once processing control returns to
- *                 the container.
- * DISPATCHING   - dispatch() has been called from a non-container thread. The
- *                 dispatch will be processed as soon as a container thread is
- *                 available.
- * ERROR         - Something went wrong.
+ * DISPATCHED       - Standard request. Not in Async mode.
+ * STARTING         - ServletRequest.startAsync() has been called but the
+ *                    request in which that call was made has not finished
+ *                    processing.
+ * STARTED          - ServletRequest.startAsync() has been called and the
+ *                    request in which that call was made has finished
+ *                    processing.
+ * MUST_COMPLETE    - ServletRequest.startAsync() followed by complete() have
+ *                    been called during a single Servlet.service() method. The
+ *                    complete() will be processed as soon as the request
+ *                    finishes.
+ * COMPLETE_PENDING - ServletRequest.startAsync() has been called and before the
+ *                    request in which that call was had finished processing,
+ *                    complete() was called for a non-container thread. The
+ *                    complete() will be processed as soon as the request
+ *                    finishes. This is different to MUST_COMPLETE because of
+ *                    differences required to avoid race conditions during error
+ *                    handling.
+ * COMPLETING       - The call to complete() was made once the request was in
+ *                    the STARTED state. May or may not be triggered by a
+ *                    container thread - depends if start(Runnable) was used.
+ * TIMING_OUT       - The async request has timed out and is waiting for a call
+ *                    to complete(). If that isn't made, the error state will
+ *                    entered.
+ * MUST_DISPATCH    - ServletRequest.startAsync() followed by dispatch() have
+ *                    been called during a single Servlet.service() method. The
+ *                    dispatch() will be processed as soon as the request
+ *                    finishes.
+ * DISPATCH_PENDING - ServletRequest.startAsync() has been called and before the
+ *                    request in which that call was had finished processing,
+ *                    dispatch() was called for a non-container thread. The
+ *                    dispatch() will be processed as soon as the request
+ *                    finishes. This is different to MUST_DISPATCH because of
+ *                    differences required to avoid race conditions during error
+ *                    handling.
+ * DISPATCHING      - The dispatch is being processed.
+ * ERROR            - Something went wrong.
  *
  * |----------------->------|
  * |                       \|/
@@ -62,39 +76,48 @@
  * |   |    |                |                          |dispatch()
  * |   |    |                |                         \|/
  * |   |    |                |          |--|timeout()   |
- * |   |    |  postProcess() |          | \|/           |         auto
+ * |   |    |     post()     |          | \|/           |     post()
  * |   |    |    |---------- | -->DISPATCHED<---------- | --------------COMPLETING<-----|
- * |   |    |    |           |   /|\  |                 |                 | /|\         |
- * |   |    |    |    |--->- | ---|   |                 |                 |--|          |
- * |   |    ^    ^    |      |        |startAsync()     |               timeout()       |
- * |   |    |    |    |       \       |                 |                               |
- * |   |    |    |    |        \      |                 |                               |
- * |   |    |    |    |         \     |                 |                               |
- * |   |    |    |    |          \    |                 |                               |
- * |  \|/   |    |    |           \  \|/  postProcess() |                               |
- * | MUST_COMPLETE-<- | ----<------STARTING-->--------- | ------------|                 ^
- * |         /|\      |               |                 |             |      complete() |         
- * |          |       |               |                 |             |     /-----------|         
- * |          |       ^               |dispatch()       |             |    /                      
- * |          |       |               |                 |             |   /                       
- * |          |       |              \|/                |            \|/ /                        
- * |          |       |----<----MUST_DISPATCH----<------|          STARTED
- * |          |       |   auto              \                      /|  
- * |          |       |                      \                    / |   
- * ^          ^       ^                       \        dispatch()/  |    
- * |          |       |                        \                /   |    
- * |          |       |               |-------- \ -------------/    |auto
- * |          |       |               |          \                  |   
- * |          |       |               |           \                 |   
- * |          |       | auto         \|/           \               \|/  
- * |          |       |---<------DISPATCHING        \---------TIMING_OUT
- * |          |                                   dispatch()    |   |
+ * |   |    |    |           |   /|\/|\ |               |                | /|\ /|\      |
+ * |   |    |    |    |--->- | ---|  |  |startAsync()   |       timeout()|--|   |       |
+ * |   |    ^    ^    |      |       |  |               |                       |       |
+ * |   |    |    |    |   |-- \ -----|  |   complete()  |                       |post() |
+ * |   |    |    |    |   |    \        |     /-->----- | ---COMPLETE_PENDING->-|       |
+ * |   |    |    |    |   |     \       |    /          |                               |
+ * |   |    |    |    |   ^      \      |   /           |                               |
+ * |  \|/   |    |    |   |       \    \|/ /   post()   |                               |
+ * | MUST_COMPLETE-<- | - | --<----STARTING-->--------- | -------------|                ^
+ * |         /|\      |   |  complete()  | \            |              |     complete() |
+ * |          |       |   |              |  \           |    post()    |     /----------|
+ * |          |       ^   |    dispatch()|   \          |    |-----|   |    /
+ * |          |       |   |              |    \         |    |     |   |   /
+ * |          |       |   |             \|/    \        |    |    \|/ \|/ /
+ * |          |       |   |--<--MUST_DISPATCH-----<-----|    |--<--STARTED
+ * |          |       | dispatched() /|\   |     \                / |
+ * |          |       |               |    |      \              /  |
+ * |          |       |               |    |       \            /   |
+ * |          |       |               |    |post()  \           |   |
+ * ^          ^       |               |    |       \|/          |   |
+ * |          |       ^               |    |  DISPATCH_PENDING  |   |
+ * |          |       |               |    |  |post()           |   |
+ * |          |       |               |    |  |      |----------|   |
+ * |          |       |               |    |  |      |  dispatch()  |
+ * |          |       |               |    |  |      |              |
+ * |          |       |post()         |    |  |      |     timeout()|
+ * |          |       |dispatched()   |   \|/\|/    \|/             |
+ * |          |       |---<---------- | ---DISPATCHING              |
+ * |          |                       |     |    ^                  |
+ * |          |                       |     |----|                  |
+ * |          |                       |    timeout()                |
+ * |          |                       |                             |
+ * |          |                       |       dispatch()           \|/
+ * |          |                       |-----------<-----------TIMING_OUT
  * |          |                                                 |   |
  * |          |-------<----------------------------------<------|   |
- * |                              complete()                        |  
- * |                                                                |  
- * |<--------<-------------------<-------------------------------<--|  
- *                                 error()                             
+ * |                              complete()                        |
+ * |                                                                |
+ * |<--------<-------------------<-------------------------------<--|
+ *                                 error()
  * 
*/ public class AsyncStateMachine { @@ -106,39 +129,39 @@ StringManager.getManager(Constants.Package); private static enum AsyncState { - DISPATCHED (false, false, false, false, false), - STARTING (true, true, false, false, true), - STARTED (true, true, false, false, false), - MUST_COMPLETE(true, true, true, false, false), - COMPLETING (true, false, true, false, false), - TIMING_OUT (true, true, false, false, false), - MUST_DISPATCH(true, true, false, true, true), - DISPATCHING (true, false, false, true, false), - ERROR (true, true, false, false, false); - + DISPATCHED (false, false, false, false), + STARTING (true, true, false, false), + STARTED (true, true, false, false), + MUST_COMPLETE (true, true, true, false), + COMPLETE_PENDING(true, true, false, false), + COMPLETING (true, false, true, false), + TIMING_OUT (true, true, false, false), + MUST_DISPATCH (true, true, false, true), + DISPATCH_PENDING(true, true, false, false), + DISPATCHING (true, false, false, true), + ERROR (true, true, false, false); + private final boolean isAsync; private final boolean isStarted; private final boolean isCompleting; private final boolean isDispatching; - private final boolean pauseNonContainerThread; private AsyncState(boolean isAsync, boolean isStarted, boolean isCompleting, - boolean isDispatching, boolean pauseNonContainerThread) { + boolean isDispatching) { this.isAsync = isAsync; this.isStarted = isStarted; this.isCompleting = isCompleting; this.isDispatching = isDispatching; - this.pauseNonContainerThread = pauseNonContainerThread; } - + public boolean isAsync() { return isAsync; } - + public boolean isStarted() { return isStarted; } - + public boolean isDispatching() { return isDispatching; } @@ -146,19 +169,15 @@ public boolean isCompleting() { return isCompleting; } - - public boolean getPauseNonContainerThread() { - return pauseNonContainerThread; - } } - + private volatile AsyncState state = AsyncState.DISPATCHED; // Need this to fire listener on complete private AsyncContextCallback asyncCtxt = null; private Processor processor; - - + + public AsyncStateMachine(Processor processor) { this.processor = processor; } @@ -198,21 +217,20 @@ "asyncStart()", state)); } } - + /* * Async has been processed. Whether or not to enter a long poll depends on * current state. For example, as per SRV.2.3.3.3 can now process calls to * complete() or dispatch(). */ public synchronized SocketState asyncPostProcess() { - - // Unpause any non-container threads that may be waiting for this - // container thread to complete this method. Note because of the syncs - // those non-container threads won't start back up until until this - // method exits. - notifyAll(); - - if (state == AsyncState.STARTING) { + if (state == AsyncState.COMPLETE_PENDING) { + doComplete(); + return SocketState.ASYNC_END; + } else if (state == AsyncState.DISPATCH_PENDING) { + doDispatch(); + return SocketState.ASYNC_END; + } else if (state == AsyncState.STARTING) { state = AsyncState.STARTED; return SocketState.LONG; } else if (state == AsyncState.MUST_COMPLETE) { @@ -239,14 +257,23 @@ "asyncPostProcess()", state)); } } - + public synchronized boolean asyncComplete() { - pauseNonContainerThread(); + if (!ContainerThreadMarker.isContainerThread() && state == AsyncState.STARTING) { + state = AsyncState.COMPLETE_PENDING; + return false; + } else { + return doComplete(); + } + } + + + private synchronized boolean doComplete() { boolean doComplete = false; if (state == AsyncState.STARTING) { state = AsyncState.MUST_COMPLETE; - } else if (state == AsyncState.STARTED) { + } else if (state == AsyncState.STARTED || state == AsyncState.COMPLETE_PENDING) { state = AsyncState.COMPLETING; doComplete = true; } else if (state == AsyncState.TIMING_OUT || @@ -256,12 +283,12 @@ throw new IllegalStateException( sm.getString("asyncStateMachine.invalidAsyncState", "asyncComplete()", state)); - + } return doComplete; } - - + + public synchronized boolean asyncTimeout() { if (state == AsyncState.STARTED) { state = AsyncState.TIMING_OUT; @@ -278,10 +305,19 @@ "asyncTimeout()", state)); } } - - + + public synchronized boolean asyncDispatch() { - pauseNonContainerThread(); + if (!ContainerThreadMarker.isContainerThread() && state == AsyncState.STARTING) { + state = AsyncState.DISPATCH_PENDING; + return false; + } else { + return doDispatch(); + } + } + + + private synchronized boolean doDispatch() { boolean doDispatch = false; if (state == AsyncState.STARTING || state == AsyncState.TIMING_OUT || @@ -289,7 +325,7 @@ // In these three cases processing is on a container thread so no // need to transfer processing to a new container thread state = AsyncState.MUST_DISPATCH; - } else if (state == AsyncState.STARTED) { + } else if (state == AsyncState.STARTED || state == AsyncState.DISPATCH_PENDING) { // A dispatch is always required. // If on a non-container thread, need to get back onto a container // thread to complete the processing. @@ -305,8 +341,8 @@ } return doDispatch; } - - + + public synchronized void asyncDispatched() { if (state == AsyncState.DISPATCHING || state == AsyncState.MUST_DISPATCH) { @@ -317,8 +353,8 @@ "asyncDispatched()", state)); } } - - + + public synchronized boolean asyncError() { boolean doDispatch = false; if (state == AsyncState.STARTING || @@ -333,7 +369,7 @@ } return doDispatch; } - + public synchronized void asyncRun(Runnable runnable) { if (state == AsyncState.STARTING || state == AsyncState.STARTED) { // Execute the runnable using a container thread from the @@ -354,7 +390,7 @@ Thread.currentThread().setContextClassLoader( this.getClass().getClassLoader()); } - + processor.getExecutor().execute(runnable); } finally { if (Constants.IS_SECURITY_ENABLED) { @@ -372,8 +408,8 @@ } } - - + + public synchronized void recycle() { // Ensure in case of error that any non-container threads that have been // paused are unpaused. @@ -381,24 +417,4 @@ asyncCtxt = null; state = AsyncState.DISPATCHED; } - - - /* - * startAsync() has been called but the container thread where this was - * called has not completed processing. To avoid various race conditions - - * including several related to error page handling - pause this - * non-container thread until the container thread has finished processing. - * The non-container thread will be paused until the container thread - * completes asyncPostProcess(). - */ - private synchronized void pauseNonContainerThread() { - while (!ContainerThreadMarker.isContainerThread() && - state.getPauseNonContainerThread()) { - try { - wait(); - } catch (InterruptedException e) { - // TODO Log this? - } - } - } } diff -Nru tomcat7-7.0.70/java/org/apache/coyote/http11/AbstractHttp11Processor.java tomcat7-7.0.72/java/org/apache/coyote/http11/AbstractHttp11Processor.java --- tomcat7-7.0.70/java/org/apache/coyote/http11/AbstractHttp11Processor.java 2016-06-05 18:53:35.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/coyote/http11/AbstractHttp11Processor.java 2016-08-11 18:57:23.000000000 +0000 @@ -1007,6 +1007,7 @@ keptAlive = true; // Set this every time in case limit has been changed via JMX request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount()); + request.getCookies().setLimit(getMaxCookieCount()); // Currently only NIO will ever return false here if (!getInputBuffer().parseHeaders()) { // We've read part of the request, don't recycle it diff -Nru tomcat7-7.0.70/java/org/apache/coyote/http11/Http11AprProtocol.java tomcat7-7.0.72/java/org/apache/coyote/http11/Http11AprProtocol.java --- tomcat7-7.0.70/java/org/apache/coyote/http11/Http11AprProtocol.java 2015-03-13 11:11:26.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/coyote/http11/Http11AprProtocol.java 2016-08-11 18:57:23.000000000 +0000 @@ -317,6 +317,7 @@ processor.setMaxSavePostSize(proto.getMaxSavePostSize()); processor.setServer(proto.getServer()); processor.setClientCertProvider(proto.getClientCertProvider()); + processor.setMaxCookieCount(proto.getMaxCookieCount()); register(processor); return processor; } diff -Nru tomcat7-7.0.70/java/org/apache/coyote/http11/Http11NioProtocol.java tomcat7-7.0.72/java/org/apache/coyote/http11/Http11NioProtocol.java --- tomcat7-7.0.70/java/org/apache/coyote/http11/Http11NioProtocol.java 2016-04-11 16:12:04.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/coyote/http11/Http11NioProtocol.java 2016-08-11 18:57:23.000000000 +0000 @@ -281,6 +281,7 @@ processor.setSocketBuffer(proto.getSocketBuffer()); processor.setMaxSavePostSize(proto.getMaxSavePostSize()); processor.setServer(proto.getServer()); + processor.setMaxCookieCount(proto.getMaxCookieCount()); register(processor); return processor; } diff -Nru tomcat7-7.0.70/java/org/apache/coyote/http11/Http11Protocol.java tomcat7-7.0.72/java/org/apache/coyote/http11/Http11Protocol.java --- tomcat7-7.0.70/java/org/apache/coyote/http11/Http11Protocol.java 2015-03-13 11:11:26.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/coyote/http11/Http11Protocol.java 2016-08-11 18:57:23.000000000 +0000 @@ -182,6 +182,7 @@ processor.setServer(proto.getServer()); processor.setDisableKeepAlivePercentage( proto.getDisableKeepAlivePercentage()); + processor.setMaxCookieCount(proto.getMaxCookieCount()); register(processor); return processor; } diff -Nru tomcat7-7.0.70/java/org/apache/jasper/compiler/Generator.java tomcat7-7.0.72/java/org/apache/jasper/compiler/Generator.java --- tomcat7-7.0.70/java/org/apache/jasper/compiler/Generator.java 2015-11-02 13:23:54.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/compiler/Generator.java 2016-07-26 11:53:12.000000000 +0000 @@ -2335,6 +2335,10 @@ writeNewInstance(tagHandlerVar, tagHandlerClassName); } + // Wrap use of tag in try/finally to ensure clean-up takes place + out.printil("try {"); + out.pushIndent(); + // includes setting the context generateSetters(n, tagHandlerVar, handlerInfo, false); @@ -2495,18 +2499,6 @@ out.print(tagHandlerVar); out.println(".doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE) {"); out.pushIndent(); - if (!n.implementsTryCatchFinally()) { - if (isPoolingEnabled && !(n.implementsJspIdConsumer())) { - out.printin(n.getTagHandlerPoolName()); - out.print(".reuse("); - out.print(tagHandlerVar); - out.println(");"); - } else { - out.printin(tagHandlerVar); - out.println(".release();"); - writeDestroyInstance(tagHandlerVar); - } - } if (isTagFile || isFragment) { out.printil("throw new javax.servlet.jsp.SkipPageException();"); } else { @@ -2539,6 +2531,15 @@ out.println(".doFinally();"); } + if (n.implementsTryCatchFinally()) { + out.popIndent(); + out.printil("}"); + } + + // Ensure clean-up takes place + out.popIndent(); + out.printil("} finally {"); + out.pushIndent(); if (isPoolingEnabled && !(n.implementsJspIdConsumer())) { out.printin(n.getTagHandlerPoolName()); out.print(".reuse("); @@ -2549,11 +2550,8 @@ out.println(".release();"); writeDestroyInstance(tagHandlerVar); } - - if (n.implementsTryCatchFinally()) { - out.popIndent(); - out.printil("}"); - } + out.popIndent(); + out.printil("}"); // Declare and synchronize AT_END scripting variables (must do this // outside the try/catch/finally block) diff -Nru tomcat7-7.0.70/java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java tomcat7-7.0.72/java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java --- tomcat7-7.0.70/java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java 2015-11-06 23:18:22.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java 2016-08-02 12:31:19.000000000 +0000 @@ -32,7 +32,7 @@ import org.apache.jasper.Constants; import org.apache.jasper.JasperException; import org.apache.jasper.JspCompilationContext; -import org.apache.jasper.util.ExceptionUtils; +import org.apache.jasper.runtime.ExceptionUtils; import org.apache.jasper.xmlparser.ParserUtils; import org.apache.jasper.xmlparser.TreeNode; diff -Nru tomcat7-7.0.70/java/org/apache/jasper/compiler/JspReader.java tomcat7-7.0.72/java/org/apache/jasper/compiler/JspReader.java --- tomcat7-7.0.70/java/org/apache/jasper/compiler/JspReader.java 2015-12-18 14:10:56.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/compiler/JspReader.java 2016-08-02 12:31:19.000000000 +0000 @@ -27,7 +27,7 @@ import org.apache.jasper.JasperException; import org.apache.jasper.JspCompilationContext; -import org.apache.jasper.util.ExceptionUtils; +import org.apache.jasper.runtime.ExceptionUtils; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; diff -Nru tomcat7-7.0.70/java/org/apache/jasper/compiler/JspRuntimeContext.java tomcat7-7.0.72/java/org/apache/jasper/compiler/JspRuntimeContext.java --- tomcat7-7.0.70/java/org/apache/jasper/compiler/JspRuntimeContext.java 2016-04-27 11:14:52.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/compiler/JspRuntimeContext.java 2016-09-12 08:27:56.000000000 +0000 @@ -37,10 +37,10 @@ import org.apache.jasper.Constants; import org.apache.jasper.JspCompilationContext; import org.apache.jasper.Options; +import org.apache.jasper.runtime.ExceptionUtils; import org.apache.jasper.runtime.JspFactoryImpl; import org.apache.jasper.security.SecurityClassLoad; import org.apache.jasper.servlet.JspServletWrapper; -import org.apache.jasper.util.ExceptionUtils; import org.apache.jasper.util.FastRemovalDequeue; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; @@ -90,8 +90,6 @@ factory.getClass().getClassLoader().loadClass( basePackage + "runtime.JspRuntimeLibrary"); factory.getClass().getClassLoader().loadClass( basePackage + - "runtime.JspRuntimeLibrary$PrivilegedIntrospectHelper"); - factory.getClass().getClassLoader().loadClass( basePackage + "runtime.ServletResponseWrapperInclude"); factory.getClass().getClassLoader().loadClass( basePackage + "servlet.JspServletWrapper"); diff -Nru tomcat7-7.0.70/java/org/apache/jasper/compiler/Localizer.java tomcat7-7.0.72/java/org/apache/jasper/compiler/Localizer.java --- tomcat7-7.0.70/java/org/apache/jasper/compiler/Localizer.java 2014-01-27 13:35:02.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/compiler/Localizer.java 2016-08-02 12:31:19.000000000 +0000 @@ -21,7 +21,7 @@ import java.util.MissingResourceException; import java.util.ResourceBundle; -import org.apache.jasper.util.ExceptionUtils; +import org.apache.jasper.runtime.ExceptionUtils; /** * Class responsible for converting error codes to corresponding localized diff -Nru tomcat7-7.0.70/java/org/apache/jasper/compiler/ParserController.java tomcat7-7.0.72/java/org/apache/jasper/compiler/ParserController.java --- tomcat7-7.0.70/java/org/apache/jasper/compiler/ParserController.java 2015-11-06 23:18:22.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/compiler/ParserController.java 2016-08-02 12:31:19.000000000 +0000 @@ -25,7 +25,7 @@ import org.apache.jasper.JasperException; import org.apache.jasper.JspCompilationContext; -import org.apache.jasper.util.ExceptionUtils; +import org.apache.jasper.runtime.ExceptionUtils; import org.apache.jasper.xmlparser.XMLEncodingDetector; import org.xml.sax.Attributes; diff -Nru tomcat7-7.0.70/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java tomcat7-7.0.72/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java --- tomcat7-7.0.70/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java 2016-06-06 13:34:45.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java 2016-08-02 12:31:19.000000000 +0000 @@ -47,7 +47,7 @@ import org.apache.jasper.Constants; import org.apache.jasper.JasperException; import org.apache.jasper.JspCompilationContext; -import org.apache.jasper.util.ExceptionUtils; +import org.apache.jasper.runtime.ExceptionUtils; import org.apache.jasper.xmlparser.ParserUtils; import org.apache.jasper.xmlparser.TreeNode; import org.apache.juli.logging.Log; diff -Nru tomcat7-7.0.70/java/org/apache/jasper/compiler/TldLocationsCache.java tomcat7-7.0.72/java/org/apache/jasper/compiler/TldLocationsCache.java --- tomcat7-7.0.70/java/org/apache/jasper/compiler/TldLocationsCache.java 2016-01-02 17:37:54.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/compiler/TldLocationsCache.java 2016-08-02 12:31:19.000000000 +0000 @@ -32,7 +32,7 @@ import org.apache.jasper.Constants; import org.apache.jasper.JasperException; -import org.apache.jasper.util.ExceptionUtils; +import org.apache.jasper.runtime.ExceptionUtils; import org.apache.jasper.xmlparser.ParserUtils; import org.apache.jasper.xmlparser.TreeNode; import org.apache.juli.logging.Log; diff -Nru tomcat7-7.0.70/java/org/apache/jasper/EmbeddedServletOptions.java tomcat7-7.0.72/java/org/apache/jasper/EmbeddedServletOptions.java --- tomcat7-7.0.70/java/org/apache/jasper/EmbeddedServletOptions.java 2015-11-25 13:47:58.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/EmbeddedServletOptions.java 2016-08-30 22:45:07.000000000 +0000 @@ -650,6 +650,10 @@ * scratchdir */ String dir = config.getInitParameter("scratchdir"); + if (dir != null && Constants.IS_SECURITY_ENABLED) { + log.info(Localizer.getMessage("jsp.info.ignoreSetting", "scratchdir", dir)); + dir = null; + } if (dir != null) { scratchDir = new File(dir); } else { diff -Nru tomcat7-7.0.70/java/org/apache/jasper/JspCompilationContext.java tomcat7-7.0.72/java/org/apache/jasper/JspCompilationContext.java --- tomcat7-7.0.70/java/org/apache/jasper/JspCompilationContext.java 2015-04-23 12:59:53.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/JspCompilationContext.java 2016-08-31 11:29:27.000000000 +0000 @@ -111,7 +111,7 @@ this.baseURI = jspUri.substring(0, jspUri.lastIndexOf('/') + 1); // hack fix for resolveRelativeURI - if (baseURI == null) { + if (baseURI.isEmpty()) { baseURI = "/"; } else if (baseURI.charAt(0) != '/') { // strip the base slash since it will be combined with the diff -Nru tomcat7-7.0.70/java/org/apache/jasper/resources/LocalStrings.properties tomcat7-7.0.72/java/org/apache/jasper/resources/LocalStrings.properties --- tomcat7-7.0.70/java/org/apache/jasper/resources/LocalStrings.properties 2016-06-06 13:34:45.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/resources/LocalStrings.properties 2016-08-30 22:45:07.000000000 +0000 @@ -457,6 +457,7 @@ jsp.error.invalid.bean=The value for the useBean class attribute {0} is invalid. jsp.error.prefix.use_before_dcl=The prefix {0} specified in this tag directive has been previously used by an action in file {1} line {2}. jsp.error.lastModified=Unable to determine last modified date for file [{0}] +jsp.info.ignoreSetting=Ignored setting for [{0}] of [{1}] because a SecurityManager was enabled jsp.exception=An exception occurred processing JSP page {0} at line {1} @@ -473,6 +474,9 @@ jsp.error.page.conflict.trimdirectivewhitespaces=Page directive: illegal to have multiple occurrences of 'trimDirectiveWhitespaces' with different values (old: {0}, new: {1}) jsp.error.tag.conflict.trimdirectivewhitespaces=Tag directive: illegal to have multiple occurrences of 'trimDirectiveWhitespaces' with different values (old: {0}, new: {1}) +# JSP Servlet +jsp.error.servlet.destroy.failed=Exception during Servlet.destroy() for JSP page + # JarScanner jsp.warning.noJarScanner=Warning: No org.apache.tomcat.JarScanner set in ServletContext. Falling back to default JarScanner implementation. diff -Nru tomcat7-7.0.70/java/org/apache/jasper/runtime/ExceptionUtils.java tomcat7-7.0.72/java/org/apache/jasper/runtime/ExceptionUtils.java --- tomcat7-7.0.70/java/org/apache/jasper/runtime/ExceptionUtils.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/runtime/ExceptionUtils.java 2016-08-02 12:31:19.000000000 +0000 @@ -0,0 +1,60 @@ +/* + * 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.jasper.runtime; + +import java.lang.reflect.InvocationTargetException; + + +/** + * Utilities for handling Throwables and Exceptions. + */ +public class ExceptionUtils { + + /** + * Checks whether the supplied Throwable is one that needs to be + * rethrown and swallows all others. + * @param t the Throwable to check + */ + public static void handleThrowable(Throwable t) { + if (t instanceof ThreadDeath) { + throw (ThreadDeath) t; + } + if (t instanceof StackOverflowError) { + // Swallow silently - it should be recoverable + return; + } + if (t instanceof VirtualMachineError) { + throw (VirtualMachineError) t; + } + // All other instances of Throwable will be silently swallowed + } + + /** + * Checks whether the supplied Throwable is an instance of + * InvocationTargetException and returns the throwable that is + * wrapped by it, if there is any. + * + * @param t the Throwable to check + * @return t or t.getCause() + */ + public static Throwable unwrapInvocationTargetException(Throwable t) { + if (t instanceof InvocationTargetException && t.getCause() != null) { + return t.getCause(); + } + return t; + } +} diff -Nru tomcat7-7.0.70/java/org/apache/jasper/runtime/JspFactoryImpl.java tomcat7-7.0.72/java/org/apache/jasper/runtime/JspFactoryImpl.java --- tomcat7-7.0.70/java/org/apache/jasper/runtime/JspFactoryImpl.java 2015-11-02 13:32:14.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/runtime/JspFactoryImpl.java 2016-08-02 12:31:19.000000000 +0000 @@ -29,7 +29,6 @@ import javax.servlet.jsp.PageContext; import org.apache.jasper.Constants; -import org.apache.jasper.util.ExceptionUtils; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; diff -Nru tomcat7-7.0.70/java/org/apache/jasper/runtime/JspRuntimeLibrary.java tomcat7-7.0.72/java/org/apache/jasper/runtime/JspRuntimeLibrary.java --- tomcat7-7.0.70/java/org/apache/jasper/runtime/JspRuntimeLibrary.java 2015-12-28 01:12:43.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/runtime/JspRuntimeLibrary.java 2016-08-02 12:31:19.000000000 +0000 @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.jasper.runtime; import java.beans.PropertyEditor; @@ -23,9 +22,6 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.lang.reflect.Method; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import java.util.Enumeration; import javax.servlet.RequestDispatcher; @@ -37,10 +33,8 @@ import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.BodyContent; -import org.apache.jasper.Constants; import org.apache.jasper.JasperException; import org.apache.jasper.compiler.Localizer; -import org.apache.jasper.util.ExceptionUtils; /** * Bunch of util methods that are used by code generated for useBean, @@ -56,36 +50,6 @@ */ public class JspRuntimeLibrary { - protected static class PrivilegedIntrospectHelper - implements PrivilegedExceptionAction { - - private Object bean; - private String prop; - private String value; - private ServletRequest request; - private String param; - private boolean ignoreMethodNF; - - PrivilegedIntrospectHelper(Object bean, String prop, - String value, ServletRequest request, - String param, boolean ignoreMethodNF) - { - this.bean = bean; - this.prop = prop; - this.value = value; - this.request = request; - this.param = param; - this.ignoreMethodNF = ignoreMethodNF; - } - - @Override - public Void run() throws JasperException { - internalIntrospecthelper( - bean,prop,value,request,param,ignoreMethodNF); - return null; - } - } - /** * Returns the value of the javax.servlet.error.exception request * attribute value, if present, otherwise the value of the @@ -290,29 +254,7 @@ public static void introspecthelper(Object bean, String prop, String value, ServletRequest request, String param, boolean ignoreMethodNF) - throws JasperException - { - if( Constants.IS_SECURITY_ENABLED ) { - try { - PrivilegedIntrospectHelper dp = - new PrivilegedIntrospectHelper( - bean,prop,value,request,param,ignoreMethodNF); - AccessController.doPrivileged(dp); - } catch( PrivilegedActionException pe) { - Exception e = pe.getException(); - throw (JasperException)e; - } - } else { - internalIntrospecthelper( - bean,prop,value,request,param,ignoreMethodNF); - } - } - - private static void internalIntrospecthelper(Object bean, String prop, - String value, ServletRequest request, - String param, boolean ignoreMethodNF) - throws JasperException - { + throws JasperException { Method method = null; Class type = null; Class propertyEditorClass = null; diff -Nru tomcat7-7.0.70/java/org/apache/jasper/runtime/TagHandlerPool.java tomcat7-7.0.72/java/org/apache/jasper/runtime/TagHandlerPool.java --- tomcat7-7.0.70/java/org/apache/jasper/runtime/TagHandlerPool.java 2014-01-27 13:35:02.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/runtime/TagHandlerPool.java 2016-08-02 12:31:19.000000000 +0000 @@ -22,7 +22,6 @@ import javax.servlet.jsp.tagext.Tag; import org.apache.jasper.Constants; -import org.apache.jasper.util.ExceptionUtils; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.InstanceManager; @@ -144,15 +143,7 @@ } } // There is no need for other threads to wait for us to release - handler.release(); - try { - instanceManager.destroyInstance(handler); - } catch (Exception e) { - Throwable t = ExceptionUtils.unwrapInvocationTargetException(e); - ExceptionUtils.handleThrowable(t); - log.warn("Error processing preDestroy on tag instance of " + - handler.getClass().getName(), t); - } + doRelease(handler); } /** @@ -161,19 +152,30 @@ */ public synchronized void release() { for (int i = current; i >= 0; i--) { - Tag handler = handlers[i]; + doRelease(handlers[i]); + } + } + + + private void doRelease(Tag handler) { + try { handler.release(); - try { - instanceManager.destroyInstance(handler); - } catch (Exception e) { - Throwable t = ExceptionUtils.unwrapInvocationTargetException(e); - ExceptionUtils.handleThrowable(t); - log.warn("Error processing preDestroy on tag instance of " - + handler.getClass().getName(), t); - } + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.warn("Error processing release on tag instance of " + + handler.getClass().getName(), t); + } + try { + instanceManager.destroyInstance(handler); + } catch (Exception e) { + Throwable t = ExceptionUtils.unwrapInvocationTargetException(e); + ExceptionUtils.handleThrowable(t); + log.warn("Error processing preDestroy on tag instance of " + + handler.getClass().getName(), t); } } + protected static String getOption(ServletConfig config, String name, String defaultV) { if (config == null) diff -Nru tomcat7-7.0.70/java/org/apache/jasper/security/SecurityClassLoad.java tomcat7-7.0.72/java/org/apache/jasper/security/SecurityClassLoad.java --- tomcat7-7.0.70/java/org/apache/jasper/security/SecurityClassLoad.java 2015-02-13 13:54:16.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/security/SecurityClassLoad.java 2016-09-12 08:27:56.000000000 +0000 @@ -46,8 +46,6 @@ loader.loadClass( basePackage + "runtime.JspRuntimeLibrary"); - loader.loadClass( basePackage + - "runtime.JspRuntimeLibrary$PrivilegedIntrospectHelper"); loader.loadClass( basePackage + "runtime.ServletResponseWrapperInclude"); diff -Nru tomcat7-7.0.70/java/org/apache/jasper/servlet/JspCServletContext.java tomcat7-7.0.72/java/org/apache/jasper/servlet/JspCServletContext.java --- tomcat7-7.0.70/java/org/apache/jasper/servlet/JspCServletContext.java 2016-04-27 11:14:52.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/servlet/JspCServletContext.java 2016-08-02 12:31:19.000000000 +0000 @@ -48,7 +48,7 @@ import javax.servlet.descriptor.JspConfigDescriptor; import org.apache.jasper.JasperException; -import org.apache.jasper.util.ExceptionUtils; +import org.apache.jasper.runtime.ExceptionUtils; /** diff -Nru tomcat7-7.0.70/java/org/apache/jasper/servlet/JspServlet.java tomcat7-7.0.72/java/org/apache/jasper/servlet/JspServlet.java --- tomcat7-7.0.70/java/org/apache/jasper/servlet/JspServlet.java 2015-11-03 09:18:06.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/servlet/JspServlet.java 2016-08-30 22:45:07.000000000 +0000 @@ -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. @@ -38,8 +38,8 @@ import org.apache.jasper.Options; import org.apache.jasper.compiler.JspRuntimeContext; import org.apache.jasper.compiler.Localizer; +import org.apache.jasper.runtime.ExceptionUtils; import org.apache.jasper.security.SecurityUtil; -import org.apache.jasper.util.ExceptionUtils; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.PeriodicEventListener; @@ -71,8 +71,8 @@ private ServletConfig config; private transient Options options; private transient JspRuntimeContext rctxt; - //jspFile for a jsp configured explicitly as a servlet, in environments where this configuration is - //translated into an init-param for this servlet. + // jspFile for a jsp configured explicitly as a servlet, in environments where this + // configuration is translated into an init-param for this servlet. private String jspFile; @@ -81,26 +81,26 @@ */ @Override public void init(ServletConfig config) throws ServletException { - + super.init(config); this.config = config; this.context = config.getServletContext(); - + // Initialize the JSP Runtime Context // Check for a custom Options implementation - String engineOptionsName = - config.getInitParameter("engineOptionsClass"); + String engineOptionsName = config.getInitParameter("engineOptionsClass"); + if (Constants.IS_SECURITY_ENABLED && engineOptionsName != null) { + log.info(Localizer.getMessage( + "jsp.info.ignoreSetting", "engineOptionsClass", engineOptionsName)); + engineOptionsName = null; + } if (engineOptionsName != null) { // Instantiate the indicated Options implementation try { - ClassLoader loader = Thread.currentThread() - .getContextClassLoader(); - Class engineOptionsClass = - loader.loadClass(engineOptionsName); - Class[] ctorSig = - { ServletConfig.class, ServletContext.class }; - Constructor ctor = - engineOptionsClass.getConstructor(ctorSig); + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + Class engineOptionsClass = loader.loadClass(engineOptionsName); + Class[] ctorSig = { ServletConfig.class, ServletContext.class }; + Constructor ctor = engineOptionsClass.getConstructor(ctorSig); Object[] args = { config, context }; options = (Options) ctor.newInstance(args); } catch (Throwable e) { @@ -274,11 +274,11 @@ } } - + @SuppressWarnings("deprecation") // Use of JSP_FILE to be removed in 9.0.x @Override - public void service (HttpServletRequest request, + public void service (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //jspFile may be configured as an init-param for this servlet instance @@ -313,7 +313,7 @@ } } else { /* - * Requested JSP has not been the target of a + * Requested JSP has not been the target of a * RequestDispatcher.include(). Reconstruct its path from the * request's getServletPath() and getPathInfo() */ @@ -325,7 +325,7 @@ } } - if (log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("JspEngine --> " + jspUri); log.debug("\t ServletPath: " + request.getServletPath()); log.debug("\t PathInfo: " + request.getPathInfo()); diff -Nru tomcat7-7.0.70/java/org/apache/jasper/servlet/JspServletWrapper.java tomcat7-7.0.72/java/org/apache/jasper/servlet/JspServletWrapper.java --- tomcat7-7.0.70/java/org/apache/jasper/servlet/JspServletWrapper.java 2015-04-23 12:59:53.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/servlet/JspServletWrapper.java 2016-08-02 12:31:19.000000000 +0000 @@ -42,9 +42,9 @@ import org.apache.jasper.compiler.JavacErrorDetail; import org.apache.jasper.compiler.JspRuntimeContext; import org.apache.jasper.compiler.Localizer; +import org.apache.jasper.runtime.ExceptionUtils; import org.apache.jasper.runtime.InstanceManagerFactory; import org.apache.jasper.runtime.JspSourceDependent; -import org.apache.jasper.util.ExceptionUtils; import org.apache.jasper.util.FastRemovalDequeue; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; @@ -482,7 +482,12 @@ public void destroy() { if (theServlet != null) { - theServlet.destroy(); + try { + theServlet.destroy(); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error(Localizer.getMessage("jsp.error.servlet.destroy.failed"), t); + } InstanceManager instanceManager = InstanceManagerFactory.getInstanceManager(config); try { instanceManager.destroyInstance(theServlet); diff -Nru tomcat7-7.0.70/java/org/apache/jasper/util/ExceptionUtils.java tomcat7-7.0.72/java/org/apache/jasper/util/ExceptionUtils.java --- tomcat7-7.0.70/java/org/apache/jasper/util/ExceptionUtils.java 2014-04-17 15:46:42.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/jasper/util/ExceptionUtils.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -/* - * 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.jasper.util; - -import java.lang.reflect.InvocationTargetException; - - -/** - * Utilities for handling Throwables and Exceptions. - */ -public class ExceptionUtils { - - /** - * Checks whether the supplied Throwable is one that needs to be - * rethrown and swallows all others. - * @param t the Throwable to check - */ - public static void handleThrowable(Throwable t) { - if (t instanceof ThreadDeath) { - throw (ThreadDeath) t; - } - if (t instanceof StackOverflowError) { - // Swallow silently - it should be recoverable - return; - } - if (t instanceof VirtualMachineError) { - throw (VirtualMachineError) t; - } - // All other instances of Throwable will be silently swallowed - } - - /** - * Checks whether the supplied Throwable is an instance of - * InvocationTargetException and returns the throwable that is - * wrapped by it, if there is any. - * - * @param t the Throwable to check - * @return t or t.getCause() - */ - public static Throwable unwrapInvocationTargetException(Throwable t) { - if (t instanceof InvocationTargetException && t.getCause() != null) { - return t.getCause(); - } - return t; - } -} diff -Nru tomcat7-7.0.70/java/org/apache/naming/ContextAccessController.java tomcat7-7.0.72/java/org/apache/naming/ContextAccessController.java --- tomcat7-7.0.70/java/org/apache/naming/ContextAccessController.java 2014-05-13 14:15:06.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/naming/ContextAccessController.java 2016-08-02 12:20:10.000000000 +0000 @@ -49,9 +49,9 @@ /** - * Set a security token for a context. Can be set only once. + * Set a security token for a Catalina context. Can be set only once. * - * @param name Name of the context + * @param name Name of the Catalina context * @param token Security token */ public static void setSecurityToken(Object name, Object token) { @@ -70,7 +70,7 @@ /** * Remove a security token for a context. * - * @param name Name of the context + * @param name Name of the Catalina context * @param token Security token */ public static void unsetSecurityToken(Object name, Object token) { @@ -85,7 +85,7 @@ * the token present in the repository. If no token is present for the * context, then returns true. * - * @param name Name of the context + * @param name Name of the Catalina context * @param token Submitted security token */ public static boolean checkSecurityToken @@ -98,7 +98,7 @@ /** * Allow writing to a context. * - * @param name Name of the context + * @param name Name of the Catalina context * @param token Security token */ public static void setWritable(Object name, Object token) { @@ -108,9 +108,9 @@ /** - * Set whether or not a context is writable. + * Set whether or not a Catalina context is writable. * - * @param name Name of the context + * @param name Name of the Catalina context */ public static void setReadOnly(Object name) { readOnlyContexts.put(name, name); @@ -120,7 +120,7 @@ /** * Returns if a context is writable. * - * @param name Name of the context + * @param name Name of the Catalina context */ public static boolean isWritable(Object name) { return !(readOnlyContexts.containsKey(name)); diff -Nru tomcat7-7.0.70/java/org/apache/naming/factory/ResourceLinkFactory.java tomcat7-7.0.72/java/org/apache/naming/factory/ResourceLinkFactory.java --- tomcat7-7.0.70/java/org/apache/naming/factory/ResourceLinkFactory.java 2016-01-21 12:56:13.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/naming/factory/ResourceLinkFactory.java 2016-08-22 21:28:40.000000000 +0000 @@ -5,20 +5,21 @@ * 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.naming.factory; +import java.util.HashMap; import java.util.Hashtable; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import javax.naming.Context; import javax.naming.Name; @@ -29,34 +30,28 @@ import org.apache.naming.ResourceLinkRef; - /** *

Object factory for resource links.

- * + * * @author Remy Maucherat */ -public class ResourceLinkFactory - implements ObjectFactory { - - - // ----------------------------------------------------------- Constructors - +public class ResourceLinkFactory implements ObjectFactory { // ------------------------------------------------------- Static Variables - /** * Global naming context. */ private static Context globalContext = null; + private static Map> globalResourceRegistrations = + new ConcurrentHashMap>(); // --------------------------------------------------------- Public Methods - /** * Set the global context (note: can only be used once). - * + * * @param newGlobalContext new global context value */ public static void setGlobalContext(Context newGlobalContext) { @@ -69,21 +64,70 @@ } - // -------------------------------------------------- ObjectFactory Methods + public static void registerGlobalResourceAccess(Context globalContext, String localName, + String globalName) { + validateGlobalContext(globalContext); + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Map registrations = globalResourceRegistrations.get(cl); + if (registrations == null) { + // Web application initialization is single threaded so this is + // safe. + registrations = new HashMap(); + globalResourceRegistrations.put(cl, registrations); + } + registrations.put(localName, globalName); + } + + + public static void deregisterGlobalResourceAccess(Context globalContext, String localName) { + validateGlobalContext(globalContext); + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Map registrations = globalResourceRegistrations.get(cl); + if (registrations != null) { + registrations.remove(localName); + } + } + + public static void deregisterGlobalResourceAccess(Context globalContext) { + validateGlobalContext(globalContext); + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + globalResourceRegistrations.remove(cl); + } + + + private static void validateGlobalContext(Context globalContext) { + if (ResourceLinkFactory.globalContext != null && + ResourceLinkFactory.globalContext != globalContext) { + throw new SecurityException("Caller provided invalid global context"); + } + } + + + private static boolean validateGlobalResourceAccess(String globalName) { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Map registrations = globalResourceRegistrations.get(cl); + if (registrations != null && registrations.containsValue(globalName)) { + return true; + } + return false; + } + + + // -------------------------------------------------- ObjectFactory Methods /** * Create a new DataSource instance. - * + * * @param obj The reference object describing the DataSource */ @Override public Object getObjectInstance(Object obj, Name name, Context nameCtx, - Hashtable environment) - throws NamingException { - - if (!(obj instanceof ResourceLinkRef)) + Hashtable environment) throws NamingException { + + if (!(obj instanceof ResourceLinkRef)) { return null; + } // Can we process this request? Reference ref = (Reference) obj; @@ -93,16 +137,27 @@ RefAddr refAddr = ref.get(ResourceLinkRef.GLOBALNAME); if (refAddr != null) { globalName = refAddr.getContent().toString(); + // Confirm that the current web application is currently configured + // to access the specified global resource + if (!validateGlobalResourceAccess(globalName)) { + return null; + } Object result = null; result = globalContext.lookup(globalName); - // FIXME: Check type + // Check the expected type + String expectedClassName = ref.getClassName(); + try { + Class expectedClazz = Class.forName( + expectedClassName, true, Thread.currentThread().getContextClassLoader()); + if (!expectedClazz.isAssignableFrom(result.getClass())) { + throw new IllegalArgumentException(); + } + } catch (ClassNotFoundException e) { + throw new IllegalStateException(e); + } return result; } - return (null); - - + return null; } - - } diff -Nru tomcat7-7.0.70/java/org/apache/naming/NamingContext.java tomcat7-7.0.72/java/org/apache/naming/NamingContext.java --- tomcat7-7.0.70/java/org/apache/naming/NamingContext.java 2014-01-27 13:05:23.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/naming/NamingContext.java 2016-08-02 12:20:10.000000000 +0000 @@ -65,13 +65,15 @@ /** - * Builds a naming context using the given environment. + * Builds a naming context. + * + * @param env The environment to use to construct the naming context + * @param name The name of the associated Catalina Context */ public NamingContext(Hashtable env, String name) throws NamingException { this.bindings = new HashMap(); this.env = new Hashtable(); - // FIXME ? Could be put in the environment ? this.name = name; // Populating the environment hashtable if (env != null ) { @@ -85,7 +87,11 @@ /** - * Builds a naming context using the given environment. + * Builds a naming context. + * + * @param env The environment to use to construct the naming context + * @param name The name of the associated Catalina Context + * @param bindings The initial bindings for the naming context */ public NamingContext(Hashtable env, String name, HashMap bindings) diff -Nru tomcat7-7.0.70/java/org/apache/naming/resources/BaseDirContext.java tomcat7-7.0.72/java/org/apache/naming/resources/BaseDirContext.java --- tomcat7-7.0.70/java/org/apache/naming/resources/BaseDirContext.java 2014-07-29 04:50:19.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/naming/resources/BaseDirContext.java 2016-08-02 01:46:52.000000000 +0000 @@ -497,7 +497,13 @@ return obj; } - // Check the alternate locations + // Class files may not be loaded from the alternate locations so don't + // waste cycles looking. + if (name.endsWith(".class")) { + return null; + } + + // Check the alternate locations (Resource JARs) String resourceName = "/META-INF/resources" + name; for (DirContext altDirContext : altDirContexts) { if (altDirContext instanceof BaseDirContext) { diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/AnnotationElementValue.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/AnnotationElementValue.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/AnnotationElementValue.java 2014-09-17 12:03:10.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/AnnotationElementValue.java 2016-08-22 10:48:26.000000000 +0000 @@ -22,8 +22,8 @@ // For annotation element values, this is the annotation private final AnnotationEntry annotationEntry; - AnnotationElementValue(int type, AnnotationEntry annotationEntry, - ConstantPool cpool) + AnnotationElementValue(final int type, final AnnotationEntry annotationEntry, + final ConstantPool cpool) { super(type, cpool); if (type != ANNOTATION) { diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/AnnotationEntry.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/AnnotationEntry.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/AnnotationEntry.java 2015-05-28 19:22:12.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/AnnotationEntry.java 2016-08-22 10:48:26.000000000 +0000 @@ -22,46 +22,43 @@ import java.util.ArrayList; import java.util.List; -import org.apache.tomcat.util.bcel.Constants; +import org.apache.tomcat.util.bcel.Const; /** * represents one annotation in the annotation table - * - * @author D. Brosius - * @since 6.0 */ -public class AnnotationEntry implements Constants { +public class AnnotationEntry { private final int type_index; private final ConstantPool constant_pool; private final List element_value_pairs; - - /** + + /* * Creates an AnnotationEntry from a DataInputStream - * + * * @param input * @param constant_pool * @throws IOException */ - AnnotationEntry(DataInput input, ConstantPool constant_pool) throws IOException { + AnnotationEntry(final DataInput input, final ConstantPool constant_pool) throws IOException { this.constant_pool = constant_pool; type_index = input.readUnsignedShort(); - int num_element_value_pairs = input.readUnsignedShort(); + final int num_element_value_pairs = input.readUnsignedShort(); element_value_pairs = new ArrayList(num_element_value_pairs); for (int i = 0; i < num_element_value_pairs; i++) { element_value_pairs.add(new ElementValuePair(input, constant_pool)); } } - + /** * @return the annotation type name */ public String getAnnotationType() { - final ConstantUtf8 c = (ConstantUtf8) constant_pool.getConstant(type_index, CONSTANT_Utf8); + final ConstantUtf8 c = (ConstantUtf8) constant_pool.getConstant(type_index, Const.CONSTANT_Utf8); return c.getBytes(); } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/Annotations.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/Annotations.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/Annotations.java 2015-05-28 19:22:12.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/Annotations.java 2016-08-22 10:48:26.000000000 +0000 @@ -22,20 +22,17 @@ /** * base class for annotations - * - * @author D. Brosius - * @since 6.0 */ public class Annotations { private final AnnotationEntry[] annotation_table; - + /** * @param input Input stream * @param constant_pool Array of constants */ - Annotations(DataInput input, ConstantPool constant_pool) throws IOException { - final int annotation_table_length = (input.readUnsignedShort()); + Annotations(final DataInput input, final ConstantPool constant_pool) throws IOException { + final int annotation_table_length = input.readUnsignedShort(); annotation_table = new AnnotationEntry[annotation_table_length]; for (int i = 0; i < annotation_table_length; i++) { annotation_table[i] = new AnnotationEntry(input, constant_pool); @@ -44,7 +41,7 @@ /** - * returns the array of annotation entries in this annotation + * @return the array of annotation entries in this annotation */ public AnnotationEntry[] getAnnotationEntries() { return annotation_table; diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ArrayElementValue.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ArrayElementValue.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ArrayElementValue.java 2014-09-17 12:03:10.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ArrayElementValue.java 2016-08-22 10:48:26.000000000 +0000 @@ -22,7 +22,7 @@ // For array types, this is the array private final ElementValue[] evalues; - ArrayElementValue(int type, ElementValue[] datums, ConstantPool cpool) + ArrayElementValue(final int type, final ElementValue[] datums, final ConstantPool cpool) { super(type, cpool); if (type != ARRAY) { @@ -35,7 +35,7 @@ @Override public String stringifyValue() { - StringBuilder sb = new StringBuilder(); + final StringBuilder sb = new StringBuilder(); sb.append("["); for (int i = 0; i < evalues.length; i++) { diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ClassElementValue.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ClassElementValue.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ClassElementValue.java 2014-09-17 12:03:10.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ClassElementValue.java 2016-08-22 10:48:26.000000000 +0000 @@ -17,7 +17,7 @@ */ package org.apache.tomcat.util.bcel.classfile; -import org.apache.tomcat.util.bcel.Constants; +import org.apache.tomcat.util.bcel.Const; public class ClassElementValue extends ElementValue { @@ -26,7 +26,7 @@ // For 'class' this points to the class entry in the cpool private final int idx; - ClassElementValue(int type, int idx, ConstantPool cpool) { + ClassElementValue(final int type, final int idx, final ConstantPool cpool) { super(type, cpool); this.idx = idx; } @@ -35,8 +35,8 @@ @Override public String stringifyValue() { - ConstantUtf8 cu8 = (ConstantUtf8) cpool.getConstant(idx, - Constants.CONSTANT_Utf8); + final ConstantUtf8 cu8 = (ConstantUtf8) super.getConstantPool().getConstant(idx, + Const.CONSTANT_Utf8); return cu8.getBytes(); } } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ClassFormatException.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ClassFormatException.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ClassFormatException.java 2014-09-12 11:59:12.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ClassFormatException.java 2016-08-22 10:48:26.000000000 +0000 @@ -17,12 +17,10 @@ */ package org.apache.tomcat.util.bcel.classfile; -/** +/** * Thrown when the BCEL attempts to read a class file and determines * that the file is malformed or otherwise cannot be interpreted as a * class file. - * - * @author M. Dahm */ public class ClassFormatException extends RuntimeException { @@ -33,7 +31,7 @@ } - public ClassFormatException(String s) { + public ClassFormatException(final String s) { super(s); } } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ClassParser.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ClassParser.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ClassParser.java 2015-05-28 19:22:12.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ClassParser.java 2016-08-22 10:48:26.000000000 +0000 @@ -23,7 +23,7 @@ import java.io.IOException; import java.io.InputStream; -import org.apache.tomcat.util.bcel.Constants; +import org.apache.tomcat.util.bcel.Const; /** * Wrapper class that parses a given Java .class file. The method * JVM specification 1.0. See this paper for * further details about the structure of a bytecode file. - * - * @author M. Dahm */ public final class ClassParser { @@ -58,7 +56,7 @@ * * @param inputStream Input stream */ - public ClassParser(InputStream inputStream) { + public ClassParser(final InputStream inputStream) { this.dataInputStream = new DataInputStream(new BufferedInputStream(inputStream, BUFSIZE)); } @@ -71,8 +69,8 @@ * is performed by the java interpreter). * * @return Class object representing the parsed class file - * @throws IOException - * @throws ClassFormatException + * @throws IOException If an I/O occurs reading the byte code + * @throws ClassFormatException If the byte code is invalid */ public JavaClass parse() throws IOException, ClassFormatException { /****************** Read headers ********************************/ @@ -108,7 +106,7 @@ * @throws ClassFormatException */ private void readAttributes() throws IOException, ClassFormatException { - int attributes_count = dataInputStream.readUnsignedShort(); + final int attributes_count = dataInputStream.readUnsignedShort(); for (int i = 0; i < attributes_count; i++) { ConstantUtf8 c; String name; @@ -117,7 +115,7 @@ // Get class name from constant pool via `name_index' indirection name_index = dataInputStream.readUnsignedShort(); c = (ConstantUtf8) constant_pool.getConstant(name_index, - Constants.CONSTANT_Utf8); + Const.CONSTANT_Utf8); name = c.getBytes(); // Length of data in bytes length = dataInputStream.readInt(); @@ -146,11 +144,11 @@ /* Interfaces are implicitely abstract, the flag should be set * according to the JVM specification. */ - if ((access_flags & Constants.ACC_INTERFACE) != 0) { - access_flags |= Constants.ACC_ABSTRACT; + if ((access_flags & Const.ACC_INTERFACE) != 0) { + access_flags |= Const.ACC_ABSTRACT; } - if (((access_flags & Constants.ACC_ABSTRACT) != 0) - && ((access_flags & Constants.ACC_FINAL) != 0)) { + if (((access_flags & Const.ACC_ABSTRACT) != 0) + && ((access_flags & Const.ACC_FINAL) != 0)) { throw new ClassFormatException("Class can't be both final and abstract"); } @@ -183,7 +181,7 @@ * @throws ClassFormatException */ private void readFields() throws IOException, ClassFormatException { - int fields_count = dataInputStream.readUnsignedShort(); + final int fields_count = dataInputStream.readUnsignedShort(); for (int i = 0; i < fields_count; i++) { Utility.swallowFieldOrMethod(dataInputStream); } @@ -210,7 +208,7 @@ * @throws ClassFormatException */ private void readInterfaces() throws IOException, ClassFormatException { - int interfaces_count = dataInputStream.readUnsignedShort(); + final int interfaces_count = dataInputStream.readUnsignedShort(); if (interfaces_count > 0) { interface_names = new String[interfaces_count]; for (int i = 0; i < interfaces_count; i++) { @@ -229,7 +227,7 @@ * @throws ClassFormatException */ private void readMethods() throws IOException, ClassFormatException { - int methods_count = dataInputStream.readUnsignedShort(); + final int methods_count = dataInputStream.readUnsignedShort(); for (int i = 0; i < methods_count; i++) { Utility.swallowFieldOrMethod(dataInputStream); } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ConstantClass.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ConstantClass.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ConstantClass.java 2014-09-17 12:03:10.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ConstantClass.java 2016-08-22 10:48:26.000000000 +0000 @@ -20,14 +20,12 @@ import java.io.DataInput; import java.io.IOException; -import org.apache.tomcat.util.bcel.Constants; +import org.apache.tomcat.util.bcel.Const; -/** - * This class is derived from the abstract - * Constant class +/** + * This class is derived from the abstract {@link Constant} * and represents a reference to a (external) class. * - * @author M. Dahm * @see Constant */ public final class ConstantClass extends Constant { @@ -41,13 +39,13 @@ * @param file Input stream * @throws IOException */ - ConstantClass(DataInput file) throws IOException { - super(Constants.CONSTANT_Class); + ConstantClass(final DataInput file) throws IOException { + super(Const.CONSTANT_Class); this.name_index = file.readUnsignedShort(); } - /** + /** * @return Name index in constant pool of class name. */ public final int getNameIndex() { diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ConstantDouble.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ConstantDouble.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ConstantDouble.java 2014-09-17 12:03:10.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ConstantDouble.java 2016-08-22 10:48:26.000000000 +0000 @@ -20,14 +20,12 @@ import java.io.DataInput; import java.io.IOException; -import org.apache.tomcat.util.bcel.Constants; +import org.apache.tomcat.util.bcel.Const; -/** - * This class is derived from the abstract - * Constant class +/** + * This class is derived from the abstract {@link Constant} * and represents a reference to a Double object. * - * @author M. Dahm * @see Constant */ public final class ConstantDouble extends Constant { @@ -35,14 +33,14 @@ private final double bytes; - /** + /** * Initialize instance from file data. * * @param file Input stream * @throws IOException */ - ConstantDouble(DataInput file) throws IOException { - super(Constants.CONSTANT_Double); + ConstantDouble(final DataInput file) throws IOException { + super(Const.CONSTANT_Double); this.bytes = file.readDouble(); } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ConstantFloat.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ConstantFloat.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ConstantFloat.java 2014-09-17 12:03:10.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ConstantFloat.java 2016-08-22 10:48:26.000000000 +0000 @@ -20,14 +20,12 @@ import java.io.DataInput; import java.io.IOException; -import org.apache.tomcat.util.bcel.Constants; +import org.apache.tomcat.util.bcel.Const; -/** - * This class is derived from the abstract - * Constant class +/** + * This class is derived from the abstract {@link Constant} * and represents a reference to a float object. * - * @author M. Dahm * @see Constant */ public final class ConstantFloat extends Constant { @@ -35,18 +33,18 @@ private final float bytes; - /** + /** * Initialize instance from file data. * * @param file Input stream * @throws IOException */ - ConstantFloat(DataInput file) throws IOException { - super(Constants.CONSTANT_Float); + ConstantFloat(final DataInput file) throws IOException { + super(Const.CONSTANT_Float); this.bytes = file.readFloat(); } - + /** * @return data, i.e., 4 bytes. */ diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ConstantInteger.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ConstantInteger.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ConstantInteger.java 2014-09-17 12:03:10.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ConstantInteger.java 2016-08-22 10:48:26.000000000 +0000 @@ -20,14 +20,12 @@ import java.io.DataInput; import java.io.IOException; -import org.apache.tomcat.util.bcel.Constants; +import org.apache.tomcat.util.bcel.Const; -/** - * This class is derived from the abstract - * Constant class +/** + * This class is derived from the abstract {@link Constant} * and represents a reference to an int object. * - * @author M. Dahm * @see Constant */ public final class ConstantInteger extends Constant { @@ -35,14 +33,14 @@ private final int bytes; - /** + /** * Initialize instance from file data. * * @param file Input stream * @throws IOException */ - ConstantInteger(DataInput file) throws IOException { - super(Constants.CONSTANT_Integer); + ConstantInteger(final DataInput file) throws IOException { + super(Const.CONSTANT_Integer); this.bytes = file.readInt(); } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/Constant.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/Constant.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/Constant.java 2015-05-28 19:22:12.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/Constant.java 2016-08-22 10:48:26.000000000 +0000 @@ -20,7 +20,7 @@ import java.io.DataInput; import java.io.IOException; -import org.apache.tomcat.util.bcel.Constants; +import org.apache.tomcat.util.bcel.Const; /** * Abstract superclass for classes to represent the different constant types @@ -36,13 +36,13 @@ * places we will use the tag for switch()es anyway. * * First, we want match the specification as closely as possible. Second we - * need the tag as an index to select the corresponding class name from the + * need the tag as an index to select the corresponding class name from the * `CONSTANT_NAMES' array. */ protected final byte tag; - Constant(byte tag) { + Constant(final byte tag) { this.tag = tag; } @@ -62,35 +62,35 @@ * @param input Input stream * @return Constant object */ - static Constant readConstant(DataInput input) throws IOException, + static Constant readConstant(final DataInput input) throws IOException, ClassFormatException { - byte b = input.readByte(); // Read tag byte + final byte b = input.readByte(); // Read tag byte int skipSize; switch (b) { - case Constants.CONSTANT_Class: + case Const.CONSTANT_Class: return new ConstantClass(input); - case Constants.CONSTANT_Integer: + case Const.CONSTANT_Integer: return new ConstantInteger(input); - case Constants.CONSTANT_Float: + case Const.CONSTANT_Float: return new ConstantFloat(input); - case Constants.CONSTANT_Long: + case Const.CONSTANT_Long: return new ConstantLong(input); - case Constants.CONSTANT_Double: + case Const.CONSTANT_Double: return new ConstantDouble(input); - case Constants.CONSTANT_Utf8: + case Const.CONSTANT_Utf8: return ConstantUtf8.getInstance(input); - case Constants.CONSTANT_String: - case Constants.CONSTANT_MethodType: + case Const.CONSTANT_String: + case Const.CONSTANT_MethodType: skipSize = 2; // unsigned short break; - case Constants.CONSTANT_MethodHandle: + case Const.CONSTANT_MethodHandle: skipSize = 3; // unsigned byte, unsigned short break; - case Constants.CONSTANT_Fieldref: - case Constants.CONSTANT_Methodref: - case Constants.CONSTANT_InterfaceMethodref: - case Constants.CONSTANT_NameAndType: - case Constants.CONSTANT_InvokeDynamic: + case Const.CONSTANT_Fieldref: + case Const.CONSTANT_Methodref: + case Const.CONSTANT_InterfaceMethodref: + case Const.CONSTANT_NameAndType: + case Const.CONSTANT_InvokeDynamic: skipSize = 4; // unsigned short, unsigned short break; default: @@ -101,7 +101,6 @@ } - @Override public String toString() { return "[" + tag + "]"; diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ConstantLong.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ConstantLong.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ConstantLong.java 2015-05-28 19:22:12.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ConstantLong.java 2016-08-22 10:48:26.000000000 +0000 @@ -20,14 +20,12 @@ import java.io.DataInput; import java.io.IOException; -import org.apache.tomcat.util.bcel.Constants; +import org.apache.tomcat.util.bcel.Const; -/** - * This class is derived from the abstract - * Constant class +/** + * This class is derived from the abstract {@link Constant} * and represents a reference to a long object. * - * @author M. Dahm * @see Constant */ public final class ConstantLong extends Constant { @@ -35,14 +33,14 @@ private final long bytes; - /** + /** * Initialize instance from file data. * * @param file Input stream * @throws IOException */ - ConstantLong(DataInput input) throws IOException { - super(Constants.CONSTANT_Long); + ConstantLong(final DataInput input) throws IOException { + super(Const.CONSTANT_Long); this.bytes = input.readLong(); } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ConstantPool.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ConstantPool.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ConstantPool.java 2015-05-28 19:22:12.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ConstantPool.java 2016-08-22 10:48:26.000000000 +0000 @@ -20,7 +20,7 @@ import java.io.DataInput; import java.io.IOException; -import org.apache.tomcat.util.bcel.Constants; +import org.apache.tomcat.util.bcel.Const; /** * This class represents the constant pool, i.e., a table of constants, of @@ -31,7 +31,6 @@ * ConstantPoolGen. * @see Constant - * @author M. Dahm */ public class ConstantPool { @@ -45,8 +44,8 @@ * @throws IOException * @throws ClassFormatException */ - ConstantPool(DataInput input) throws IOException, ClassFormatException { - int constant_pool_count = input.readUnsignedShort(); + ConstantPool(final DataInput input) throws IOException, ClassFormatException { + final int constant_pool_count = input.readUnsignedShort(); constant_pool = new Constant[constant_pool_count]; /* constant_pool[0] is unused by the compiler and may be used freely * by the implementation. @@ -57,12 +56,12 @@ * "All eight byte constants take up two spots in the constant pool. * If this is the n'th byte in the constant pool, then the next item * will be numbered n+2" - * + * * Thus we have to increment the index counter. */ if (constant_pool[i] != null) { byte tag = constant_pool[i].getTag(); - if ((tag == Constants.CONSTANT_Double) || (tag == Constants.CONSTANT_Long)) { + if ((tag == Const.CONSTANT_Double) || (tag == Const.CONSTANT_Long)) { i++; } } @@ -77,7 +76,7 @@ * @return Constant value * @see Constant */ - public Constant getConstant( int index ) { + public Constant getConstant( final int index ) { if (index >= constant_pool.length || index < 0) { throw new ClassFormatException("Invalid constant pool reference: " + index + ". Constant pool size is: " + constant_pool.length); @@ -94,16 +93,16 @@ * @param tag Tag of expected constant, i.e., its type * @return Constant value * @see Constant - * @throws ClassFormatException + * @throws ClassFormatException If the constant is not of the expected type */ - public Constant getConstant( int index, byte tag ) throws ClassFormatException { + public Constant getConstant( final int index, final byte tag ) throws ClassFormatException { Constant c; c = getConstant(index); if (c == null) { throw new ClassFormatException("Constant pool at index " + index + " is null."); } if (c.getTag() != tag) { - throw new ClassFormatException("Expected class `" + Constants.CONSTANT_NAMES[tag] + throw new ClassFormatException("Expected class `" + Const.getConstantName(tag) + "' at index " + index + " and got " + c); } return c; diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ConstantUtf8.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ConstantUtf8.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ConstantUtf8.java 2015-05-28 19:22:12.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ConstantUtf8.java 2016-08-22 10:48:26.000000000 +0000 @@ -19,14 +19,13 @@ import java.io.DataInput; import java.io.IOException; -import org.apache.tomcat.util.bcel.Constants; +import org.apache.tomcat.util.bcel.Const; -/** - * This class is derived from the abstract - * Constant class +/** + * This class is derived from the abstract + * Constant class * and represents a reference to a Utf8 encoded string. * - * @author M. Dahm * @see Constant */ public final class ConstantUtf8 extends Constant { @@ -34,7 +33,7 @@ private final String bytes; - static ConstantUtf8 getInstance(DataInput input) throws IOException { + static ConstantUtf8 getInstance(final DataInput input) throws IOException { return new ConstantUtf8(input.readUTF()); } @@ -42,8 +41,8 @@ /** * @param bytes Data */ - private ConstantUtf8(String bytes) { - super(Constants.CONSTANT_Utf8); + private ConstantUtf8(final String bytes) { + super(Const.CONSTANT_Utf8); if (bytes == null) { throw new IllegalArgumentException("bytes must not be null!"); } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ElementValue.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ElementValue.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ElementValue.java 2015-05-28 19:22:12.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ElementValue.java 2016-08-22 10:48:26.000000000 +0000 @@ -19,18 +19,14 @@ import java.io.DataInput; import java.io.IOException; -/** - * @author D. Brosius - * @since 6.0 - */ public abstract class ElementValue { - protected final int type; + private final int type; - protected final ConstantPool cpool; + private final ConstantPool cpool; - ElementValue(int type, ConstantPool cpool) { + ElementValue(final int type, final ConstantPool cpool) { this.type = type; this.cpool = cpool; } @@ -51,9 +47,9 @@ public static final byte PRIMITIVE_SHORT = 'S'; public static final byte PRIMITIVE_BOOLEAN = 'Z'; - public static ElementValue readElementValue(DataInput input, ConstantPool cpool) throws IOException + public static ElementValue readElementValue(final DataInput input, final ConstantPool cpool) throws IOException { - byte type = input.readByte(); + final byte type = input.readByte(); switch (type) { case PRIMITIVE_BYTE: @@ -79,8 +75,8 @@ return new AnnotationElementValue(ANNOTATION, new AnnotationEntry(input, cpool), cpool); case ARRAY: - int numArrayVals = input.readUnsignedShort(); - ElementValue[] evalues = new ElementValue[numArrayVals]; + final int numArrayVals = input.readUnsignedShort(); + final ElementValue[] evalues = new ElementValue[numArrayVals]; for (int j = 0; j < numArrayVals; j++) { evalues[j] = ElementValue.readElementValue(input, cpool); @@ -92,4 +88,12 @@ "Unexpected element value kind in annotation: " + type); } } + + final ConstantPool getConstantPool() { + return cpool; + } + + final int getType() { + return type; + } } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ElementValuePair.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ElementValuePair.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/ElementValuePair.java 2014-09-17 12:42:29.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/ElementValuePair.java 2016-08-22 10:48:26.000000000 +0000 @@ -20,12 +20,11 @@ import java.io.DataInput; import java.io.IOException; -import org.apache.tomcat.util.bcel.Constants; +import org.apache.tomcat.util.bcel.Const; /** * an annotation's element value pair - * - * @author D. Brosius + * * @since 6.0 */ public class ElementValuePair @@ -36,7 +35,7 @@ private final int elementNameIndex; - ElementValuePair(DataInput file, ConstantPool constantPool) throws IOException { + ElementValuePair(final DataInput file, final ConstantPool constantPool) throws IOException { this.constantPool = constantPool; this.elementNameIndex = file.readUnsignedShort(); this.elementValue = ElementValue.readElementValue(file, constantPool); @@ -44,8 +43,8 @@ public String getNameString() { - ConstantUtf8 c = (ConstantUtf8) constantPool.getConstant( - elementNameIndex, Constants.CONSTANT_Utf8); + final ConstantUtf8 c = (ConstantUtf8) constantPool.getConstant( + elementNameIndex, Const.CONSTANT_Utf8); return c.getBytes(); } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/EnumElementValue.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/EnumElementValue.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/EnumElementValue.java 2014-09-17 12:03:10.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/EnumElementValue.java 2016-08-22 10:48:26.000000000 +0000 @@ -17,13 +17,13 @@ */ package org.apache.tomcat.util.bcel.classfile; -import org.apache.tomcat.util.bcel.Constants; +import org.apache.tomcat.util.bcel.Const; public class EnumElementValue extends ElementValue { private final int valueIdx; - EnumElementValue(int type, int valueIdx, ConstantPool cpool) { + EnumElementValue(final int type, final int valueIdx, final ConstantPool cpool) { super(type, cpool); if (type != ENUM_CONSTANT) throw new RuntimeException( @@ -34,8 +34,8 @@ @Override public String stringifyValue() { - ConstantUtf8 cu8 = (ConstantUtf8) cpool.getConstant(valueIdx, - Constants.CONSTANT_Utf8); + final ConstantUtf8 cu8 = (ConstantUtf8) super.getConstantPool().getConstant(valueIdx, + Const.CONSTANT_Utf8); return cu8.getBytes(); } } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/JavaClass.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/JavaClass.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/JavaClass.java 2015-05-28 19:22:12.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/JavaClass.java 2016-08-22 10:48:26.000000000 +0000 @@ -24,8 +24,6 @@ * The intent of this class is to represent a parsed or otherwise existing * class file. Those interested in programatically generating classes * should see the ClassGen class. - - * @author M. Dahm */ public class JavaClass { @@ -45,9 +43,9 @@ * @param interfaces Implemented interfaces * @param runtimeVisibleAnnotations "RuntimeVisibleAnnotations" attribute defined on the Class, or null */ - JavaClass(String class_name, String superclass_name, - int access_flags, ConstantPool constant_pool, String[] interface_names, - Annotations runtimeVisibleAnnotations) { + JavaClass(final String class_name, final String superclass_name, + final int access_flags, final ConstantPool constant_pool, final String[] interface_names, + final Annotations runtimeVisibleAnnotations) { this.access_flags = access_flags; this.runtimeVisibleAnnotations = runtimeVisibleAnnotations; this.class_name = class_name; diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/SimpleElementValue.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/SimpleElementValue.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/SimpleElementValue.java 2014-09-17 12:03:10.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/SimpleElementValue.java 2016-08-22 10:48:26.000000000 +0000 @@ -17,13 +17,13 @@ */ package org.apache.tomcat.util.bcel.classfile; -import org.apache.tomcat.util.bcel.Constants; +import org.apache.tomcat.util.bcel.Const; public class SimpleElementValue extends ElementValue { private final int index; - SimpleElementValue(int type, int index, ConstantPool cpool) { + SimpleElementValue(final int type, final int index, final ConstantPool cpool) { super(type, cpool); this.index = index; } @@ -35,57 +35,57 @@ { return index; } - + // Whatever kind of value it is, return it as a string @Override public String stringifyValue() { - switch (type) + final ConstantPool cpool = super.getConstantPool(); + final int _type = super.getType(); + switch (_type) { case PRIMITIVE_INT: - ConstantInteger c = (ConstantInteger) cpool.getConstant(getIndex(), - Constants.CONSTANT_Integer); + final ConstantInteger c = (ConstantInteger) cpool.getConstant(getIndex(), + Const.CONSTANT_Integer); return Integer.toString(c.getBytes()); case PRIMITIVE_LONG: - ConstantLong j = (ConstantLong) cpool.getConstant(getIndex(), - Constants.CONSTANT_Long); + final ConstantLong j = (ConstantLong) cpool.getConstant(getIndex(), + Const.CONSTANT_Long); return Long.toString(j.getBytes()); case PRIMITIVE_DOUBLE: - ConstantDouble d = (ConstantDouble) cpool.getConstant(getIndex(), - Constants.CONSTANT_Double); + final ConstantDouble d = (ConstantDouble) cpool.getConstant(getIndex(), + Const.CONSTANT_Double); return Double.toString(d.getBytes()); case PRIMITIVE_FLOAT: - ConstantFloat f = (ConstantFloat) cpool.getConstant(getIndex(), - Constants.CONSTANT_Float); + final ConstantFloat f = (ConstantFloat) cpool.getConstant(getIndex(), + Const.CONSTANT_Float); return Float.toString(f.getBytes()); case PRIMITIVE_SHORT: - ConstantInteger s = (ConstantInteger) cpool.getConstant(getIndex(), - Constants.CONSTANT_Integer); + final ConstantInteger s = (ConstantInteger) cpool.getConstant(getIndex(), + Const.CONSTANT_Integer); return Integer.toString(s.getBytes()); case PRIMITIVE_BYTE: - ConstantInteger b = (ConstantInteger) cpool.getConstant(getIndex(), - Constants.CONSTANT_Integer); + final ConstantInteger b = (ConstantInteger) cpool.getConstant(getIndex(), + Const.CONSTANT_Integer); return Integer.toString(b.getBytes()); case PRIMITIVE_CHAR: - ConstantInteger ch = (ConstantInteger) cpool.getConstant( - getIndex(), Constants.CONSTANT_Integer); + final ConstantInteger ch = (ConstantInteger) cpool.getConstant( + getIndex(), Const.CONSTANT_Integer); return String.valueOf((char)ch.getBytes()); case PRIMITIVE_BOOLEAN: - ConstantInteger bo = (ConstantInteger) cpool.getConstant( - getIndex(), Constants.CONSTANT_Integer); + final ConstantInteger bo = (ConstantInteger) cpool.getConstant( + getIndex(), Const.CONSTANT_Integer); if (bo.getBytes() == 0) { return "false"; } return "true"; case STRING: - ConstantUtf8 cu8 = (ConstantUtf8) cpool.getConstant(getIndex(), - Constants.CONSTANT_Utf8); + final ConstantUtf8 cu8 = (ConstantUtf8) cpool.getConstant(getIndex(), + Const.CONSTANT_Utf8); return cu8.getBytes(); default: - throw new RuntimeException( - "SimpleElementValue class does not know how to stringify type " - + type); + throw new RuntimeException("SimpleElementValue class does not know how to stringify type " + _type); } } } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/Utility.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/Utility.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/classfile/Utility.java 2015-05-28 19:22:12.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/classfile/Utility.java 2016-08-22 10:48:26.000000000 +0000 @@ -20,12 +20,10 @@ import java.io.EOFException; import java.io.IOException; -import org.apache.tomcat.util.bcel.Constants; +import org.apache.tomcat.util.bcel.Const; /** * Utility functions that do not really belong to any class in particular. - * - * @author M. Dahm */ final class Utility { @@ -42,29 +40,29 @@ * @param str The long class name * @return Compacted class name */ - static String compactClassName(String str) { + static String compactClassName(final String str) { return str.replace('/', '.'); // Is `/' on all systems, even DOS } - static String getClassName(ConstantPool constant_pool, int index) { - Constant c = constant_pool.getConstant(index, Constants.CONSTANT_Class); + static String getClassName(final ConstantPool constant_pool, final int index) { + Constant c = constant_pool.getConstant(index, Const.CONSTANT_Class); int i = ((ConstantClass) c).getNameIndex(); // Finally get the string from the constant pool - c = constant_pool.getConstant(i, Constants.CONSTANT_Utf8); + c = constant_pool.getConstant(i, Const.CONSTANT_Utf8); String name = ((ConstantUtf8) c).getBytes(); return compactClassName(name); } - static void skipFully(DataInput file, int length) throws IOException { + static void skipFully(final DataInput file, final int length) throws IOException { int total = file.skipBytes(length); if (total != length) { throw new EOFException(); } } - static void swallowFieldOrMethod(DataInput file) + static void swallowFieldOrMethod(final DataInput file) throws IOException { // file.readUnsignedShort(); // Unused access flags // file.readUnsignedShort(); // name index @@ -77,7 +75,7 @@ } } - static void swallowAttribute(DataInput file) + static void swallowAttribute(final DataInput file) throws IOException { //file.readUnsignedShort(); // Unused name index skipFully(file, 2); diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/Constants.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/Constants.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/Constants.java 2015-05-28 19:22:12.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/Constants.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,97 +0,0 @@ -/* - * 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.bcel; - -/** - * Constants for the project, mostly defined in the JVM specification. - * - * @author M. Dahm - */ -public interface Constants { - - /** - * One of the access flags for fields, methods, or classes. - * @see "Flag definitions for Fields in the Java Virtual Machine Specification (Java SE 8 Edition)." - * @see "Flag definitions for Methods in the Java Virtual Machine Specification (Java SE 8 Edition)." - * @see "Flag definitions for Classes in the Java Virtual Machine Specification (Java SE 8 Edition)." - */ - public static final short ACC_FINAL = 0x0010; - - /** One of the access flags for fields, methods, or classes. - */ - public static final short ACC_INTERFACE = 0x0200; - - /** One of the access flags for fields, methods, or classes. - */ - public static final short ACC_ABSTRACT = 0x0400; - - /** One of the access flags for fields, methods, or classes. - */ - public static final short ACC_ANNOTATION = 0x2000; - - /** Marks a constant pool entry as type UTF-8. */ - public static final byte CONSTANT_Utf8 = 1; - - /** Marks a constant pool entry as type Integer. */ - public static final byte CONSTANT_Integer = 3; - - /** Marks a constant pool entry as type Float. */ - public static final byte CONSTANT_Float = 4; - - /** Marks a constant pool entry as type Long. */ - public static final byte CONSTANT_Long = 5; - - /** Marks a constant pool entry as type Double. */ - public static final byte CONSTANT_Double = 6; - - /** Marks a constant pool entry as a Class. */ - public static final byte CONSTANT_Class = 7; - - /** Marks a constant pool entry as a Field Reference. */ - public static final byte CONSTANT_Fieldref = 9; - - /** Marks a constant pool entry as type String. */ - public static final byte CONSTANT_String = 8; - - /** Marks a constant pool entry as a Method Reference. */ - public static final byte CONSTANT_Methodref = 10; - - /** Marks a constant pool entry as an Interface Method Reference. */ - public static final byte CONSTANT_InterfaceMethodref = 11; - - /** Marks a constant pool entry as a name and type. */ - public static final byte CONSTANT_NameAndType = 12; - - /** Marks a constant pool entry as a Method Handle. */ - public static final byte CONSTANT_MethodHandle = 15; - - /** Marks a constant pool entry as a Method Type. */ - public static final byte CONSTANT_MethodType = 16; - - /** Marks a constant pool entry as an Invoke Dynamic */ - public static final byte CONSTANT_InvokeDynamic = 18; - - /** The names of the types of entries in a constant pool. */ - public static final String[] CONSTANT_NAMES = { - "", "CONSTANT_Utf8", "", "CONSTANT_Integer", - "CONSTANT_Float", "CONSTANT_Long", "CONSTANT_Double", - "CONSTANT_Class", "CONSTANT_String", "CONSTANT_Fieldref", - "CONSTANT_Methodref", "CONSTANT_InterfaceMethodref", - "CONSTANT_NameAndType", "", "", "CONSTANT_MethodHandle", - "CONSTANT_MethodType", "", "CONSTANT_InvokeDynamic" }; -} diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/Const.java tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/Const.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/bcel/Const.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/bcel/Const.java 2016-08-22 10:48:26.000000000 +0000 @@ -0,0 +1,132 @@ +/* + * 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.bcel; + +/** + * Constants for the project, mostly defined in the JVM specification. + */ +public final class Const { + + /** One of the access flags for fields, methods, or classes. + * @see + * Flag definitions for Fields in the Java Virtual Machine Specification (Java SE 8 Edition). + * @see + * Flag definitions for Methods in the Java Virtual Machine Specification (Java SE 8 Edition). + * @see + * Flag definitions for Classes in the Java Virtual Machine Specification (Java SE 8 Edition). + */ + public static final short ACC_FINAL = 0x0010; + + /** One of the access flags for fields, methods, or classes. + */ + public static final short ACC_INTERFACE = 0x0200; + + /** One of the access flags for fields, methods, or classes. + */ + public static final short ACC_ABSTRACT = 0x0400; + + /** One of the access flags for fields, methods, or classes. + */ + public static final short ACC_ANNOTATION = 0x2000; + + /** Marks a constant pool entry as type UTF-8. + * @see + * The Constant Pool in The Java Virtual Machine Specification */ + public static final byte CONSTANT_Utf8 = 1; + + /** Marks a constant pool entry as type Integer. + * @see + * The Constant Pool in The Java Virtual Machine Specification */ + public static final byte CONSTANT_Integer = 3; + + /** Marks a constant pool entry as type Float. + * @see + * The Constant Pool in The Java Virtual Machine Specification */ + public static final byte CONSTANT_Float = 4; + + /** Marks a constant pool entry as type Long. + * @see + * The Constant Pool in The Java Virtual Machine Specification */ + public static final byte CONSTANT_Long = 5; + + /** Marks a constant pool entry as type Double. + * @see + * The Constant Pool in The Java Virtual Machine Specification */ + public static final byte CONSTANT_Double = 6; + + /** Marks a constant pool entry as a Class + * @see + * The Constant Pool in The Java Virtual Machine Specification */ + public static final byte CONSTANT_Class = 7; + + /** Marks a constant pool entry as a Field Reference. + * @see + * The Constant Pool in The Java Virtual Machine Specification */ + public static final byte CONSTANT_Fieldref = 9; + + /** Marks a constant pool entry as type String + * @see + * The Constant Pool in The Java Virtual Machine Specification */ + public static final byte CONSTANT_String = 8; + + /** Marks a constant pool entry as a Method Reference. + * @see + * The Constant Pool in The Java Virtual Machine Specification */ + public static final byte CONSTANT_Methodref = 10; + + /** Marks a constant pool entry as an Interface Method Reference. + * @see + * The Constant Pool in The Java Virtual Machine Specification */ + public static final byte CONSTANT_InterfaceMethodref = 11; + + /** Marks a constant pool entry as a name and type. + * @see + * The Constant Pool in The Java Virtual Machine Specification */ + public static final byte CONSTANT_NameAndType = 12; + + /** Marks a constant pool entry as a Method Handle. + * @see + * The Constant Pool in The Java Virtual Machine Specification */ + public static final byte CONSTANT_MethodHandle = 15; + + /** Marks a constant pool entry as a Method Type. + * @see + * The Constant Pool in The Java Virtual Machine Specification */ + public static final byte CONSTANT_MethodType = 16; + + /** Marks a constant pool entry as an Invoke Dynamic + * @see + * The Constant Pool in The Java Virtual Machine Specification */ + public static final byte CONSTANT_InvokeDynamic = 18; + + /** + * The names of the types of entries in a constant pool. + * Use getConstantName instead + */ + private static final String[] CONSTANT_NAMES = { + "", "CONSTANT_Utf8", "", "CONSTANT_Integer", + "CONSTANT_Float", "CONSTANT_Long", "CONSTANT_Double", + "CONSTANT_Class", "CONSTANT_String", "CONSTANT_Fieldref", + "CONSTANT_Methodref", "CONSTANT_InterfaceMethodref", + "CONSTANT_NameAndType", "", "", "CONSTANT_MethodHandle", + "CONSTANT_MethodType", "", "CONSTANT_InvokeDynamic" }; + + public static String getConstantName(int index) { + return CONSTANT_NAMES[index]; + } +} diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java tomcat7-7.0.72/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java 2016-01-02 18:03:43.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java 2016-08-22 14:05:44.000000000 +0000 @@ -441,9 +441,9 @@ * false, otherwise */ public boolean isInAlphabet(final byte[] arrayOctet, final boolean allowWSPad) { - for (int i = 0; i < arrayOctet.length; i++) { - if (!isInAlphabet(arrayOctet[i]) && - (!allowWSPad || (arrayOctet[i] != PAD) && !isWhiteSpace(arrayOctet[i]))) { + for (byte octet : arrayOctet) { + if (!isInAlphabet(octet) && + (!allowWSPad || (octet != PAD) && !isWhiteSpace(octet))) { return false; } } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/descriptor/InputSourceUtil.java tomcat7-7.0.72/java/org/apache/tomcat/util/descriptor/InputSourceUtil.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/descriptor/InputSourceUtil.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/descriptor/InputSourceUtil.java 2016-08-24 14:57:11.000000000 +0000 @@ -0,0 +1,47 @@ +/* + * 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.InputStream; + +import org.apache.tomcat.util.ExceptionUtils; +import org.xml.sax.InputSource; + +public final class InputSourceUtil { + + private InputSourceUtil() { + // Utility class. Hide default constructor. + } + + + public static void close(InputSource inputSource) { + if (inputSource == null) { + // Nothing to do + return; + } + + InputStream is = inputSource.getByteStream(); + if (is != null) { + try { + is.close(); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + } + } + + } +} diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/digester/Digester.java tomcat7-7.0.72/java/org/apache/tomcat/util/digester/Digester.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/digester/Digester.java 2015-04-01 11:10:53.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/digester/Digester.java 2016-09-14 09:02:05.000000000 +0000 @@ -26,11 +26,13 @@ import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.net.URISyntaxException; +import java.security.Permission; import java.util.EmptyStackException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.PropertyPermission; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; @@ -40,6 +42,7 @@ import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.ExceptionUtils; import org.apache.tomcat.util.IntrospectionUtils; +import org.apache.tomcat.util.security.PermissionCheck; import org.xml.sax.Attributes; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; @@ -75,39 +78,26 @@ */ public class Digester extends DefaultHandler2 { - - // ---------------------------------------------------------- Static Fields - private static class SystemPropertySource - implements IntrospectionUtils.PropertySource { - @Override - public String getProperty( String key ) { - return System.getProperty(key); - } - } - protected static IntrospectionUtils.PropertySource source[] = - new IntrospectionUtils.PropertySource[] { new SystemPropertySource() }; - + // ---------------------------------------------------------- Static Fields + + protected static IntrospectionUtils.PropertySource propertySource = null; + static { String className = System.getProperty("org.apache.tomcat.util.digester.PROPERTY_SOURCE"); if (className!=null) { - IntrospectionUtils.PropertySource[] sources = new IntrospectionUtils.PropertySource[2]; - sources[1] = source[0]; ClassLoader[] cls = new ClassLoader[] {Digester.class.getClassLoader(),Thread.currentThread().getContextClassLoader()}; - boolean initialized = false; - for (int i=0; i clazz = Class.forName(className,true,cls[i]); - IntrospectionUtils.PropertySource src = (IntrospectionUtils.PropertySource)clazz.newInstance(); - sources[0] = src; - initialized = true; + Class clazz = Class.forName(className, true, cls[i]); + propertySource = (IntrospectionUtils.PropertySource) clazz.newInstance(); + break; } catch (Throwable t) { ExceptionUtils.handleThrowable(t); LogFactory.getLog("org.apache.tomcat.util.digester.Digester"). error("Unable to load property source["+className+"].",t); } } - if (initialized) source = sources; } } @@ -122,6 +112,10 @@ super(); + if (propertySource != null) { + source = new IntrospectionUtils.PropertySource[] { propertySource, source[0] }; + } + } @@ -138,6 +132,10 @@ this.parser = parser; + if (propertySource != null) { + source = new IntrospectionUtils.PropertySource[] { propertySource, source[0] }; + } + } @@ -154,12 +152,35 @@ this.reader = reader; + if (propertySource != null) { + source = new IntrospectionUtils.PropertySource[] { propertySource, source[0] }; + } + } // --------------------------------------------------- Instance Variables + private class SystemPropertySource implements IntrospectionUtils.PropertySource { + @Override + public String getProperty(String key) { + ClassLoader cl = getClassLoader(); + if (cl instanceof PermissionCheck) { + Permission p = new PropertyPermission(key, "read"); + if (!((PermissionCheck) cl).check(p)) { + return null; + } + } + return System.getProperty(key); + } + } + + + protected IntrospectionUtils.PropertySource source[] = new IntrospectionUtils.PropertySource[] { + new SystemPropertySource() }; + + /** * The body text of the current element. */ diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/Cookies.java tomcat7-7.0.72/java/org/apache/tomcat/util/http/Cookies.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/Cookies.java 2015-12-08 08:55:26.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/Cookies.java 2016-08-11 18:57:23.000000000 +0000 @@ -48,6 +48,7 @@ public static final int INITIAL_SIZE=4; ServerCookie scookies[]=new ServerCookie[INITIAL_SIZE]; int cookieCount=0; + private int limit = 200; boolean unprocessed=true; MimeHeaders headers; @@ -63,6 +64,18 @@ this.headers=headers; } + + public void setLimit(int limit) { + this.limit = limit; + if (limit > -1 && scookies.length > limit && cookieCount <= limit) { + // shrink cookie list array + ServerCookie scookiesTmp[] = new ServerCookie[limit]; + System.arraycopy(scookies, 0, scookiesTmp, 0, cookieCount); + scookies = scookiesTmp; + } + } + + /** * Recycle. */ @@ -115,8 +128,14 @@ * The caller can set the name/value and attributes for the cookie */ private ServerCookie addCookie() { - if( cookieCount >= scookies.length ) { - ServerCookie scookiesTmp[]=new ServerCookie[2*cookieCount]; + if (limit > -1 && cookieCount >= limit) { + throw new IllegalArgumentException( + sm.getString("cookies.maxCountFail", Integer.valueOf(limit))); + } + + if (cookieCount >= scookies.length) { + int newSize = Math.min(2*cookieCount, limit); + ServerCookie scookiesTmp[] = new ServerCookie[newSize]; System.arraycopy( scookies, 0, scookiesTmp, 0, cookieCount); scookies=scookiesTmp; } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/ByteArrayOutputStream.java tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/ByteArrayOutputStream.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/ByteArrayOutputStream.java 2014-01-27 12:17:29.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/ByteArrayOutputStream.java 2016-08-22 15:38:48.000000000 +0000 @@ -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. @@ -15,7 +15,7 @@ * limitations under the License. */ package org.apache.tomcat.util.http.fileupload; - + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -23,10 +23,10 @@ import java.util.List; /** - * This class implements an output stream in which the data is - * written into a byte array. The buffer automatically grows as data + * This class implements an output stream in which the data is + * written into a byte array. The buffer automatically grows as data * is written to it. - *

+ *

* The data can be retrieved using toByteArray() and * toString(). *

@@ -60,16 +60,16 @@ private int count; /** - * Creates a new byte array output stream. The buffer capacity is - * initially 1024 bytes, though its size increases if necessary. + * Creates a new byte array output stream. The buffer capacity is + * initially 1024 bytes, though its size increases if necessary. */ public ByteArrayOutputStream() { this(1024); } /** - * Creates a new byte array output stream, with a buffer capacity of - * the specified size, in bytes. + * Creates a new byte array output stream, with a buffer capacity of + * the specified size, in bytes. * * @param size the initial size * @throws IllegalArgumentException if size is negative @@ -94,7 +94,7 @@ if (currentBufferIndex < buffers.size() - 1) { //Recycling old buffer filledBufferSum += currentBuffer.length; - + currentBufferIndex++; currentBuffer = buffers.get(currentBufferIndex); } else { @@ -105,11 +105,11 @@ filledBufferSum = 0; } else { newBufferSize = Math.max( - currentBuffer.length << 1, + currentBuffer.length << 1, newcount - filledBufferSum); filledBufferSum += currentBuffer.length; } - + currentBufferIndex++; currentBuffer = new byte[newBufferSize]; buffers.add(currentBuffer); @@ -124,10 +124,10 @@ */ @Override public void write(byte[] b, int off, int len) { - if ((off < 0) - || (off > b.length) - || (len < 0) - || ((off + len) > b.length) + if ((off < 0) + || (off > b.length) + || (len < 0) + || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { @@ -236,7 +236,7 @@ public synchronized byte[] toByteArray() { int remaining = count; if (remaining == 0) { - return EMPTY_BYTE_ARRAY; + return EMPTY_BYTE_ARRAY; } byte newbuf[] = new byte[remaining]; int pos = 0; diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/DeferredFileOutputStream.java tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/DeferredFileOutputStream.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/DeferredFileOutputStream.java 2014-01-27 12:17:29.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/DeferredFileOutputStream.java 2016-08-22 15:38:48.000000000 +0000 @@ -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. @@ -204,8 +204,8 @@ { return outputFile; } - - + + /** * Closes underlying output stream, and mark this as closed * diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItemFactory.java tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItemFactory.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItemFactory.java 2014-01-27 12:17:29.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItemFactory.java 2016-08-22 15:38:48.000000000 +0000 @@ -18,7 +18,6 @@ import java.io.File; -import org.apache.tomcat.util.http.fileupload.FileCleaningTracker; import org.apache.tomcat.util.http.fileupload.FileItem; import org.apache.tomcat.util.http.fileupload.FileItemFactory; @@ -78,13 +77,6 @@ */ private int sizeThreshold = DEFAULT_SIZE_THRESHOLD; - /** - *

The instance of {@link FileCleaningTracker}, which is responsible - * for deleting temporary files.

- *

May be null, if tracking files is not required.

- */ - private FileCleaningTracker fileCleaningTracker; - // ----------------------------------------------------------- Constructors /** @@ -181,36 +173,7 @@ @Override public FileItem createItem(String fieldName, String contentType, boolean isFormField, String fileName) { - DiskFileItem result = new DiskFileItem(fieldName, contentType, + return new DiskFileItem(fieldName, contentType, isFormField, fileName, sizeThreshold, repository); - FileCleaningTracker tracker = getFileCleaningTracker(); - if (tracker != null) { - tracker.track(result.getTempFile(), result); - } - return result; - } - - /** - * Returns the tracker, which is responsible for deleting temporary - * files. - * - * @return An instance of {@link FileCleaningTracker}, or null - * (default), if temporary files aren't tracked. - */ - public FileCleaningTracker getFileCleaningTracker() { - return fileCleaningTracker; } - - /** - * Sets the tracker, which is responsible for deleting temporary - * files. - * - * @param pTracker An instance of {@link FileCleaningTracker}, - * which will from now on track the created files, or null - * (default), to disable tracking. - */ - public void setFileCleaningTracker(FileCleaningTracker pTracker) { - fileCleaningTracker = pTracker; - } - } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java 2015-05-28 20:33:45.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java 2016-08-22 15:38:48.000000000 +0000 @@ -276,12 +276,13 @@ * contents of the file were not yet cached in memory, they will be * loaded from the disk storage and cached. * - * @return The contents of the file as an array of bytes. + * @return The contents of the file as an array of bytes + * or {@code null} if the data cannot be read */ @Override public byte[] get() { if (isInMemory()) { - if (cachedContent == null) { + if (cachedContent == null && dfos != null) { cachedContent = dfos.getData(); } return cachedContent; @@ -291,18 +292,12 @@ InputStream fis = null; try { - fis = new BufferedInputStream(new FileInputStream(dfos.getFile())); - fis.read(fileData); + fis = new FileInputStream(dfos.getFile()); + IOUtils.readFully(fis, fileData); } catch (IOException e) { fileData = null; } finally { - if (fis != null) { - try { - fis.close(); - } catch (IOException e) { - // ignore - } - } + IOUtils.closeQuietly(fis); } return fileData; @@ -399,21 +394,10 @@ out = new BufferedOutputStream( new FileOutputStream(file)); IOUtils.copy(in, out); + out.close(); } finally { - if (in != null) { - try { - in.close(); - } catch (IOException e) { - // ignore - } - } - if (out != null) { - try { - out.close(); - } catch (IOException e) { - // ignore - } - } + IOUtils.closeQuietly(in); + IOUtils.closeQuietly(out); } } } else { @@ -551,6 +535,9 @@ */ @Override protected void finalize() { + if (dfos == null) { + return; + } File outputFile = dfos.getFile(); if (outputFile != null && outputFile.exists()) { @@ -563,6 +550,9 @@ * named temporary file in the configured repository path. The lifetime of * the file is tied to the lifetime of the FileItem instance; * the file will be deleted when the instance is garbage collected. + *

+ * Note: Subclasses that override this method must ensure that they return the + * same File each time. * * @return The {@link java.io.File File} to be used for temporary storage. */ diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/FileCleaningTracker.java tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/FileCleaningTracker.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/FileCleaningTracker.java 2014-01-27 12:17:29.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/FileCleaningTracker.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,198 +0,0 @@ -/* - * 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.http.fileupload; - -import java.io.File; -import java.lang.ref.PhantomReference; -import java.lang.ref.ReferenceQueue; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; - -/** - * Keeps track of files awaiting deletion, and deletes them when an associated - * marker object is reclaimed by the garbage collector. - *

- * This utility creates a background thread to handle file deletion. - * Each file to be deleted is registered with a handler object. - * When the handler object is garbage collected, the file is deleted. - *

- * In an environment with multiple class loaders (a servlet container, for - * example), you should consider stopping the background thread if it is no - * longer needed. This is done by invoking the method - * {@link #exitWhenFinished}, typically in - * {@link javax.servlet.ServletContextListener#contextDestroyed} or similar. - */ -public class FileCleaningTracker { - /** - * Queue of Tracker instances being watched. - */ - private final ReferenceQueue q = new ReferenceQueue(); - /** - * Collection of Tracker instances in existence. - */ - private final Collection trackers = Collections.synchronizedSet(new HashSet()); // synchronized - /** - * Collection of File paths that failed to delete. - */ - private final List deleteFailures = Collections.synchronizedList(new ArrayList()); - /** - * Whether to terminate the thread when the tracking is complete. - */ - private volatile boolean exitWhenFinished = false; - /** - * The thread that will clean up registered files. - */ - private Thread reaper; - - //----------------------------------------------------------------------- - /** - * Track the specified file, using the provided marker, deleting the file - * when the marker instance is garbage collected. - * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used. - * - * @param file the file to be tracked, not null - * @param marker the marker object used to track the file, not null - * @throws NullPointerException if the file is null - */ - public void track(File file, Object marker) { - track(file, marker, (FileDeleteStrategy) null); - } - - /** - * Track the specified file, using the provided marker, deleting the file - * when the marker instance is garbage collected. - * The speified deletion strategy is used. - * - * @param file the file to be tracked, not null - * @param marker the marker object used to track the file, not null - * @param deleteStrategy the strategy to delete the file, null means normal - * @throws NullPointerException if the file is null - */ - public void track(File file, Object marker, FileDeleteStrategy deleteStrategy) { - if (file == null) { - throw new NullPointerException("The file must not be null"); - } - addTracker(file.getPath(), marker, deleteStrategy); - } - - /** - * Adds a tracker to the list of trackers. - * - * @param path the full path to the file to be tracked, not null - * @param marker the marker object used to track the file, not null - * @param deleteStrategy the strategy to delete the file, null means normal - */ - private synchronized void addTracker(String path, Object marker, FileDeleteStrategy deleteStrategy) { - // synchronized block protects reaper - if (exitWhenFinished) { - throw new IllegalStateException("No new trackers can be added once exitWhenFinished() is called"); - } - if (reaper == null) { - reaper = new Reaper(); - reaper.start(); - } - trackers.add(new Tracker(path, deleteStrategy, marker, q)); - } - - //----------------------------------------------------------------------- - /** - * The reaper thread. - */ - private final class Reaper extends Thread { - /** Construct a new Reaper */ - Reaper() { - super("File Reaper"); - setPriority(Thread.MAX_PRIORITY); - setDaemon(true); - } - - /** - * Run the reaper thread that will delete files as their associated - * marker objects are reclaimed by the garbage collector. - */ - @Override - public void run() { - // thread exits when exitWhenFinished is true and there are no more tracked objects - while (exitWhenFinished == false || trackers.size() > 0) { - try { - // Wait for a tracker to remove. - Tracker tracker = (Tracker) q.remove(); // cannot return null - trackers.remove(tracker); - if (!tracker.delete()) { - deleteFailures.add(tracker.getPath()); - } - tracker.clear(); - } catch (InterruptedException e) { - continue; - } - } - } - } - - //----------------------------------------------------------------------- - /** - * Inner class which acts as the reference for a file pending deletion. - */ - private static final class Tracker extends PhantomReference { - - /** - * The full path to the file being tracked. - */ - private final String path; - /** - * The strategy for deleting files. - */ - private final FileDeleteStrategy deleteStrategy; - - /** - * Constructs an instance of this class from the supplied parameters. - * - * @param path the full path to the file to be tracked, not null - * @param deleteStrategy the strategy to delete the file, null means normal - * @param marker the marker object used to track the file, not null - * @param queue the queue on to which the tracker will be pushed, not null - */ - Tracker(String path, FileDeleteStrategy deleteStrategy, Object marker, ReferenceQueue queue) { - super(marker, queue); - this.path = path; - this.deleteStrategy = deleteStrategy == null ? FileDeleteStrategy.NORMAL : deleteStrategy; - } - - /** - * Return the path. - * - * @return the path - */ - public String getPath() { - return path; - } - - /** - * Deletes the file associated with this tracker instance. - * - * @return {@code true} if the file was deleted successfully; - * {@code false} otherwise. - */ - public boolean delete() { - return deleteStrategy.deleteQuietly(new File(path)); - } - } - -} diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/FileDeleteStrategy.java tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/FileDeleteStrategy.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/FileDeleteStrategy.java 2014-01-27 12:17:29.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/FileDeleteStrategy.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,135 +0,0 @@ -/* - * 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.http.fileupload; - -import java.io.File; -import java.io.IOException; - -/** - * Strategy for deleting files. - *

- * There is more than one way to delete a file. - * You may want to limit access to certain directories, to only delete - * directories if they are empty, or maybe to force deletion. - *

- * This class captures the strategy to use and is designed for user subclassing. - * - * @since 1.3 - */ -public class FileDeleteStrategy { - - /** - * The singleton instance for normal file deletion, which does not permit - * the deletion of directories that are not empty. - */ - public static final FileDeleteStrategy NORMAL = new FileDeleteStrategy("Normal"); - - /** The name of the strategy. */ - private final String name; - - //----------------------------------------------------------------------- - /** - * Restricted constructor. - * - * @param name the name by which the strategy is known - */ - protected FileDeleteStrategy(String name) { - this.name = name; - } - - //----------------------------------------------------------------------- - /** - * Deletes the file object, which may be a file or a directory. - * All IOExceptions are caught and false returned instead. - * If the file does not exist or is null, true is returned. - *

- * Subclass writers should override {@link #doDelete(File)}, not this method. - * - * @param fileToDelete the file to delete, null returns true - * @return true if the file was deleted, or there was no such file - */ - public boolean deleteQuietly(File fileToDelete) { - if (fileToDelete == null || fileToDelete.exists() == false) { - return true; - } - try { - return doDelete(fileToDelete); - } catch (IOException ex) { - return false; - } - } - - /** - * Actually deletes the file object, which may be a file or a directory. - *

- * This method is designed for subclasses to override. - * The implementation may return either false or an IOException - * when deletion fails. The {@link #deleteQuietly(File)} method will handle - * either response appropriately. - * A check has been made to ensure that the file will exist. - *

- * This implementation uses {@link File#delete()}. - * - * @param fileToDelete the file to delete, exists, not null - * @return true if the file was deleteds - * @throws NullPointerException if the file is null - * @throws IOException if an error occurs during file deletion - */ - protected boolean doDelete(File fileToDelete) throws IOException { - return fileToDelete.delete(); - } - - //----------------------------------------------------------------------- - /** - * Gets a string describing the delete strategy. - * - * @return a string describing the delete strategy - */ - @Override - public String toString() { - return "FileDeleteStrategy[" + name + "]"; - } - - //----------------------------------------------------------------------- - /** - * Force file deletion strategy. - */ - static class ForceFileDeleteStrategy extends FileDeleteStrategy { - /** Default Constructor */ - ForceFileDeleteStrategy() { - super("Force"); - } - - /** - * Deletes the file object. - *

- * This implementation uses FileUtils.forceDelete() - * if the file exists. - * - * @param fileToDelete the file to delete, not null - * @return Always returns {@code true} - * @throws NullPointerException if the file is null - * @throws IOException if an error occurs during file deletion - */ - @Override - protected boolean doDelete(File fileToDelete) throws IOException { - FileUtils.forceDelete(fileToDelete); - return true; - } - } - -} diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java 2015-05-28 20:33:45.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java 2016-08-22 15:38:48.000000000 +0000 @@ -806,10 +806,10 @@ MULTIPART_FORM_DATA, MULTIPART_MIXED, contentType)); } - InputStream input = ctx.getInputStream(); final long requestSize = ((UploadContext) ctx).contentLength(); + InputStream input; // N.B. this is eventually closed in MultipartStream processing if (sizeMax >= 0) { if (requestSize != -1 && requestSize > sizeMax) { throw new SizeLimitExceededException(String.format( @@ -817,7 +817,8 @@ Long.valueOf(requestSize), Long.valueOf(sizeMax)), requestSize, sizeMax); } - input = new LimitedInputStream(input, sizeMax) { + // N.B. this is eventually closed in MultipartStream processing + input = new LimitedInputStream(ctx.getInputStream(), sizeMax) { @Override protected void raiseError(long pSizeMax, long pCount) throws IOException { @@ -828,6 +829,8 @@ throw new FileUploadIOException(ex); } }; + } else { + input = ctx.getInputStream(); } String charEncoding = headerEncoding; @@ -837,6 +840,7 @@ boundary = getBoundary(contentType); if (boundary == null) { + IOUtils.closeQuietly(input); // avoid possible resource leak throw new FileUploadException("the request was rejected because no multipart boundary was found"); } @@ -844,9 +848,9 @@ try { multi = new MultipartStream(input, boundary, notifier); } catch (IllegalArgumentException iae) { - throw new InvalidContentTypeException(String.format( - "The boundary specified in the %s header is too long", - CONTENT_TYPE), iae); + IOUtils.closeQuietly(input); // avoid possible resource leak + throw new InvalidContentTypeException( + String.format("The boundary specified in the %s header is too long", CONTENT_TYPE), iae); } multi.setHeaderEncoding(charEncoding); @@ -1038,6 +1042,15 @@ super(message); } + /** + * Constructs an InvalidContentTypeException with + * the specified detail message and cause. + * + * @param msg The detail message. + * @param cause the original cause + * + * @since 1.3.1 + */ public InvalidContentTypeException(String msg, Throwable cause) { super(msg, cause); } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/FileUtils.java tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/FileUtils.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/FileUtils.java 2014-01-27 12:17:29.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/FileUtils.java 2016-08-22 15:38:48.000000000 +0000 @@ -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. @@ -51,7 +51,7 @@ //----------------------------------------------------------------------- /** - * Deletes a directory recursively. + * Deletes a directory recursively. * * @param directory directory to delete * @throws IOException in case deletion is unsuccessful diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/IOUtils.java tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/IOUtils.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/IOUtils.java 2015-05-28 20:33:45.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/IOUtils.java 2016-08-22 15:38:48.000000000 +0000 @@ -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. @@ -17,6 +17,7 @@ package org.apache.tomcat.util.http.fileupload; import java.io.Closeable; +import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -119,7 +120,7 @@ // ignore } } - + // copy from InputStream //----------------------------------------------------------------------- /** @@ -133,7 +134,7 @@ * -1 after the copy has completed since the correct * number of bytes cannot be returned as an int. For large streams * use the copyLarge(InputStream, OutputStream) method. - * + * * @param input the InputStream to read from * @param output the OutputStream to write to * @return the number of bytes copied, or -1 if > Integer.MAX_VALUE @@ -177,4 +178,75 @@ } return count; } + + /** + * Reads bytes from an input stream. + * This implementation guarantees that it will read as many bytes + * as possible before giving up; this may not always be the case for + * subclasses of {@link InputStream}. + * + * @param input where to read input from + * @param buffer destination + * @param offset initial offset into buffer + * @param length length to read, must be >= 0 + * @return actual length read; may be less than requested if EOF was reached + * @throws IOException if a read error occurs + * @since 2.2 + */ + public static int read(final InputStream input, final byte[] buffer, final int offset, final int length) throws IOException { + if (length < 0) { + throw new IllegalArgumentException("Length must not be negative: " + length); + } + int remaining = length; + while (remaining > 0) { + final int location = length - remaining; + final int count = input.read(buffer, offset + location, remaining); + if (EOF == count) { // EOF + break; + } + remaining -= count; + } + return length - remaining; + } + + /** + * Reads the requested number of bytes or fail if there are not enough left. + *

+ * This allows for the possibility that {@link InputStream#read(byte[], int, int)} may + * not read as many bytes as requested (most likely because of reaching EOF). + * + * @param input where to read input from + * @param buffer destination + * @param offset initial offset into buffer + * @param length length to read, must be >= 0 + * + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if length is negative + * @throws EOFException if the number of bytes read was incorrect + * @since 2.2 + */ + public static void readFully(final InputStream input, final byte[] buffer, final int offset, final int length) throws IOException { + final int actual = read(input, buffer, offset, length); + if (actual != length) { + throw new EOFException("Length to read: " + length + " actual: " + actual); + } + } + + /** + * Reads the requested number of bytes or fail if there are not enough left. + *

+ * This allows for the possibility that {@link InputStream#read(byte[], int, int)} may + * not read as many bytes as requested (most likely because of reaching EOF). + * + * @param input where to read input from + * @param buffer destination + * + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if length is negative + * @throws EOFException if the number of bytes read was incorrect + * @since 2.2 + */ + public static void readFully(final InputStream input, final byte[] buffer) throws IOException { + readFully(input, buffer, 0, buffer.length); + } } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java 2016-05-13 20:08:22.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java 2016-08-22 15:38:48.000000000 +0000 @@ -49,7 +49,7 @@ * header-part := 1*header CRLF
* header := header-name ":" header-value
* header-name := <printable ascii characters except ":">
- * header-value := <any ascii characters except CR & LF>
+ * header-value := <any ascii characters except CR & LF>
* body-data := <arbitrary data>
*
* @@ -218,12 +218,17 @@ * The amount of data, in bytes, that must be kept in the buffer in order * to detect delimiters reliably. */ - private int keepRegion; + private final int keepRegion; /** * The byte sequence that partitions the stream. */ - private byte[] boundary; + private final byte[] boundary; + + /** + * The table for Knuth-Morris-Pratt search algorithm + */ + private int[] boundaryTable; /** * The length of the buffer used for processing the request. @@ -277,6 +282,8 @@ * progress listener, if any. * * @throws IllegalArgumentException If the buffer size is too small + * + * @since 1.3.1 */ public MultipartStream(InputStream input, byte[] boundary, @@ -300,12 +307,14 @@ this.notifier = pNotifier; this.boundary = new byte[this.boundaryLength]; + this.boundaryTable = new int[this.boundaryLength + 1]; this.keepRegion = this.boundary.length; System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0, BOUNDARY_PREFIX.length); System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, boundary.length); + computeBoundaryTable(); head = 0; tail = 0; @@ -447,10 +456,35 @@ throws IllegalBoundaryException { if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) { throw new IllegalBoundaryException( - "The length of a boundary token can not be changed"); + "The length of a boundary token cannot be changed"); } System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, boundary.length); + computeBoundaryTable(); + } + + /** + * Compute the table used for Knuth-Morris-Pratt search algorithm. + */ + private void computeBoundaryTable() { + int position = 2; + int candidate = 0; + + boundaryTable[0] = -1; + boundaryTable[1] = 0; + + while (position <= boundaryLength) { + if (boundary[position - 1] == boundary[candidate]) { + boundaryTable[position] = candidate + 1; + candidate++; + position++; + } else if (candidate > 0) { + candidate = boundaryTable[candidate]; + } else { + boundaryTable[position] = 0; + position++; + } + } } /** @@ -534,8 +568,7 @@ */ public int readBodyData(OutputStream output) throws MalformedStreamException, IOException { - final InputStream istream = newInputStream(); - return (int) Streams.copy(istream, output, false); + return (int) Streams.copy(newInputStream(), output, false); // N.B. Streams.copy closes the input stream } /** @@ -574,6 +607,7 @@ // First delimiter may be not preceeded with a CRLF. System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2); boundaryLength = boundary.length - 2; + computeBoundaryTable(); try { // Discard all data up to the delimiter. discardBodyData(); @@ -589,6 +623,7 @@ boundaryLength = boundary.length; boundary[0] = CR; boundary[1] = LF; + computeBoundaryTable(); } } @@ -644,25 +679,20 @@ * not found. */ protected int findSeparator() { - int first; - int match = 0; - int maxpos = tail - boundaryLength; - for (first = head; - (first <= maxpos) && (match != boundaryLength); - first++) { - first = findByte(boundary[0], first); - if (first == -1 || (first > maxpos)) { - return -1; - } - for (match = 1; match < boundaryLength; match++) { - if (buffer[first + match] != boundary[match]) { - break; - } + + int bufferPos = this.head; + int tablePos = 0; + + while (bufferPos < this.tail) { + while (tablePos >= 0 && buffer[bufferPos] != boundary[tablePos]) { + tablePos = boundaryTable[tablePos]; + } + bufferPos++; + tablePos++; + if (tablePos == boundaryLength) { + return bufferPos - boundaryLength; } } - if (match == boundaryLength) { - return first - 1; - } return -1; } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/ParameterParser.java tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/ParameterParser.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/ParameterParser.java 2015-05-28 20:33:45.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/ParameterParser.java 2016-08-22 15:38:48.000000000 +0000 @@ -127,8 +127,8 @@ */ private boolean isOneOf(char ch, final char[] charray) { boolean result = false; - for (int i = 0; i < charray.length; i++) { - if (ch == charray[i]) { + for (char element : charray) { + if (ch == element) { result = true; break; } @@ -233,11 +233,11 @@ char separator = separators[0]; if (str != null) { int idx = str.length(); - for (int i = 0; i < separators.length; i++) { - int tmp = str.indexOf(separators[i]); + for (char separator2 : separators) { + int tmp = str.indexOf(separator2); if (tmp != -1 && tmp < idx) { idx = tmp; - separator = separators[i]; + separator = separator2; } } } @@ -264,24 +264,24 @@ * Extracts a map of name/value pairs from the given array of * characters. Names are expected to be unique. * - * @param chars the array of characters that contains a sequence of + * @param charArray the array of characters that contains a sequence of * name/value pairs * @param separator the name/value pairs separator * * @return a map of name/value pairs */ - public Map parse(final char[] chars, char separator) { - if (chars == null) { + public Map parse(final char[] charArray, char separator) { + if (charArray == null) { return new HashMap(); } - return parse(chars, 0, chars.length, separator); + return parse(charArray, 0, charArray.length, separator); } /** * Extracts a map of name/value pairs from the given array of * characters. Names are expected to be unique. * - * @param chars the array of characters that contains a sequence of + * @param charArray the array of characters that contains a sequence of * name/value pairs * @param offset - the initial offset. * @param length - the length. @@ -290,16 +290,16 @@ * @return a map of name/value pairs */ public Map parse( - final char[] chars, + final char[] charArray, int offset, int length, char separator) { - if (chars == null) { + if (charArray == null) { return new HashMap(); } HashMap params = new HashMap(); - this.chars = chars; + this.chars = charArray; this.pos = offset; this.len = length; @@ -309,7 +309,7 @@ paramName = parseToken(new char[] { '=', separator }); paramValue = null; - if (hasChar() && (chars[pos] == '=')) { + if (hasChar() && (charArray[pos] == '=')) { pos++; // skip '=' paramValue = parseQuotedToken(new char[] { separator }); @@ -322,7 +322,7 @@ } } } - if (hasChar() && (chars[pos] == separator)) { + if (hasChar() && (charArray[pos] == separator)) { pos++; // skip separator } if ((paramName != null) && (paramName.length() > 0)) { diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/ThresholdingOutputStream.java tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/ThresholdingOutputStream.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/ThresholdingOutputStream.java 2014-01-27 12:17:29.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/ThresholdingOutputStream.java 2016-08-22 15:38:48.000000000 +0000 @@ -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. diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/util/LimitedInputStream.java tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/util/LimitedInputStream.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/util/LimitedInputStream.java 2014-01-27 12:17:29.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/util/LimitedInputStream.java 2016-08-22 15:38:48.000000000 +0000 @@ -44,12 +44,12 @@ /** * Creates a new instance. * - * @param pIn The input stream, which shall be limited. + * @param inputStream The input stream, which shall be limited. * @param pSizeMax The limit; no more than this number of bytes * shall be returned by the source stream. */ - public LimitedInputStream(InputStream pIn, long pSizeMax) { - super(pIn); + public LimitedInputStream(InputStream inputStream, long pSizeMax) { + super(inputStream); sizeMax = pSizeMax; } @@ -91,7 +91,7 @@ * * @return the next byte of data, or -1 if the end of the * stream is reached. - * @exception IOException if an I/O error occurs. + * @throws IOException if an I/O error occurs. * @see java.io.FilterInputStream#in */ @Override @@ -120,11 +120,11 @@ * @return the total number of bytes read into the buffer, or * -1 if there is no more data because the end of * the stream has been reached. - * @exception NullPointerException If b is null. - * @exception IndexOutOfBoundsException If off is negative, + * @throws NullPointerException If b is null. + * @throws IndexOutOfBoundsException If off is negative, * len is negative, or len is greater than * b.length - off - * @exception IOException if an I/O error occurs. + * @throws IOException if an I/O error occurs. * @see java.io.FilterInputStream#in */ @Override @@ -154,7 +154,7 @@ * This * method simply performs in.close(). * - * @exception IOException if an I/O error occurs. + * @throws IOException if an I/O error occurs. * @see java.io.FilterInputStream#in */ @Override diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/util/mime/QuotedPrintableDecoder.java tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/util/mime/QuotedPrintableDecoder.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/util/mime/QuotedPrintableDecoder.java 2014-01-27 12:17:29.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/util/mime/QuotedPrintableDecoder.java 2016-08-22 15:38:48.000000000 +0000 @@ -44,7 +44,8 @@ * @param out The output stream used to return the decoded data. * * @return the number of bytes produced. - * @exception IOException + * @throws IOException if a problem occurs during either decoding or + * writing to the stream */ public static int decode(byte[] data, OutputStream out) throws IOException { int off = 0; diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/util/Streams.java tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/util/Streams.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/fileupload/util/Streams.java 2014-01-27 12:17:29.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/fileupload/util/Streams.java 2016-08-22 15:38:48.000000000 +0000 @@ -21,6 +21,7 @@ import java.io.InputStream; import java.io.OutputStream; +import org.apache.tomcat.util.http.fileupload.IOUtils; import org.apache.tomcat.util.http.fileupload.InvalidFileNameException; /** @@ -49,66 +50,64 @@ * copy(pInputStream, pOutputStream, new byte[8192]); * * - * @param pInputStream The input stream, which is being read. + * @param inputStream The input stream, which is being read. * It is guaranteed, that {@link InputStream#close()} is called * on the stream. - * @param pOutputStream The output stream, to which data should + * @param outputStream The output stream, to which data should * be written. May be null, in which case the input streams * contents are simply discarded. - * @param pClose True guarantees, that {@link OutputStream#close()} + * @param closeOutputStream True guarantees, that {@link OutputStream#close()} * is called on the stream. False indicates, that only * {@link OutputStream#flush()} should be called finally. * * @return Number of bytes, which have been copied. * @throws IOException An I/O error occurred. */ - public static long copy(InputStream pInputStream, - OutputStream pOutputStream, boolean pClose) + public static long copy(InputStream inputStream, OutputStream outputStream, boolean closeOutputStream) throws IOException { - return copy(pInputStream, pOutputStream, pClose, - new byte[DEFAULT_BUFFER_SIZE]); + return copy(inputStream, outputStream, closeOutputStream, new byte[DEFAULT_BUFFER_SIZE]); } /** * Copies the contents of the given {@link InputStream} * to the given {@link OutputStream}. * - * @param pIn The input stream, which is being read. + * @param inputStream The input stream, which is being read. * It is guaranteed, that {@link InputStream#close()} is called * on the stream. - * @param pOut The output stream, to which data should + * @param outputStream The output stream, to which data should * be written. May be null, in which case the input streams * contents are simply discarded. - * @param pClose True guarantees, that {@link OutputStream#close()} + * @param closeOutputStream True guarantees, that {@link OutputStream#close()} * is called on the stream. False indicates, that only * {@link OutputStream#flush()} should be called finally. - * @param pBuffer Temporary buffer, which is to be used for + * @param buffer Temporary buffer, which is to be used for * copying data. * @return Number of bytes, which have been copied. * @throws IOException An I/O error occurred. */ - public static long copy(InputStream pIn, - OutputStream pOut, boolean pClose, - byte[] pBuffer) + public static long copy(InputStream inputStream, + OutputStream outputStream, boolean closeOutputStream, + byte[] buffer) throws IOException { - OutputStream out = pOut; - InputStream in = pIn; + OutputStream out = outputStream; + InputStream in = inputStream; try { long total = 0; for (;;) { - int res = in.read(pBuffer); + int res = in.read(buffer); if (res == -1) { break; } if (res > 0) { total += res; if (out != null) { - out.write(pBuffer, 0, res); + out.write(buffer, 0, res); } } } if (out != null) { - if (pClose) { + if (closeOutputStream) { out.close(); } else { out.flush(); @@ -119,19 +118,9 @@ in = null; return total; } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ioe) { - /* Ignore me */ - } - } - if (pClose && out != null) { - try { - out.close(); - } catch (IOException ioe) { - /* Ignore me */ - } + IOUtils.closeQuietly(in); + if (closeOutputStream) { + IOUtils.closeQuietly(out); } } } @@ -142,14 +131,14 @@ * content into a string. The platform's default character encoding * is used for converting bytes into characters. * - * @param pStream The input stream to read. + * @param inputStream The input stream to read. * @see #asString(InputStream, String) * @return The streams contents, as a string. * @throws IOException An I/O error occurred. */ - public static String asString(InputStream pStream) throws IOException { + public static String asString(InputStream inputStream) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - copy(pStream, baos, true); + copy(inputStream, baos, true); return baos.toString(); } @@ -158,17 +147,16 @@ * {@link org.apache.tomcat.util.http.fileupload.FileItemStream}'s * content into a string, using the given character encoding. * - * @param pStream The input stream to read. - * @param pEncoding The character encoding, typically "UTF-8". + * @param inputStream The input stream to read. + * @param encoding The character encoding, typically "UTF-8". * @see #asString(InputStream) * @return The streams contents, as a string. * @throws IOException An I/O error occurred. */ - public static String asString(InputStream pStream, String pEncoding) - throws IOException { + public static String asString(InputStream inputStream, String encoding) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - copy(pStream, baos, true); - return baos.toString(pEncoding); + copy(inputStream, baos, true); + return baos.toString(encoding); } /** @@ -177,16 +165,16 @@ * is valid, it will be returned without any modifications. Otherwise, * an {@link InvalidFileNameException} is raised. * - * @param pFileName The file name to check + * @param fileName The file name to check * @return Unmodified file name, if valid. * @throws InvalidFileNameException The file name was found to be invalid. */ - public static String checkFileName(String pFileName) { - if (pFileName != null && pFileName.indexOf('\u0000') != -1) { + public static String checkFileName(String fileName) { + if (fileName != null && fileName.indexOf('\u0000') != -1) { // pFileName.replace("\u0000", "\\0") final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < pFileName.length(); i++) { - char c = pFileName.charAt(i); + for (int i = 0; i < fileName.length(); i++) { + char c = fileName.charAt(i); switch (c) { case 0: sb.append("\\0"); @@ -196,10 +184,10 @@ break; } } - throw new InvalidFileNameException(pFileName, + throw new InvalidFileNameException(fileName, "Invalid file name: " + sb); } - return pFileName; + return fileName; } } diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/http/LocalStrings.properties tomcat7-7.0.72/java/org/apache/tomcat/util/http/LocalStrings.properties --- tomcat7-7.0.70/java/org/apache/tomcat/util/http/LocalStrings.properties 2012-06-14 15:59:15.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/http/LocalStrings.properties 2016-08-11 18:57:23.000000000 +0000 @@ -28,5 +28,6 @@ cookies.invalidCookieToken=Cookies: Invalid cookie. Value not a token or quoted value cookies.invalidSpecial=Cookies: Unknown Special Cookie cookies.fallToDebug=\n Note: further occurrences of Cookie errors will be logged at DEBUG level. +cookies.maxCountFail=More than the maximum allowed number of cookies, [{0}], were detected. -headers.maxCountFail=More than the maximum allowed number of headers ([{0}]) were detected. +headers.maxCountFail=More than the maximum allowed number of headers, [{0}], were detected. diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/net/SocketProperties.java tomcat7-7.0.72/java/org/apache/tomcat/util/net/SocketProperties.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/net/SocketProperties.java 2015-10-27 10:17:55.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/net/SocketProperties.java 2016-08-11 09:12:16.000000000 +0000 @@ -58,7 +58,7 @@ /** * Enable/disable direct buffers for the network buffers - * Default value is enabled + * Default value is disabled */ protected boolean directBuffer = false; diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/util/security/PermissionCheck.java tomcat7-7.0.72/java/org/apache/tomcat/util/security/PermissionCheck.java --- tomcat7-7.0.70/java/org/apache/tomcat/util/security/PermissionCheck.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/util/security/PermissionCheck.java 2016-08-02 12:13:00.000000000 +0000 @@ -0,0 +1,43 @@ +/* + * 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.security; + +import java.security.Permission; + +/** + * This interface is implemented by components to enable privileged code to + * check whether the component has a given permission. + * This is typically used when a privileged component (e.g. the container) is + * performing an action on behalf of an untrusted component (e.g. a web + * application) without the current thread having passed through a code source + * provided by the untrusted component. Because the current thread has not + * passed through a code source provided by the untrusted component the + * SecurityManager assumes the code is trusted so the standard checking + * mechanisms can't be used. + */ +public interface PermissionCheck { + + /** + * Does this component have the given permission? + * + * @param permission The permission to test + * + * @return {@code false} if a SecurityManager is enabled and the component + * does not have the given permission, otherwise {@code true} + */ + boolean check(Permission permission); +} diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/websocket/FutureToSendHandler.java tomcat7-7.0.72/java/org/apache/tomcat/websocket/FutureToSendHandler.java --- tomcat7-7.0.70/java/org/apache/tomcat/websocket/FutureToSendHandler.java 2016-02-23 15:00:53.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/websocket/FutureToSendHandler.java 2016-08-11 11:30:03.000000000 +0000 @@ -25,11 +25,16 @@ import javax.websocket.SendHandler; import javax.websocket.SendResult; +import org.apache.tomcat.util.res.StringManager; + + /** * Converts a Future to a SendHandler. */ class FutureToSendHandler implements Future, SendHandler { + private static final StringManager sm = StringManager.getManager(Constants.PACKAGE_NAME); + private final CountDownLatch latch = new CountDownLatch(1); private final WsSession wsSession; private final boolean closeMessage; @@ -108,7 +113,8 @@ } if (retval == false) { - throw new TimeoutException(); + throw new TimeoutException(sm.getString("futureToSendHandler.timeout", + Long.valueOf(timeout), unit.toString().toLowerCase())); } if (result.getException() != null) { throw new ExecutionException(result.getException()); diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/websocket/LocalStrings.properties tomcat7-7.0.72/java/org/apache/tomcat/websocket/LocalStrings.properties --- tomcat7-7.0.70/java/org/apache/tomcat/websocket/LocalStrings.properties 2015-12-11 12:51:51.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/websocket/LocalStrings.properties 2016-08-10 22:55:00.000000000 +0000 @@ -30,6 +30,8 @@ caseInsensitiveKeyMap.nullKey=Null keys are not permitted +futureToSendHandler.timeout=Operation timed out after waiting [{0}] [{1}] to complete + perMessageDeflate.deflateFailed=Failed to decompress a compressed WebSocket frame perMessageDeflate.duplicateParameter=Duplicate definition of the [{0}] extension parameter perMessageDeflate.invalidWindowSize=An invalid windows of [{1}] size was specified for [{0}]. Valid values are whole numbers from 8 to 15 inclusive. diff -Nru tomcat7-7.0.70/java/org/apache/tomcat/websocket/WsSession.java tomcat7-7.0.72/java/org/apache/tomcat/websocket/WsSession.java --- tomcat7-7.0.70/java/org/apache/tomcat/websocket/WsSession.java 2016-04-14 20:04:25.000000000 +0000 +++ tomcat7-7.0.72/java/org/apache/tomcat/websocket/WsSession.java 2016-07-01 20:15:43.000000000 +0000 @@ -506,21 +506,33 @@ private void fireEndpointOnClose(CloseReason closeReason) { // Fire the onClose event + Throwable throwable = null; InstanceManager instanceManager = webSocketContainer.getInstanceManager(); Thread t = Thread.currentThread(); ClassLoader cl = t.getContextClassLoader(); t.setContextClassLoader(applicationClassLoader); try { localEndpoint.onClose(this, closeReason); + } catch (Throwable t1) { + ExceptionUtils.handleThrowable(t1); + throwable = t1; + } finally { if (instanceManager != null) { - instanceManager.destroyInstance(localEndpoint); + try { + instanceManager.destroyInstance(localEndpoint); + } catch (Throwable t2) { + ExceptionUtils.handleThrowable(t2); + if (throwable == null) { + throwable = t2; + } + } } - } catch (Throwable throwable) { - ExceptionUtils.handleThrowable(throwable); - localEndpoint.onError(this, throwable); - } finally { t.setContextClassLoader(cl); } + + if (throwable != null) { + fireEndpointOnError(throwable); + } } diff -Nru tomcat7-7.0.70/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java tomcat7-7.0.72/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java --- tomcat7-7.0.70/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java 2016-05-20 10:57:27.000000000 +0000 +++ tomcat7-7.0.72/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java 2016-09-12 08:16:06.000000000 +0000 @@ -556,9 +556,11 @@ } /** - * thread safe way to abandon a connection - * signals a connection to be abandoned. - * this will disconnect the connection, and log the stack trace if logAbanded=true + * Thread safe way to suspect a connection. Similar to + * {@link #abandon(PooledConnection)}, but instead of actually abandoning + * the connection, this will log a warning and set the suspect flag on the + * {@link PooledConnection} if logAbandoned=true + * * @param con PooledConnection */ protected void suspect(PooledConnection con) { @@ -628,7 +630,6 @@ if (con!=null) { //configure the connection and return it PooledConnection result = borrowConnection(now, con, username, password); - //null should never be returned, but was in a previous impl. if (result!=null) return result; } @@ -766,20 +767,9 @@ } if (!con.isDiscarded() && !con.isInitialized()) { - //attempt to connect - try { - con.connect(); - } catch (Exception x) { - release(con); - setToNull = true; - if (x instanceof SQLException) { - throw (SQLException)x; - } else { - SQLException ex = new SQLException(x.getMessage()); - ex.initCause(x); - throw ex; - } - } + //here it states that the connection not discarded, but the connection is null + //don't attempt a connect here. It will be done during the reconnect. + usercheck = false; } if (usercheck) { @@ -803,7 +793,10 @@ //the connection shouldn't have to poll again. try { con.reconnect(); - if (con.validate(PooledConnection.VALIDATE_INIT)) { + int validationMode = getPoolProperties().isTestOnConnect() || getPoolProperties().getInitSQL()!=null ? + PooledConnection.VALIDATE_INIT : + PooledConnection.VALIDATE_BORROW; + if (con.validate(validationMode)) { //set the timestamp con.setTimestamp(now); if (getPoolProperties().isLogAbandoned()) { @@ -816,8 +809,6 @@ return con; } else { //validation failed. - release(con); - setToNull = true; throw new SQLException("Failed to validate a newly established connection."); } } catch (Exception x) { @@ -900,7 +891,16 @@ if (con != null) { try { con.lock(); - + if (con.isSuspect()) { + if (poolProperties.isLogAbandoned() && log.isInfoEnabled()) { + log.info("Connection(" + con + ") that has been marked suspect was returned." + + " The processing time is " + (System.currentTimeMillis()-con.getTimestamp()) + " ms."); + } + if (jmxPool!=null) { + jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.SUSPECT_RETURNED_NOTIFICATION, + "Connection(" + con + ") that has been marked suspect was returned."); + } + } if (busy.remove(con)) { if (!shouldClose(con,PooledConnection.VALIDATE_RETURN)) { @@ -936,6 +936,7 @@ * @return true if the connection should be abandoned */ protected boolean shouldAbandon() { + if (!poolProperties.isRemoveAbandoned()) return false; if (poolProperties.getAbandonWhenPercentageFull()==0) return true; float used = busy.size(); float max = poolProperties.getMaxActive(); @@ -956,9 +957,9 @@ boolean setToNull = false; try { con.lock(); - //the con has been returned to the pool + //the con has been returned to the pool or released //ignore it - if (idle.contains(con)) + if (idle.contains(con) || con.isReleased()) continue; long time = con.getTimestamp(); long now = System.currentTimeMillis(); @@ -1301,7 +1302,7 @@ Thread.currentThread().setContextClassLoader(loader); } } - poolCleanTimer.scheduleAtFixedRate(cleaner, cleaner.sleepTime,cleaner.sleepTime); + poolCleanTimer.schedule(cleaner, cleaner.sleepTime,cleaner.sleepTime); } private static synchronized void unregisterCleaner(PoolCleaner cleaner) { @@ -1341,7 +1342,6 @@ protected static class PoolCleaner extends TimerTask { protected WeakReference pool; protected long sleepTime; - protected volatile long lastRun = 0; PoolCleaner(ConnectionPool pool, long sleepTime) { this.pool = new WeakReference(pool); @@ -1359,11 +1359,10 @@ ConnectionPool pool = this.pool.get(); if (pool == null) { stopRunning(); - } else if (!pool.isClosed() && - (System.currentTimeMillis() - lastRun) > sleepTime) { - lastRun = System.currentTimeMillis(); + } else if (!pool.isClosed()) { try { - if (pool.getPoolProperties().isRemoveAbandoned()) + if (pool.getPoolProperties().isRemoveAbandoned() + || pool.getPoolProperties().getSuspectTimeout() > 0) pool.checkAbandoned(); if (pool.getPoolProperties().getMinIdle() < pool.idle .size()) diff -Nru tomcat7-7.0.70/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java tomcat7-7.0.72/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java --- tomcat7-7.0.70/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java 2013-08-08 18:38:34.000000000 +0000 +++ tomcat7-7.0.72/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java 2016-07-20 06:29:33.000000000 +0000 @@ -18,6 +18,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.sql.ResultSet; import java.sql.Statement; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -236,6 +237,11 @@ //cache a proxy so that we don't reuse the facade CachedStatement proxy = new CachedStatement(getDelegate(),getSql()); try { + // clear Resultset + ResultSet result = getDelegate().getResultSet(); + if (result != null && !result.isClosed()) { + result.close(); + } //create a new facade Object actualProxy = getConstructor().newInstance(new Object[] { proxy }); proxy.setActualProxy(actualProxy); diff -Nru tomcat7-7.0.70/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java tomcat7-7.0.72/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java --- tomcat7-7.0.70/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java 2012-12-03 11:15:12.000000000 +0000 +++ tomcat7-7.0.72/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java 2016-08-04 08:49:14.000000000 +0000 @@ -40,7 +40,11 @@ private static final Log logger = LogFactory.getLog(StatementDecoratorInterceptor.class); - private static final String[] EXECUTE_QUERY_TYPES = { "executeQuery" }; + protected static final String EXECUTE_QUERY = "executeQuery"; + protected static final String GET_GENERATED_KEYS = "getGeneratedKeys"; + protected static final String GET_RESULTSET = "getResultSet"; + + protected static final String[] RESULTSET_TYPES = {EXECUTE_QUERY, GET_GENERATED_KEYS, GET_RESULTSET}; /** * the constructors that are used to create statement proxies @@ -154,13 +158,17 @@ } protected boolean isExecuteQuery(String methodName) { - return EXECUTE_QUERY_TYPES[0].equals(methodName); + return EXECUTE_QUERY.equals(methodName); } protected boolean isExecuteQuery(Method method) { return isExecuteQuery(method.getName()); } + protected boolean isResultSet(Method method, boolean process) { + return process(RESULTSET_TYPES, method, process); + } + /** * Class to measure query execute time * @@ -239,7 +247,8 @@ if (compare(GETCONNECTION_VAL,method)){ return connection; } - boolean process = isExecuteQuery(method); + boolean process = false; + process = isResultSet(method, process); // check to see if we are about to execute a query // if we are executing, get the current time Object result = null; @@ -259,7 +268,7 @@ throw t; } } - if (process){ + if (process && result != null) { Constructor cons = getResultSetConstructor(); result = cons.newInstance(new Object[]{new ResultSetProxy(actualProxy, result)}); } diff -Nru tomcat7-7.0.70/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java tomcat7-7.0.72/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java --- tomcat7-7.0.70/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java 2015-08-25 06:39:51.000000000 +0000 +++ tomcat7-7.0.72/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java 2016-09-12 09:11:19.000000000 +0000 @@ -76,6 +76,7 @@ public static final String FAILED_QUERY_NOTIFICATION = "FAILED QUERY"; public static final String SUSPECT_ABANDONED_NOTIFICATION = "SUSPECT CONNETION ABANDONED"; public static final String POOL_EMPTY = "POOL EMPTY"; + public static final String SUSPECT_RETURNED_NOTIFICATION = "SUSPECT CONNETION RETURNED"; @Override public MBeanNotificationInfo[] getNotificationInfo() { @@ -88,7 +89,8 @@ } public static MBeanNotificationInfo[] getDefaultNotificationInfo() { - String[] types = new String[] {NOTIFY_INIT, NOTIFY_CONNECT, NOTIFY_ABANDON, SLOW_QUERY_NOTIFICATION, FAILED_QUERY_NOTIFICATION, SUSPECT_ABANDONED_NOTIFICATION}; + String[] types = new String[] {NOTIFY_INIT, NOTIFY_CONNECT, NOTIFY_ABANDON, SLOW_QUERY_NOTIFICATION, + FAILED_QUERY_NOTIFICATION, SUSPECT_ABANDONED_NOTIFICATION, POOL_EMPTY, SUSPECT_RETURNED_NOTIFICATION}; String name = Notification.class.getName(); String description = "A connection pool error condition was met."; MBeanNotificationInfo info = new MBeanNotificationInfo(types, name, description); diff -Nru tomcat7-7.0.70/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java tomcat7-7.0.72/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java --- tomcat7-7.0.70/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java 2015-11-03 09:18:06.000000000 +0000 +++ tomcat7-7.0.72/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java 2016-08-03 08:45:25.000000000 +0000 @@ -73,7 +73,7 @@ private volatile String name = "Tomcat Connection Pool["+(poolCounter.addAndGet(1))+"-"+System.identityHashCode(PoolProperties.class)+"]"; private volatile String password; private volatile String username; - private volatile long validationInterval = 30000; + private volatile long validationInterval = 3000; private volatile boolean jmxEnabled = true; private volatile String initSQL; private volatile boolean testOnConnect =false; diff -Nru tomcat7-7.0.70/res/maven/mvn.properties.default tomcat7-7.0.72/res/maven/mvn.properties.default --- tomcat7-7.0.70/res/maven/mvn.properties.default 2016-04-11 10:56:33.000000000 +0000 +++ tomcat7-7.0.72/res/maven/mvn.properties.default 2016-09-07 11:06:50.000000000 +0000 @@ -35,7 +35,7 @@ maven.asf.release.repo.repositoryId=apache.releases # Release version info -maven.asf.release.deploy.version=7.0.70 +maven.asf.release.deploy.version=7.0.72 #Where do we load the libraries from tomcat.lib.path=../../output/build/lib diff -Nru tomcat7-7.0.70/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcher.java tomcat7-7.0.72/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcher.java --- tomcat7-7.0.70/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcher.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.72/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcher.java 2016-07-27 15:58:11.000000000 +0000 @@ -0,0 +1,504 @@ +/* + * 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.catalina.core; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; + +import javax.servlet.AsyncContext; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import org.apache.catalina.Context; +import org.apache.catalina.Wrapper; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.catalina.util.URLEncoder; +import org.apache.tomcat.util.buf.ByteChunk; + +@RunWith(value = Parameterized.class) +public class TestApplicationContextGetRequestDispatcher extends TomcatBaseTest { + + private final boolean useAsync; + + public TestApplicationContextGetRequestDispatcher(boolean useAsync) { + this.useAsync = useAsync; + } + + @Parameters(name = "{index}: useAsync[{0}]") + public static Collection data() { + return Arrays.asList(new Object[][]{ + {Boolean.TRUE}, + {Boolean.FALSE} + }); + } + + @Test + public void testGetRequestDispatcherNullPath01() throws Exception { + doTestGetRequestDispatcher(true, "/start", null, null, "/target", DispatcherServlet.NULL); + } + + + @Test + public void testGetRequestDispatcherNullPath02() throws Exception { + doTestGetRequestDispatcher(false, "/start", null, null, "/target", DispatcherServlet.NULL); + } + + + @Test + public void testGetRequestDispatcherOutsideContextRoot01() throws Exception { + doTestGetRequestDispatcher( + true, "/start", null, "../outside", "/target", DispatcherServlet.NULL); + } + + + @Test + public void testGetRequestDispatcherOutsideContextRoot02() throws Exception { + doTestGetRequestDispatcher( + false, "/start", null, "../outside", "/target", DispatcherServlet.NULL); + } + + + @Test + public void testGetRequestDispatcherEncodedTraversal() throws Exception { + doTestGetRequestDispatcher( + true, "/prefix/start", null, "%2E%2E/target", "/target", DispatcherServlet.NULL); + } + + + @Test + public void testGetRequestDispatcherTraversal01() throws Exception { + doTestGetRequestDispatcher( + true, "/prefix/start", null, "../target", "/target", TargetServlet.OK); + } + + + @Test + public void testGetRequestDispatcherTraversal02() throws Exception { + doTestGetRequestDispatcher( + false, "/prefix/start", null, "../target", "/target", TargetServlet.OK); + } + + + @Test + public void testGetRequestDispatcherTraversal03() throws Exception { + doTestGetRequestDispatcher( + true, "/prefix/start", null, "../target?a=b", "/target", TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcherTraversal04() throws Exception { + doTestGetRequestDispatcher( + false, "/prefix/start", null, "../target?a=b", "/target", TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcherTraversal05() throws Exception { + doTestGetRequestDispatcher( + true, "/prefix/start", "a=b", "../target", "/target", TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcherTraversal06() throws Exception { + doTestGetRequestDispatcher( + false, "/prefix/start", "a=b", "../target", "/target", TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcher01() throws Exception { + doTestGetRequestDispatcher( + true, "/prefix/start", null, "target", "/prefix/target", TargetServlet.OK); + } + + + @Test + public void testGetRequestDispatcher02() throws Exception { + doTestGetRequestDispatcher( + false, "/prefix/start", null, "target", "/prefix/target", TargetServlet.OK); + } + + + @Test + public void testGetRequestDispatcher03() throws Exception { + doTestGetRequestDispatcher(true, "/prefix/start", null, "target?a=b", "/prefix/target", + TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcher04() throws Exception { + doTestGetRequestDispatcher(false, "/prefix/start", null, "target?a=b", "/prefix/target", + TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcher05() throws Exception { + doTestGetRequestDispatcher(true, "/prefix/start", "a=b", "target", "/prefix/target", + TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcher06() throws Exception { + doTestGetRequestDispatcher(false, "/prefix/start", "a=b", "target", "/prefix/target", + TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcher11() throws Exception { + doTestGetRequestDispatcher(true, "/aa%3Fbb%3Dcc/start", null, "target", + "/aa%3Fbb%3Dcc/target", TargetServlet.OK); + } + + + @Test + public void testGetRequestDispatcher12() throws Exception { + // Expected to fail because when the RD processes this as unencoded it + // sees /aa?bb=cc/target which it thinks is a query string. This is why + // Tomcat encodes by default. + doTestGetRequestDispatcher(false, "/aa%3Fbb%3Dcc/start", null, "target", + "/aa%3Fbb%3Dcc/target", Default404Servlet.DEFAULT_404); + } + + + @Test + public void testGetRequestDispatcher13() throws Exception { + doTestGetRequestDispatcher(true, "/aa%3Fbb%3Dcc/start", null, "target?a=b", + "/aa%3Fbb%3Dcc/target", TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcher14() throws Exception { + // Expected to fail because when the RD processes this as unencoded it + // sees /aa?bb=cc/target which it thinks is a query string. This is why + // Tomcat encodes by default. + doTestGetRequestDispatcher(false, "/aa%3Fbb%3Dcc/start", null, "target?a=b", + "/aa%3Fbb%3Dcc/target", Default404Servlet.DEFAULT_404); + } + + + @Test + public void testGetRequestDispatcher15() throws Exception { + doTestGetRequestDispatcher(true, "/aa%3Fbb%3Dcc/start", "a=b", "target", + "/aa%3Fbb%3Dcc/target", TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcher16() throws Exception { + // Expected to fail because when the RD processes this as unencoded it + // sees /aa?bb=cc/target which it thinks is a query string. This is why + // Tomcat encodes by default. + doTestGetRequestDispatcher(false, "/aa%3Fbb%3Dcc/start", "a=b", "target", + "/aa%3Fbb%3Dcc/target", Default404Servlet.DEFAULT_404); + } + + + @Test + public void testGetRequestDispatcher21() throws Exception { + doTestGetRequestDispatcher(true, "/aa%3Dbb%3Dcc/start", null, "target", + "/aa%3Dbb%3Dcc/target", TargetServlet.OK); + } + + + @Test + public void testGetRequestDispatcher22() throws Exception { + doTestGetRequestDispatcher(false, "/aa%3Dbb%3Dcc/start", null, "target", + "/aa%3Dbb%3Dcc/target", TargetServlet.OK); + } + + + @Test + public void testGetRequestDispatcher23() throws Exception { + doTestGetRequestDispatcher(true, "/aa%3Dbb%3Dcc/start", null, "target?a=b", + "/aa%3Dbb%3Dcc/target", TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcher24() throws Exception { + doTestGetRequestDispatcher(false, "/aa%3Dbb%3Dcc/start", null, "target?a=b", + "/aa%3Dbb%3Dcc/target", TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcher25() throws Exception { + doTestGetRequestDispatcher(true, "/aa%3Dbb%3Dcc/start", "a=b", "target", + "/aa%3Dbb%3Dcc/target", TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcher26() throws Exception { + doTestGetRequestDispatcher(false, "/aa%3Dbb%3Dcc/start", "a=b", "target", + "/aa%3Dbb%3Dcc/target", TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcher31() throws Exception { + doTestGetRequestDispatcher(true, "/prefix/start", null, "aa%3Fbb%3Dcc", + "/prefix/aa%3Fbb%3Dcc", TargetServlet.OK); + } + + + @Test + public void testGetRequestDispatcher32() throws Exception { + doTestGetRequestDispatcher(false, "/prefix/start", null, "aa%3Fbb%3Dcc", + "/prefix/aa%3Fbb%3Dcc", Default404Servlet.DEFAULT_404); + } + + + @Test + public void testGetRequestDispatcher33() throws Exception { + doTestGetRequestDispatcher(true, "/prefix/start", null, "aa%3Fbb%3Dcc?a=b", + "/prefix/aa%3Fbb%3Dcc", TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcher34() throws Exception { + doTestGetRequestDispatcher(false, "/prefix/start", null, "aa%3Fbb%3Dcc?a=b", + "/prefix/aa%3Fbb%3Dcc", Default404Servlet.DEFAULT_404); + } + + + @Test + public void testGetRequestDispatcher35() throws Exception { + doTestGetRequestDispatcher(true, "/prefix/start", "a=b", "aa%3Fbb%3Dcc", + "/prefix/aa%3Fbb%3Dcc", TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcher36() throws Exception { + doTestGetRequestDispatcher(false, "/prefix/start", "a=b", "aa%3Fbb%3Dcc", + "/prefix/aa%3Fbb%3Dcc", Default404Servlet.DEFAULT_404); + } + + + @Test + public void testGetRequestDispatcher41() throws Exception { + doTestGetRequestDispatcher(true, "/prefix/start", null, "aa%3Fbb%3Dcc", + "/prefix/aa%253Fbb%253Dcc", Default404Servlet.DEFAULT_404); + } + + + @Test + public void testGetRequestDispatcher42() throws Exception { + doTestGetRequestDispatcher(false, "/prefix/start", null, "aa%3Fbb%3Dcc", + "/prefix/aa%253Fbb%253Dcc", TargetServlet.OK); + } + + + @Test + public void testGetRequestDispatcher43() throws Exception { + doTestGetRequestDispatcher(true, "/prefix/start", null, "aa%3Fbb%3Dcc?a=b", + "/prefix/aa%253Fbb%253Dcc", Default404Servlet.DEFAULT_404); + } + + + @Test + public void testGetRequestDispatcher44() throws Exception { + doTestGetRequestDispatcher(false, "/prefix/start", null, "aa%3Fbb%3Dcc?a=b", + "/prefix/aa%253Fbb%253Dcc", TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcher45() throws Exception { + doTestGetRequestDispatcher(true, "/prefix/start", "a=b", "aa%3Fbb%3Dcc", + "/prefix/aa%253Fbb%253Dcc", Default404Servlet.DEFAULT_404); + } + + + @Test + public void testGetRequestDispatcher46() throws Exception { + doTestGetRequestDispatcher(false, "/prefix/start", "a=b", "aa%3Fbb%3Dcc", + "/prefix/aa%253Fbb%253Dcc", TargetServlet.OK + "a=b"); + } + + + private void doTestGetRequestDispatcher(boolean useEncodedDispatchPaths, String startPath, + String startQueryString, String dispatchPath, String targetPath, String expectedBody) + throws Exception { + + // Setup Tomcat instance + Tomcat tomcat = getTomcatInstance(); + + // No file system docBase required + Context ctx = tomcat.addContext("/test", null); + ctx.setDispatchersUseEncodedPaths(useEncodedDispatchPaths); + + // Add a default servlet to return 404 for not found resources + Tomcat.addServlet(ctx, "Default", new Default404Servlet()); + ctx.addServletMapping("/*", "Default"); + + // Add a target servlet to dispatch to + Tomcat.addServlet(ctx, "target", new TargetServlet()); + // Note: This will decode the provided path + ctx.addServletMapping(targetPath, "target"); + + if (useAsync) { + Wrapper w = Tomcat.addServlet( + ctx, "rd", new AsyncDispatcherServlet(dispatchPath, useEncodedDispatchPaths)); + w.setAsyncSupported(true); + } else { + Tomcat.addServlet(ctx, "rd", new DispatcherServlet(dispatchPath)); + } + // Note: This will decode the provided path + ctx.addServletMapping(startPath, "rd"); + + tomcat.start(); + + StringBuilder url = new StringBuilder("http://localhost:"); + url.append(getPort()); + url.append("/test"); + url.append(startPath); + if (startQueryString != null) { + url.append('?'); + url.append(startQueryString); + } + + ByteChunk bc = getUrl(url.toString()); + String body = bc.toString(); + + Assert.assertEquals(expectedBody, body); + } + + + private static class Default404Servlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + private static final String DEFAULT_404 = "DEFAULT-404"; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + resp.setContentType("text/plain"); + resp.setCharacterEncoding("UTF-8"); + resp.getWriter().print(DEFAULT_404); + resp.setStatus(404); + } + } + + + private static class DispatcherServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + private static final String NULL = "RD-NULL"; + + private final String dispatchPath; + + public DispatcherServlet(String dispatchPath) { + this.dispatchPath = dispatchPath; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + RequestDispatcher rd = req.getRequestDispatcher(dispatchPath); + if (rd == null) { + resp.setContentType("text/plain"); + resp.setCharacterEncoding("UTF-8"); + resp.getWriter().print(NULL); + } else { + rd.forward(req, resp); + } + } + } + + + private static class TargetServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + private static final String OK = "OK"; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + resp.setContentType("text/plain"); + resp.setCharacterEncoding("UTF-8"); + resp.getWriter().print(OK); + String qs = req.getQueryString(); + if (qs != null) { + resp.getWriter().print(qs); + } + } + } + + + private static class AsyncDispatcherServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + private static final String NULL = "RD-NULL"; + + private final String dispatchPath; + private final boolean encodePath; + + public AsyncDispatcherServlet(String dispatchPath, boolean encodePath) { + this.dispatchPath = dispatchPath; + this.encodePath = encodePath; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + AsyncContext ac = req.startAsync(); + // Quick and dirty. Sufficient for this test but ignores lots of + // edge cases. + String target = null; + if (dispatchPath != null) { + target = req.getServletPath(); + int lastSlash = target.lastIndexOf('/'); + target = target.substring(0, lastSlash + 1); + if (encodePath) { + target = URLEncoder.DEFAULT.encode(target, "UTF-8"); + } + target += dispatchPath; + } + try { + ac.dispatch(target); + } catch (UnsupportedOperationException uoe) { + ac.complete(); + resp.setContentType("text/plain"); + resp.setCharacterEncoding("UTF-8"); + resp.getWriter().print(NULL); + } + } + } +} diff -Nru tomcat7-7.0.70/test/org/apache/catalina/core/TestStandardHostValve.java tomcat7-7.0.72/test/org/apache/catalina/core/TestStandardHostValve.java --- tomcat7-7.0.70/test/org/apache/catalina/core/TestStandardHostValve.java 2015-11-06 02:45:03.000000000 +0000 +++ tomcat7-7.0.72/test/org/apache/catalina/core/TestStandardHostValve.java 2016-08-11 20:17:20.000000000 +0000 @@ -74,6 +74,22 @@ } + @Test(expected=IllegalArgumentException.class) + public void testInvalidErrorPage() throws Exception { + // Set up a container + Tomcat tomcat = getTomcatInstance(); + + // No file system docBase required + Context ctx = tomcat.addContext("", null); + + // Add a broken error page configuration + ErrorPage errorPage500 = new ErrorPage(); + errorPage500.setErrorCode("java.lang.Exception"); + errorPage500.setLocation("/report/500"); + ctx.addErrorPage(errorPage500); + } + + @Test public void testSRLAfterError() throws Exception { // Set up a container diff -Nru tomcat7-7.0.70/test/org/apache/catalina/startup/TestContextConfigAnnotation.java tomcat7-7.0.72/test/org/apache/catalina/startup/TestContextConfigAnnotation.java --- tomcat7-7.0.70/test/org/apache/catalina/startup/TestContextConfigAnnotation.java 2014-01-27 11:53:13.000000000 +0000 +++ tomcat7-7.0.72/test/org/apache/catalina/startup/TestContextConfigAnnotation.java 2016-08-31 14:59:13.000000000 +0000 @@ -18,6 +18,7 @@ import java.beans.PropertyChangeListener; import java.io.File; +import java.net.URISyntaxException; import java.net.URL; import java.util.HashSet; import java.util.Set; @@ -358,12 +359,13 @@ * * @param className * @return File Resource + * @throws URISyntaxException */ - private File paramClassResource(String className) { + private File paramClassResource(String className) throws URISyntaxException { URL url = getClass().getClassLoader().getResource(className + ".class"); assertNotNull(url); - File file = new File(url.getPath()); + File file = new File(url.toURI()); return file; } } diff -Nru tomcat7-7.0.70/test/org/apache/catalina/startup/TestTomcat.java tomcat7-7.0.72/test/org/apache/catalina/startup/TestTomcat.java --- tomcat7-7.0.70/test/org/apache/catalina/startup/TestTomcat.java 2016-04-22 09:06:20.000000000 +0000 +++ tomcat7-7.0.72/test/org/apache/catalina/startup/TestTomcat.java 2016-08-02 11:30:59.000000000 +0000 @@ -375,6 +375,7 @@ ContextResourceLink link = new ContextResourceLink(); link.setGlobal("globalTest"); link.setName(HelloWorldJndi.JNDI_ENV_NAME); + link.setType("java.lang.String"); ctx.getNamingResources().addResourceLink(link); Tomcat.addServlet(ctx, "jndiServlet", new HelloWorldJndi()); diff -Nru tomcat7-7.0.70/test/org/apache/naming/TestNamingContext.java tomcat7-7.0.72/test/org/apache/naming/TestNamingContext.java --- tomcat7-7.0.70/test/org/apache/naming/TestNamingContext.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.72/test/org/apache/naming/TestNamingContext.java 2016-08-23 10:17:32.000000000 +0000 @@ -0,0 +1,104 @@ +/* + * 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.naming; + +import javax.naming.Context; +import javax.naming.NamingException; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.catalina.deploy.ContextEnvironment; +import org.apache.catalina.deploy.ContextResourceLink; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.naming.factory.ResourceLinkFactory; + +public class TestNamingContext extends TomcatBaseTest { + + private static final String COMP_ENV = "comp/env"; + private static final String GLOBAL_NAME = "global"; + private static final String LOCAL_NAME = "local"; + private static final String DATA = "Cabbage"; + + + @Test + public void testGlobalNaming() throws Exception { + Tomcat tomcat = getTomcatInstance(); + tomcat.enableNaming(); + + org.apache.catalina.Context ctx = tomcat.addContext("", null); + + tomcat.start(); + + Context webappInitial = ContextBindings.getContext(ctx); + + // Nothing added at the moment so should be null + Object obj = doLookup(webappInitial, COMP_ENV + "/" + LOCAL_NAME); + Assert.assertNull(obj); + + ContextEnvironment ce = new ContextEnvironment(); + ce.setName(GLOBAL_NAME); + ce.setValue(DATA); + ce.setType(DATA.getClass().getName()); + + tomcat.getServer().getGlobalNamingResources().addEnvironment(ce); + + // No link so still should be null + obj = doLookup(webappInitial, COMP_ENV + "/" + LOCAL_NAME); + Assert.assertNull(obj); + + // Now add a resource link to the context + ContextResourceLink crl = new ContextResourceLink(); + crl.setGlobal(GLOBAL_NAME); + crl.setName(LOCAL_NAME); + crl.setType(DATA.getClass().getName()); + ctx.getNamingResources().addResourceLink(crl); + + // Link exists so should be OK now + obj = doLookup(webappInitial, COMP_ENV + "/" + LOCAL_NAME); + Assert.assertEquals(DATA, obj); + + // Try shortcut + ResourceLinkFactory factory = new ResourceLinkFactory(); + ResourceLinkRef rlr = new ResourceLinkRef(DATA.getClass().getName(), GLOBAL_NAME, null, null); + obj = factory.getObjectInstance(rlr, null, null, null); + Assert.assertEquals(DATA, obj); + + // Remove the link + ctx.getNamingResources().removeResourceLink(LOCAL_NAME); + + // No link so should be null + obj = doLookup(webappInitial, COMP_ENV + "/" + LOCAL_NAME); + Assert.assertNull(obj); + + // Shortcut should fail too + obj = factory.getObjectInstance(rlr, null, null, null); + Assert.assertNull(obj); + } + + + private Object doLookup(Context context, String name) { + Object result = null; + try { + result = context.lookup(name); + } catch (NamingException nnfe) { + // Ignore + } + return result; + } +} diff -Nru tomcat7-7.0.70/test/org/apache/tomcat/unittest/TesterContext.java tomcat7-7.0.72/test/org/apache/tomcat/unittest/TesterContext.java --- tomcat7-7.0.70/test/org/apache/tomcat/unittest/TesterContext.java 2016-04-06 20:24:19.000000000 +0000 +++ tomcat7-7.0.72/test/org/apache/tomcat/unittest/TesterContext.java 2016-07-27 15:58:11.000000000 +0000 @@ -1255,4 +1255,9 @@ public void setUseRelativeRedirects(boolean useRelativeRedirects) { /* NO-OP */ } @Override public boolean getUseRelativeRedirects() { return true; } + + @Override + public void setDispatchersUseEncodedPaths(boolean dispatchersUseEncodedPaths) { /* NO-OP */ } + @Override + public boolean getDispatchersUseEncodedPaths() { return true; } } diff -Nru tomcat7-7.0.70/webapps/docs/cgi-howto.xml tomcat7-7.0.72/webapps/docs/cgi-howto.xml --- tomcat7-7.0.70/webapps/docs/cgi-howto.xml 2015-11-12 10:04:40.000000000 +0000 +++ tomcat7-7.0.72/webapps/docs/cgi-howto.xml 2016-08-19 16:57:53.000000000 +0000 @@ -96,14 +96,6 @@ By default there is no value, which results in the web application root directory being used as the search path. The recommended value is WEB-INF/cgi -

  • debug - Debugging detail level for messages logged -by this servlet. Useful values range from 0 to 5 where 0 means no logging and 5 -means maximum logging. Values of 10 or more mean maximum logging plus debug info -added to the HTTP response. If an error occurs and debug is 10 or more the -standard error page mechanism will be disabled and a response body with debug -information will be produced. The debug page is not considered secure and should -not be enabled for production systems. Note that any value of 10 or more has the -same effect as a value of 10. Default is 0.
  • executable - The of the executable to be used to run the script. You may explicitly set this parameter to be an empty string if your script is itself executable (e.g. an exe file). Default is @@ -111,6 +103,12 @@
  • executable-arg-1, executable-arg-2, and so on - additional arguments for the executable. These precede the CGI script name. By default there are no additional arguments.
  • +
  • envHttpHeaders - A regular expression used to select the +HTTP headers passed to the CGI process as environment variables. Note that +headers are converted to upper case before matching and that the entire header +name must match the pattern. Default is +ACCEPT[-0-9A-Z]*|CACHE-CONTROL|COOKIE|HOST|IF-[-0-9A-Z]*|REFERER|USER-AGENT +
  • parameterEncoding - Name of the parameter encoding to be used with the CGI servlet. Default is System.getProperty("file.encoding","UTF-8"). That is the system diff -Nru tomcat7-7.0.70/webapps/docs/changelog.xml tomcat7-7.0.72/webapps/docs/changelog.xml --- tomcat7-7.0.70/webapps/docs/changelog.xml 2016-06-15 11:10:48.000000000 +0000 +++ tomcat7-7.0.72/webapps/docs/changelog.xml 2016-09-14 09:02:05.000000000 +0000 @@ -57,7 +57,325 @@ They eventually become mixed with the numbered issues. (I.e., numbered issues do not "pop up" wrt. others). --> -
    +
    + + + + Ensure Digester.useContextClassLoader is considered in + case the class loader is used. (violetagg) + + + + + + + 60101: Remove preloading of the class that was deleted. + (violetagg) + + + + + + + Notify jmx when returning the connection that has been marked suspect. + (kfujino) + + + Ensure that the POOL_EMPTY notification has been added to + the jmx notification types. (kfujino) + + + +
    +
    + + + + 57705: Add debug logging for requests denied by the remote + host and remote address valves and filters. Based on a patch by Graham + Leggett. (markt) + + + Change the default of the + sessionCookiePathUsesTrailingSlash attribute of the + Context element to false since the problems + caused when a Servlet is mapped to /* are more significant + than the security risk of not enabling this option by default. (markt) + + + 59708: Modify the LockOutRealm logic. Valid authentication + attempts during the lock out period will no longer reset the lock out + timer to zero. (markt) + + + Improve error handling around user code prior to calling + InstanceManager.destroy() to ensure that the method is + executed. (markt) + + + Ensure that reading the singleThreadModel attribute of a + StandardWrapper via JMX does not trigger initialisation of + the associated servlet. With some frameworks this can trigger an + unexpected initialisation thread and if initilisation is not thread-safe + the initialisation can then fail. (markt) + + + By default, treat paths used to obtain a request dispatcher as encoded. + This behaviour can be changed per web application via the + dispatchersUseEncodedPaths attribute of the Context. + (markt) + + + 59839: Apply roleSearchAsUser to all nested searches + in JNDIRealm. (fschumacher) + + + Provide a mechanism that enables the container to check if a component + (typically a web application) has been granted a given permission when + running under a SecurityManager without the current execution stack + having to have passed through the component. Use this new mechanism to + extend SecurityManager protection to the system property replacement + feature of the digester. (markt) + + + When retrieving an object via a ResourceLink, ensure that + the object obtained is of the expected type. (markt) + + + 59866: When scanning WEB-INF/classes for + annotations, don't scan the contents of + WEB-INF/classes/META-INF (if present) since classes will + never be loaded from that location. (markt) + + + 59912: Fix an edge case in input stream handling where an + IOException could be thrown when reading a POST body. + (markt) + + + 59966: Do not start the web application if the error page + configuration in web.xml is invalid. (markt) + + + Switch the CGI servlet to the standard logging mechanism and remove + support for the debug attribute. (markt) + + + Add a new initialisation parameter, envHttpHeaders, to + the CGI Servlet to mitigate httpoxy + (CVE-2016-5388) by default and to provide a mechanism that can be + used to mitigate any future, similar issues. (markt) + + + When adding and removing ResourceLinks dynamically, ensure + that the global resource is only visible via the + ResourceLinkFactory when it is meant to be. (markt) + + + 60008: When processing CORs requests, treat any origin with a + URI scheme of file as a valid origin. (markt) + + + Improve handling of exceptions during a Lifecycle events triggered by a + state transition. The exception is now caught and the component is now + placed into the FAILED state. (markt) + + + Fix a file descriptor leak when reading the global web.xml. (markt) + + + 60041: Better error message if a JAR is deleted while a web + application is running. Note: Deleting a JAR while the application is + running is not supported and errors are expected. Based on a patch by + gehui. (markt) + + + + + + + Improve error handling around user code prior to calling + InstanceManager.destroy() to ensure that the method is + executed. (markt) + + + 59904: Add a limit (default 200) for the number of cookies + allowed per request. Based on a patch by gehui. (markt) + + + Make timing attacks against the Realm implementations harder. (schultz) + + + Refactor the code that implements the requirement that a call to + complete() or dispatch() made from a + non-container thread before the container initiated thread that called + startAsync() completes must be delayed until the container + initiated thread has completed. Rather than implementing this by + blocking the non-container thread, extend the internal state machine to + track this. This removes the possibility that blocking the non-container + thread could trigger a deadlock. (markt) + + + + + + + Improve error handling around user code prior to calling + InstanceManager.destroy() to ensure that the method is + executed. (markt) + + + Improve the error handling for custom tags to ensure that the tag is + returned to the pool or released and destroyed once used. (markt) + + + Fixed StringIndexOutOfBoundsException. Based on a patch provided by + wuwen via Github. (violetagg) + + + + + + + Improve error handling around user code prior to calling + InstanceManager.destroy() to ensure that the method is + executed. (markt) + + + 59868: Clarify the documentation for the Manager web + application to make clearer that the host name and IP address in the + server section are the primary host name and IP address. (markt) + + + 59908: Ensure that a reason phrase is included in the close + message if a session is closed due to a timeout. (markt) + + + + + + + Do not log an additional case of IOExceptions in the + error handler for the Drawboard WebSocket example when the root cause is + the client disconnecting since the logs add no value. (markt) + + + 59642: Mention the localDataSource in the + DataSourceRealm section of the Realm How-To. (markt) + + + Follow-up to the fix for 59399. Ensure that the new attribute + transportGuaranteeRedirectStatus is documented for all + Realms. Also document the NullRealm and + when it is automatically created for an Engine. (markt) + + + MBeans Descriptors How-To is moved to + mbeans-descriptors-howto.html. Patch provided by Radoslav + Husar. (violetagg) + + + 60034: Correct a typo in the Manager How-To page of the + documentation web application. (markt) + + + + + + + Add log message when the ping has timed-out. (kfujino) + + + If the ping message has been received at the + AbstractReplicatedMap#leftOver method, ensure that notify + the member is alive than ignore it. (kfujino) + + + + + + + Fix the duplicated connection release when connection verification + failed. (kfujino) + + + Ensure that do not remove the abandoned connection that has been already + released. (kfujino) + + + In order to avoid the unintended skip of PoolCleaner, + remove the check code of the execution interval in the task that has + been scheduled. (kfujino) + + + 59849: Ensure that the connection verification is executed by + initSQL (if required) if the borrowing + PooledConnection has not been initialized. (kfujino) + + + 59850: Ensure that the ResultSet is closed when + enabling the StatementCache interceptor. (kfujino) + + + 59923: Reduce the default value of + validationInterval in order to avoid the potential issue + that continues to return an invalid connection after database restart. + (kfujino) + + + Ensure that the ResultSet is returned as Proxy object when + enabling the StatementDecoratorInterceptor. (kfujino) + + + 60043: Ensure that the suspectTimeout works + without removing connection when the removeAbandoned is + disabled. (kfujino) + + + Add log message of when returning the connection that has been marked + suspect. (kfujino) + + + Correct Javadoc for ConnectionPool.suspect(). Based on a + patch by Yahya Cahyadi. (markt) + + + + + + + Use the mirror network rather than the ASF master site to download the + current ASF dependencies. (markt) + + + Update the packaged version of the Tomcat Native Library to 1.2.8 to + pick up the latest fixes and make 1.2.8 the minimum recommended version. + (markt) + + + Fixed typos in mbeans-descriptors.xml files. (violetagg) + + + Update the internal fork of Commons BCEL to r1757132 to align with the + BCEL 6 release. (markt) + + + Update the internal fork of Commons Codec to r1757174. Code formatting + changes only. (markt) + + + Update the internal fork of Commons FileUpload to afdedc9. This pulls in + a fix to improve the performance with large multipart boundaries. + (markt) + + + Update the download location for Objenesis. (violetagg) + + + +
    +
    @@ -115,12 +433,12 @@ enabled but without the command line option it requires. (markt) - Fix potential concurrency issue with web application class loader with - concurrent reads and writes of the resource cache. (markt) + Fix a potential concurrency issue with the web application class loader + and concurrent reads and writes of the resource cache. (markt) 59619: Within the web application class loader, always use - path as the key for the resource cache to improve the hit ration. This + path as the key for the resource cache to improve the hit ratio. This also fixes a problem exposed by the fix for 56777 that enabled file based configuration resources to be loaded from the class path. (markt) @@ -669,6 +987,10 @@ Fix handling of missing messages in org.apache.el.util.MessageFactory. (violetagg) + + Ignore engineOptionsClass and scratchdir when + running under a security manager. (markt) + diff -Nru tomcat7-7.0.70/webapps/docs/config/ajp.xml tomcat7-7.0.72/webapps/docs/config/ajp.xml --- tomcat7-7.0.70/webapps/docs/config/ajp.xml 2016-03-30 10:58:45.000000000 +0000 +++ tomcat7-7.0.72/webapps/docs/config/ajp.xml 2016-08-11 18:57:23.000000000 +0000 @@ -388,6 +388,12 @@ and connections are not counted.

    + +

    The maximum number of cookies that are permitted for a request. A value + of less than zero means no limit. If not specified, a default value of 200 + will be used.

    +
    +

    The maximum number of request processing threads to be created by this Connector, which therefore determines the diff -Nru tomcat7-7.0.70/webapps/docs/config/context.xml tomcat7-7.0.72/webapps/docs/config/context.xml --- tomcat7-7.0.70/webapps/docs/config/context.xml 2016-05-20 11:10:18.000000000 +0000 +++ tomcat7-7.0.72/webapps/docs/config/context.xml 2016-07-27 15:58:11.000000000 +0000 @@ -334,6 +334,14 @@ sufficient.

    + +

    Controls whether paths used in calls to obtain a request dispatcher + ares expected to be encoded. This affects both how Tomcat handles calls + to obtain a request dispatcher as well as how Tomcat generates paths + used to obtain request dispatchers internally. If not specified, the + default value of true is used.

    +
    +

    Set to true to have the context fail its startup if any servlet that has load-on-startup >=0 fails its own startup.

    @@ -492,15 +500,23 @@
    -

    Some browsers, such as IE, will send a session cookie for a context - with a path of /foo with a request to /foobar. To prevent this, Tomcat - will add a trailing slash to the path associated with the session cookie - so, in the above example, the cookie path becomes /foo/. However, with a - cookie path of /foo/, IE will no longer send the cookie with a request - to /foo. This should not be a problem unless there is a servlet mapped - to /*. In this case this feature will need to be disabled. The default - value for this attribute is true. To disable this feature, - set the attribute to false.

    +

    Some browsers, such as Internet Explorer, Safari and Edge, will send + a session cookie for a context with a path of /foo with a + request to /foobar in violation of RFC6265. This could + expose a session ID from an application deployed at /foo to + an application deployed at /foobar. If the application + deployed at /foobar is untrusted, this could create a + security risk. However, it should be noted that RFC 6265, section 8.5 + makes clear that path alone should not be view as sufficient to prevent + untrusted applications accessing cookies from other applications. To + mitigate this risk, this attribute may bet ste to true and + Tomcat will add a trailing slash to the path associated with the session + cookie so, in the above example, the cookie path becomes /foo/. However, + with a cookie path of /foo/, browsers will no longer send the cookie + with a request to /foo. This should not be a problem unless there is a + servlet mapped to /*. In this case this attribute will need to be set to + false to disable this feature. The default value for this + attribute is false.

    diff -Nru tomcat7-7.0.70/webapps/docs/config/http.xml tomcat7-7.0.72/webapps/docs/config/http.xml --- tomcat7-7.0.70/webapps/docs/config/http.xml 2016-03-31 15:52:41.000000000 +0000 +++ tomcat7-7.0.72/webapps/docs/config/http.xml 2016-09-12 19:45:56.000000000 +0000 @@ -432,6 +432,12 @@ and connections are not counted.

    + +

    The maximum number of cookies that are permitted for a request. A value + of less than zero means no limit. If not specified, a default value of 200 + will be used.

    +
    +

    Limits the total length of chunk extensions in chunked HTTP requests. If the value is -1, no limit will be imposed. If not @@ -706,7 +712,11 @@

    (bool)Boolean value, whether to use direct ByteBuffers or java mapped - ByteBuffers. Default is false.
    + ByteBuffers. If true then + java.nio.ByteBuffer.allocateDirect() is used to allocate + the buffers, if false then + java.nio.ByteBuffer.allocate() is used. The default value + is false.
    When you are using direct buffers, make sure you allocate the appropriate amount of memory for the direct memory space. On Sun's JDK that would be something like -XX:MaxDirectMemorySize=256m. @@ -1248,6 +1258,12 @@

    Name of the file that contains the server certificate. The format is PEM-encoded.

    +

    In addition to the certificate, the file can also contain as optional + elements DH parameters and/or an EC curve name for ephemeral keys, as + generated by openssl dhparam and openssl ecparam, + respectively. The output of the respective OpenSSL command can simply + be concatenated to the certificate file. This feature needs APR/native + version 1.1.34 or later.

    diff -Nru tomcat7-7.0.70/webapps/docs/config/realm.xml tomcat7-7.0.72/webapps/docs/config/realm.xml --- tomcat7-7.0.70/webapps/docs/config/realm.xml 2016-06-15 11:10:48.000000000 +0000 +++ tomcat7-7.0.72/webapps/docs/config/realm.xml 2016-06-24 09:36:54.000000000 +0000 @@ -49,8 +49,9 @@ this one Realm may itself contain multiple nested Realms). In addition, the Realm associated with an Engine or a Host is automatically inherited by lower-level containers unless the lower level container explicitly defines its - own Realm. -

    + own Realm. If no Realm is configured for the Engine, an instance of the + Null Realm + will be configured for the Engine automatically.

    For more in-depth information about container managed security in web applications, as well as more information on configuring and using the @@ -172,7 +173,7 @@

    The HTTP status code to use when the container needs to issue an HTTP redirect to meet the requirements of a configured transport - guarantee. The prpvoded status code is not validated. If not + guarantee. The provided status code is not validated. If not specified, the default value of 302 is used.

    @@ -289,6 +290,13 @@ a rare case when it can be omitted.

    + +

    The HTTP status code to use when the container needs to issue an HTTP + redirect to meet the requirements of a configured transport + guarantee. The provided status code is not validated. If not + specified, the default value of 302 is used.

    +
    +

    When processing users authenticated via the GSS-API, this attribute controls if any "@..." is removed from the end of the user @@ -618,6 +626,13 @@ limit.

    + +

    The HTTP status code to use when the container needs to issue an HTTP + redirect to meet the requirements of a configured transport + guarantee. The provided status code is not validated. If not + specified, the default value of 302 is used.

    +
    +

    When the JNDIRealm is used with the SPNEGO authenticator, delegated credentials for the user may be available. If such credentials are @@ -762,6 +777,13 @@ that this realm will use for user, password and role information.

    + +

    The HTTP status code to use when the container needs to issue an HTTP + redirect to meet the requirements of a configured transport + guarantee. The provided status code is not validated. If not + specified, the default value of 302 is used.

    +
    +

    When using X509 client certificates, this specifies the class name that will be used to retrieve the user name from the certificate. @@ -830,6 +852,13 @@ name. If not specified, the default is true.

    + +

    The HTTP status code to use when the container needs to issue an HTTP + redirect to meet the requirements of a configured transport + guarantee. The provided status code is not validated. If not + specified, the default value of 302 is used.

    +
    +

    When using X509 client certificates, this specifies the class name that will be used to retrieve the user name from the certificate. @@ -939,6 +968,13 @@ name. If not specified, the default is true.

    + +

    The HTTP status code to use when the container needs to issue an HTTP + redirect to meet the requirements of a configured transport + guarantee. The provided status code is not validated. If not + specified, the default value of 302 is used.

    +
    +

    Instructs JAASRealm to use the context class loader for loading the user-specified LoginModule class and associated @@ -1004,6 +1040,13 @@ one of those roles.

    + +

    The HTTP status code to use when the container needs to issue an HTTP + redirect to meet the requirements of a configured transport + guarantee. The provided status code is not validated. If not + specified, the default value of 302 is used.

    +
    +
    @@ -1067,7 +1110,18 @@

    The time (in seconds) a user is locked out for after too many - authentication failures. Defaults to 300 (5 minutes).

    + authentication failures. Defaults to 300 (5 minutes). Further + authentication failures during the lock out time will cause the lock out + timer to reset to zero, effectively extending the lock out time. Valid + authentication attempts during the lock out period will not succeed but + will also not reset the lock out time.

    +
    + + +

    The HTTP status code to use when the container needs to issue an HTTP + redirect to meet the requirements of a configured transport + guarantee. The provided status code is not validated. If not + specified, the default value of 302 is used.

    @@ -1078,6 +1132,30 @@ + + +

    NullRealm is a minimal implementation of the Tomcat + Realm interface that always returns null when an attempt is + made to validate a user name and associated credentials. It is intended to + be used as a default Realm implementation when no other Realm is + specified.

    + +

    The NullRealm implementation supports the following additional + attributes.

    + + + + +

    The HTTP status code to use when the container needs to issue an HTTP + redirect to meet the requirements of a configured transport + guarantee. The provided status code is not validated. If not + specified, the default value of 302 is used.

    +
    + +
    + +
    +
    diff -Nru tomcat7-7.0.70/webapps/docs/html-manager-howto.xml tomcat7-7.0.72/webapps/docs/html-manager-howto.xml --- tomcat7-7.0.70/webapps/docs/html-manager-howto.xml 2014-12-07 03:15:18.000000000 +0000 +++ tomcat7-7.0.72/webapps/docs/html-manager-howto.xml 2016-08-02 15:41:35.000000000 +0000 @@ -526,9 +526,11 @@
    -

    This section displays information about Tomcat, the operating system of -the server Tomcat is hosted on, and the Java Virtual Machine Tomcat is -running in.

    +

    This section displays information about Tomcat, the operating system of the +server Tomcat is hosted on, the Java Virtual Machine Tomcat is running in, the +primary host name of the server (may not be the host name used to access Tomcat) +and the primary IP address of the server (may not be the IP address used to +access Tomcat).

    diff -Nru tomcat7-7.0.70/webapps/docs/index.xml tomcat7-7.0.72/webapps/docs/index.xml --- tomcat7-7.0.70/webapps/docs/index.xml 2015-08-11 19:37:04.000000000 +0000 +++ tomcat7-7.0.72/webapps/docs/index.xml 2016-08-10 11:34:37.000000000 +0000 @@ -101,7 +101,7 @@
  • Proxy Support - Configuring Apache Tomcat to run behind a proxy server (or a web server functioning as a proxy server).
  • -
  • MBean Descriptor - +
  • MBeans Descriptors - Configuring MBean descriptors files for custom components.
  • Default Servlet - Configuring the default servlet and customizing directory listings.
  • diff -Nru tomcat7-7.0.70/webapps/docs/jasper-howto.xml tomcat7-7.0.72/webapps/docs/jasper-howto.xml --- tomcat7-7.0.70/webapps/docs/jasper-howto.xml 2015-11-25 13:47:58.000000000 +0000 +++ tomcat7-7.0.72/webapps/docs/jasper-howto.xml 2016-08-30 22:45:07.000000000 +0000 @@ -132,7 +132,7 @@
  • engineOptionsClass - Allows specifying the Options class used to configure Jasper. If not present, the default EmbeddedServletOptions -will be used. +will be used. This option is ignored if running under a SecurityManager.
  • errorOnUseBeanInvalidClassAttribute - Should Jasper issue @@ -185,7 +185,7 @@
  • scratchdir - What scratch directory should we use when compiling JSP pages? Default is the work directory for the current web -application.
  • +application. This option is ignored if running under a SecurityManager.
  • suppressSmap - Should the generation of SMAP info for JSR45 debugging be suppressed? true or false, default diff -Nru tomcat7-7.0.70/webapps/docs/manager-howto.xml tomcat7-7.0.72/webapps/docs/manager-howto.xml --- tomcat7-7.0.70/webapps/docs/manager-howto.xml 2016-02-04 11:59:55.000000000 +0000 +++ tomcat7-7.0.72/webapps/docs/manager-howto.xml 2016-08-24 11:40:51.000000000 +0000 @@ -974,7 +974,7 @@ The JMX Proxy Servlet is a lightweight proxy to get and set the tomcat internals. (Or any class that has been exposed via an MBean) Its usage is not very user friendly but the UI is - extremely help for integrating command line scripts for monitoring + extremely helpful for integrating command line scripts for monitoring and changing the internals of tomcat. You can do two things with the proxy: get information and set information. For you to really understand the JMX Proxy Servlet, you should have a general understanding of JMX. diff -Nru tomcat7-7.0.70/webapps/docs/mbeans-descriptor-howto.xml tomcat7-7.0.72/webapps/docs/mbeans-descriptor-howto.xml --- tomcat7-7.0.70/webapps/docs/mbeans-descriptor-howto.xml 2014-01-26 22:13:11.000000000 +0000 +++ tomcat7-7.0.72/webapps/docs/mbeans-descriptor-howto.xml 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ - - - -]> - - - &project; - - - Amy Roh - MBean Descriptor How To - - - - -
    - -
    - -
    - -

    Tomcat uses JMX MBeans as the technology for implementing -manageability of Tomcat.

    - -

    The descriptions of JMX MBeans for Catalina are in the mbeans-descriptor.xml -file in each package.

    - -

    You will need to add MBean descriptions for your custom components -in order to avoid a "ManagedBean is not found" exception.

    - -
    - -
    - -

    You may also add MBean descriptions for custom components in -a mbeans-descriptor.xml file, located in the same package as the class files -it describes.

    - - - <mbean name="LDAPRealm" - className="org.apache.catalina.mbeans.ClassNameMBean" - description="Custom LDAPRealm" - domain="Catalina" - group="Realm" - type="com.myfirm.mypackage.LDAPRealm"> - - <attribute name="className" - description="Fully qualified class name of the managed object" - type="java.lang.String" - writeable="false"/> - - <attribute name="debug" - description="The debugging detail level for this component" - type="int"/> - . - . - . - - </mbean> - - - -
    - - - -
    diff -Nru tomcat7-7.0.70/webapps/docs/mbeans-descriptors-howto.xml tomcat7-7.0.72/webapps/docs/mbeans-descriptors-howto.xml --- tomcat7-7.0.70/webapps/docs/mbeans-descriptors-howto.xml 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.72/webapps/docs/mbeans-descriptors-howto.xml 2016-08-10 11:34:37.000000000 +0000 @@ -0,0 +1,83 @@ + + + +]> + + + &project; + + + Amy Roh + MBeans Descriptors How To + + + + +
    + +
    + +
    + +

    Tomcat uses JMX MBeans as the technology for implementing +manageability of Tomcat.

    + +

    The descriptions of JMX MBeans for Catalina are in the mbeans-descriptors.xml +file in each package.

    + +

    You will need to add MBean descriptions for your custom components +in order to avoid a "ManagedBean is not found" exception.

    + +
    + +
    + +

    You may also add MBean descriptions for custom components in +a mbeans-descriptors.xml file, located in the same package as the class files +it describes.

    + + + <mbean name="LDAPRealm" + className="org.apache.catalina.mbeans.ClassNameMBean" + description="Custom LDAPRealm" + domain="Catalina" + group="Realm" + type="com.myfirm.mypackage.LDAPRealm"> + + <attribute name="className" + description="Fully qualified class name of the managed object" + type="java.lang.String" + writeable="false"/> + + <attribute name="debug" + description="The debugging detail level for this component" + type="int"/> + . + . + . + + </mbean> + + + +
    + + + +
    diff -Nru tomcat7-7.0.70/webapps/docs/project.xml tomcat7-7.0.72/webapps/docs/project.xml --- tomcat7-7.0.70/webapps/docs/project.xml 2014-12-10 05:50:59.000000000 +0000 +++ tomcat7-7.0.72/webapps/docs/project.xml 2016-08-10 11:34:37.000000000 +0000 @@ -51,8 +51,8 @@ - + diff -Nru tomcat7-7.0.70/webapps/docs/realm-howto.xml tomcat7-7.0.72/webapps/docs/realm-howto.xml --- tomcat7-7.0.70/webapps/docs/realm-howto.xml 2015-08-11 19:37:04.000000000 +0000 +++ tomcat7-7.0.72/webapps/docs/realm-howto.xml 2016-08-10 11:34:37.000000000 +0000 @@ -109,7 +109,7 @@
  • Implement org.apache.catalina.Realm,
  • Place your compiled realm in $CATALINA_HOME/lib,
  • Declare your realm as described in the "Configuring a Realm" section below,
  • -
  • Declare your realm to the MBeans Descriptor.
  • +
  • Declare your realm to the MBeans Descriptors.
  • @@ -426,8 +426,11 @@ at least read only access to the tables described above. (Tomcat will never attempt to write to these tables.)
  • Configure a JNDI named JDBC DataSource for your database. Refer to the - JNDI DataSource Example HOW-TO - for information on how to configure a JNDI named JDBC DataSource.
  • + JNDI DataSource Example + HOW-TO for information on how to configure a JNDI named JDBC DataSource. + Be sure to set the Realm's localDataSource + attribute appropriately, depending on where the JNDI DataSource is + defined.
  • Set up a <Realm> element, as described below, in your $CATALINA_BASE/conf/server.xml file.
  • Restart Tomcat if it is already running.
  • diff -Nru tomcat7-7.0.70/webapps/docs/security-howto.xml tomcat7-7.0.72/webapps/docs/security-howto.xml --- tomcat7-7.0.70/webapps/docs/security-howto.xml 2015-11-13 16:00:24.000000000 +0000 +++ tomcat7-7.0.72/webapps/docs/security-howto.xml 2016-06-20 10:02:37.000000000 +0000 @@ -342,6 +342,15 @@ operating systems (this includes Windows) will disable a number of security measures and allow, among other things, direct access to the WEB-INF directory.

    + +

    The sessionCookiePathUsesTrailingSlash can be used to + work around a bug in a number of browsers (Internet Explorer, Safari and + Edge) to prevent session cookies being exposed across applications when + applications share a common path prefix. However, enabling this option + can create problems for applications with Servlets mapped to + /*. It should also be noted the RFC6265 section 8.5 makes it + clear that different paths should not be considered sufficient to isolate + cookies from other applications.

    diff -Nru tomcat7-7.0.70/webapps/docs/ssl-howto.xml tomcat7-7.0.72/webapps/docs/ssl-howto.xml --- tomcat7-7.0.70/webapps/docs/ssl-howto.xml 2016-05-26 06:44:20.000000000 +0000 +++ tomcat7-7.0.72/webapps/docs/ssl-howto.xml 2016-09-12 19:45:56.000000000 +0000 @@ -460,6 +460,24 @@ sensitive!

    +
  • My Java-based client aborts handshakes with exceptions such as + "java.lang.RuntimeException: Could not generate DH keypair" and + "java.security.InvalidAlgorithmParameterException: Prime size must be multiple + of 64, and can only range from 512 to 1024 (inclusive)" + +

    If you are using the APR/native connector, starting with version 1.1.34 + it will determine the strength of ephemeral DH keys from the key size of + your RSA certificate. For example a 2048 bit RSA key will result in + using a 2048 bit primefor the DH keys. Unfortunately Java 6 only supports + 768 bit and Java 7 only supports 1024 bit. So if your certificate has a + stronger key, old Java clients might produce such handshake failures. + As a mitigation you can either try to force them to use another cipher by + configuring an appropriate SSLCipherSuite and activate + SSLHonorCipherOrder, or embed weak DH params in your + certificate file. The latter approach is not recommended because it weakens + the SSL security (logjam attack).

    +
  • +

    If you are still having problems, a good source of information is the diff -Nru tomcat7-7.0.70/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint.java tomcat7-7.0.72/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint.java --- tomcat7-7.0.70/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint.java 2013-10-22 23:32:26.000000000 +0000 +++ tomcat7-7.0.72/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint.java 2016-06-22 12:57:30.000000000 +0000 @@ -17,6 +17,7 @@ package websocket.drawboard; import java.io.EOFException; +import java.io.IOException; import javax.websocket.CloseReason; import javax.websocket.Endpoint; @@ -153,6 +154,9 @@ if (root instanceof EOFException) { // Assume this is triggered by the user closing their browser and // ignore it. + } else if (!session.isOpen() && root instanceof IOException) { + // IOException after close. Assume this is a variation of the user + // closing their browser (or refreshing very quickly) and ignore it. } else { log.error("onError: " + t.toString(), t); } diff -Nru tomcat7-7.0.70/webapps/examples/websocket/drawboard.xhtml tomcat7-7.0.72/webapps/examples/websocket/drawboard.xhtml --- tomcat7-7.0.70/webapps/examples/websocket/drawboard.xhtml 2013-10-30 13:07:32.000000000 +0000 +++ tomcat7-7.0.72/webapps/examples/websocket/drawboard.xhtml 2016-06-22 12:55:30.000000000 +0000 @@ -864,7 +864,7 @@

    About Drawbord WebSocket Example

    + >About Drawboard WebSocket Example

    This drawboard is a page where you can draw with your mouse or touch input