diff --git a/java/org/apache/catalina/servlets/CGIServlet.java b/java/org/apache/catalina/servlets/CGIServlet.java index 571cd10871c8..86d29f3c4f6c 100644 --- a/java/org/apache/catalina/servlets/CGIServlet.java +++ b/java/org/apache/catalina/servlets/CGIServlet.java @@ -57,20 +57,15 @@ /** - * CGI-invoking servlet for web applications, used to execute scripts which - * comply to the Common Gateway Interface (CGI) specification and are named - * in the path-info used to invoke this servlet. - * + * CGI-invoking servlet for web applications, used to execute scripts which comply to the Common Gateway Interface (CGI) + * specification and are named in the path-info used to invoke this servlet. *

- * Note: This code compiles and even works for simple CGI cases. - * Exhaustive testing has not been done. Please consider it beta - * quality. Feedback is appreciated to the author (see below). + * Note: This code compiles and even works for simple CGI cases. Exhaustive testing has not been done. Please + * consider it beta quality. Feedback is appreciated to the author (see below). *

*

- * * Example:
- * If an instance of this servlet was mapped (using - * <web-app>/WEB-INF/web.xml) to: + * If an instance of this servlet was mapped (using <web-app>/WEB-INF/web.xml) to: *

*

* @@ -97,47 +92,33 @@ * with the script's PATH_INFO set to /pathinfo1. *

*

- * Recommendation: House all your CGI scripts under - * <webapp>/WEB-INF/cgi. This will ensure that you do not - * accidentally expose your cgi scripts' code to the outside world and that - * your cgis will be cleanly ensconced underneath the WEB-INF (i.e., - * non-content) area. + * Recommendation: House all your CGI scripts under <webapp>/WEB-INF/cgi. This will ensure that you + * do not accidentally expose your cgi scripts' code to the outside world and that your cgis will be cleanly ensconced + * underneath the WEB-INF (i.e., non-content) area. *

*

- * The default CGI location is mentioned above. You have the flexibility to - * put CGIs wherever you want, however: + * The default CGI location is mentioned above. You have the flexibility to put CGIs wherever you want, however: *

*

- * The CGI search path will start at - * webAppRootDir + File.separator + cgiPathPrefix - * (or webAppRootDir alone if cgiPathPrefix is - * null). + * The CGI search path will start at webAppRootDir + File.separator + cgiPathPrefix (or webAppRootDir alone if + * cgiPathPrefix is null). *

*

- * cgiPathPrefix is defined by setting - * this servlet's cgiPathPrefix init parameter + * cgiPathPrefix is defined by setting this servlet's cgiPathPrefix init parameter *

- * *

- * - * CGI Specification:
derived from - * http://cgi-spec.golux.com. - * 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 - * Community Practice (BCP). - * Such instances should be well-documented here. Please email the - * Tomcat group - * with amendments. - * + * CGI Specification:
+ * derived from http://cgi-spec.golux.com. 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 Community Practice (BCP). Such instances should be well-documented here. Please + * email the Tomcat group with amendments. *

*

- * * Canonical metavariables:
- * The CGI specification defines the following canonical metavariables: - *
+ * The CGI specification defines the following canonical metavariables:
* [excerpt from CGI specification] + * *

  *  AUTH_TYPE
  *  CONTENT_LENGTH
@@ -158,79 +139,58 @@
  *  SERVER_SOFTWARE
  * 
*

- * Metavariables with names beginning with the protocol name (e.g., - * "HTTP_ACCEPT") are also canonical in their description of request header - * fields. The number and meaning of these fields may change independently - * of this specification. (See also section 6.1.5 [of the CGI specification].) + * Metavariables with names beginning with the protocol name (e.g., "HTTP_ACCEPT") are also canonical in their + * description of request header fields. The number and meaning of these fields may change independently of this + * specification. (See also section 6.1.5 [of the CGI specification].) *

* [end excerpt] - * - *

Implementation notes

+ *

Implementation notes

*

- * - * standard input handling: If your script accepts standard input, - * then the client must start sending input within a certain timeout period, - * otherwise the servlet will assume no input is coming and carry on running - * the script. The script's the standard input will be closed and handling of - * any further input from the client is undefined. Most likely it will be - * ignored. If this behavior becomes undesirable, then this servlet needs - * to be enhanced to handle threading of the spawned process' stdin, stdout, - * 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 stderrTimeout of your webapps' cgi-handling - * servlet. + * standard input handling: If your script accepts standard input, then the client must start sending input + * within a certain timeout period, otherwise the servlet will assume no input is coming and carry on running the + * script. The script's the standard input will be closed and handling of any further input from the client is + * undefined. Most likely it will be ignored. If this behavior becomes undesirable, then this servlet needs to be + * enhanced to handle threading of the spawned process' stdin, stdout, 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 + * stderrTimeout of your webapps' cgi-handling servlet. *

*

- * - * Metavariable Values: According to the CGI specification, - * implementations may choose to represent both null or missing values in an - * implementation-specific manner, but must define that manner. This - * implementation chooses to always define all required metavariables, but - * set the value to "" for all metavariables whose value is either null or - * undefined. PATH_TRANSLATED is the sole exception to this rule, as per the - * CGI Specification. - * + * Metavariable Values: According to the CGI specification, implementations may choose to represent both null or + * missing values in an implementation-specific manner, but must define that manner. This implementation chooses to + * always define all required metavariables, but set the value to "" for all metavariables whose value is either null or + * undefined. PATH_TRANSLATED is the sole exception to this rule, as per the CGI Specification. *

*

- * - * NPH -- Non-parsed-header implementation: This implementation does - * not support the CGI NPH concept, whereby server ensures that the data - * supplied to the script are precisely as supplied by the client and - * unaltered by the server. + * NPH -- Non-parsed-header implementation: This implementation does not support the CGI NPH concept, whereby + * server ensures that the data supplied to the script are precisely as supplied by the client and unaltered by the + * server. *

*

- * The function of a servlet container (including Tomcat) is specifically - * designed to parse and possible alter CGI-specific variables, and as - * such makes NPH functionality difficult to support. + * The function of a servlet container (including Tomcat) is specifically designed to parse and possible alter + * CGI-specific variables, and as such makes NPH functionality difficult to support. *

*

- * The CGI specification states that compliant servers MAY support NPH output. - * It does not state servers MUST support NPH output to be unconditionally - * compliant. Thus, this implementation maintains unconditional compliance - * with the specification though NPH support is not present. + * The CGI specification states that compliant servers MAY support NPH output. It does not state servers MUST support + * NPH output to be unconditionally compliant. Thus, this implementation maintains unconditional compliance with the + * specification though NPH support is not present. *

*

- * - * The CGI specification is located at - * http://cgi-spec.golux.com. - * + * The CGI specification is located at http://cgi-spec.golux.com. *

*

TODO:

* * * @author Martin T Dengler [root@martindengler.com] @@ -265,10 +225,8 @@ public final class CGIServlet extends HttpServlet { /** - * The CGI search path will start at - * webAppRootDir + File.separator + cgiPathPrefix - * (or webAppRootDir alone if cgiPathPrefix is - * null) + * The CGI search path will start at webAppRootDir + File.separator + cgiPathPrefix (or webAppRootDir alone if + * cgiPathPrefix is null) */ private String cgiPathPrefix = null; @@ -279,8 +237,7 @@ public final class CGIServlet extends HttpServlet { private List cgiExecutableArgs = null; /** the encoding to use for parameters */ - private String parameterEncoding = - System.getProperty("file.encoding", "UTF-8"); + private String parameterEncoding = System.getProperty("file.encoding", "UTF-8"); /* The HTTP methods this Servlet will pass to the CGI script */ private Set cgiMethods = new HashSet<>(); @@ -288,20 +245,17 @@ public final class CGIServlet extends HttpServlet { /** - * The time (in milliseconds) to wait for the reading of stderr to complete - * before terminating the CGI process. + * The time (in milliseconds) to wait for the reading of stderr to complete before terminating the CGI process. */ 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 _. + * 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"); + 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 */ private static final Object expandFileLock = new Object(); @@ -310,44 +264,33 @@ public final class CGIServlet extends HttpServlet { private final Map shellEnv = new HashMap<>(); /** - * Enable creation of script command line arguments from query-string. - * See https://tools.ietf.org/html/rfc3875#section-4.4 - * 4.4. The Script Command Line + * Enable creation of script command line arguments from query-string. See + * https://tools.ietf.org/html/rfc3875#section-4.4 4.4. The Script Command Line */ private boolean enableCmdLineArguments = false; /** - * Limits the encoded form of individual command line arguments. By default - * values are limited to those allowed by the RFC. - * See https://tools.ietf.org/html/rfc3875#section-4.4 - * - * Uses \Q...\E to avoid individual quoting. + * Limits the encoded form of individual command line arguments. By default values are limited to those allowed by + * the RFC. See https://tools.ietf.org/html/rfc3875#section-4.4 Uses \Q...\E to avoid individual quoting. */ - private Pattern cmdLineArgumentsEncodedPattern = - Pattern.compile("[\\w\\Q%;/?:@&,$-.!~*'()\\E]+"); + private Pattern cmdLineArgumentsEncodedPattern = Pattern.compile("[\\w\\Q%;/?:@&,$-.!~*'()\\E]+"); /** - * Limits the decoded form of individual command line arguments. Default - * varies by platform. + * Limits the decoded form of individual command line arguments. Default varies by platform. */ private Pattern cmdLineArgumentsDecodedPattern = DEFAULT_CMD_LINE_ARGUMENTS_DECODED_PATTERN; - /** * Sets instance variables. *

* Modified from Craig R. McClanahan's InvokerServlet *

* - * @param config a ServletConfig object - * containing the servlet's - * configuration and initialization - * parameters + * @param config a ServletConfig object containing the servlet's configuration and initialization + * parameters * - * @exception ServletException if an exception has occurred that - * interferes with the servlet's normal - * operation + * @exception ServletException if an exception has occurred that interferes with the servlet's normal operation */ @Override public void init(ServletConfig config) throws ServletException { @@ -357,14 +300,14 @@ public void init(ServletConfig config) throws ServletException { // Set our properties from the initialization parameters cgiPathPrefix = getServletConfig().getInitParameter("cgiPathPrefix"); boolean passShellEnvironment = - Boolean.parseBoolean(getServletConfig().getInitParameter("passShellEnvironment")); + Boolean.parseBoolean(getServletConfig().getInitParameter("passShellEnvironment")); if (passShellEnvironment) { shellEnv.putAll(System.getenv()); } Enumeration e = config.getInitParameterNames(); - while(e.hasMoreElements()) { + while (e.hasMoreElements()) { String initParamName = e.nextElement(); if (initParamName.startsWith("environment-variable-")) { if (initParamName.length() == 21) { @@ -381,8 +324,7 @@ public void init(ServletConfig config) throws ServletException { if (getServletConfig().getInitParameter("executable-arg-1") != null) { List args = new ArrayList<>(); for (int i = 1;; i++) { - String arg = getServletConfig().getInitParameter( - "executable-arg-" + i); + String arg = getServletConfig().getInitParameter("executable-arg-" + i); if (arg == null) { break; } @@ -396,18 +338,15 @@ public void init(ServletConfig config) throws ServletException { } if (getServletConfig().getInitParameter("stderrTimeout") != null) { - stderrTimeout = Long.parseLong(getServletConfig().getInitParameter( - "stderrTimeout")); + stderrTimeout = Long.parseLong(getServletConfig().getInitParameter("stderrTimeout")); } if (getServletConfig().getInitParameter("envHttpHeaders") != null) { - envHttpHeadersPattern = - Pattern.compile(getServletConfig().getInitParameter("envHttpHeaders")); + envHttpHeadersPattern = Pattern.compile(getServletConfig().getInitParameter("envHttpHeaders")); } if (getServletConfig().getInitParameter("enableCmdLineArguments") != null) { - enableCmdLineArguments = - Boolean.parseBoolean(config.getInitParameter("enableCmdLineArguments")); + enableCmdLineArguments = Boolean.parseBoolean(config.getInitParameter("enableCmdLineArguments")); } if (getServletConfig().getInitParameter("cgiMethods") != null) { @@ -444,14 +383,13 @@ public void init(ServletConfig config) throws ServletException { /** * Logs important Servlet API and container information. - * *

* Based on SnoopAllServlet by Craig R. McClanahan *

* - * @param req HttpServletRequest object used as source of information + * @param req HttpServletRequest object used as source of information * - * @exception IOException if a write operation exception occurs + * @exception IOException if a write operation exception occurs */ private void printServletEnvironment(HttpServletRequest req) throws IOException { @@ -460,7 +398,7 @@ private void printServletEnvironment(HttpServletRequest req) throws IOException Enumeration attrs = req.getAttributeNames(); while (attrs.hasMoreElements()) { String attr = attrs.nextElement(); - log.trace("Request Attribute: " + attr + ": [ " + req.getAttribute(attr) +"]"); + log.trace("Request Attribute: " + attr + ": [ " + req.getAttribute(attr) + "]"); } log.trace("Character Encoding: [" + req.getCharacterEncoding() + "]"); log.trace("Content Length: [" + req.getContentLengthLong() + "]"); @@ -468,7 +406,7 @@ private void printServletEnvironment(HttpServletRequest req) throws IOException Enumeration locales = req.getLocales(); while (locales.hasMoreElements()) { Locale locale = locales.nextElement(); - log.trace("Locale: [" +locale + "]"); + log.trace("Locale: [" + locale + "]"); } Enumeration params; try { @@ -511,8 +449,7 @@ private void printServletEnvironment(HttpServletRequest req) throws IOException 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 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() + "]"); @@ -571,20 +508,18 @@ private void printServletEnvironment(HttpServletRequest req) throws IOException attrs = getServletContext().getAttributeNames(); while (attrs.hasMoreElements()) { String attr = attrs.nextElement(); - log.trace("Servlet Context Attribute: " + attr + - ": [" + getServletContext().getAttribute(attr) + "]"); + log.trace("Servlet Context Attribute: " + attr + ": [" + getServletContext().getAttribute(attr) + "]"); } } @Override - protected void service(HttpServletRequest req, HttpServletResponse res) - throws ServletException, IOException { + protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { String method = req.getMethod(); if (cgiMethodsAll || cgiMethods.contains(method)) { doGet(req, res); - } else if (DEFAULT_SUPER_METHODS.contains(method)){ + } else if (DEFAULT_SUPER_METHODS.contains(method)) { // If the CGI servlet is explicitly configured to handle one of // these methods it will be handled in the previous condition super.service(req, res); @@ -598,23 +533,20 @@ protected void service(HttpServletRequest req, HttpServletResponse res) /** * Provides CGI Gateway service. * - * @param req HttpServletRequest passed in by servlet container - * @param res HttpServletResponse passed in by servlet container + * @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 + * @exception ServletException if a servlet-specific exception occurs + * @exception IOException if a read/write exception occurs */ @Override - protected void doGet(HttpServletRequest req, HttpServletResponse res) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { CGIEnvironment cgiEnv = new CGIEnvironment(req, getServletContext()); if (cgiEnv.isValid()) { - CGIRunner cgi = new CGIRunner(cgiEnv.getCommand(), - cgiEnv.getEnvironment(), - cgiEnv.getWorkingDirectory(), - cgiEnv.getParameters()); + CGIRunner cgi = new CGIRunner(cgiEnv.getCommand(), cgiEnv.getEnvironment(), cgiEnv.getWorkingDirectory(), + cgiEnv.getParameters()); if ("POST".equals(req.getMethod())) { cgi.setInput(req.getInputStream()); @@ -637,10 +569,9 @@ protected void doGet(HttpServletRequest req, HttpServletResponse res) @Override - protected void doOptions(HttpServletRequest req, HttpServletResponse res) - throws ServletException, IOException { + protected void doOptions(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // Note: This method will never be called if cgiMethods is "*" so that - // case does nto need to be handled here. + // case does nto need to be handled here. Set allowedMethods = new HashSet<>(); allowedMethods.addAll(cgiMethods); allowedMethods.addAll(DEFAULT_SUPER_METHODS); @@ -662,11 +593,10 @@ protected void doOptions(HttpServletRequest req, HttpServletResponse res) /* * Behaviour depends on the status code. * - * Status < 400 - Calls setStatus. Returns 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. + * 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) { @@ -680,8 +610,8 @@ private boolean setStatus(HttpServletResponse response, int status) throws IOExc /** - * Encapsulates the CGI environment and rules to derive - * that environment from the servlet container and request information. + * Encapsulates the CGI environment and rules to derive that environment from the servlet container and request + * information. */ protected class CGIEnvironment { @@ -705,7 +635,7 @@ protected class CGIEnvironment { private File tmpDir = null; /** derived cgi environment */ - private Map env = null; + private Map env = null; /** cgi command to be invoked */ private String command = null; @@ -721,17 +651,15 @@ protected class CGIEnvironment { /** - * Creates a CGIEnvironment and derives the necessary environment, - * query parameters, working directory, cgi command, etc. + * Creates a CGIEnvironment and derives the necessary environment, query parameters, working directory, cgi + * command, etc. + * + * @param req HttpServletRequest for information provided by the Servlet API + * @param context ServletContext for information provided by the Servlet API * - * @param req HttpServletRequest for information provided by - * 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 { + protected CGIEnvironment(HttpServletRequest req, ServletContext context) throws IOException { setupFromContext(context); boolean valid = setupFromRequest(req); @@ -740,8 +668,7 @@ protected CGIEnvironment(HttpServletRequest req, } if (valid) { - workingDirectory = new File(command.substring(0, - command.lastIndexOf(File.separator))); + workingDirectory = new File(command.substring(0, command.lastIndexOf(File.separator))); } else { workingDirectory = null; } @@ -753,8 +680,7 @@ protected CGIEnvironment(HttpServletRequest req, /** * Uses the ServletContext to set some CGI variables * - * @param context ServletContext for information provided by the - * Servlet API + * @param context ServletContext for information provided by the Servlet API */ protected void setupFromContext(ServletContext context) { this.context = context; @@ -766,31 +692,24 @@ protected void setupFromContext(ServletContext context) { /** * Uses the HttpServletRequest to set most CGI variables * - * @param req HttpServletRequest for information provided by - * the Servlet API + * @param req HttpServletRequest for information provided by the Servlet API * - * @return true if the request was parsed without error, false if there - * was a problem + * @return true if the request was parsed without error, false if there was a problem * * @throws UnsupportedEncodingException Unknown encoding */ - protected boolean setupFromRequest(HttpServletRequest req) - throws UnsupportedEncodingException { + protected boolean setupFromRequest(HttpServletRequest req) throws UnsupportedEncodingException { boolean isIncluded = false; // Look to see if this request is an include - if (req.getAttribute( - RequestDispatcher.INCLUDE_REQUEST_URI) != null) { + if (req.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null) { isIncluded = true; } if (isIncluded) { - this.contextPath = (String) req.getAttribute( - RequestDispatcher.INCLUDE_CONTEXT_PATH); - this.servletPath = (String) req.getAttribute( - RequestDispatcher.INCLUDE_SERVLET_PATH); - this.pathInfo = (String) req.getAttribute( - RequestDispatcher.INCLUDE_PATH_INFO); + this.contextPath = (String) req.getAttribute(RequestDispatcher.INCLUDE_CONTEXT_PATH); + this.servletPath = (String) req.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH); + this.pathInfo = (String) req.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO); } else { this.contextPath = req.getContextPath(); this.servletPath = req.getServletPath(); @@ -810,8 +729,7 @@ protected boolean setupFromRequest(HttpServletRequest req) req.getMethod().equals("HEAD"))) { String qs; if (isIncluded) { - qs = (String) req.getAttribute( - RequestDispatcher.INCLUDE_QUERY_STRING); + qs = (String) req.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING); } else { qs = req.getQueryString(); } @@ -821,8 +739,8 @@ protected boolean setupFromRequest(HttpServletRequest req) String encodedArgument = qsTokens.nextToken(); if (!cmdLineArgumentsEncodedPattern.matcher(encodedArgument).matches()) { if (log.isDebugEnabled()) { - log.debug(sm.getString("cgiServlet.invalidArgumentEncoded", - encodedArgument, cmdLineArgumentsEncodedPattern.toString())); + log.debug(sm.getString("cgiServlet.invalidArgumentEncoded", encodedArgument, + cmdLineArgumentsEncodedPattern.toString())); } return false; } @@ -831,8 +749,8 @@ protected boolean setupFromRequest(HttpServletRequest req) if (cmdLineArgumentsDecodedPattern != null && !cmdLineArgumentsDecodedPattern.matcher(decodedArgument).matches()) { if (log.isDebugEnabled()) { - log.debug(sm.getString("cgiServlet.invalidArgumentDecoded", - decodedArgument, cmdLineArgumentsDecodedPattern.toString())); + log.debug(sm.getString("cgiServlet.invalidArgumentDecoded", decodedArgument, + cmdLineArgumentsDecodedPattern.toString())); } return false; } @@ -848,11 +766,13 @@ protected boolean setupFromRequest(HttpServletRequest req) /** * Resolves core information about the cgi script. - * *

* Example URI: *

- *
 /servlet/cgigateway/dir1/realCGIscript/pathinfo1 
+ * + *
+         *  /servlet/cgigateway/dir1/realCGIscript/pathinfo1
+         * 
*
    *
  • path = $CATALINA_HOME/mywebapp/dir1/realCGIscript *
  • scriptName = /servlet/cgigateway/dir1/realCGIscript @@ -860,64 +780,45 @@ protected boolean setupFromRequest(HttpServletRequest req) *
  • name = realCGIscript *
*

- * CGI search algorithm: search the real path below - * <my-webapp-root> and find the first non-directory in - * the getPathTranslated("/"), reading/searching from left-to-right. + * CGI search algorithm: search the real path below <my-webapp-root> and find the first non-directory in + * the getPathTranslated("/"), reading/searching from left-to-right. *

*

- * The CGI search path will start at - * webAppRootDir + File.separator + cgiPathPrefix - * (or webAppRootDir alone if cgiPathPrefix is - * null). + * The CGI search path will start at webAppRootDir + File.separator + cgiPathPrefix (or webAppRootDir alone if + * cgiPathPrefix is null). *

*

- * cgiPathPrefix is defined by setting - * this servlet's cgiPathPrefix init parameter - * + * cgiPathPrefix is defined by setting this servlet's cgiPathPrefix init parameter *

* - * @param pathInfo String from HttpServletRequest.getPathInfo() - * @param webAppRootDir String from context.getRealPath("/") - * @param contextPath String as from - * HttpServletRequest.getContextPath() - * @param servletPath String as from - * HttpServletRequest.getServletPath() - * @param cgiPathPrefix subdirectory of webAppRootDir below which - * the web app's CGIs may be stored; can be null. - * The CGI search path will start at - * webAppRootDir + File.separator + cgiPathPrefix - * (or webAppRootDir alone if cgiPathPrefix is - * null). cgiPathPrefix is defined by setting - * the servlet's cgiPathPrefix init parameter. - * + * @param pathInfo String from HttpServletRequest.getPathInfo() + * @param webAppRootDir String from context.getRealPath("/") + * @param contextPath String as from HttpServletRequest.getContextPath() + * @param servletPath String as from HttpServletRequest.getServletPath() + * @param cgiPathPrefix subdirectory of webAppRootDir below which the web app's CGIs may be stored; can be null. + * The CGI search path will start at webAppRootDir + File.separator + cgiPathPrefix (or + * webAppRootDir alone if cgiPathPrefix is null). cgiPathPrefix is defined by setting + * the servlet's cgiPathPrefix init parameter. * * @return - *
    - *
  • - * path - full file-system path to valid cgi script, - * or null if no cgi was found - *
  • - * scriptName - - * CGI variable SCRIPT_NAME; the full URL path - * to valid cgi script or null if no cgi was - * found - *
  • - * cgiName - servlet pathInfo fragment corresponding to - * the cgi script itself, or null if not found - *
  • - * name - simple name (no directories) of the - * cgi script, or null if no cgi was found - *
+ *
    + *
  • path - full file-system path to valid cgi script, or null if no cgi was found + *
  • scriptName - CGI variable SCRIPT_NAME; the full URL path to valid cgi script or + * null if no cgi was found + *
  • cgiName - servlet pathInfo fragment corresponding to the cgi script itself, or + * null if not found + *
  • name - simple name (no directories) of the cgi script, or null if no cgi was + * found + *
*/ - protected String[] findCGI(String pathInfo, String webAppRootDir, - String contextPath, String servletPath, - String cgiPathPrefix) { + protected String[] findCGI(String pathInfo, String webAppRootDir, String contextPath, String servletPath, + String cgiPathPrefix) { String path = null; String name = null; String scriptname = null; if (webAppRootDir.lastIndexOf(File.separator) == (webAppRootDir.length() - 1)) { - //strip the trailing "/" from the webAppRootDir + // strip the trailing "/" from the webAppRootDir webAppRootDir = webAppRootDir.substring(0, (webAppRootDir.length() - 1)); } @@ -932,8 +833,7 @@ protected String[] findCGI(String pathInfo, String webAppRootDir, File currentLocation = new File(webAppRootDir); StringTokenizer dirWalker = new StringTokenizer(pathInfo, "/"); if (log.isDebugEnabled()) { - log.debug(sm.getString("cgiServlet.find.location", - currentLocation.getAbsolutePath())); + log.debug(sm.getString("cgiServlet.find.location", currentLocation.getAbsolutePath())); } StringBuilder cginameBuilder = new StringBuilder(); while (!currentLocation.isFile() && dirWalker.hasMoreElements()) { @@ -941,8 +841,7 @@ protected String[] findCGI(String pathInfo, String webAppRootDir, currentLocation = new File(currentLocation, nextElement); cginameBuilder.append('/').append(nextElement); if (log.isDebugEnabled()) { - log.debug(sm.getString("cgiServlet.find.location", - currentLocation.getAbsolutePath())); + log.debug(sm.getString("cgiServlet.find.location", currentLocation.getAbsolutePath())); } } String cginame = cginameBuilder.toString(); @@ -966,26 +865,24 @@ protected String[] findCGI(String pathInfo, String webAppRootDir, } /** - * Constructs the CGI environment to be supplied to the invoked CGI - * script; relies heavily on Servlet API methods and findCGI + * Constructs the CGI environment to be supplied to the invoked CGI script; relies heavily on Servlet API + * methods and findCGI + * + * @param req request associated with the CGI Invocation * - * @param req request associated with the CGI - * Invocation + * @return true if environment was set OK, false if there was a problem and no environment was set * - * @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 { /* * This method is slightly ugly; c'est la vie. - * "You cannot stop [ugliness], you can only hope to contain [it]" - * (apologies to Marv Albert regarding MJ) + * "You cannot stop [ugliness], you can only hope to contain [it]" (apologies to Marv Albert regarding MJ) */ // Add the shell environment variables (if any) - Map envp = new HashMap<>(shellEnv); + Map envp = new HashMap<>(shellEnv); // Add the CGI environment variables String sPathInfoOrig = null; @@ -1001,17 +898,13 @@ protected boolean setCGIEnvironment(HttpServletRequest req) throws IOException { sPathInfoOrig = this.pathInfo; sPathInfoOrig = sPathInfoOrig == null ? "" : sPathInfoOrig; - if (webAppRootDir == null ) { + if (webAppRootDir == null) { // The app has not been deployed in exploded form webAppRootDir = tmpDir.toString(); expandCGIScript(); } - sCGINames = findCGI(sPathInfoOrig, - webAppRootDir, - contextPath, - servletPath, - cgiPathPrefix); + sCGINames = findCGI(sPathInfoOrig, webAppRootDir, contextPath, servletPath, cgiPathPrefix); sCGIFullPath = sCGINames[0]; sCGIScriptName = sCGINames[1]; @@ -1031,8 +924,7 @@ protected boolean setCGIEnvironment(HttpServletRequest req) throws IOException { envp.put("SERVER_PROTOCOL", nullsToBlanks(req.getProtocol())); int port = req.getServerPort(); - Integer iPort = - (port == 0 ? Integer.valueOf(-1) : Integer.valueOf(port)); + Integer iPort = (port == 0 ? Integer.valueOf(-1) : Integer.valueOf(port)); envp.put("SERVER_PORT", iPort.toString()); envp.put("REQUEST_METHOD", nullsToBlanks(req.getMethod())); @@ -1082,7 +974,7 @@ protected boolean setCGIEnvironment(HttpServletRequest req) throws IOException { sPathTranslatedCGI = context.getRealPath(sPathInfoCGI); } if (sPathTranslatedCGI == null || "".equals(sPathTranslatedCGI)) { - //NOOP + // NOOP } else { envp.put("PATH_TRANSLATED", nullsToBlanks(sPathTranslatedCGI)); } @@ -1100,18 +992,17 @@ protected boolean setCGIEnvironment(HttpServletRequest req) throws IOException { envp.put("REMOTE_USER", nullsToBlanks(req.getRemoteUser())); - envp.put("REMOTE_IDENT", ""); //not necessary for full compliance + envp.put("REMOTE_IDENT", ""); // not necessary for full compliance envp.put("CONTENT_TYPE", nullsToBlanks(req.getContentType())); - /* Note CGI spec says CONTENT_LENGTH must be NULL ("") or undefined - * if there is no content, so we cannot put 0 or -1 in as per the - * Servlet API spec. + /* + * Note CGI spec says CONTENT_LENGTH must be NULL ("") or undefined if there is no content, so we cannot put + * 0 or -1 in as per the Servlet API spec. */ long contentLength = req.getContentLengthLong(); - String sContentLength = (contentLength <= 0 ? "" : - Long.toString(contentLength)); + String sContentLength = (contentLength <= 0 ? "" : Long.toString(contentLength)); envp.put("CONTENT_LENGTH", sContentLength); @@ -1120,9 +1011,9 @@ protected boolean setCGIEnvironment(HttpServletRequest req) throws IOException { while (headers.hasMoreElements()) { header = null; header = headers.nextElement().toUpperCase(Locale.ENGLISH); - //REMIND: rewrite multiple headers as if received as single - //REMIND: change character set - //REMIND: I forgot what the previous REMIND means + // REMIND: rewrite multiple headers as if received as single + // REMIND: change character set + // REMIND: I forgot what the previous REMIND means if (envHttpHeadersPattern.matcher(header).matches()) { envp.put("HTTP_" + header.replace('-', '_'), req.getHeader(header)); } @@ -1131,9 +1022,9 @@ protected boolean setCGIEnvironment(HttpServletRequest req) throws IOException { File fCGIFullPath = new File(sCGIFullPath); command = fCGIFullPath.getCanonicalPath(); - envp.put("X_TOMCAT_SCRIPT_PATH", command); //for kicks + envp.put("X_TOMCAT_SCRIPT_PATH", command); // for kicks - envp.put("SCRIPT_FILENAME", command); //for PHP + envp.put("SCRIPT_FILENAME", command); // for PHP this.env = envp; @@ -1141,8 +1032,8 @@ protected boolean setCGIEnvironment(HttpServletRequest req) throws IOException { } /** - * Extracts requested resource from web app archive to context work - * directory to enable CGI script to be executed. + * Extracts requested resource from web app archive to context work directory to enable CGI script to be + * executed. */ protected void expandCGIScript() { StringBuilder srcPath = new StringBuilder(); @@ -1150,7 +1041,7 @@ protected void expandCGIScript() { InputStream is = null; // paths depend on mapping - if (cgiPathPrefix == null ) { + if (cgiPathPrefix == null) { srcPath.append(pathInfo); is = context.getResourceAsStream(srcPath.toString()); destPath.append(tmpDir); @@ -1158,8 +1049,7 @@ protected void expandCGIScript() { } else { // essentially same search algorithm as findCGI() srcPath.append(cgiPathPrefix); - StringTokenizer pathWalker = - new StringTokenizer (pathInfo, "/"); + StringTokenizer pathWalker = new StringTokenizer(pathInfo, "/"); // start with first element while (pathWalker.hasMoreElements() && (is == null)) { srcPath.append('/'); @@ -1229,8 +1119,7 @@ protected void expandCGIScript() { /** - * Returns important CGI environment information in a multi-line text - * format. + * Returns important CGI environment information in a multi-line text format. * * @return CGI environment info */ @@ -1298,8 +1187,7 @@ public String toString() { /** * Gets derived command string * - * @return command string - * + * @return command string */ protected String getCommand() { return command; @@ -1309,8 +1197,7 @@ protected String getCommand() { /** * Gets derived CGI working directory * - * @return working directory - * + * @return working directory */ protected File getWorkingDirectory() { return workingDirectory; @@ -1320,8 +1207,7 @@ protected File getWorkingDirectory() { /** * Gets derived CGI environment * - * @return CGI environment - * + * @return CGI environment */ protected Map getEnvironment() { return env; @@ -1331,8 +1217,7 @@ protected Map getEnvironment() { /** * Gets derived CGI query parameters * - * @return CGI query parameters - * + * @return CGI query parameters */ protected ArrayList getParameters() { return cmdLineParameters; @@ -1342,9 +1227,7 @@ protected ArrayList getParameters() { /** * Gets validity status * - * @return true if this environment is valid, false - * otherwise - * + * @return true if this environment is valid, false otherwise */ protected boolean isValid() { return valid; @@ -1354,9 +1237,9 @@ protected boolean isValid() { /** * Converts null strings to blank strings ("") * - * @param s string to be converted if necessary - * @return a non-null string, either the original or the empty string - * ("") if the original was null + * @param s string to be converted if necessary + * + * @return a non-null string, either the original or the empty string ("") if the original was null */ protected String nullsToBlanks(String s) { return nullsToString(s, ""); @@ -1366,13 +1249,12 @@ protected String nullsToBlanks(String s) { /** * Converts null strings to another string * - * @param couldBeNull string to be converted if necessary - * @param subForNulls string to return instead of a null string - * @return a non-null string, either the original or the substitute - * string if the original was null + * @param couldBeNull string to be converted if necessary + * @param subForNulls string to return instead of a null string + * + * @return a non-null string, either the original or the substitute string if the original was null */ - protected String nullsToString(String couldBeNull, - String subForNulls) { + protected String nullsToString(String couldBeNull, String subForNulls) { return (couldBeNull == null ? subForNulls : couldBeNull); } @@ -1380,40 +1262,32 @@ protected String nullsToString(String couldBeNull, /** * Converts blank strings to another string * - * @param couldBeBlank string to be converted if necessary - * @param subForBlanks string to return instead of a blank string - * @return a non-null string, either the original or the substitute - * string if the original was null or empty ("") + * @param couldBeBlank string to be converted if necessary + * @param subForBlanks string to return instead of a blank string + * + * @return a non-null string, either the original or the substitute string if the original was null + * or empty ("") */ - protected String blanksToString(String couldBeBlank, - String subForBlanks) { + protected String blanksToString(String couldBeBlank, String subForBlanks) { return (couldBeBlank == null || couldBeBlank.isEmpty()) ? subForBlanks : couldBeBlank; } - } //class CGIEnvironment + } // class CGIEnvironment /** - * Encapsulates the knowledge of how to run a CGI script, given the - * script's desired environment and (optionally) input/output streams - * + * Encapsulates the knowledge of how to run a CGI script, given the script's desired environment and (optionally) + * input/output streams *

- * - * Exposes a run method used to actually invoke the - * CGI. - * + * Exposes a run method used to actually invoke the CGI. *

*

- * - * The CGI environment and settings are derived from the information - * passed to the constructor. - * + * The CGI environment and settings are derived from the information passed to the constructor. *

*

- * - * The input and output streams can be set by the setInput - * and setResponse methods, respectively. + * The input and output streams can be set by the setInput and setResponse methods, + * respectively. *

*/ protected class CGIRunner { @@ -1441,21 +1315,16 @@ protected class CGIRunner { /** - * Creates a CGIRunner and initializes its environment, working - * directory, and query parameters. - *
- * Input/output streams (optional) are set using the - * setInput and setResponse methods, - * respectively. + * Creates a CGIRunner and initializes its environment, working directory, and query parameters.
+ * Input/output streams (optional) are set using the setInput and setResponse methods, + * respectively. * - * @param command string full path to command to be executed - * @param env Map with the desired script environment - * @param wd File with the script's desired working directory - * @param params ArrayList with the script's query command line - * parameters as strings + * @param command string full path to command to be executed + * @param env Map with the desired script environment + * @param wd File with the script's desired working directory + * @param params ArrayList with the script's query command line parameters as strings */ - protected CGIRunner(String command, Map env, - File wd, ArrayList params) { + protected CGIRunner(String command, Map env, File wd, ArrayList params) { this.command = command; this.env = env; this.wd = wd; @@ -1479,8 +1348,7 @@ protected void updateReadyStatus() { /** * Gets ready status * - * @return false if not ready (run will throw - * an exception), true if ready + * @return false if not ready (run will throw an exception), true if ready */ protected boolean isReady() { return readyToRun; @@ -1488,11 +1356,9 @@ protected boolean isReady() { /** - * Sets HttpServletResponse object used to set headers and send - * output to - * - * @param response HttpServletResponse to be used + * Sets HttpServletResponse object used to set headers and send output to * + * @param response HttpServletResponse to be used */ protected void setResponse(HttpServletResponse response) { this.response = response; @@ -1503,8 +1369,7 @@ protected void setResponse(HttpServletResponse response) { /** * Sets standard input to be passed on to the invoked cgi script * - * @param stdin InputStream to be used - * + * @param stdin InputStream to be used */ protected void setInput(InputStream stdin) { this.stdin = stdin; @@ -1513,16 +1378,14 @@ protected void setInput(InputStream stdin) { /** - * Converts a Map to a String array by converting each - * key/value pair in the Map to a String in the form + * Converts a Map to a String array by converting each key/value pair in the Map to a String in the form * "key=value" (key + "=" + map.get(key).toString()) * - * @param map Map to convert + * @param map Map to convert * - * @return converted string array - * - * @exception NullPointerException if a hash key has a null value + * @return converted string array * + * @exception NullPointerException if a hash key has a null value */ protected String[] mapToStringArray(Map map) throws NullPointerException { List list = new ArrayList<>(map.size()); @@ -1534,61 +1397,44 @@ protected String[] mapToStringArray(Map map) throws NullPointerExcepti /** - * Executes a CGI script with the desired environment, current working - * directory, and input/output streams - * + * Executes a CGI script with the desired environment, current working directory, and input/output streams *

* This implements the following CGI specification recommendations: *

*
    - *
  • Servers SHOULD provide the "query" component of - * the script-URI as command-line arguments to scripts if it - * does not contain any unencoded "=" characters and the - * command-line arguments can be generated in an unambiguous - * manner. - *
  • Servers SHOULD set the AUTH_TYPE metavariable to the value - * of the "auth-scheme" token of the - * "Authorization" if it was supplied as part of the - * request header. See getCGIEnvironment method. - *
  • Where applicable, servers SHOULD set the current working - * directory to the directory in which the script is located - * before invoking it. - *
  • Server implementations SHOULD define their behavior for the - * following cases: - *
      - *
    • Allowed characters in pathInfo: This implementation - * does not allow ASCII NUL nor any character which cannot - * be URL-encoded according to internet standards; - *
    • Allowed characters in path segments: This - * implementation does not allow non-terminal NULL - * segments in the the path -- IOExceptions may be thrown; - *
    • "." and ".." path - * segments: - * This implementation does not allow "." and - * ".." in the the path, and such characters - * will result in an IOException being thrown (this should - * never happen since Tomcat normalises the requestURI - * before determining the contextPath, servletPath and - * pathInfo); - *
    • Implementation limitations: This implementation - * does not impose any limitations except as documented - * above. This implementation may be limited by the - * servlet container used to house this implementation. - * In particular, all the primary CGI variable values - * are derived either directly or indirectly from the - * container's implementation of the Servlet API methods. - *
    + *
  • Servers SHOULD provide the "query" component of the script-URI as command-line arguments to + * scripts if it does not contain any unencoded "=" characters and the command-line arguments can be generated + * in an unambiguous manner. + *
  • Servers SHOULD set the AUTH_TYPE metavariable to the value of the "auth-scheme" token of the + * "Authorization" if it was supplied as part of the request header. See + * getCGIEnvironment method. + *
  • Where applicable, servers SHOULD set the current working directory to the directory in which the script + * is located before invoking it. + *
  • Server implementations SHOULD define their behavior for the following cases: + *
      + *
    • Allowed characters in pathInfo: This implementation does not allow ASCII NUL nor any character + * which cannot be URL-encoded according to internet standards; + *
    • Allowed characters in path segments: This implementation does not allow non-terminal NULL segments + * in the the path -- IOExceptions may be thrown; + *
    • "." and ".." path segments: This implementation does not allow + * "." and ".." in the the path, and such characters will result in an IOException + * being thrown (this should never happen since Tomcat normalises the requestURI before determining the + * contextPath, servletPath and pathInfo); + *
    • Implementation limitations: This implementation does not impose any limitations except as + * documented above. This implementation may be limited by the servlet container used to house this + * implementation. In particular, all the primary CGI variable values are derived either directly or indirectly + * from the container's implementation of the Servlet API methods. + *
    *
* * @exception IOException if problems during reading/writing occur * - * @see java.lang.Runtime#exec(String command, String[] envp, - * File dir) + * @see java.lang.Runtime#exec(String command, String[] envp, File dir) */ protected void run() throws IOException { /* - * REMIND: this method feels too big; should it be re-written? + * REMIND: this method feels too big; should it be re-written? */ if (!isReady()) { @@ -1600,15 +1446,14 @@ protected void run() throws IOException { } if ((command.contains(File.separator + "." + File.separator)) || - (command.contains(File.separator + "..")) || - (command.contains(".." + File.separator))) { + (command.contains(File.separator + "..")) || (command.contains(".." + File.separator))) { throw new IOException(this.getClass().getName() + "Illegal Character in CGI command path " + "('.' or '..') detected. Not running CGI [" + command + "]."); } - /* original content/structure of this section taken from - * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4216884 - * with major modifications by Martin Dengler + /* + * original content/structure of this section taken from + * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4216884 with major modifications by Martin Dengler */ Runtime rt = null; BufferedReader cgiHeaderReader = null; @@ -1631,36 +1476,31 @@ protected void run() throws IOException { try { rt = Runtime.getRuntime(); - proc = rt.exec( - cmdAndArgs.toArray(new String[0]), - mapToStringArray(env), wd); + proc = rt.exec(cmdAndArgs.toArray(new String[0]), mapToStringArray(env), wd); String sContentLength = env.get("CONTENT_LENGTH"); - if(!"".equals(sContentLength)) { + if (!"".equals(sContentLength)) { commandsStdIn = new BufferedOutputStream(proc.getOutputStream()); IOTools.flow(stdin, commandsStdIn); commandsStdIn.flush(); commandsStdIn.close(); } - /* we want to wait for the process to exit, Process.waitFor() - * is useless in our situation; see + /* + * we want to wait for the process to exit, Process.waitFor() is useless in our situation; see * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4223650 */ boolean isRunning = true; - commandsStdErr = new BufferedReader - (new InputStreamReader(proc.getErrorStream())); - final BufferedReader stdErrRdr = commandsStdErr ; + commandsStdErr = new BufferedReader(new InputStreamReader(proc.getErrorStream())); + final BufferedReader stdErrRdr = commandsStdErr; errReaderThread = new Thread(() -> sendToLog(stdErrRdr)); errReaderThread.start(); - InputStream cgiHeaderStream = - new HTTPHeaderInputStream(proc.getInputStream()); - cgiHeaderReader = - new BufferedReader(new InputStreamReader(cgiHeaderStream)); + InputStream cgiHeaderStream = new HTTPHeaderInputStream(proc.getInputStream()); + cgiHeaderReader = new BufferedReader(new InputStreamReader(cgiHeaderStream)); // Need to be careful here. If sendError() is called the // response body should be provided by the standard error page @@ -1670,7 +1510,7 @@ protected void run() throws IOException { while (isRunning) { try { - //set headers + // set headers String line = null; while (((line = cgiHeaderReader.readLine()) != null) && !line.isEmpty()) { if (log.isTraceEnabled()) { @@ -1679,21 +1519,19 @@ protected void run() throws IOException { if (line.startsWith("HTTP")) { skipBody = setStatus(response, getSCFromHttpStatusLine(line)); } else if (line.indexOf(':') >= 0) { - String header = - line.substring(0, line.indexOf(':')).trim(); - String value = - line.substring(line.indexOf(':') + 1).trim(); + String header = line.substring(0, line.indexOf(':')).trim(); + String value = line.substring(line.indexOf(':') + 1).trim(); if (header.equalsIgnoreCase("status")) { skipBody = setStatus(response, getSCFromCGIStatusHeader(value)); } else { - response.addHeader(header , value); + response.addHeader(header, value); } } else { log.info(sm.getString("cgiServlet.runBadHeader", line)); } } - //write output + // write output byte[] bBuf = new byte[2048]; OutputStream out = response.getOutputStream(); @@ -1728,9 +1566,9 @@ protected void run() throws IOException { // Ignore } } - } //replacement for Process.waitFor() + } // replacement for Process.waitFor() - } catch (IOException e){ + } catch (IOException e) { log.warn(sm.getString("cgiServlet.runFail"), e); throw e; } finally { @@ -1755,9 +1593,10 @@ protected void run() throws IOException { try { errReaderThread.join(stderrTimeout); } catch (InterruptedException e) { - log.warn(sm.getString("cgiServlet.runReaderInterrupt")); } + log.warn(sm.getString("cgiServlet.runReaderInterrupt")); + } } - if (proc != null){ + if (proc != null) { proc.destroy(); proc = null; } @@ -1768,8 +1607,9 @@ protected void run() throws IOException { * Parses the Status-Line and extracts the status code. * * @param line The HTTP Status-Line (RFC2616, section 6.1) - * @return The extracted status code or the code representing an - * internal error if a valid status code cannot be extracted. + * + * @return The extracted status code or the code representing an internal error if a valid status code cannot be + * extracted. */ private int getSCFromHttpStatusLine(String line) { int statusStart = line.indexOf(' ') + 1; @@ -1799,8 +1639,9 @@ private int getSCFromHttpStatusLine(String line) { * * @param value The CGI Status value of the form * digit digit digit SP reason-phrase - * @return The extracted status code or the code representing an - * internal error if a valid status code cannot be extracted. + * + * @return The extracted status code or the code representing an internal error if a valid status code cannot be + * extracted. */ private int getSCFromCGIStatusHeader(String value) { if (value.length() < 3) { @@ -1825,11 +1666,11 @@ private int getSCFromCGIStatusHeader(String value) { private void sendToLog(BufferedReader rdr) { String line = null; - int lineCount = 0 ; + int lineCount = 0; try { while ((line = rdr.readLine()) != null) { log.warn(sm.getString("cgiServlet.runStdErr", line)); - lineCount++ ; + lineCount++; } } catch (IOException e) { log.warn(sm.getString("cgiServlet.runStdErrFail"), e); @@ -1844,12 +1685,11 @@ private void sendToLog(BufferedReader rdr) { log.warn(sm.getString("cgiServlet.runStdErrCount", Integer.valueOf(lineCount))); } } - } //class CGIRunner + } // class CGIRunner /** - * This is an input stream specifically for reading HTTP headers. It reads - * up to and including the two blank lines terminating the headers. It - * allows the content to be read using bytes or characters as appropriate. + * This is an input stream specifically for reading HTTP headers. It reads up to and including the two blank lines + * terminating the headers. It allows the content to be read using bytes or characters as appropriate. */ protected static class HTTPHeaderInputStream extends InputStream { private static final int STATE_CHARACTER = 0; @@ -1899,7 +1739,7 @@ public int read() throws IOException { */ if (i == 10) { // LF - switch(state) { + switch (state) { case STATE_CHARACTER: state = STATE_FIRST_LF; break; @@ -1914,7 +1754,7 @@ public int read() throws IOException { } else if (i == 13) { // CR - switch(state) { + switch (state) { case STATE_CHARACTER: state = STATE_FIRST_CR; break; @@ -1932,6 +1772,6 @@ public int read() throws IOException { return i; } - } // class HTTPHeaderInputStream + } // class HTTPHeaderInputStream -} //class CGIServlet +} // class CGIServlet diff --git a/java/org/apache/catalina/servlets/DefaultServlet.java b/java/org/apache/catalina/servlets/DefaultServlet.java index 250d200780c3..f9015729b906 100644 --- a/java/org/apache/catalina/servlets/DefaultServlet.java +++ b/java/org/apache/catalina/servlets/DefaultServlet.java @@ -82,23 +82,26 @@ /** - *

The default resource-serving servlet for most web applications, - * used to serve static resources such as HTML pages and images. + *

+ * The default resource-serving servlet for most web applications, used to serve static resources such as HTML pages and + * images. *

*

* This servlet is intended to be mapped to / e.g.: *

+ * *
  *   <servlet-mapping>
  *       <servlet-name>default</servlet-name>
  *       <url-pattern>/</url-pattern>
  *   </servlet-mapping>
  * 
- *

It can be mapped to sub-paths, however in all cases resources are served - * from the web application resource root using the full path from the root - * of the web application context. - *
e.g. given a web application structure: + *

+ * It can be mapped to sub-paths, however in all cases resources are served from the web application resource root using + * the full path from the root of the web application context.
+ * e.g. given a web application structure: *

+ * *
  * /context
  *   /images
@@ -110,6 +113,7 @@
  * 

* ... and a servlet mapping that maps only /static/* to the default servlet: *

+ * *
  *   <servlet-mapping>
  *       <servlet-name>default</servlet-name>
@@ -117,9 +121,10 @@
  *   </servlet-mapping>
  * 
*

- * Then a request to /context/static/images/tomcat.jpg will succeed - * while a request to /context/images/tomcat2.jpg will fail. + * Then a request to /context/static/images/tomcat.jpg will succeed while a request to + * /context/images/tomcat2.jpg will fail. *

+ * * @author Craig R. McClanahan * @author Remy Maucherat */ @@ -208,15 +213,14 @@ public class DefaultServlet extends HttpServlet { protected transient WebResourceRoot resources = null; /** - * File encoding to be used when reading static files. If none is specified - * the platform default is used. + * File encoding to be used when reading static files. If none is specified the platform default is used. */ protected String fileEncoding = null; private transient Charset fileEncodingCharset = null; /** - * If a file has a BOM, should that be used in preference to fileEncoding? - * Will default to {@link BomConfig#TRUE} in {@link #init()}. + * If a file has a BOM, should that be used in preference to fileEncoding? Will default to {@link BomConfig#TRUE} in + * {@link #init()}. */ private BomConfig useBomIfPresent = null; @@ -286,8 +290,7 @@ public void init() throws ServletException { readOnly = Boolean.parseBoolean(getServletConfig().getInitParameter("readonly")); } - compressionFormats = parseCompressionFormats( - getServletConfig().getInitParameter("precompressed"), + compressionFormats = parseCompressionFormats(getServletConfig().getInitParameter("precompressed"), getServletConfig().getInitParameter("gzip")); if (getServletConfig().getInitParameter("sendfileSize") != null) { @@ -319,8 +322,8 @@ public void init() throws ServletException { } if (this.useBomIfPresent == null) { // Unrecognised configuration value - IllegalArgumentException iae = new IllegalArgumentException( - sm.getString("defaultServlet.unknownBomConfig", useBomIfPresent)); + IllegalArgumentException iae = + new IllegalArgumentException(sm.getString("defaultServlet.unknownBomConfig", useBomIfPresent)); throw new ServletException(iae); } } @@ -343,8 +346,7 @@ public void init() throws ServletException { } if (debug > 0) { - log("DefaultServlet.init: input buffer size=" + input + - ", output buffer size=" + output); + log("DefaultServlet.init: input buffer size=" + input + ", output buffer size=" + output); } // Load the web resources @@ -361,10 +363,11 @@ public void init() throws ServletException { if (getServletConfig().getInitParameter("sortListings") != null) { sortListings = Boolean.parseBoolean(getServletConfig().getInitParameter("sortListings")); - if(sortListings) { + if (sortListings) { boolean sortDirectoriesFirst; if (getServletConfig().getInitParameter("sortDirectoriesFirst") != null) { - sortDirectoriesFirst = Boolean.parseBoolean(getServletConfig().getInitParameter("sortDirectoriesFirst")); + sortDirectoriesFirst = + Boolean.parseBoolean(getServletConfig().getInitParameter("sortDirectoriesFirst")); } else { sortDirectoriesFirst = false; } @@ -407,6 +410,7 @@ private CompressionFormat[] parseCompressionFormats(String precompressed, String * Return the relative path associated with this servlet. * * @param request The servlet request we are processing + * * @return the relative path */ protected String getRelativePath(HttpServletRequest request) { @@ -448,10 +452,11 @@ protected String getRelativePath(HttpServletRequest request, boolean allowEmptyP /** - * Determines the appropriate path to prepend resources with - * when generating directory listings. Depending on the behaviour of - * {@link #getRelativePath(HttpServletRequest)} this will change. + * Determines the appropriate path to prepend resources with when generating directory listings. Depending on the + * behaviour of {@link #getRelativePath(HttpServletRequest)} this will change. + * * @param request the request to determine the path for + * * @return the prefix to apply to all resources in the listing. */ protected String getPathPrefix(final HttpServletRequest request) { @@ -460,8 +465,7 @@ protected String getPathPrefix(final HttpServletRequest request) { @Override - protected void service(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (req.getDispatcherType() == DispatcherType.ERROR) { doGet(req, resp); @@ -474,16 +478,15 @@ protected void service(HttpServletRequest req, HttpServletResponse resp) /** * Process a GET request for the specified resource. * - * @param request The servlet request we are processing + * @param request The servlet request we are processing * @param response The servlet response we are creating * - * @exception IOException if an input/output error occurs + * @exception IOException if an input/output error occurs * @exception ServletException if a servlet-specified error occurs */ @Override - protected void doGet(HttpServletRequest request, - HttpServletResponse response) - throws IOException, ServletException { + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { // Serve the requested resource, including the data content serveResource(request, response, true, fileEncoding); @@ -494,10 +497,10 @@ protected void doGet(HttpServletRequest request, /** * Process a HEAD request for the specified resource. * - * @param request The servlet request we are processing + * @param request The servlet request we are processing * @param response The servlet response we are creating * - * @exception IOException if an input/output error occurs + * @exception IOException if an input/output error occurs * @exception ServletException if a servlet-specified error occurs */ @Override @@ -512,27 +515,16 @@ protected void doHead(HttpServletRequest request, HttpServletResponse response) /** - * Override default implementation to ensure that TRACE is correctly - * handled. + * Override default implementation to ensure that TRACE is correctly handled. * - * @param req the {@link HttpServletRequest} object that - * contains the request the client made of - * the servlet + * @param req the {@link HttpServletRequest} object that contains the request the client made of the servlet + * @param resp the {@link HttpServletResponse} object that contains the response the servlet returns to the client * - * @param resp the {@link HttpServletResponse} object that - * contains the response the servlet returns - * to the client - * - * @exception IOException if an input or output error occurs - * while the servlet is handling the - * OPTIONS request - * - * @exception ServletException if the request for the - * OPTIONS cannot be handled + * @exception IOException if an input or output error occurs while the servlet is handling the OPTIONS request + * @exception ServletException if the request for the OPTIONS cannot be handled */ @Override - protected void doOptions(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setHeader("Allow", determineMethodsAllowed(req)); } @@ -550,8 +542,7 @@ protected String determineMethodsAllowed(HttpServletRequest req) { } // Trace - assume disabled unless we can prove otherwise - if (req instanceof RequestFacade && - ((RequestFacade) req).getAllowTrace()) { + if (req instanceof RequestFacade && ((RequestFacade) req).getAllowTrace()) { allow.append(", TRACE"); } @@ -559,8 +550,7 @@ protected String determineMethodsAllowed(HttpServletRequest req) { } - protected void sendNotAllowed(HttpServletRequest req, HttpServletResponse resp) - throws IOException { + protected void sendNotAllowed(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.addHeader("Allow", determineMethodsAllowed(req)); resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); } @@ -569,16 +559,15 @@ protected void sendNotAllowed(HttpServletRequest req, HttpServletResponse resp) /** * Process a POST request for the specified resource. * - * @param request The servlet request we are processing + * @param request The servlet request we are processing * @param response The servlet response we are creating * - * @exception IOException if an input/output error occurs + * @exception IOException if an input/output error occurs * @exception ServletException if a servlet-specified error occurs */ @Override - protected void doPost(HttpServletRequest request, - HttpServletResponse response) - throws IOException, ServletException { + protected void doPost(HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { doGet(request, response); } @@ -586,15 +575,14 @@ protected void doPost(HttpServletRequest request, /** * Process a PUT request for the specified resource. * - * @param req The servlet request we are processing + * @param req The servlet request we are processing * @param resp The servlet response we are creating * - * @exception IOException if an input/output error occurs + * @exception IOException if an input/output error occurs * @exception ServletException if a servlet-specified error occurs */ @Override - protected void doPut(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (readOnly) { sendNotAllowed(req, resp); @@ -648,24 +636,23 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) /** - * Handle a partial PUT. New content specified in request is appended to - * existing content in oldRevisionContent (if present). This code does - * not support simultaneous partial updates to the same resource. - * @param req The Servlet request + * Handle a partial PUT. New content specified in request is appended to existing content in oldRevisionContent (if + * present). This code does not support simultaneous partial updates to the same resource. + * + * @param req The Servlet request * @param range The range that will be written - * @param path The path + * @param path The path + * * @return the associated file object + * * @throws IOException an IO error occurred */ - protected File executePartialPut(HttpServletRequest req, ContentRange range, - String path) - throws IOException { + protected File executePartialPut(HttpServletRequest req, ContentRange range, String path) throws IOException { // Append data specified in ranges to existing content for this // resource - create a temp. file on the local filesystem to // perform this operation - File tempDir = (File) getServletContext().getAttribute - (ServletContext.TEMPDIR); + File tempDir = (File) getServletContext().getAttribute(ServletContext.TEMPDIR); // Convert all '/' characters to '.' in resourcePath String convertedResourcePath = path.replace('/', '.'); File contentFile = new File(tempDir, convertedResourcePath); @@ -674,16 +661,14 @@ protected File executePartialPut(HttpServletRequest req, ContentRange range, contentFile.deleteOnExit(); } - try (RandomAccessFile randAccessContentFile = - new RandomAccessFile(contentFile, "rw")) { + try (RandomAccessFile randAccessContentFile = new RandomAccessFile(contentFile, "rw")) { WebResource oldResource = resources.getResource(path); // Copy data in oldRevisionContent to contentFile if (oldResource.isFile()) { try (BufferedInputStream bufOldRevStream = - new BufferedInputStream(oldResource.getInputStream(), - BUFFER_SIZE)) { + new BufferedInputStream(oldResource.getInputStream(), BUFFER_SIZE)) { int numBytesRead; byte[] copyBuffer = new byte[BUFFER_SIZE]; @@ -700,8 +685,7 @@ protected File executePartialPut(HttpServletRequest req, ContentRange range, randAccessContentFile.seek(range.getStart()); int numBytesRead; byte[] transferBuffer = new byte[BUFFER_SIZE]; - try (BufferedInputStream requestBufInStream = - new BufferedInputStream(req.getInputStream(), BUFFER_SIZE)) { + try (BufferedInputStream requestBufInStream = new BufferedInputStream(req.getInputStream(), BUFFER_SIZE)) { while ((numBytesRead = requestBufInStream.read(transferBuffer)) != -1) { randAccessContentFile.write(transferBuffer, 0, numBytesRead); } @@ -715,15 +699,14 @@ protected File executePartialPut(HttpServletRequest req, ContentRange range, /** * Process a DELETE request for the specified resource. * - * @param req The servlet request we are processing + * @param req The servlet request we are processing * @param resp The servlet response we are creating * - * @exception IOException if an input/output error occurs + * @exception IOException if an input/output error occurs * @exception ServletException if a servlet-specified error occurs */ @Override - protected void doDelete(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (readOnly) { sendNotAllowed(req, resp); @@ -748,26 +731,22 @@ protected void doDelete(HttpServletRequest req, HttpServletResponse resp) /** - * Check if the conditions specified in the optional If headers are - * satisfied. - * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * @param resource The resource - * @return true if the resource meets all the specified - * conditions, and false if any of the conditions is not - * satisfied, in which case request processing is stopped + * Check if the conditions specified in the optional If headers are satisfied. + * + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * @param resource The resource + * + * @return true if the resource meets all the specified conditions, and false if any of + * the conditions is not satisfied, in which case request processing is stopped + * * @throws IOException an IO error occurred */ - protected boolean checkIfHeaders(HttpServletRequest request, - HttpServletResponse response, - WebResource resource) - throws IOException { + protected boolean checkIfHeaders(HttpServletRequest request, HttpServletResponse response, WebResource resource) + throws IOException { - return checkIfMatch(request, response, resource) - && checkIfModifiedSince(request, response, resource) - && checkIfNoneMatch(request, response, resource) - && checkIfUnmodifiedSince(request, response, resource); + return checkIfMatch(request, response, resource) && checkIfModifiedSince(request, response, resource) && + checkIfNoneMatch(request, response, resource) && checkIfUnmodifiedSince(request, response, resource); } @@ -776,6 +755,7 @@ && checkIfNoneMatch(request, response, resource) * URL rewriter. * * @param path Path which has to be rewritten + * * @return the rewritten path */ protected String rewriteUrl(String path) { @@ -789,17 +769,14 @@ protected String rewriteUrl(String path) { * @param request The servlet request we are processing * @param response The servlet response we are creating * @param content Should the content be included? - * @param inputEncoding The encoding to use if it is necessary to access the - * source as characters rather than as bytes + * @param inputEncoding The encoding to use if it is necessary to access the source as characters rather than as + * bytes * - * @exception IOException if an input/output error occurs + * @exception IOException if an input/output error occurs * @exception ServletException if a servlet-specified error occurs */ - protected void serveResource(HttpServletRequest request, - HttpServletResponse response, - boolean content, - String inputEncoding) - throws IOException, ServletException { + protected void serveResource(HttpServletRequest request, HttpServletResponse response, boolean content, + String inputEncoding) throws IOException, ServletException { boolean serveContent = content; @@ -808,11 +785,9 @@ protected void serveResource(HttpServletRequest request, if (debug > 0) { if (serveContent) { - log("DefaultServlet.serveResource: Serving resource '" + - path + "' headers and data"); + log("DefaultServlet.serveResource: Serving resource '" + path + "' headers and data"); } else { - log("DefaultServlet.serveResource: Serving resource '" + - path + "' headers only"); + log("DefaultServlet.serveResource: Serving resource '" + path + "' headers only"); } } @@ -828,20 +803,17 @@ protected void serveResource(HttpServletRequest request, if (!resource.exists()) { // Check if we're included so we can return the appropriate // missing resource name in the error - String requestUri = (String) request.getAttribute( - RequestDispatcher.INCLUDE_REQUEST_URI); + String requestUri = (String) request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI); if (requestUri == null) { requestUri = request.getRequestURI(); } else { // We're included // SRV.9.3 says we must throw a FNFE - throw new FileNotFoundException(sm.getString( - "defaultServlet.missingResource", requestUri)); + throw new FileNotFoundException(sm.getString("defaultServlet.missingResource", requestUri)); } if (isError) { - response.sendError(((Integer) request.getAttribute( - RequestDispatcher.ERROR_STATUS_CODE)).intValue()); + response.sendError(((Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)).intValue()); } else { response.sendError(HttpServletResponse.SC_NOT_FOUND, sm.getString("defaultServlet.missingResource", requestUri)); @@ -852,21 +824,18 @@ protected void serveResource(HttpServletRequest request, if (!resource.canRead()) { // Check if we're included so we can return the appropriate // missing resource name in the error - String requestUri = (String) request.getAttribute( - RequestDispatcher.INCLUDE_REQUEST_URI); + String requestUri = (String) request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI); if (requestUri == null) { requestUri = request.getRequestURI(); } else { // We're included // Spec doesn't say what to do in this case but a FNFE seems // reasonable - throw new FileNotFoundException(sm.getString( - "defaultServlet.missingResource", requestUri)); + throw new FileNotFoundException(sm.getString("defaultServlet.missingResource", requestUri)); } if (isError) { - response.sendError(((Integer) request.getAttribute( - RequestDispatcher.ERROR_STATUS_CODE)).intValue()); + response.sendError(((Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)).intValue()); } else { response.sendError(HttpServletResponse.SC_FORBIDDEN, requestUri); } @@ -878,8 +847,7 @@ protected void serveResource(HttpServletRequest request, // satisfied. if (resource.isFile()) { // Checking If headers - included = (request.getAttribute( - RequestDispatcher.INCLUDE_CONTEXT_PATH) != null); + included = (request.getAttribute(RequestDispatcher.INCLUDE_CONTEXT_PATH) != null); if (!included && !isError && !checkIfHeaders(request, response, resource)) { return; } @@ -905,14 +873,11 @@ protected void serveResource(HttpServletRequest request, // Serve a precompressed version of the file if present boolean usingPrecompressedVersion = false; - if (compressionFormats.length > 0 && !included && resource.isFile() && - !pathEndsWithCompressedExtension(path)) { - List precompressedResources = - getAvailablePrecompressedResources(path); + if (compressionFormats.length > 0 && !included && resource.isFile() && !pathEndsWithCompressedExtension(path)) { + List precompressedResources = getAvailablePrecompressedResources(path); if (!precompressedResources.isEmpty()) { ResponseUtil.addVaryFieldName(response, "accept-encoding"); - PrecompressedResource bestResource = - getBestPrecompressedResource(request, precompressedResources); + PrecompressedResource bestResource = getBestPrecompressedResource(request, precompressedResources); if (bestResource != null) { response.addHeader("Content-Encoding", bestResource.format.encoding); resource = bestResource.resource; @@ -1006,17 +971,13 @@ protected void serveResource(HttpServletRequest request, Charset charset = B2CConverter.getCharset(outputEncoding); boolean conversionRequired; /* - * The test below deliberately uses != to compare two Strings. This is - * because the code is looking to see if the default character encoding - * has been returned because no explicit character encoding has been - * defined. There is no clean way of doing this via the Servlet API. It - * would be possible to add a Tomcat specific API but that would require - * quite a bit of code to get to the Tomcat specific request object that - * may have been wrapped. The != test is a (slightly hacky) quick way of - * doing this. + * The test below deliberately uses != to compare two Strings. This is because the code is looking to see if the + * default character encoding has been returned because no explicit character encoding has been defined. There + * is no clean way of doing this via the Servlet API. It would be possible to add a Tomcat specific API but that + * would require quite a bit of code to get to the Tomcat specific request object that may have been wrapped. + * The != test is a (slightly hacky) quick way of doing this. */ - boolean outputEncodingSpecified = - outputEncoding != org.apache.coyote.Constants.DEFAULT_BODY_CHARSET.name() && + boolean outputEncodingSpecified = outputEncoding != org.apache.coyote.Constants.DEFAULT_BODY_CHARSET.name() && outputEncoding != resources.getContext().getResponseCharacterEncoding(); if (!usingPrecompressedVersion && isText(contentType) && outputEncodingSpecified && !charset.equals(fileEncodingCharset)) { @@ -1028,23 +989,20 @@ protected void serveResource(HttpServletRequest request, conversionRequired = false; } - if (resource.isDirectory() || isError || ranges == FULL ) { + if (resource.isDirectory() || isError || ranges == FULL) { // Set the appropriate output headers if (contentType != null) { if (debug > 0) { - log("DefaultServlet.serveFile: contentType='" + - contentType + "'"); + log("DefaultServlet.serveFile: contentType='" + contentType + "'"); } // Don't override a previously set content type if (response.getContentType() == null) { response.setContentType(contentType); } } - if (resource.isFile() && contentLength >= 0 && - (!serveContent || ostream != null)) { + if (resource.isFile() && contentLength >= 0 && (!serveContent || ostream != null)) { if (debug > 0) { - log("DefaultServlet.serveFile: contentLength=" + - contentLength); + log("DefaultServlet.serveFile: contentLength=" + contentLength); } // Don't set a content length if something else has already // written to the response or if conversion will be taking place @@ -1156,15 +1114,13 @@ protected void serveResource(HttpServletRequest request, Ranges.Entry range = ranges.getEntries().get(0); long start = getStart(range, contentLength); long end = getEnd(range, contentLength); - response.addHeader("Content-Range", - "bytes " + start + "-" + end + "/" + contentLength); + response.addHeader("Content-Range", "bytes " + start + "-" + end + "/" + contentLength); long length = end - start + 1; response.setContentLengthLong(length); if (contentType != null) { if (debug > 0) { - log("DefaultServlet.serveFile: contentType='" + - contentType + "'"); + log("DefaultServlet.serveFile: contentType='" + contentType + "'"); } response.setContentType(contentType); } @@ -1176,8 +1132,7 @@ protected void serveResource(HttpServletRequest request, // Silent catch } if (ostream != null) { - if (!checkSendfile(request, response, resource, - contentLength, range)) { + if (!checkSendfile(request, response, resource, contentLength, range)) { copy(resource, contentLength, ostream, range); } } else { @@ -1186,8 +1141,7 @@ protected void serveResource(HttpServletRequest request, } } } else { - response.setContentType("multipart/byteranges; boundary=" - + mimeSeparation); + response.setContentType("multipart/byteranges; boundary=" + mimeSeparation); if (serveContent) { try { response.setBufferSize(output); @@ -1290,14 +1244,14 @@ private static void skip(InputStream is, int skip, boolean stripBom) throws IOEx private static boolean isText(String contentType) { - return contentType == null || contentType.startsWith("text") || - contentType.endsWith("xml") || contentType.contains("/javascript"); + return contentType == null || contentType.startsWith("text") || contentType.endsWith("xml") || + contentType.contains("/javascript"); } private static boolean validate(ContentRange range) { // bytes is the only range unit supported - return (range != null) && ("bytes".equals(range.getUnits())) && (range.getStart() >= 0) - && (range.getEnd() >= 0) && (range.getStart() <= range.getEnd()) && (range.getLength() > 0); + return (range != null) && ("bytes".equals(range.getUnits())) && (range.getStart() >= 0) && + (range.getEnd() >= 0) && (range.getStart() <= range.getEnd()) && (range.getLength() > 0); } private static boolean validate(Ranges.Entry range, long length) { @@ -1308,7 +1262,7 @@ private static boolean validate(Ranges.Entry range, long length) { private static long getStart(Ranges.Entry range, long length) { long start = range.getStart(); - if (start == -1 ) { + if (start == -1) { long end = range.getEnd(); // If there is no start, then the start is based on the end if (end >= length) { @@ -1353,8 +1307,9 @@ private List getAvailablePrecompressedResources(String pa /** * Match the client preferred encoding formats to the available precompressed resources. * - * @param request The servlet request we are processing - * @param precompressedResources List of available precompressed resources. + * @param request The servlet request we are processing + * @param precompressedResources List of available precompressed resources. + * * @return The best matching precompressed resource or null if no match was found. */ private PrecompressedResource getBestPrecompressedResource(HttpServletRequest request, @@ -1410,8 +1365,7 @@ private PrecompressedResource getBestPrecompressedResource(HttpServletRequest re return bestResource; } - private void doDirectoryRedirect(HttpServletRequest request, HttpServletResponse response) - throws IOException { + private void doDirectoryRedirect(HttpServletRequest request, HttpServletResponse response) throws IOException { StringBuilder location = new StringBuilder(request.getRequestURI()); location.append('/'); if (request.getQueryString() != null) { @@ -1428,16 +1382,16 @@ private void doDirectoryRedirect(HttpServletRequest request, HttpServletResponse /** * Parse the content-range header. * - * @param request The servlet request we are processing + * @param request The servlet request we are processing * @param response The servlet response we are creating - * @return the partial content-range, {@code null} if the content-range - * header was invalid or {@code #IGNORE} if there is no header to - * process + * + * @return the partial content-range, {@code null} if the content-range header was invalid or {@code #IGNORE} if + * there is no header to process + * * @throws IOException an IO error occurred */ - protected ContentRange parseContentRange(HttpServletRequest request, - HttpServletResponse response) - throws IOException { + protected ContentRange parseContentRange(HttpServletRequest request, HttpServletResponse response) + throws IOException { // Retrieving the content-range header (if any is specified String contentRangeHeader = request.getHeader("Content-Range"); @@ -1470,16 +1424,17 @@ protected ContentRange parseContentRange(HttpServletRequest request, /** * Parse the range header. * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * @param resource The resource - * @return a list of ranges, {@code null} if the range header was invalid or - * {@code #FULL} if the Range header should be ignored. + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * @param resource The resource + * + * @return a list of ranges, {@code null} if the range header was invalid or {@code #FULL} if the Range header + * should be ignored. + * * @throws IOException an IO error occurred */ - protected Ranges parseRange(HttpServletRequest request, - HttpServletResponse response, - WebResource resource) throws IOException { + protected Ranges parseRange(HttpServletRequest request, HttpServletResponse response, WebResource resource) + throws IOException { // Range headers are only valid on GET requests. That implies they are // also valid on HEAD requests. This method is only called by doGet() @@ -1574,11 +1529,11 @@ protected Ranges parseRange(HttpServletRequest request, * * @return the input stream with the rendered output * - * @throws IOException an IO error occurred + * @throws IOException an IO error occurred * @throws ServletException rendering error */ protected InputStream render(HttpServletRequest request, String contextPath, WebResource resource, String encoding) - throws IOException, ServletException { + throws IOException, ServletException { Source xsltSource = findXsltSource(resource); @@ -1590,8 +1545,7 @@ protected InputStream render(HttpServletRequest request, String contextPath, Web /** - * Return an InputStream to an XML representation of the contents this - * directory. + * Return an InputStream to an XML representation of the contents this directory. * * @param request The HttpServletRequest being served * @param contextPath Context path to which our internal paths are relative @@ -1601,12 +1555,11 @@ protected InputStream render(HttpServletRequest request, String contextPath, Web * * @return the XML data * - * @throws IOException an IO error occurred + * @throws IOException an IO error occurred * @throws ServletException rendering error */ - protected InputStream renderXml(HttpServletRequest request, String contextPath, WebResource resource, Source xsltSource, - String encoding) - throws IOException, ServletException { + protected InputStream renderXml(HttpServletRequest request, String contextPath, WebResource resource, + Source xsltSource, String encoding) throws IOException, ServletException { StringBuilder sb = new StringBuilder(); @@ -1626,13 +1579,12 @@ protected InputStream renderXml(HttpServletRequest request, String contextPath, String[] entries = resources.list(resource.getWebappPath()); // rewriteUrl(contextPath) is expensive. cache result for later reuse - String rewrittenContextPath = rewriteUrl(contextPath); + String rewrittenContextPath = rewriteUrl(contextPath); String directoryWebappPath = resource.getWebappPath(); for (String entry : entries) { - if (entry.equalsIgnoreCase("WEB-INF") || - entry.equalsIgnoreCase("META-INF") || + if (entry.equalsIgnoreCase("WEB-INF") || entry.equalsIgnoreCase("META-INF") || entry.equalsIgnoreCase(localXsltFile)) { continue; } @@ -1641,32 +1593,21 @@ protected InputStream renderXml(HttpServletRequest request, String contextPath, continue; } - WebResource childResource = - resources.getResource(directoryWebappPath + entry); + WebResource childResource = resources.getResource(directoryWebappPath + entry); if (!childResource.exists()) { continue; } sb.append("'); sb.append(Escape.htmlElementContent(entry)); @@ -1679,7 +1620,7 @@ protected InputStream renderXml(HttpServletRequest request, String contextPath, String readme = getReadme(resource, encoding); - if (readme!=null) { + if (readme != null) { sb.append(""); @@ -1712,8 +1653,7 @@ protected InputStream renderXml(HttpServletRequest request, String contextPath, } /** - * Return an InputStream to an HTML representation of the contents of this - * directory. + * Return an InputStream to an HTML representation of the contents of this directory. * * @param request The HttpServletRequest being served * @param contextPath Context path to which our internal paths are relative @@ -1724,8 +1664,8 @@ protected InputStream renderXml(HttpServletRequest request, String contextPath, * * @throws IOException an IO error occurred */ - protected InputStream renderHtml(HttpServletRequest request, String contextPath, WebResource resource, String encoding) - throws IOException { + protected InputStream renderHtml(HttpServletRequest request, String contextPath, WebResource resource, + String encoding) throws IOException { // Prepare a writer to a buffered area ByteArrayOutputStream stream = new ByteArrayOutputStream(); @@ -1738,14 +1678,14 @@ protected InputStream renderHtml(HttpServletRequest request, String contextPath, WebResource[] entries = resources.listResources(directoryWebappPath); // rewriteUrl(contextPath) is expensive. cache result for later reuse - String rewrittenContextPath = rewriteUrl(contextPath); + String rewrittenContextPath = rewriteUrl(contextPath); // Render the page header sb.append("\r\n"); - /* TODO Activate this as soon as we use smClient with the request locales - sb.append("\r\n"); - */ + /* + * TODO Activate this as soon as we use smClient with the request locales + * sb.append("\r\n"); + */ sb.append("\r\n"); sb.append(""); sb.append(sm.getString("directory.title", directoryWebappPath)); @@ -1761,8 +1701,7 @@ protected InputStream renderHtml(HttpServletRequest request, String contextPath, // Render the link to our parent (if required) String parentDirectory = directoryWebappPath; if (parentDirectory.endsWith("/")) { - parentDirectory = - parentDirectory.substring(0, parentDirectory.length() - 1); + parentDirectory = parentDirectory.substring(0, parentDirectory.length() - 1); } int slash = parentDirectory.lastIndexOf('/'); if (slash >= 0) { @@ -1786,11 +1725,10 @@ protected InputStream renderHtml(HttpServletRequest request, String contextPath, sb.append("</h1>"); sb.append("<hr class=\"line\">"); - sb.append("<table width=\"100%\" cellspacing=\"0\"" + - " cellpadding=\"5\" align=\"center\">\r\n"); + sb.append("<table width=\"100%\" cellspacing=\"0\"" + " cellpadding=\"5\" align=\"center\">\r\n"); SortManager.Order order; - if(sortListings && null != request) { + if (sortListings && null != request) { order = sortManager.getOrder(request.getQueryString()); } else { order = null; @@ -1798,7 +1736,7 @@ protected InputStream renderHtml(HttpServletRequest request, String contextPath, // Render the column headings sb.append("<tr>\r\n"); sb.append("<td align=\"left\"><font size=\"+1\"><strong>"); - if(sortListings && null != request) { + if (sortListings && null != request) { sb.append("<a href=\"?C=N;O="); sb.append(getOrderChar(order, 'N')); sb.append("\">"); @@ -1809,7 +1747,7 @@ protected InputStream renderHtml(HttpServletRequest request, String contextPath, } sb.append("</strong></font></td>\r\n"); sb.append("<td align=\"center\"><font size=\"+1\"><strong>"); - if(sortListings && null != request) { + if (sortListings && null != request) { sb.append("<a href=\"?C=S;O="); sb.append(getOrderChar(order, 'S')); sb.append("\">"); @@ -1820,7 +1758,7 @@ protected InputStream renderHtml(HttpServletRequest request, String contextPath, } sb.append("</strong></font></td>\r\n"); sb.append("<td align=\"right\"><font size=\"+1\"><strong>"); - if(sortListings && null != request) { + if (sortListings && null != request) { sb.append("<a href=\"?C=M;O="); sb.append(getOrderChar(order, 'M')); sb.append("\">"); @@ -1832,15 +1770,14 @@ protected InputStream renderHtml(HttpServletRequest request, String contextPath, sb.append("</strong></font></td>\r\n"); sb.append("</tr>"); - if(null != sortManager && null != request) { + if (null != sortManager && null != request) { sortManager.sort(entries, request.getQueryString()); } boolean shade = false; for (WebResource childResource : entries) { String filename = childResource.getName(); - if (filename.equalsIgnoreCase("WEB-INF") || - filename.equalsIgnoreCase("META-INF")) { + if (filename.equalsIgnoreCase("WEB-INF") || filename.equalsIgnoreCase("META-INF")) { continue; } @@ -1890,7 +1827,7 @@ protected InputStream renderHtml(HttpServletRequest request, String contextPath, sb.append("<hr class=\"line\">"); String readme = getReadme(resource, encoding); - if (readme!=null) { + if (readme != null) { sb.append(readme); sb.append("<hr class=\"line\">"); } @@ -1913,12 +1850,13 @@ protected InputStream renderHtml(HttpServletRequest request, String contextPath, * Render the specified file size (in bytes). * * @param size File size (in bytes) + * * @return the formatted size */ protected String renderSize(long size) { long leftSide = size / 1024; - long rightSide = (size % 1024) / 103; // Makes 1 digit + long rightSide = (size % 1024) / 103; // Makes 1 digit if ((leftSide == 0) && (rightSide == 0) && (size > 0)) { rightSide = 1; } @@ -1930,19 +1868,20 @@ protected String renderSize(long size) { /** * Get the readme file as a string. + * * @param directory The directory to search - * @param encoding The readme encoding + * @param encoding The readme encoding + * * @return the readme for the specified directory */ protected String getReadme(WebResource directory, String encoding) { if (readmeFile != null) { - WebResource resource = resources.getResource( - directory.getWebappPath() + readmeFile); + WebResource resource = resources.getResource(directory.getWebappPath() + readmeFile); if (resource.isFile()) { StringWriter buffer = new StringWriter(); InputStreamReader reader = null; - try (InputStream is = resource.getInputStream()){ + try (InputStream is = resource.getInputStream()) { if (encoding != null) { reader = new InputStreamReader(is, encoding); } else { @@ -1975,16 +1914,17 @@ protected String getReadme(WebResource directory, String encoding) { /** * Return a Source for the xsl template (if possible). + * * @param directory The directory to search + * * @return the source for the specified directory + * * @throws IOException an IO error occurred */ - protected Source findXsltSource(WebResource directory) - throws IOException { + protected Source findXsltSource(WebResource directory) throws IOException { if (localXsltFile != null) { - WebResource resource = resources.getResource( - directory.getWebappPath() + localXsltFile); + WebResource resource = resources.getResource(directory.getWebappPath() + localXsltFile); if (resource.isFile()) { InputStream is = resource.getInputStream(); if (is != null) { @@ -1997,8 +1937,7 @@ protected Source findXsltSource(WebResource directory) } if (contextXsltFile != null) { - InputStream is = - getServletContext().getResourceAsStream(contextXsltFile); + InputStream is = getServletContext().getResourceAsStream(contextXsltFile); if (is != null) { return new StreamSource(is); } @@ -2008,8 +1947,8 @@ protected Source findXsltSource(WebResource directory) } } - /* Open and read in file in one fell swoop to reduce chance - * chance of leaving handle open. + /* + * Open and read in file in one fell swoop to reduce chance chance of leaving handle open. */ if (globalXsltFile != null) { File f = validateGlobalXsltFile(); @@ -2018,8 +1957,8 @@ protected Source findXsltSource(WebResource directory) if (globalXsltFileSize > Integer.MAX_VALUE) { log("globalXsltFile [" + f.getAbsolutePath() + "] is too big to buffer"); } else { - try (FileInputStream fis = new FileInputStream(f)){ - byte b[] = new byte[(int)f.length()]; + try (FileInputStream fis = new FileInputStream(f)) { + byte b[] = new byte[(int) f.length()]; IOTools.readFully(fis, b); return new StreamSource(new ByteArrayInputStream(b)); } @@ -2080,28 +2019,23 @@ private File validateGlobalXsltFile(File base) { /** * Check if sendfile can be used. - * @param request The Servlet request + * + * @param request The Servlet request * @param response The Servlet response * @param resource The resource - * @param length The length which will be written (will be used only if - * range is null) - * @param range The range that will be written - * @return <code>true</code> if sendfile should be used (writing is then - * delegated to the endpoint) + * @param length The length which will be written (will be used only if range is null) + * @param range The range that will be written + * + * @return <code>true</code> if sendfile should be used (writing is then delegated to the endpoint) */ - protected boolean checkSendfile(HttpServletRequest request, - HttpServletResponse response, - WebResource resource, - long length, Ranges.Entry range) { + protected boolean checkSendfile(HttpServletRequest request, HttpServletResponse response, WebResource resource, + long length, Ranges.Entry range) { String canonicalPath; - if (sendfileSize > 0 - && length > sendfileSize - && (Boolean.TRUE.equals(request.getAttribute(Globals.SENDFILE_SUPPORTED_ATTR))) - && (request.getClass().getName().equals("org.apache.catalina.connector.RequestFacade")) - && (response.getClass().getName().equals("org.apache.catalina.connector.ResponseFacade")) - && resource.isFile() - && ((canonicalPath = resource.getCanonicalPath()) != null) - ) { + if (sendfileSize > 0 && length > sendfileSize && + (Boolean.TRUE.equals(request.getAttribute(Globals.SENDFILE_SUPPORTED_ATTR))) && + (request.getClass().getName().equals("org.apache.catalina.connector.RequestFacade")) && + (response.getClass().getName().equals("org.apache.catalina.connector.ResponseFacade")) && + resource.isFile() && ((canonicalPath = resource.getCanonicalPath()) != null)) { request.setAttribute(Globals.SENDFILE_FILENAME_ATTR, canonicalPath); if (range == null) { request.setAttribute(Globals.SENDFILE_FILE_START_ATTR, Long.valueOf(0L)); @@ -2119,12 +2053,13 @@ protected boolean checkSendfile(HttpServletRequest request, /** * Check if the if-match condition is satisfied. * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * @param resource The resource - * @return <code>true</code> if the resource meets the specified condition, - * and <code>false</code> if the condition is not satisfied, in which case - * request processing is stopped + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * @param resource The resource + * + * @return <code>true</code> if the resource meets the specified condition, and <code>false</code> if the condition + * is not satisfied, in which case request processing is stopped + * * @throws IOException an IO error occurred */ protected boolean checkIfMatch(HttpServletRequest request, HttpServletResponse response, WebResource resource) @@ -2167,15 +2102,15 @@ protected boolean checkIfMatch(HttpServletRequest request, HttpServletResponse r /** * Check if the if-modified-since condition is satisfied. * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * @param resource The resource - * @return <code>true</code> if the resource meets the specified condition, - * and <code>false</code> if the condition is not satisfied, in which case - * request processing is stopped + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * @param resource The resource + * + * @return <code>true</code> if the resource meets the specified condition, and <code>false</code> if the condition + * is not satisfied, in which case request processing is stopped */ - protected boolean checkIfModifiedSince(HttpServletRequest request, - HttpServletResponse response, WebResource resource) { + protected boolean checkIfModifiedSince(HttpServletRequest request, HttpServletResponse response, + WebResource resource) { try { long headerValue = request.getDateHeader("If-Modified-Since"); long lastModified = resource.getLastModified(); @@ -2183,8 +2118,7 @@ protected boolean checkIfModifiedSince(HttpServletRequest request, // If an If-None-Match header has been specified, if modified since // is ignored. - if ((request.getHeader("If-None-Match") == null) - && (lastModified < headerValue + 1000)) { + if ((request.getHeader("If-None-Match") == null) && (lastModified < headerValue + 1000)) { // The entity has not been modified since the date // specified by the client. This is not an error case. response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); @@ -2203,12 +2137,13 @@ protected boolean checkIfModifiedSince(HttpServletRequest request, /** * Check if the if-none-match condition is satisfied. * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * @param resource The resource - * @return <code>true</code> if the resource meets the specified condition, - * and <code>false</code> if the condition is not satisfied, in which case - * request processing is stopped + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * @param resource The resource + * + * @return <code>true</code> if the resource meets the specified condition, and <code>false</code> if the condition + * is not satisfied, in which case request processing is stopped + * * @throws IOException an IO error occurred */ protected boolean checkIfNoneMatch(HttpServletRequest request, HttpServletResponse response, WebResource resource) @@ -2260,29 +2195,29 @@ protected boolean checkIfNoneMatch(HttpServletRequest request, HttpServletRespon /** * Check if the if-unmodified-since condition is satisfied. * - * @param request The servlet request we are processing - * @param response The servlet response we are creating - * @param resource The resource - * @return <code>true</code> if the resource meets the specified condition, - * and <code>false</code> if the condition is not satisfied, in which case - * request processing is stopped + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * @param resource The resource + * + * @return <code>true</code> if the resource meets the specified condition, and <code>false</code> if the condition + * is not satisfied, in which case request processing is stopped + * * @throws IOException an IO error occurred */ - protected boolean checkIfUnmodifiedSince(HttpServletRequest request, - HttpServletResponse response, WebResource resource) - throws IOException { + protected boolean checkIfUnmodifiedSince(HttpServletRequest request, HttpServletResponse response, + WebResource resource) throws IOException { try { long lastModified = resource.getLastModified(); long headerValue = request.getDateHeader("If-Unmodified-Since"); if (headerValue != -1) { - if ( lastModified >= (headerValue + 1000)) { + if (lastModified >= (headerValue + 1000)) { // The entity has not been modified since the date // specified by the client. This is not an error case. response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); return false; } } - } catch(IllegalArgumentException illegalArgument) { + } catch (IllegalArgumentException illegalArgument) { return true; } return true; @@ -2290,14 +2225,12 @@ protected boolean checkIfUnmodifiedSince(HttpServletRequest request, /** - * Provides the entity tag (the ETag header) for the given resource. - * Intended to be over-ridden by custom DefaultServlet implementations that - * wish to use an alternative format for the entity tag. + * Provides the entity tag (the ETag header) for the given resource. Intended to be over-ridden by custom + * DefaultServlet implementations that wish to use an alternative format for the entity tag. * - * @param resource The resource for which an entity tag is required. + * @param resource The resource for which an entity tag is required. * - * @return The result of calling {@link WebResource#getETag()} on the given - * resource + * @return The result of calling {@link WebResource#getETag()} on the given resource */ protected String generateETag(WebResource resource) { return resource.getETag(); @@ -2305,12 +2238,11 @@ protected String generateETag(WebResource resource) { /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). + * Copy the contents of the specified input stream to the specified output stream, and ensure that both streams are + * closed before returning (even in the face of an exception). * - * @param is The input stream to read the source resource from - * @param ostream The output stream to write to + * @param is The input stream to read the source resource from + * @param ostream The output stream to write to * * @exception IOException if an input/output error occurs */ @@ -2333,13 +2265,12 @@ protected void copy(InputStream is, ServletOutputStream ostream) throws IOExcept /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). + * Copy the contents of the specified input stream to the specified output stream, and ensure that both streams are + * closed before returning (even in the face of an exception). * - * @param is The input stream to read the source resource from - * @param writer The writer to write to - * @param encoding The encoding to use when reading the source input stream + * @param is The input stream to read the source resource from + * @param writer The writer to write to + * @param encoding The encoding to use when reading the source input stream * * @exception IOException if an input/output error occurs */ @@ -2367,25 +2298,23 @@ protected void copy(InputStream is, PrintWriter writer, String encoding) throws /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). - * - * @param resource The source resource - * @param length the resource length - * @param ostream The output stream to write to - * @param range Range the client wanted to retrieve + * Copy the contents of the specified input stream to the specified output stream, and ensure that both streams are + * closed before returning (even in the face of an exception). + * + * @param resource The source resource + * @param length the resource length + * @param ostream The output stream to write to + * @param range Range the client wanted to retrieve + * * @exception IOException if an input/output error occurs */ - protected void copy(WebResource resource, long length, ServletOutputStream ostream, - Ranges.Entry range) - throws IOException { + protected void copy(WebResource resource, long length, ServletOutputStream ostream, Ranges.Entry range) + throws IOException { IOException exception = null; InputStream resourceInputStream = resource.getInputStream(); - InputStream istream = - new BufferedInputStream(resourceInputStream, input); + InputStream istream = new BufferedInputStream(resourceInputStream, input); exception = copyRange(istream, ostream, getStart(range, length), getEnd(range, length)); // Clean up the input stream @@ -2400,21 +2329,19 @@ protected void copy(WebResource resource, long length, ServletOutputStream ostre /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). - * - * @param resource The source resource - * @param length the resource length - * @param ostream The output stream to write to - * @param ranges Enumeration of the ranges the client wanted to - * retrieve - * @param contentType Content type of the resource + * Copy the contents of the specified input stream to the specified output stream, and ensure that both streams are + * closed before returning (even in the face of an exception). + * + * @param resource The source resource + * @param length the resource length + * @param ostream The output stream to write to + * @param ranges Enumeration of the ranges the client wanted to retrieve + * @param contentType Content type of the resource + * * @exception IOException if an input/output error occurs */ - protected void copy(WebResource resource, long length, ServletOutputStream ostream, - Ranges ranges, String contentType) - throws IOException { + protected void copy(WebResource resource, long length, ServletOutputStream ostream, Ranges ranges, + String contentType) throws IOException { IOException exception = null; @@ -2433,9 +2360,7 @@ protected void copy(WebResource resource, long length, ServletOutputStream ostre } long start = getStart(range, length); long end = getEnd(range, length); - ostream.println("Content-Range: bytes " + start - + "-" + end + "/" - + (end - start)); + ostream.println("Content-Range: bytes " + start + "-" + end + "/" + (end - start)); ostream.println(); // Printing content @@ -2455,16 +2380,15 @@ protected void copy(WebResource resource, long length, ServletOutputStream ostre /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). + * Copy the contents of the specified input stream to the specified output stream, and ensure that both streams are + * closed before returning (even in the face of an exception). * * @param istream The input stream to read from * @param ostream The output stream to write to + * * @return Exception which occurred during processing */ - protected IOException copyRange(InputStream istream, - ServletOutputStream ostream) { + protected IOException copyRange(InputStream istream, ServletOutputStream ostream) { // Copy the input stream to the output stream IOException exception = null; @@ -2489,12 +2413,12 @@ protected IOException copyRange(InputStream istream, /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). + * Copy the contents of the specified input stream to the specified output stream, and ensure that both streams are + * closed before returning (even in the face of an exception). * * @param reader The reader to read from * @param writer The writer to write to + * * @return Exception which occurred during processing */ protected IOException copyRange(Reader reader, PrintWriter writer) { @@ -2522,19 +2446,17 @@ protected IOException copyRange(Reader reader, PrintWriter writer) { /** - * Copy the contents of the specified input stream to the specified - * output stream, and ensure that both streams are closed before returning - * (even in the face of an exception). + * Copy the contents of the specified input stream to the specified output stream, and ensure that both streams are + * closed before returning (even in the face of an exception). * * @param istream The input stream to read from * @param ostream The output stream to write to - * @param start Start of the range which will be copied - * @param end End of the range which will be copied + * @param start Start of the range which will be copied + * @param end End of the range which will be copied + * * @return Exception which occurred during processing */ - protected IOException copyRange(InputStream istream, - ServletOutputStream ostream, - long start, long end) { + protected IOException copyRange(InputStream istream, ServletOutputStream ostream, long start, long end) { if (debug > 10) { log("Serving bytes:" + start + "-" + end); @@ -2547,8 +2469,7 @@ protected IOException copyRange(InputStream istream, return e; } if (skipped < start) { - return new IOException(sm.getString("defaultServlet.skipfail", - Long.valueOf(skipped), Long.valueOf(start))); + return new IOException(sm.getString("defaultServlet.skipfail", Long.valueOf(skipped), Long.valueOf(start))); } IOException exception = null; @@ -2556,7 +2477,7 @@ protected IOException copyRange(InputStream istream, byte buffer[] = new byte[input]; int len = buffer.length; - while ( (bytesToRead > 0) && (len >= buffer.length)) { + while ((bytesToRead > 0) && (len >= buffer.length)) { try { len = istream.read(buffer); if (bytesToRead >= len) { @@ -2609,12 +2530,11 @@ private PrecompressedResource(WebResource resource, CompressionFormat format) { * @param order The order that is currently being applied * @param column The column that will be rendered. * - * @return Either 'A' or 'D', to indicate "ascending" or "descending" sort - * order. + * @return Either 'A' or 'D', to indicate "ascending" or "descending" sort order. */ private char getOrderChar(SortManager.Order order, char column) { - if(column == order.column) { - if(order.ascending) { + if (column == order.column) { + if (order.ascending) { return 'D'; } else { return 'A'; @@ -2698,7 +2618,7 @@ private static class SortManager { public void sort(WebResource[] resources, String order) { Comparator<WebResource> comparator = getComparator(order); - if(null != comparator) { + if (null != comparator) { Arrays.sort(resources, comparator); } } @@ -2708,28 +2628,28 @@ public Comparator<WebResource> getComparator(String order) { } public Comparator<WebResource> getComparator(Order order) { - if(null == order) { + if (null == order) { return defaultResourceComparator; } - if('N' == order.column) { - if(order.ascending) { + if ('N' == order.column) { + if (order.ascending) { return resourceNameComparatorAsc; } else { return resourceNameComparator; } } - if('S' == order.column) { - if(order.ascending) { + if ('S' == order.column) { + if (order.ascending) { return resourceSizeComparatorAsc; } else { return resourceSizeComparator; } } - if('M' == order.column) { - if(order.ascending) { + if ('M' == order.column) { + if (order.ascending) { return resourceLastModifiedComparatorAsc; } else { return resourceLastModifiedComparator; @@ -2740,61 +2660,59 @@ public Comparator<WebResource> getComparator(Order order) { } /** - * Gets the Order to apply given an ordering-string. This - * ordering-string matches a subset of the ordering-strings - * supported by - * <a href="https://httpd.apache.org/docs/2.4/mod/mod_autoindex.html#query">Apache httpd</a>. + * Gets the Order to apply given an ordering-string. This ordering-string matches a subset of the + * ordering-strings supported by <a href="https://httpd.apache.org/docs/2.4/mod/mod_autoindex.html#query">Apache + * httpd</a>. * * @param order The ordering-string provided by the client. * - * @return An Order specifying the column and ascending/descending to - * be applied to resources. + * @return An Order specifying the column and ascending/descending to be applied to resources. */ public Order getOrder(String order) { - if(null == order || 0 == order.trim().length()) { + if (null == order || 0 == order.trim().length()) { return Order.DEFAULT; } String[] options = order.split(";"); - if(0 == options.length) { + if (0 == options.length) { return Order.DEFAULT; } char column = '\0'; boolean ascending = false; - for(String option : options) { + for (String option : options) { option = option.trim(); - if(2 < option.length()) { + if (2 < option.length()) { char opt = option.charAt(0); - if('C' == opt) { + if ('C' == opt) { column = option.charAt(2); - } else if('O' == opt) { + } else if ('O' == opt) { ascending = ('A' == option.charAt(2)); } } } - if('N' == column) { - if(ascending) { + if ('N' == column) { + if (ascending) { return Order.NAME_ASC; } else { return Order.NAME; } } - if('S' == column) { - if(ascending) { + if ('S' == column) { + if (ascending) { return Order.SIZE_ASC; } else { return Order.SIZE; } } - if('M' == column) { - if(ascending) { + if ('M' == column) { + if (ascending) { return Order.LAST_MODIFIED_ASC; } else { return Order.LAST_MODIFIED; @@ -2846,18 +2764,15 @@ private static Comparator<WebResource> comparingTrueFirst(Function<WebResource,B enum BomConfig { /** - * BoM is stripped if present and any BoM found used to determine the - * encoding used to read the resource. + * BoM is stripped if present and any BoM found used to determine the encoding used to read the resource. */ TRUE("true", true, true), /** - * BoM is stripped if present but the configured file encoding is used - * to read the resource. + * BoM is stripped if present but the configured file encoding is used to read the resource. */ FALSE("false", true, false), /** - * BoM is not stripped and the configured file encoding is used to read - * the resource. + * BoM is not stripped and the configured file encoding is used to read the resource. */ PASS_THROUGH("pass-through", false, false); diff --git a/java/org/apache/catalina/servlets/WebdavServlet.java b/java/org/apache/catalina/servlets/WebdavServlet.java index bd75fc776094..f941a23d6e41 100644 --- a/java/org/apache/catalina/servlets/WebdavServlet.java +++ b/java/org/apache/catalina/servlets/WebdavServlet.java @@ -67,19 +67,17 @@ import org.xml.sax.SAXException; /** - * Servlet which adds support for - * <a href="https://tools.ietf.org/html/rfc4918">WebDAV</a> - * <a href="https://tools.ietf.org/html/rfc4918#section-18">level 2</a>. - * All the basic HTTP requests - * are handled by the DefaultServlet. The WebDAVServlet must not be used as the - * default servlet (ie mapped to '/') as it will not work in this configuration. + * Servlet which adds support for <a href="https://tools.ietf.org/html/rfc4918">WebDAV</a> + * <a href="https://tools.ietf.org/html/rfc4918#section-18">level 2</a>. All the basic HTTP requests are handled by the + * DefaultServlet. The WebDAVServlet must not be used as the default servlet (ie mapped to '/') as it will not work in + * this configuration. * <p> - * Mapping a subpath (e.g. <code>/webdav/*</code> to this servlet has the effect - * of re-mounting the entire web application under that sub-path, with WebDAV - * access to all the resources. The <code>WEB-INF</code> and <code>META-INF</code> - * directories are protected in this re-mounted resource tree. + * Mapping a subpath (e.g. <code>/webdav/*</code> to this servlet has the effect of re-mounting the entire web + * application under that sub-path, with WebDAV access to all the resources. The <code>WEB-INF</code> and + * <code>META-INF</code> directories are protected in this re-mounted resource tree. * <p> * To enable WebDAV for a context add the following to web.xml: + * * <pre> * <servlet> * <servlet-name>webdav</servlet-name> @@ -98,34 +96,37 @@ * <url-pattern>/*</url-pattern> * </servlet-mapping> * </pre> + * * This will enable read only access. To enable read-write access add: + * * <pre> * <init-param> * <param-name>readonly</param-name> * <param-value>false</param-value> * </init-param> * </pre> - * To make the content editable via a different URL, use the following - * mapping: + * + * To make the content editable via a different URL, use the following mapping: + * * <pre> * <servlet-mapping> * <servlet-name>webdav</servlet-name> * <url-pattern>/webdavedit/*</url-pattern> * </servlet-mapping> * </pre> - * By default access to /WEB-INF and META-INF are not available via WebDAV. To - * enable access to these URLs, use add: + * + * By default access to /WEB-INF and META-INF are not available via WebDAV. To enable access to these URLs, use add: + * * <pre> * <init-param> * <param-name>allowSpecialPaths</param-name> * <param-value>true</param-value> * </init-param> * </pre> - * Don't forget to secure access appropriately to the editing URLs, especially - * if allowSpecialPaths is used. With the mapping configuration above, the - * context will be accessible to normal users as before. Those users with the - * necessary access will be able to edit content available via - * http://host:port/context/content using + * + * Don't forget to secure access appropriately to the editing URLs, especially if allowSpecialPaths is used. With the + * mapping configuration above, the context will be accessible to normal users as before. Those users with the necessary + * access will be able to edit content available via http://host:port/context/content using * http://host:port/context/webdavedit/content * * @author Remy Maucherat @@ -199,8 +200,8 @@ public class WebdavServlet extends DefaultServlet { /** * Simple date format for the creation date ISO representation (partial). */ - protected static final ConcurrentDateFormat creationDateFormat = new ConcurrentDateFormat( - "yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US, TimeZone.getTimeZone("GMT")); + protected static final ConcurrentDateFormat creationDateFormat = + new ConcurrentDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US, TimeZone.getTimeZone("GMT")); // ----------------------------------------------------- Instance Variables @@ -218,9 +219,8 @@ public class WebdavServlet extends DefaultServlet { * Repository of the lock-null resources. * <p> * Key : path of the collection containing the lock-null resource<br> - * Value : List of lock-null resource which are members of the - * collection. Each element of the List is the path associated with - * the lock-null resource. + * Value : List of lock-null resource which are members of the collection. Each element of the List is the path + * associated with the lock-null resource. */ private final Map<String,List<String>> lockNullResources = new ConcurrentHashMap<>(); @@ -238,15 +238,13 @@ public class WebdavServlet extends DefaultServlet { /** - * Default depth in spec is infinite. Limit depth to 3 by default as - * infinite depth makes operations very expensive. + * Default depth in spec is infinite. Limit depth to 3 by default as infinite depth makes operations very expensive. */ private int maxDepth = 3; /** - * Is access allowed via WebDAV to the special paths (/WEB-INF and - * /META-INF)? + * Is access allowed via WebDAV to the special paths (/WEB-INF and /META-INF)? */ private boolean allowSpecialPaths = false; @@ -266,13 +264,11 @@ public void init() throws ServletException { } if (getServletConfig().getInitParameter("maxDepth") != null) { - maxDepth = Integer.parseInt( - getServletConfig().getInitParameter("maxDepth")); + maxDepth = Integer.parseInt(getServletConfig().getInitParameter("maxDepth")); } if (getServletConfig().getInitParameter("allowSpecialPaths") != null) { - allowSpecialPaths = Boolean.parseBoolean( - getServletConfig().getInitParameter("allowSpecialPaths")); + allowSpecialPaths = Boolean.parseBoolean(getServletConfig().getInitParameter("allowSpecialPaths")); } } @@ -281,9 +277,11 @@ public void init() throws ServletException { /** * Return JAXP document builder instance. + * * @return the document builder - * @throws ServletException document builder creation failed - * (wrapped <code>ParserConfigurationException</code> exception) + * + * @throws ServletException document builder creation failed (wrapped <code>ParserConfigurationException</code> + * exception) */ protected DocumentBuilder getDocumentBuilder() throws ServletException { DocumentBuilder documentBuilder = null; @@ -294,7 +292,7 @@ protected DocumentBuilder getDocumentBuilder() throws ServletException { documentBuilderFactory.setExpandEntityReferences(false); documentBuilder = documentBuilderFactory.newDocumentBuilder(); documentBuilder.setEntityResolver(new WebdavResolver(this.getServletContext())); - } catch(ParserConfigurationException e) { + } catch (ParserConfigurationException e) { throw new ServletException(sm.getString("webdavservlet.jaxpfailed")); } return documentBuilder; @@ -355,14 +353,14 @@ protected void service(HttpServletRequest req, HttpServletResponse resp) throws /** - * Checks whether a given path refers to a resource under - * <code>WEB-INF</code> or <code>META-INF</code>. + * Checks whether a given path refers to a resource under <code>WEB-INF</code> or <code>META-INF</code>. + * * @param path the full path of the resource being accessed + * * @return <code>true</code> if the resource specified is under a special path */ private boolean isSpecialPath(final String path) { - return !allowSpecialPaths && ( - path.toUpperCase(Locale.ENGLISH).startsWith("/WEB-INF") || + return !allowSpecialPaths && (path.toUpperCase(Locale.ENGLISH).startsWith("/WEB-INF") || path.toUpperCase(Locale.ENGLISH).startsWith("/META-INF")); } @@ -381,10 +379,9 @@ protected boolean checkIfHeaders(HttpServletRequest request, HttpServletResponse /** - * Override the DefaultServlet implementation and only use the PathInfo. If - * the ServletPath is non-null, it will be because the WebDAV servlet has - * been mapped to a url other than /* to configure editing at different url - * than normal viewing. + * Override the DefaultServlet implementation and only use the PathInfo. If the ServletPath is non-null, it will be + * because the WebDAV servlet has been mapped to a url other than /* to configure editing at different url than + * normal viewing. * * @param request The servlet request we are processing */ @@ -423,7 +420,7 @@ protected String getRelativePath(HttpServletRequest request, boolean allowEmptyP protected String getPathPrefix(final HttpServletRequest request) { // Repeat the servlet path (e.g. /webdav/) in the listing path String contextPath = request.getContextPath(); - if (request.getServletPath() != null) { + if (request.getServletPath() != null) { contextPath = contextPath + request.getServletPath(); } return contextPath; @@ -433,10 +430,11 @@ protected String getPathPrefix(final HttpServletRequest request) { /** * OPTIONS Method. * - * @param req The Servlet request + * @param req The Servlet request * @param resp The Servlet response + * * @throws ServletException If an error occurs - * @throws IOException If an IO error occurs + * @throws IOException If an IO error occurs */ @Override protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { @@ -448,10 +446,12 @@ protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throw /** * PROPFIND Method. - * @param req The Servlet request + * + * @param req The Servlet request * @param resp The Servlet response + * * @throws ServletException If an error occurs - * @throws IOException If an IO error occurs + * @throws IOException If an IO error occurs */ protected void doPropfind(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { @@ -498,23 +498,23 @@ protected void doPropfind(HttpServletRequest req, HttpServletResponse resp) thro Element rootElement = document.getDocumentElement(); NodeList childList = rootElement.getChildNodes(); - for (int i=0; i < childList.getLength(); i++) { + for (int i = 0; i < childList.getLength(); i++) { Node currentNode = childList.item(i); switch (currentNode.getNodeType()) { - case Node.TEXT_NODE: - break; - case Node.ELEMENT_NODE: - if (currentNode.getNodeName().endsWith("prop")) { - type = FIND_BY_PROPERTY; - propNode = currentNode; - } - if (currentNode.getNodeName().endsWith("propname")) { - type = FIND_PROPERTY_NAMES; - } - if (currentNode.getNodeName().endsWith("allprop")) { - type = FIND_ALL_PROP; - } - break; + case Node.TEXT_NODE: + break; + case Node.ELEMENT_NODE: + if (currentNode.getNodeName().endsWith("prop")) { + type = FIND_BY_PROPERTY; + propNode = currentNode; + } + if (currentNode.getNodeName().endsWith("propname")) { + type = FIND_PROPERTY_NAMES; + } + if (currentNode.getNodeName().endsWith("allprop")) { + type = FIND_ALL_PROP; + } + break; } } } catch (SAXException | IOException e) { @@ -530,23 +530,22 @@ protected void doPropfind(HttpServletRequest req, HttpServletResponse resp) thro @SuppressWarnings("null") NodeList childList = propNode.getChildNodes(); - for (int i=0; i < childList.getLength(); i++) { + for (int i = 0; i < childList.getLength(); i++) { Node currentNode = childList.item(i); switch (currentNode.getNodeType()) { - case Node.TEXT_NODE: - break; - case Node.ELEMENT_NODE: - String nodeName = currentNode.getNodeName(); - String propertyName = null; - if (nodeName.indexOf(':') != -1) { - propertyName = nodeName.substring - (nodeName.indexOf(':') + 1); - } else { - propertyName = nodeName; - } - // href is a live property which is handled differently - properties.add(propertyName); - break; + case Node.TEXT_NODE: + break; + case Node.ELEMENT_NODE: + String nodeName = currentNode.getNodeName(); + String propertyName = null; + if (nodeName.indexOf(':') != -1) { + propertyName = nodeName.substring(nodeName.indexOf(':') + 1); + } else { + propertyName = nodeName; + } + // href is a live property which is handled differently + properties.add(propertyName); + break; } } } @@ -655,8 +654,10 @@ protected void doPropfind(HttpServletRequest req, HttpServletResponse resp) thro /** * PROPPATCH Method. - * @param req The Servlet request + * + * @param req The Servlet request * @param resp The Servlet response + * * @throws IOException If an IO error occurs */ protected void doProppatch(HttpServletRequest req, HttpServletResponse resp) throws IOException { @@ -677,10 +678,12 @@ protected void doProppatch(HttpServletRequest req, HttpServletResponse resp) thr /** * MKCOL Method. - * @param req The Servlet request + * + * @param req The Servlet request * @param resp The Servlet response + * * @throws ServletException If an error occurs - * @throws IOException If an IO error occurs + * @throws IOException If an IO error occurs */ protected void doMkcol(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { @@ -714,7 +717,7 @@ protected void doMkcol(HttpServletRequest req, HttpServletResponse resp) throws resp.sendError(WebdavStatus.SC_NOT_IMPLEMENTED); return; - } catch(SAXException saxe) { + } catch (SAXException saxe) { // Parse error - assume invalid content resp.sendError(WebdavStatus.SC_UNSUPPORTED_MEDIA_TYPE); return; @@ -733,10 +736,12 @@ protected void doMkcol(HttpServletRequest req, HttpServletResponse resp) throws /** * DELETE Method. - * @param req The Servlet request + * + * @param req The Servlet request * @param resp The Servlet response + * * @throws ServletException If an error occurs - * @throws IOException If an IO error occurs + * @throws IOException If an IO error occurs */ @Override protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { @@ -758,10 +763,10 @@ protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws /** * Process a PUT request for the specified resource. * - * @param req The servlet request we are processing + * @param req The servlet request we are processing * @param resp The servlet response we are creating * - * @exception IOException if an input/output error occurs + * @exception IOException if an input/output error occurs * @exception ServletException if a servlet-specified error occurs */ @Override @@ -788,8 +793,10 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws Se /** * COPY Method. - * @param req The Servlet request + * + * @param req The Servlet request * @param resp The Servlet response + * * @throws IOException If an IO error occurs */ protected void doCopy(HttpServletRequest req, HttpServletResponse resp) throws IOException { @@ -805,8 +812,10 @@ protected void doCopy(HttpServletRequest req, HttpServletResponse resp) throws I /** * MOVE Method. - * @param req The Servlet request + * + * @param req The Servlet request * @param resp The Servlet response + * * @throws IOException If an IO error occurs */ protected void doMove(HttpServletRequest req, HttpServletResponse resp) throws IOException { @@ -831,10 +840,12 @@ protected void doMove(HttpServletRequest req, HttpServletResponse resp) throws I /** * LOCK Method. - * @param req The Servlet request + * + * @param req The Servlet request * @param resp The Servlet response + * * @throws ServletException If an error occurs - * @throws IOException If an IO error occurs + * @throws IOException If an IO error occurs */ protected void doLock(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { @@ -874,7 +885,7 @@ protected void doLock(HttpServletRequest req, HttpServletResponse resp) throws S int commaPos = lockDurationStr.indexOf(','); // If multiple timeouts, just use the first if (commaPos != -1) { - lockDurationStr = lockDurationStr.substring(0,commaPos); + lockDurationStr = lockDurationStr.substring(0, commaPos); } if (lockDurationStr.startsWith("Second-")) { lockDuration = Integer.parseInt(lockDurationStr.substring(7)); @@ -926,42 +937,42 @@ protected void doLock(HttpServletRequest req, HttpServletResponse resp) throws S Node lockTypeNode = null; Node lockOwnerNode = null; - for (int i=0; i < childList.getLength(); i++) { + for (int i = 0; i < childList.getLength(); i++) { Node currentNode = childList.item(i); switch (currentNode.getNodeType()) { - case Node.TEXT_NODE: - break; - case Node.ELEMENT_NODE: - String nodeName = currentNode.getNodeName(); - if (nodeName.endsWith("lockscope")) { - lockScopeNode = currentNode; - } - if (nodeName.endsWith("locktype")) { - lockTypeNode = currentNode; - } - if (nodeName.endsWith("owner")) { - lockOwnerNode = currentNode; - } - break; + case Node.TEXT_NODE: + break; + case Node.ELEMENT_NODE: + String nodeName = currentNode.getNodeName(); + if (nodeName.endsWith("lockscope")) { + lockScopeNode = currentNode; + } + if (nodeName.endsWith("locktype")) { + lockTypeNode = currentNode; + } + if (nodeName.endsWith("owner")) { + lockOwnerNode = currentNode; + } + break; } } if (lockScopeNode != null) { childList = lockScopeNode.getChildNodes(); - for (int i=0; i < childList.getLength(); i++) { + for (int i = 0; i < childList.getLength(); i++) { Node currentNode = childList.item(i); switch (currentNode.getNodeType()) { - case Node.TEXT_NODE: - break; - case Node.ELEMENT_NODE: - String tempScope = currentNode.getNodeName(); - if (tempScope.indexOf(':') != -1) { - lock.scope = tempScope.substring(tempScope.indexOf(':') + 1); - } else { - lock.scope = tempScope; - } - break; + case Node.TEXT_NODE: + break; + case Node.ELEMENT_NODE: + String tempScope = currentNode.getNodeName(); + if (tempScope.indexOf(':') != -1) { + lock.scope = tempScope.substring(tempScope.indexOf(':') + 1); + } else { + lock.scope = tempScope; + } + break; } } @@ -978,19 +989,19 @@ protected void doLock(HttpServletRequest req, HttpServletResponse resp) throws S if (lockTypeNode != null) { childList = lockTypeNode.getChildNodes(); - for (int i=0; i < childList.getLength(); i++) { + for (int i = 0; i < childList.getLength(); i++) { Node currentNode = childList.item(i); switch (currentNode.getNodeType()) { - case Node.TEXT_NODE: - break; - case Node.ELEMENT_NODE: - String tempType = currentNode.getNodeName(); - if (tempType.indexOf(':') != -1) { - lock.type = tempType.substring(tempType.indexOf(':') + 1); - } else { - lock.type = tempType; - } - break; + case Node.TEXT_NODE: + break; + case Node.ELEMENT_NODE: + String tempType = currentNode.getNodeName(); + if (tempType.indexOf(':') != -1) { + lock.type = tempType.substring(tempType.indexOf(':') + 1); + } else { + lock.type = tempType; + } + break; } } @@ -1007,18 +1018,18 @@ protected void doLock(HttpServletRequest req, HttpServletResponse resp) throws S if (lockOwnerNode != null) { childList = lockOwnerNode.getChildNodes(); - for (int i=0; i < childList.getLength(); i++) { + for (int i = 0; i < childList.getLength(); i++) { Node currentNode = childList.item(i); switch (currentNode.getNodeType()) { - case Node.TEXT_NODE: - lock.owner += currentNode.getNodeValue(); - break; - case Node.ELEMENT_NODE: - strWriter = new StringWriter(); - domWriter = new DOMWriter(strWriter); - domWriter.print(currentNode); - lock.owner += strWriter.toString(); - break; + case Node.TEXT_NODE: + lock.owner += currentNode.getNodeValue(); + break; + case Node.ELEMENT_NODE: + strWriter = new StringWriter(); + domWriter = new DOMWriter(strWriter); + domWriter.print(currentNode); + lock.owner += strWriter.toString(); + break; } } @@ -1041,13 +1052,11 @@ protected void doLock(HttpServletRequest req, HttpServletResponse resp) throws S if (lockRequestType == LOCK_CREATION) { // Generating lock id - String lockTokenStr = req.getServletPath() + "-" + lock.type + "-" + - lock.scope + "-" + req.getUserPrincipal() + "-" + - lock.depth + "-" + lock.owner + "-" + lock.tokens + "-" + - lock.expiresAt + "-" + System.currentTimeMillis() + "-" + - secret; - String lockToken = HexUtils.toHexString(ConcurrentMessageDigest.digestMD5( - lockTokenStr.getBytes(StandardCharsets.ISO_8859_1))); + String lockTokenStr = req.getServletPath() + "-" + lock.type + "-" + lock.scope + "-" + + req.getUserPrincipal() + "-" + lock.depth + "-" + lock.owner + "-" + lock.tokens + "-" + + lock.expiresAt + "-" + System.currentTimeMillis() + "-" + secret; + String lockToken = HexUtils + .toHexString(ConcurrentMessageDigest.digestMD5(lockTokenStr.getBytes(StandardCharsets.ISO_8859_1))); if (resource.isDirectory() && lock.depth == maxDepth) { @@ -1063,8 +1072,7 @@ protected void doLock(HttpServletRequest req, HttpServletResponse resp) throws S collectionLocksIterator.remove(); continue; } - if (currentLock.path.startsWith(lock.path) && - (currentLock.isExclusive() || lock.isExclusive())) { + if (currentLock.path.startsWith(lock.path) && (currentLock.isExclusive() || lock.isExclusive())) { // A child collection of this collection is locked lockPaths.add(currentLock.path); } @@ -1074,8 +1082,7 @@ protected void doLock(HttpServletRequest req, HttpServletResponse resp) throws S resourceLocks.remove(currentLock.path); continue; } - if (currentLock.path.startsWith(lock.path) && - (currentLock.isExclusive() || lock.isExclusive())) { + if (currentLock.path.startsWith(lock.path) && (currentLock.isExclusive() || lock.isExclusive())) { // A child resource of this collection is locked lockPaths.add(currentLock.path); } @@ -1238,8 +1245,10 @@ protected void doLock(HttpServletRequest req, HttpServletResponse resp) throws S /** * UNLOCK Method. - * @param req The Servlet request + * + * @param req The Servlet request * @param resp The Servlet response + * * @throws IOException If an IO error occurs */ protected void doUnlock(HttpServletRequest req, HttpServletResponse resp) throws IOException { @@ -1311,14 +1320,13 @@ protected void doUnlock(HttpServletRequest req, HttpServletResponse resp) throws // -------------------------------------------------------- Private Methods /** - * Check to see if a resource is currently write locked. The method - * will look at the "If" header to make sure the client - * has give the appropriate lock tokens. + * Check to see if a resource is currently write locked. The method will look at the "If" header to make sure the + * client has give the appropriate lock tokens. * * @param req Servlet request - * @return <code>true</code> if the resource is locked (and no appropriate - * lock token has been found for at least one of - * the non-shared locks which are present on the resource). + * + * @return <code>true</code> if the resource is locked (and no appropriate lock token has been found for at least + * one of the non-shared locks which are present on the resource). */ private boolean isLocked(HttpServletRequest req) { @@ -1341,11 +1349,11 @@ private boolean isLocked(HttpServletRequest req) { /** * Check to see if a resource is currently write locked. * - * @param path Path of the resource + * @param path Path of the resource * @param ifHeader "If" HTTP header which was included in the request - * @return <code>true</code> if the resource is locked (and no appropriate - * lock token has been found for at least one of - * the non-shared locks which are present on the resource). + * + * @return <code>true</code> if the resource is locked (and no appropriate lock token has been found for at least + * one of the non-shared locks which are present on the resource). */ private boolean isLocked(String path, String ifHeader) { @@ -1397,7 +1405,7 @@ private boolean isLocked(String path, String ifHeader) { /** * Copy a resource. * - * @param req Servlet request + * @param req Servlet request * @param resp Servlet response * * @return boolean true if the copy is successful @@ -1418,14 +1426,14 @@ private boolean copyResource(HttpServletRequest req, HttpServletResponse resp) t // See RFC 4918 String destinationHeader = req.getHeader("Destination"); - if (destinationHeader == null || destinationHeader.isEmpty()) { + if (destinationHeader == null || destinationHeader.isEmpty()) { resp.sendError(WebdavStatus.SC_BAD_REQUEST); return false; } URI destinationUri; try { - destinationUri = new URI (destinationHeader); + destinationUri = new URI(destinationHeader); } catch (URISyntaxException e) { resp.sendError(WebdavStatus.SC_BAD_REQUEST); return false; @@ -1449,8 +1457,7 @@ private boolean copyResource(HttpServletRequest req, HttpServletResponse resp) t // Port needs to match too but handled separately as the logic is a // little more complicated if (req.getServerPort() != destinationUri.getPort()) { - if (destinationUri.getPort() == -1 && - ("http".equals(req.getScheme()) && req.getServerPort() == 80 || + if (destinationUri.getPort() == -1 && ("http".equals(req.getScheme()) && req.getServerPort() == 80 || "https".equals(req.getScheme()) && req.getServerPort() == 443)) { // All good. } else { @@ -1554,10 +1561,10 @@ private boolean copyResource(HttpServletRequest req, HttpServletResponse resp) t /** * Copy a collection. * - * @param errorList Map containing the list of errors which occurred - * during the copy operation - * @param source Path of the resource to be copied - * @param dest Destination path + * @param errorList Map containing the list of errors which occurred during the copy operation + * @param source Path of the resource to be copied + * @param dest Destination path + * * @return <code>true</code> if the copy was successful */ private boolean copyResource(Map<String,Integer> errorList, String source, String dest) { @@ -1630,9 +1637,11 @@ private boolean copyResource(Map<String,Integer> errorList, String source, Strin /** * Delete a resource. * - * @param req Servlet request + * @param req Servlet request * @param resp Servlet response + * * @return <code>true</code> if the delete is successful + * * @throws IOException If an IO error occurs */ private boolean deleteResource(HttpServletRequest req, HttpServletResponse resp) throws IOException { @@ -1644,12 +1653,13 @@ private boolean deleteResource(HttpServletRequest req, HttpServletResponse resp) /** * Delete a resource. * - * @param path Path of the resource which is to be deleted - * @param req Servlet request - * @param resp Servlet response - * @param setStatus Should the response status be set on successful - * completion + * @param path Path of the resource which is to be deleted + * @param req Servlet request + * @param resp Servlet response + * @param setStatus Should the response status be set on successful completion + * * @return <code>true</code> if the delete is successful + * * @throws IOException If an IO error occurs */ private boolean deleteResource(String path, HttpServletRequest req, HttpServletResponse resp, boolean setStatus) @@ -1688,8 +1698,7 @@ private boolean deleteResource(String path, HttpServletRequest req, HttpServletR deleteCollection(req, path, errorList); if (!resource.delete()) { - errorList.put(path, Integer.valueOf - (WebdavStatus.SC_INTERNAL_SERVER_ERROR)); + errorList.put(path, Integer.valueOf(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); } if (!errorList.isEmpty()) { @@ -1706,8 +1715,9 @@ private boolean deleteResource(String path, HttpServletRequest req, HttpServletR /** * Deletes a collection. - * @param req The Servlet request - * @param path Path to the collection to be deleted + * + * @param req The Servlet request + * @param path Path to the collection to be deleted * @param errorList Contains the list of the errors which occurred */ private void deleteCollection(HttpServletRequest req, String path, Map<String,Integer> errorList) { @@ -1755,8 +1765,7 @@ private void deleteCollection(HttpServletRequest req, String path, Map<String,In if (!childResource.isDirectory()) { // If it's not a collection, then it's an unknown // error - errorList.put(childName, Integer.valueOf( - WebdavStatus.SC_INTERNAL_SERVER_ERROR)); + errorList.put(childName, Integer.valueOf(WebdavStatus.SC_INTERNAL_SERVER_ERROR)); } } } @@ -1765,11 +1774,10 @@ private void deleteCollection(HttpServletRequest req, String path, Map<String,In /** - * Send a multistatus element containing a complete error report to the - * client. + * Send a multistatus element containing a complete error report to the client. * - * @param req Servlet request - * @param resp Servlet response + * @param req Servlet request + * @param resp Servlet response * @param errorList List of error to be displayed * * @throws IOException If an IO error occurs @@ -1784,7 +1792,7 @@ private void sendReport(HttpServletRequest req, HttpServletResponse resp, Map<St generatedXML.writeElement("D", DEFAULT_NAMESPACE, "multistatus", XMLWriter.OPENING); - for (Map.Entry<String, Integer> errorEntry : errorList.entrySet()) { + for (Map.Entry<String,Integer> errorEntry : errorList.entrySet()) { String errorPath = errorEntry.getKey(); int errorCode = errorEntry.getValue().intValue(); @@ -1812,12 +1820,11 @@ private void sendReport(HttpServletRequest req, HttpServletResponse resp, Map<St /** * Propfind helper method. * - * @param req The servlet request + * @param req The servlet request * @param generatedXML XML response to the Propfind request - * @param path Path of the current resource - * @param type Propfind type - * @param properties If the propfind type is find properties by name, then - * this List contains those properties + * @param path Path of the current resource + * @param type Propfind type + * @param properties If the propfind type is find properties by name, then this List contains those properties */ private void parseProperties(HttpServletRequest req, XMLWriter generatedXML, String path, int type, List<String> properties) { @@ -1855,12 +1862,11 @@ private void parseProperties(HttpServletRequest req, XMLWriter generatedXML, Str /** * Propfind helper method. Displays the properties of a lock-null resource. * - * @param req The servlet request + * @param req The servlet request * @param generatedXML XML response to the Propfind request - * @param path Path of the current resource - * @param type Propfind type - * @param properties If the propfind type is find properties by name, then - * this List contains those properties + * @param path Path of the current resource + * @param type Propfind type + * @param properties If the propfind type is find properties by name, then this List contains those properties */ private void parseLockNullProperties(HttpServletRequest req, XMLWriter generatedXML, String path, int type, List<String> properties) { @@ -1911,182 +1917,160 @@ private void generatePropFindResponse(XMLWriter generatedXML, String rewrittenUr switch (propFindType) { - case FIND_ALL_PROP : + case FIND_ALL_PROP: - generatedXML.writeElement("D", "propstat", XMLWriter.OPENING); - generatedXML.writeElement("D", "prop", XMLWriter.OPENING); + generatedXML.writeElement("D", "propstat", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.OPENING); - generatedXML.writeProperty("D", "creationdate", getISOCreationDate(created)); - generatedXML.writeElement("D", "displayname", XMLWriter.OPENING); - generatedXML.writeData(resourceName); - generatedXML.writeElement("D", "displayname", XMLWriter.CLOSING); - if (isFile) { - generatedXML.writeProperty("D", "getlastmodified", FastHttpDateFormat.formatDate(lastModified)); - generatedXML.writeProperty("D", "getcontentlength", Long.toString(contentLength)); - if (contentType != null) { - generatedXML.writeProperty("D", "getcontenttype", contentType); - } - generatedXML.writeProperty("D", "getetag", eTag); - if (isLockNull) { + generatedXML.writeProperty("D", "creationdate", getISOCreationDate(created)); + generatedXML.writeElement("D", "displayname", XMLWriter.OPENING); + generatedXML.writeData(resourceName); + generatedXML.writeElement("D", "displayname", XMLWriter.CLOSING); + if (isFile) { + generatedXML.writeProperty("D", "getlastmodified", FastHttpDateFormat.formatDate(lastModified)); + generatedXML.writeProperty("D", "getcontentlength", Long.toString(contentLength)); + if (contentType != null) { + generatedXML.writeProperty("D", "getcontenttype", contentType); + } + generatedXML.writeProperty("D", "getetag", eTag); + if (isLockNull) { + generatedXML.writeElement("D", "resourcetype", XMLWriter.OPENING); + generatedXML.writeElement("D", "lock-null", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "resourcetype", XMLWriter.CLOSING); + } else { + generatedXML.writeElement("D", "resourcetype", XMLWriter.NO_CONTENT); + } + } else { + generatedXML.writeProperty("D", "getlastmodified", FastHttpDateFormat.formatDate(lastModified)); generatedXML.writeElement("D", "resourcetype", XMLWriter.OPENING); - generatedXML.writeElement("D", "lock-null", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "collection", XMLWriter.NO_CONTENT); generatedXML.writeElement("D", "resourcetype", XMLWriter.CLOSING); - } else { - generatedXML.writeElement("D", "resourcetype", XMLWriter.NO_CONTENT); } - } else { - generatedXML.writeProperty("D", "getlastmodified", FastHttpDateFormat.formatDate(lastModified)); - generatedXML.writeElement("D", "resourcetype", XMLWriter.OPENING); - generatedXML.writeElement("D", "collection", XMLWriter.NO_CONTENT); - generatedXML.writeElement("D", "resourcetype", XMLWriter.CLOSING); - } - generatedXML.writeProperty("D", "source", ""); + generatedXML.writeProperty("D", "source", ""); - String supportedLocks = "<D:lockentry>" - + "<D:lockscope><D:exclusive/></D:lockscope>" - + "<D:locktype><D:write/></D:locktype>" - + "</D:lockentry>" + "<D:lockentry>" - + "<D:lockscope><D:shared/></D:lockscope>" - + "<D:locktype><D:write/></D:locktype>" - + "</D:lockentry>"; - generatedXML.writeElement("D", "supportedlock", XMLWriter.OPENING); - generatedXML.writeText(supportedLocks); - generatedXML.writeElement("D", "supportedlock", XMLWriter.CLOSING); + String supportedLocks = "<D:lockentry>" + "<D:lockscope><D:exclusive/></D:lockscope>" + + "<D:locktype><D:write/></D:locktype>" + "</D:lockentry>" + "<D:lockentry>" + + "<D:lockscope><D:shared/></D:lockscope>" + "<D:locktype><D:write/></D:locktype>" + + "</D:lockentry>"; + generatedXML.writeElement("D", "supportedlock", XMLWriter.OPENING); + generatedXML.writeText(supportedLocks); + generatedXML.writeElement("D", "supportedlock", XMLWriter.CLOSING); - generateLockDiscovery(path, generatedXML); + generateLockDiscovery(path, generatedXML); - generatedXML.writeElement("D", "prop", XMLWriter.CLOSING); - generatedXML.writeElement("D", "status", XMLWriter.OPENING); - generatedXML.writeText(status); - generatedXML.writeElement("D", "status", XMLWriter.CLOSING); - generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING); + generatedXML.writeElement("D", "prop", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.OPENING); + generatedXML.writeText(status); + generatedXML.writeElement("D", "status", XMLWriter.CLOSING); + generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING); - break; + break; - case FIND_PROPERTY_NAMES : + case FIND_PROPERTY_NAMES: - generatedXML.writeElement("D", "propstat", XMLWriter.OPENING); - generatedXML.writeElement("D", "prop", XMLWriter.OPENING); + generatedXML.writeElement("D", "propstat", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.OPENING); - generatedXML.writeElement("D", "creationdate", XMLWriter.NO_CONTENT); - generatedXML.writeElement("D", "displayname", XMLWriter.NO_CONTENT); - if (isFile) { - generatedXML.writeElement("D", "getcontentlanguage", XMLWriter.NO_CONTENT); - generatedXML.writeElement("D", "getcontentlength", XMLWriter.NO_CONTENT); - generatedXML.writeElement("D", "getcontenttype", XMLWriter.NO_CONTENT); - generatedXML.writeElement("D", "getetag", XMLWriter.NO_CONTENT); - generatedXML.writeElement("D", "getlastmodified", XMLWriter.NO_CONTENT); - } - generatedXML.writeElement("D", "resourcetype", XMLWriter.NO_CONTENT); - generatedXML.writeElement("D", "source", XMLWriter.NO_CONTENT); - generatedXML.writeElement("D", "lockdiscovery", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "creationdate", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "displayname", XMLWriter.NO_CONTENT); + if (isFile) { + generatedXML.writeElement("D", "getcontentlanguage", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "getcontentlength", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "getcontenttype", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "getetag", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "getlastmodified", XMLWriter.NO_CONTENT); + } + generatedXML.writeElement("D", "resourcetype", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "source", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "lockdiscovery", XMLWriter.NO_CONTENT); - generatedXML.writeElement("D", "prop", XMLWriter.CLOSING); - generatedXML.writeElement("D", "status", XMLWriter.OPENING); - generatedXML.writeText(status); - generatedXML.writeElement("D", "status", XMLWriter.CLOSING); - generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING); + generatedXML.writeElement("D", "prop", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.OPENING); + generatedXML.writeText(status); + generatedXML.writeElement("D", "status", XMLWriter.CLOSING); + generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING); - break; + break; - case FIND_BY_PROPERTY : + case FIND_BY_PROPERTY: - List<String> propertiesNotFound = new ArrayList<>(); + List<String> propertiesNotFound = new ArrayList<>(); - // Parse the list of properties + // Parse the list of properties - generatedXML.writeElement("D", "propstat", XMLWriter.OPENING); - generatedXML.writeElement("D", "prop", XMLWriter.OPENING); + generatedXML.writeElement("D", "propstat", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.OPENING); - for (String property : properties) { - if (property.equals("creationdate")) { - generatedXML.writeProperty("D", "creationdate", getISOCreationDate(created)); - } else if (property.equals("displayname")) { - generatedXML.writeElement("D", "displayname", XMLWriter.OPENING); - generatedXML.writeData(resourceName); - generatedXML.writeElement("D", "displayname", XMLWriter.CLOSING); - } else if (property.equals("getcontentlanguage")) { - if (isFile) { - generatedXML.writeElement("D", "getcontentlanguage", XMLWriter.NO_CONTENT); - } else { - propertiesNotFound.add(property); - } - } else if (property.equals("getcontentlength")) { - if (isFile) { - generatedXML.writeProperty("D", "getcontentlength", Long.toString(contentLength)); - } else { - propertiesNotFound.add(property); - } - } else if (property.equals("getcontenttype")) { - if (isFile) { - generatedXML.writeProperty("D", "getcontenttype", contentType); - } else { - propertiesNotFound.add(property); - } - } else if (property.equals("getetag")) { - if (isFile) { - generatedXML.writeProperty("D", "getetag", eTag); - } else { - propertiesNotFound.add(property); - } - } else if (property.equals("getlastmodified")) { - if (isFile) { - generatedXML.writeProperty("D", "getlastmodified", FastHttpDateFormat.formatDate(lastModified)); - } else { - propertiesNotFound.add(property); - } - } else if (property.equals("resourcetype")) { - if (isFile) { - if(isLockNull) { + for (String property : properties) { + if (property.equals("creationdate")) { + generatedXML.writeProperty("D", "creationdate", getISOCreationDate(created)); + } else if (property.equals("displayname")) { + generatedXML.writeElement("D", "displayname", XMLWriter.OPENING); + generatedXML.writeData(resourceName); + generatedXML.writeElement("D", "displayname", XMLWriter.CLOSING); + } else if (property.equals("getcontentlanguage")) { + if (isFile) { + generatedXML.writeElement("D", "getcontentlanguage", XMLWriter.NO_CONTENT); + } else { + propertiesNotFound.add(property); + } + } else if (property.equals("getcontentlength")) { + if (isFile) { + generatedXML.writeProperty("D", "getcontentlength", Long.toString(contentLength)); + } else { + propertiesNotFound.add(property); + } + } else if (property.equals("getcontenttype")) { + if (isFile) { + generatedXML.writeProperty("D", "getcontenttype", contentType); + } else { + propertiesNotFound.add(property); + } + } else if (property.equals("getetag")) { + if (isFile) { + generatedXML.writeProperty("D", "getetag", eTag); + } else { + propertiesNotFound.add(property); + } + } else if (property.equals("getlastmodified")) { + if (isFile) { + generatedXML.writeProperty("D", "getlastmodified", + FastHttpDateFormat.formatDate(lastModified)); + } else { + propertiesNotFound.add(property); + } + } else if (property.equals("resourcetype")) { + if (isFile) { + if (isLockNull) { + generatedXML.writeElement("D", "resourcetype", XMLWriter.OPENING); + generatedXML.writeElement("D", "lock-null", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "resourcetype", XMLWriter.CLOSING); + } else { + generatedXML.writeElement("D", "resourcetype", XMLWriter.NO_CONTENT); + } + } else { generatedXML.writeElement("D", "resourcetype", XMLWriter.OPENING); - generatedXML.writeElement("D", "lock-null", XMLWriter.NO_CONTENT); + generatedXML.writeElement("D", "collection", XMLWriter.NO_CONTENT); generatedXML.writeElement("D", "resourcetype", XMLWriter.CLOSING); - } else { - generatedXML.writeElement("D", "resourcetype", XMLWriter.NO_CONTENT); + } + } else if (property.equals("source")) { + generatedXML.writeProperty("D", "source", ""); + } else if (property.equals("supportedlock")) { + supportedLocks = "<D:lockentry>" + "<D:lockscope><D:exclusive/></D:lockscope>" + + "<D:locktype><D:write/></D:locktype>" + "</D:lockentry>" + "<D:lockentry>" + + "<D:lockscope><D:shared/></D:lockscope>" + "<D:locktype><D:write/></D:locktype>" + + "</D:lockentry>"; + generatedXML.writeElement("D", "supportedlock", XMLWriter.OPENING); + generatedXML.writeText(supportedLocks); + generatedXML.writeElement("D", "supportedlock", XMLWriter.CLOSING); + } else if (property.equals("lockdiscovery")) { + if (!generateLockDiscovery(path, generatedXML)) { + propertiesNotFound.add(property); } } else { - generatedXML.writeElement("D", "resourcetype", XMLWriter.OPENING); - generatedXML.writeElement("D", "collection", XMLWriter.NO_CONTENT); - generatedXML.writeElement("D", "resourcetype",XMLWriter.CLOSING); - } - } else if (property.equals("source")) { - generatedXML.writeProperty("D", "source", ""); - } else if (property.equals("supportedlock")) { - supportedLocks = "<D:lockentry>" - + "<D:lockscope><D:exclusive/></D:lockscope>" - + "<D:locktype><D:write/></D:locktype>" - + "</D:lockentry>" + "<D:lockentry>" - + "<D:lockscope><D:shared/></D:lockscope>" - + "<D:locktype><D:write/></D:locktype>" - + "</D:lockentry>"; - generatedXML.writeElement("D", "supportedlock", XMLWriter.OPENING); - generatedXML.writeText(supportedLocks); - generatedXML.writeElement("D", "supportedlock", XMLWriter.CLOSING); - } else if (property.equals("lockdiscovery")) { - if (!generateLockDiscovery(path, generatedXML)) { propertiesNotFound.add(property); } - } else { - propertiesNotFound.add(property); - } - } - - generatedXML.writeElement("D", "prop", XMLWriter.CLOSING); - generatedXML.writeElement("D", "status", XMLWriter.OPENING); - generatedXML.writeText(status); - generatedXML.writeElement("D", "status", XMLWriter.CLOSING); - generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING); - - if (!propertiesNotFound.isEmpty()) { - - status = "HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND + " "; - - generatedXML.writeElement("D", "propstat", XMLWriter.OPENING); - generatedXML.writeElement("D", "prop", XMLWriter.OPENING); - - for (String propertyNotFound : propertiesNotFound) { - generatedXML.writeElement("D", propertyNotFound, XMLWriter.NO_CONTENT); } generatedXML.writeElement("D", "prop", XMLWriter.CLOSING); @@ -2094,9 +2078,26 @@ private void generatePropFindResponse(XMLWriter generatedXML, String rewrittenUr generatedXML.writeText(status); generatedXML.writeElement("D", "status", XMLWriter.CLOSING); generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING); - } - break; + if (!propertiesNotFound.isEmpty()) { + + status = "HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND + " "; + + generatedXML.writeElement("D", "propstat", XMLWriter.OPENING); + generatedXML.writeElement("D", "prop", XMLWriter.OPENING); + + for (String propertyNotFound : propertiesNotFound) { + generatedXML.writeElement("D", propertyNotFound, XMLWriter.NO_CONTENT); + } + + generatedXML.writeElement("D", "prop", XMLWriter.CLOSING); + generatedXML.writeElement("D", "status", XMLWriter.OPENING); + generatedXML.writeText(status); + generatedXML.writeElement("D", "status", XMLWriter.CLOSING); + generatedXML.writeElement("D", "propstat", XMLWriter.CLOSING); + } + + break; } generatedXML.writeElement("D", "response", XMLWriter.CLOSING); @@ -2106,8 +2107,9 @@ private void generatePropFindResponse(XMLWriter generatedXML, String rewrittenUr /** * Print the lock discovery information associated with a path. * - * @param path Path + * @param path Path * @param generatedXML XML data to which the locks info will be appended + * * @return <code>true</code> if at least one lock was displayed */ private boolean generateLockDiscovery(String path, XMLWriter generatedXML) { @@ -2126,8 +2128,7 @@ private boolean generateLockDiscovery(String path, XMLWriter generatedXML) { if (path.startsWith(currentLock.path)) { if (!wroteStart) { wroteStart = true; - generatedXML.writeElement("D", "lockdiscovery", - XMLWriter.OPENING); + generatedXML.writeElement("D", "lockdiscovery", XMLWriter.OPENING); } currentLock.toXML(generatedXML); } @@ -2145,6 +2146,7 @@ private boolean generateLockDiscovery(String path, XMLWriter generatedXML) { /** * Get creation date in ISO format. + * * @return the formatted creation date */ private String getISOCreationDate(long creationDate) { @@ -2194,7 +2196,7 @@ protected String determineMethodsAllowed(HttpServletRequest req) { } - // -------------------------------------------------- LockInfo Inner Class + // -------------------------------------------------- LockInfo Inner Class /** * Holds a lock information. @@ -2230,7 +2232,7 @@ private static class LockInfo implements Serializable { @Override public String toString() { - StringBuilder result = new StringBuilder("Type:"); + StringBuilder result = new StringBuilder("Type:"); result.append(type); result.append("\nScope:"); result.append(scope); @@ -2268,8 +2270,7 @@ public boolean isExclusive() { /** * Get an XML representation of this lock token. * - * @param generatedXML The XML write to which the fragment will be - * appended + * @param generatedXML The XML write to which the fragment will be appended */ public void toXML(XMLWriter generatedXML) { @@ -2316,9 +2317,8 @@ public void toXML(XMLWriter generatedXML) { // --------------------------------------------- WebdavResolver Inner Class /** * Work around for XML parsers that don't fully respect - * {@link DocumentBuilderFactory#setExpandEntityReferences(boolean)} when - * called with <code>false</code>. External references are filtered out for - * security reasons. See CVE-2007-5461. + * {@link DocumentBuilderFactory#setExpandEntityReferences(boolean)} when called with <code>false</code>. External + * references are filtered out for security reasons. See CVE-2007-5461. */ private static class WebdavResolver implements EntityResolver { private ServletContext context; @@ -2328,7 +2328,7 @@ private static class WebdavResolver implements EntityResolver { } @Override - public InputSource resolveEntity (String publicId, String systemId) { + public InputSource resolveEntity(String publicId, String systemId) { context.log(sm.getString("webdavservlet.externalEntityIgnored", publicId, systemId)); return new InputSource(new StringReader("Ignored external entity")); } @@ -2336,15 +2336,13 @@ public InputSource resolveEntity (String publicId, String systemId) { } -// -------------------------------------------------------- WebdavStatus Class +// -------------------------------------------------------- WebdavStatus Class /** - * Wraps the HttpServletResponse class to abstract the - * specific protocol used. To support other protocols - * we would only need to modify this class and the - * WebDavRetCode classes. + * Wraps the HttpServletResponse class to abstract the specific protocol used. To support other protocols we would only + * need to modify this class and the WebDavRetCode classes. * - * @author Marc Eaddy + * @author Marc Eaddy */ class WebdavStatus { @@ -2358,151 +2356,130 @@ class WebdavStatus { /** - * Status code (201) indicating the request succeeded and created - * a new resource on the server. + * Status code (201) indicating the request succeeded and created a new resource on the server. */ public static final int SC_CREATED = HttpServletResponse.SC_CREATED; /** - * Status code (202) indicating that a request was accepted for - * processing, but was not completed. + * Status code (202) indicating that a request was accepted for processing, but was not completed. */ public static final int SC_ACCEPTED = HttpServletResponse.SC_ACCEPTED; /** - * Status code (204) indicating that the request succeeded but that - * there was no new information to return. + * Status code (204) indicating that the request succeeded but that there was no new information to return. */ public static final int SC_NO_CONTENT = HttpServletResponse.SC_NO_CONTENT; /** - * Status code (301) indicating that the resource has permanently - * moved to a new location, and that future references should use a - * new URI with their requests. + * Status code (301) indicating that the resource has permanently moved to a new location, and that future + * references should use a new URI with their requests. */ public static final int SC_MOVED_PERMANENTLY = HttpServletResponse.SC_MOVED_PERMANENTLY; /** - * Status code (302) indicating that the resource has temporarily - * moved to another location, but that future references should - * still use the original URI to access the resource. + * Status code (302) indicating that the resource has temporarily moved to another location, but that future + * references should still use the original URI to access the resource. */ public static final int SC_MOVED_TEMPORARILY = HttpServletResponse.SC_MOVED_TEMPORARILY; /** - * Status code (304) indicating that a conditional GET operation - * found that the resource was available and not modified. + * Status code (304) indicating that a conditional GET operation found that the resource was available and not + * modified. */ public static final int SC_NOT_MODIFIED = HttpServletResponse.SC_NOT_MODIFIED; /** - * Status code (400) indicating the request sent by the client was - * syntactically incorrect. + * Status code (400) indicating the request sent by the client was syntactically incorrect. */ public static final int SC_BAD_REQUEST = HttpServletResponse.SC_BAD_REQUEST; /** - * Status code (401) indicating that the request requires HTTP - * authentication. + * Status code (401) indicating that the request requires HTTP authentication. */ public static final int SC_UNAUTHORIZED = HttpServletResponse.SC_UNAUTHORIZED; /** - * Status code (403) indicating the server understood the request - * but refused to fulfill it. + * Status code (403) indicating the server understood the request but refused to fulfill it. */ public static final int SC_FORBIDDEN = HttpServletResponse.SC_FORBIDDEN; /** - * Status code (404) indicating that the requested resource is not - * available. + * Status code (404) indicating that the requested resource is not available. */ public static final int SC_NOT_FOUND = HttpServletResponse.SC_NOT_FOUND; /** - * Status code (500) indicating an error inside the HTTP service - * which prevented it from fulfilling the request. + * Status code (500) indicating an error inside the HTTP service which prevented it from fulfilling the request. */ public static final int SC_INTERNAL_SERVER_ERROR = HttpServletResponse.SC_INTERNAL_SERVER_ERROR; /** - * Status code (501) indicating the HTTP service does not support - * the functionality needed to fulfill the request. + * Status code (501) indicating the HTTP service does not support the functionality needed to fulfill the request. */ public static final int SC_NOT_IMPLEMENTED = HttpServletResponse.SC_NOT_IMPLEMENTED; /** - * Status code (502) indicating that the HTTP server received an - * invalid response from a server it consulted when acting as a - * proxy or gateway. + * Status code (502) indicating that the HTTP server received an invalid response from a server it consulted when + * acting as a proxy or gateway. */ public static final int SC_BAD_GATEWAY = HttpServletResponse.SC_BAD_GATEWAY; /** - * Status code (503) indicating that the HTTP service is - * temporarily overloaded, and unable to handle the request. + * Status code (503) indicating that the HTTP service is temporarily overloaded, and unable to handle the request. */ public static final int SC_SERVICE_UNAVAILABLE = HttpServletResponse.SC_SERVICE_UNAVAILABLE; /** - * Status code (100) indicating the client may continue with - * its request. This interim response is used to inform the - * client that the initial part of the request has been - * received and has not yet been rejected by the server. + * Status code (100) indicating the client may continue with its request. This interim response is used to inform + * the client that the initial part of the request has been received and has not yet been rejected by the server. */ public static final int SC_CONTINUE = HttpServletResponse.SC_CONTINUE; /** - * Status code (405) indicating the method specified is not - * allowed for the resource. + * Status code (405) indicating the method specified is not allowed for the resource. */ public static final int SC_METHOD_NOT_ALLOWED = HttpServletResponse.SC_METHOD_NOT_ALLOWED; /** - * Status code (409) indicating that the request could not be - * completed due to a conflict with the current state of the - * resource. + * Status code (409) indicating that the request could not be completed due to a conflict with the current state of + * the resource. */ public static final int SC_CONFLICT = HttpServletResponse.SC_CONFLICT; /** - * Status code (412) indicating the precondition given in one - * or more of the request-header fields evaluated to false - * when it was tested on the server. + * Status code (412) indicating the precondition given in one or more of the request-header fields evaluated to + * false when it was tested on the server. */ public static final int SC_PRECONDITION_FAILED = HttpServletResponse.SC_PRECONDITION_FAILED; /** - * Status code (413) indicating the server is refusing to - * process a request because the request entity is larger + * Status code (413) indicating the server is refusing to process a request because the request entity is larger * than the server is willing or able to process. */ public static final int SC_REQUEST_TOO_LONG = HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE; /** - * Status code (415) indicating the server is refusing to service - * the request because the entity of the request is in a format - * not supported by the requested resource for the requested - * method. + * Status code (415) indicating the server is refusing to service the request because the entity of the request is + * in a format not supported by the requested resource for the requested method. */ public static final int SC_UNSUPPORTED_MEDIA_TYPE = HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE; @@ -2511,8 +2488,7 @@ class WebdavStatus { /** - * Status code (207) indicating that the response requires - * providing status for multiple independent operations. + * Status code (207) indicating that the response requires providing status for multiple independent operations. */ public static final int SC_MULTI_STATUS = 207; // This one collides with HTTP 1.1 @@ -2520,8 +2496,7 @@ class WebdavStatus { /** - * Status code (418) indicating the entity body submitted with - * the PATCH method was not understood by the resource. + * Status code (418) indicating the entity body submitted with the PATCH method was not understood by the resource. */ public static final int SC_UNPROCESSABLE_ENTITY = 418; // This one collides with HTTP 1.1 @@ -2529,9 +2504,8 @@ class WebdavStatus { /** - * Status code (419) indicating that the resource does not have - * sufficient space to record the state of the resource after the - * execution of this method. + * Status code (419) indicating that the resource does not have sufficient space to record the state of the resource + * after the execution of this method. */ public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419; // This one collides with HTTP 1.1 @@ -2539,19 +2513,15 @@ class WebdavStatus { /** - * Status code (420) indicating the method was not executed on - * a particular resource within its scope because some part of - * the method's execution failed causing the entire method to be - * aborted. + * Status code (420) indicating the method was not executed on a particular resource within its scope because some + * part of the method's execution failed causing the entire method to be aborted. */ public static final int SC_METHOD_FAILURE = 420; /** - * Status code (423) indicating the destination resource of a - * method is locked, and either the request did not contain a - * valid Lock-Info header, or the Lock-Info header identifies - * a lock held by another principal. + * Status code (423) indicating the destination resource of a method is locked, and either the request did not + * contain a valid Lock-Info header, or the Lock-Info header identifies a lock held by another principal. */ public static final int SC_LOCKED = 423; }