ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/catalina/ssi/SSIServletExternalResolver.java

Path
ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/catalina/ssi/SSIServletExternalResolver.java
Status
scanned
Type
file
Name
SSIServletExternalResolver.java
Extension
.java
Programming language
Java
Mime type
text/plain
File type
ASCII text, with CRLF line terminators
Tag

      
    
Rootfs path

      
    
Size
21522 (21.0 KB)
MD5
e8e5a0cd8431886dc055e01820199bf4
SHA1
38fcb4172d30dc03953841021997a6024d40261c
SHA256
0f44c3584e9674a4dfeb847de9c2816ab8304904649adfae252b1e08cc9b0c0f
SHA512

      
    
SHA1_git
324881506208e75cd6d1620d3aaccd9342d9fb67
Is binary

      
    
Is text
True
Is archive

      
    
Is media

      
    
Is legal

      
    
Is manifest

      
    
Is readme

      
    
Is top level

      
    
Is key file

      
    
SSIServletExternalResolver.java | 21.0 KB |

/* * 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.ssi; import java.io.IOException; import java.net.URL; import java.net.URLConnection; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Date; import java.util.Enumeration; import java.util.Locale; import java.util.Objects; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.apache.catalina.connector.Connector; import org.apache.catalina.connector.Request; import org.apache.tomcat.util.buf.B2CConverter; import org.apache.tomcat.util.buf.UDecoder; import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.RequestUtil; import org.apache.tomcat.util.res.StringManager; /** * An implementation of SSIExternalResolver that is used with servlets. */ public class SSIServletExternalResolver implements SSIExternalResolver { private static final StringManager sm = StringManager.getManager(SSIServletExternalResolver.class); protected final String[] VARIABLE_NAMES = { "AUTH_TYPE", "CONTENT_LENGTH", "CONTENT_TYPE", "DOCUMENT_NAME", "DOCUMENT_URI", "GATEWAY_INTERFACE", "HTTP_ACCEPT", "HTTP_ACCEPT_ENCODING", "HTTP_ACCEPT_LANGUAGE", "HTTP_CONNECTION", "HTTP_HOST", "HTTP_REFERER", "HTTP_USER_AGENT", "PATH_INFO", "PATH_TRANSLATED", "QUERY_STRING", "QUERY_STRING_UNESCAPED", "REMOTE_ADDR", "REMOTE_HOST", "REMOTE_PORT", "REMOTE_USER", "REQUEST_METHOD", "REQUEST_URI", "SCRIPT_FILENAME", "SCRIPT_NAME", "SERVER_ADDR", "SERVER_NAME", "SERVER_PORT", "SERVER_PROTOCOL", "SERVER_SOFTWARE", "UNIQUE_ID" }; protected final ServletContext context; protected final HttpServletRequest req; protected final HttpServletResponse res; protected final boolean isVirtualWebappRelative; protected final int debug; protected final String inputEncoding; public SSIServletExternalResolver(ServletContext context, HttpServletRequest req, HttpServletResponse res, boolean isVirtualWebappRelative, int debug, String inputEncoding) { this.context = context; this.req = req; this.res = res; this.isVirtualWebappRelative = isVirtualWebappRelative; this.debug = debug; this.inputEncoding = inputEncoding; } @Override public void log(String message, Throwable throwable) { /* * We can't assume that Servlet.log(message, null) is the same as Servlet.log( message ), since API doesn't seem * to say so. */ if (throwable != null) { context.log(message, throwable); } else { context.log(message); } } @Override public void addVariableNames(Collection<String> variableNames) { for (String variableName : VARIABLE_NAMES) { String variableValue = getVariableValue(variableName); if (variableValue != null) { variableNames.add(variableName); } } Enumeration<String> e = req.getAttributeNames(); while (e.hasMoreElements()) { String name = e.nextElement(); if (!isNameReserved(name)) { variableNames.add(name); } } } protected Object getReqAttributeIgnoreCase(String targetName) { Object object = null; if (!isNameReserved(targetName)) { object = req.getAttribute(targetName); if (object == null) { Enumeration<String> e = req.getAttributeNames(); while (e.hasMoreElements()) { String name = e.nextElement(); if (targetName.equalsIgnoreCase(name) && !isNameReserved(name)) { object = req.getAttribute(name); if (object != null) { break; } } } } } return object; } protected boolean isNameReserved(String name) { return name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("sun."); } @Override public void setVariableValue(String name, String value) { if (!isNameReserved(name)) { req.setAttribute(name, value); } } @Override public String getVariableValue(String name) { String retVal; Object object = getReqAttributeIgnoreCase(name); if (object != null) { retVal = object.toString(); } else { retVal = getCGIVariable(name); } return retVal; } protected String getCGIVariable(String name) { String retVal = null; String[] nameParts = name.toUpperCase(Locale.ENGLISH).split("_"); int requiredParts = 2; if (nameParts.length == 1) { if (nameParts[0].equals("PATH")) { requiredParts = 1; } } else if (nameParts[0].equals("AUTH")) { if (nameParts[1].equals("TYPE")) { retVal = req.getAuthType(); } } else if (nameParts[0].equals("CONTENT")) { if (nameParts[1].equals("LENGTH")) { long contentLength = req.getContentLengthLong(); if (contentLength >= 0) { retVal = Long.toString(contentLength); } } else if (nameParts[1].equals("TYPE")) { retVal = req.getContentType(); } } else if (nameParts[0].equals("DOCUMENT")) { if (nameParts[1].equals("NAME")) { String requestURI = req.getRequestURI(); retVal = requestURI.substring(requestURI.lastIndexOf('/') + 1); } else if (nameParts[1].equals("URI")) { retVal = req.getRequestURI(); } } else if (name.equalsIgnoreCase("GATEWAY_INTERFACE")) { retVal = "CGI/1.1"; } else if (nameParts[0].equals("HTTP")) { switch (nameParts[1]) { case "ACCEPT" -> { String accept = null; if (nameParts.length == 2) { accept = "Accept"; } else if (nameParts[2].equals("ENCODING")) { requiredParts = 3; accept = "Accept-Encoding"; } else if (nameParts[2].equals("LANGUAGE")) { requiredParts = 3; accept = "Accept-Language"; } if (accept != null) { Enumeration<String> acceptHeaders = req.getHeaders(accept); if (acceptHeaders != null) { if (acceptHeaders.hasMoreElements()) { StringBuilder rv = new StringBuilder(acceptHeaders.nextElement()); while (acceptHeaders.hasMoreElements()) { rv.append(", "); rv.append(acceptHeaders.nextElement()); } retVal = rv.toString(); } } } } case "CONNECTION" -> retVal = req.getHeader("Connection"); case "HOST" -> retVal = req.getHeader("Host"); case "REFERER" -> retVal = req.getHeader("Referer"); case "USER" -> { if (nameParts.length == 3) { if (nameParts[2].equals("AGENT")) { requiredParts = 3; retVal = req.getHeader("User-Agent"); } } } } } else if (nameParts[0].equals("PATH")) { if (nameParts[1].equals("INFO")) { retVal = req.getPathInfo(); } else if (nameParts[1].equals("TRANSLATED")) { retVal = req.getPathTranslated(); } } else if (nameParts[0].equals("QUERY")) { if (nameParts[1].equals("STRING")) { String queryString = req.getQueryString(); if (nameParts.length == 2) { // apache displays this as an empty string rather than (none) retVal = nullToEmptyString(queryString); } else if (nameParts[2].equals("UNESCAPED")) { requiredParts = 3; if (queryString != null) { Charset uriCharset = null; Charset requestCharset = null; boolean useBodyEncodingForURI = false; // Get encoding settings from request / connector if possible if (req instanceof Request) { requestCharset = ((Request) req).getCoyoteRequest().getCharsetHolder().getCharset(); Connector connector = ((Request) req).getConnector(); uriCharset = connector.getURICharset(); useBodyEncodingForURI = connector.getUseBodyEncodingForURI(); } Charset queryStringCharset; // If valid, apply settings from request / connector // Use default as a last resort if (useBodyEncodingForURI && requestCharset != null) { queryStringCharset = requestCharset; } else { queryStringCharset = Objects.requireNonNullElse(uriCharset, StandardCharsets.UTF_8); } retVal = UDecoder.URLDecode(queryString, queryStringCharset); } } } } else if (nameParts[0].equals("REMOTE")) { switch (nameParts[1]) { case "ADDR" -> retVal = req.getRemoteAddr(); case "HOST" -> retVal = req.getRemoteHost(); case "IDENT" -> { // Not implemented } case "PORT" -> retVal = Integer.toString(req.getRemotePort()); case "USER" -> retVal = req.getRemoteUser(); } } else if (nameParts[0].equals("REQUEST")) { if (nameParts[1].equals("METHOD")) { retVal = req.getMethod(); } else if (nameParts[1].equals("URI")) { // If this is an error page, get the original URI retVal = (String) req.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI); if (retVal == null) { retVal = req.getRequestURI(); } } } else if (nameParts[0].equals("SCRIPT")) { String scriptName = req.getServletPath(); if (nameParts[1].equals("FILENAME")) { retVal = context.getRealPath(scriptName); } else if (nameParts[1].equals("NAME")) { retVal = scriptName; } } else if (nameParts[0].equals("SERVER")) { if (nameParts[1].equals("ADDR")) { retVal = req.getLocalAddr(); } if (nameParts[1].equals("NAME")) { retVal = req.getServerName(); } else if (nameParts[1].equals("PORT")) { retVal = Integer.toString(req.getServerPort()); } else if (nameParts[1].equals("PROTOCOL")) { retVal = req.getProtocol(); } else if (nameParts[1].equals("SOFTWARE")) { retVal = context.getServerInfo() + ' ' + System.getProperty("java.vm.name") + '/' + System.getProperty("java.vm.version") + ' ' + System.getProperty("os.name"); } } else if (name.equalsIgnoreCase("UNIQUE_ID")) { retVal = req.getRequestedSessionId(); } if (requiredParts != nameParts.length) { return null; } return retVal; } @Override public Date getCurrentDate() { return new Date(); } protected String nullToEmptyString(String string) { String retVal = string; if (retVal == null) { retVal = ""; } return retVal; } protected String getPathWithoutFileName(String servletPath) { String retVal = null; int lastSlash = servletPath.lastIndexOf('/'); if (lastSlash >= 0) { // cut off file name retVal = servletPath.substring(0, lastSlash + 1); } return retVal; } protected String getPathWithoutContext(final String contextPath, final String servletPath) { if (servletPath.startsWith(contextPath)) { return servletPath.substring(contextPath.length()); } return servletPath; } protected String getAbsolutePath(String path) throws IOException { String pathWithoutContext = SSIServletRequestUtil.getRelativePath(req); String prefix = getPathWithoutFileName(pathWithoutContext); if (prefix == null) { throw new IOException(sm.getString("ssiServletExternalResolver.removeFilenameError", pathWithoutContext)); } String fullPath = prefix + path; String retVal = RequestUtil.normalize(fullPath); if (retVal == null) { throw new IOException(sm.getString("ssiServletExternalResolver.normalizationError", fullPath)); } return retVal; } protected ServletContextAndPath getServletContextAndPathFromNonVirtualPath(String nonVirtualPath) throws IOException { if (nonVirtualPath.startsWith("/") || nonVirtualPath.startsWith("\\")) { throw new IOException(sm.getString("ssiServletExternalResolver.absoluteNonVirtualPath", nonVirtualPath)); } if (nonVirtualPath.contains("../")) { throw new IOException( sm.getString("ssiServletExternalResolver.pathTraversalNonVirtualPath", nonVirtualPath)); } return new ServletContextAndPath(context, getAbsolutePath(nonVirtualPath)); } protected ServletContextAndPath getServletContextAndPathFromVirtualPath(String virtualPath) throws IOException { if (!virtualPath.startsWith("/") && !virtualPath.startsWith("\\")) { return new ServletContextAndPath(context, getAbsolutePath(virtualPath)); } String normalized = RequestUtil.normalize(virtualPath); if (isVirtualWebappRelative) { return new ServletContextAndPath(context, normalized); } ServletContext normContext = context.getContext(normalized); if (normContext == null) { throw new IOException(sm.getString("ssiServletExternalResolver.noContext", normalized)); } // If it's the root context, then there is no context element to remove. // ie: '/file1.shtml' vs '/appName1/file1.shtml' if (!isRootContext(normContext)) { String noContext = getPathWithoutContext(normContext.getContextPath(), normalized); return new ServletContextAndPath(normContext, noContext); } return new ServletContextAndPath(normContext, normalized); } // Assumes servletContext is not-null // Assumes that identity comparison will be true for the same context // Assuming the above, getContext("/") will be non-null as long as the root context is accessible. // If it isn't, then servletContext can't be the root context anyway, hence they will not match. protected boolean isRootContext(ServletContext servletContext) { return servletContext == servletContext.getContext("/"); } protected ServletContextAndPath getServletContextAndPath(String originalPath, boolean virtual) throws IOException { if (debug > 0) { log("SSIServletExternalResolver.getServletContextAndPath( " + originalPath + ", " + virtual + ")", null); } if (virtual) { return getServletContextAndPathFromVirtualPath(originalPath); } else { return getServletContextAndPathFromNonVirtualPath(originalPath); } } protected URLConnection getURLConnection(String originalPath, boolean virtual) throws IOException { ServletContextAndPath csAndP = getServletContextAndPath(originalPath, virtual); ServletContext context = csAndP.servletContext(); String path = csAndP.path(); URL url = context.getResource(path); if (url == null) { throw new IOException(sm.getString("ssiServletExternalResolver.noResource", path)); } return url.openConnection(); } @Override public long getFileLastModified(String path, boolean virtual) throws IOException { long lastModified = 0; try { URLConnection urlConnection = getURLConnection(path, virtual); lastModified = urlConnection.getLastModified(); } catch (IOException ignore) { // Ignore this. It will always fail for non-file based includes } return lastModified; } @Override public long getFileSize(String path, boolean virtual) throws IOException { long fileSize = -1; try { URLConnection urlConnection = getURLConnection(path, virtual); fileSize = urlConnection.getContentLengthLong(); } catch (IOException ignore) { // Ignore this. It will always fail for non-file based includes } return fileSize; } /* * We are making lots of unnecessary copies of the included data here. If someone ever complains that this is slow, * we should connect the included stream to the print writer that SSICommand uses. */ @Override public String getFileText(String originalPath, boolean virtual) throws IOException { try { ServletContextAndPath csAndP = getServletContextAndPath(originalPath, virtual); ServletContext context = csAndP.servletContext(); String path = csAndP.path(); RequestDispatcher rd = context.getRequestDispatcher(path); if (rd == null) { throw new IOException(sm.getString("ssiServletExternalResolver.requestDispatcherError", path)); } ByteArrayServletOutputStream basos = new ByteArrayServletOutputStream(); ResponseIncludeWrapper responseIncludeWrapper = new ResponseIncludeWrapper(res, basos); rd.include(req, responseIncludeWrapper); // We can't assume the included servlet flushed its output responseIncludeWrapper.flushOutputStreamOrWriter(); byte[] bytes = basos.toByteArray(); // Assume platform default encoding unless otherwise specified String retVal; if (inputEncoding == null) { retVal = new String(bytes); } else { retVal = new String(bytes, B2CConverter.getCharset(inputEncoding)); } /* * Make an assumption that an empty response is a failure. This is a problem if a truly empty file were * included, but not sure how else to tell. */ if (retVal.isEmpty() && !Method.HEAD.equals(req.getMethod())) { throw new IOException(sm.getString("ssiServletExternalResolver.noFile", path)); } return retVal; } catch (ServletException e) { throw new IOException(sm.getString("ssiServletExternalResolver.noIncludeFile", originalPath), e); } } protected record ServletContextAndPath(ServletContext servletContext, String path) { public ServletContext getServletContext() { return servletContext; } public String getPath() { return path; } } }
Detected license expression
apache-2.0
Detected license expression (SPDX)
Apache-2.0
Percentage of license text
6.78
Copyrights

      
    
Holders

      
    
Authors

      
    
License detections License expression License expression SPDX
apache_2_0-4bde3f57-78aa-4201-96bf-531cba09e7de apache-2.0 Apache-2.0
URL Start line End line
http://www.apache.org/licenses/LICENSE-2.0 9 9