diff --git a/scm-web-api/src/main/java/org/apache/catalina/Globals.java b/scm-web-api/src/main/java/org/apache/catalina/Globals.java deleted file mode 100644 index 1aee408d07..0000000000 --- a/scm-web-api/src/main/java/org/apache/catalina/Globals.java +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package org.apache.catalina; - - -/** - * Global constants that are applicable to multiple packages within Catalina. - * - * @author Craig R. McClanahan - * @version $Id$ - */ - -public final class Globals { - - /** - * The servlet context attribute under which we store the alternate - * deployment descriptor for this web application - */ - public static final String ALT_DD_ATTR = - "org.apache.catalina.deploy.alt_dd"; - - /** - * The request attribute under which we store the array of X509Certificate - * objects representing the certificate chain presented by our client, - * if any. - */ - public static final String CERTIFICATES_ATTR = - "javax.servlet.request.X509Certificate"; - - /** - * The request attribute under which we store the name of the cipher suite - * being used on an SSL connection (as an object of type - * java.lang.String). - */ - public static final String CIPHER_SUITE_ATTR = - "javax.servlet.request.cipher_suite"; - - - /** - * The servlet context attribute under which we store the class loader - * used for loading servlets (as an object of type java.lang.ClassLoader). - */ - public static final String CLASS_LOADER_ATTR = - "org.apache.catalina.classloader"; - - /** - * Request dispatcher state. - */ - public static final String DISPATCHER_TYPE_ATTR = - "org.apache.catalina.core.DISPATCHER_TYPE"; - - /** - * Request dispatcher path. - */ - public static final String DISPATCHER_REQUEST_PATH_ATTR = - "org.apache.catalina.core.DISPATCHER_REQUEST_PATH"; - - /** - * The JNDI directory context which is associated with the context. This - * context can be used to manipulate static files. - */ - public static final String RESOURCES_ATTR = - "org.apache.catalina.resources"; - - - /** - * The servlet context attribute under which we store the class path - * for our application class loader (as an object of type String), - * delimited with the appropriate path delimiter for this platform. - */ - public static final String CLASS_PATH_ATTR = - "org.apache.catalina.jsp_classpath"; - - - /** - * The request attribute under which we forward a Java exception - * (as an object of type Throwable) to an error page. - */ - public static final String EXCEPTION_ATTR = - "javax.servlet.error.exception"; - - - /** - * The request attribute under which we forward the request URI - * (as an object of type String) of the page on which an error occurred. - */ - public static final String EXCEPTION_PAGE_ATTR = - "javax.servlet.error.request_uri"; - - - /** - * The request attribute under which we forward a Java exception type - * (as an object of type Class) to an error page. - */ - public static final String EXCEPTION_TYPE_ATTR = - "javax.servlet.error.exception_type"; - - - /** - * The request attribute under which we forward an HTTP status message - * (as an object of type STring) to an error page. - */ - public static final String ERROR_MESSAGE_ATTR = - "javax.servlet.error.message"; - - - /** - * The request attribute under which we expose the value of the - * <jsp-file> value associated with this servlet, - * if any. - */ - public static final String JSP_FILE_ATTR = - "org.apache.catalina.jsp_file"; - - - /** - * The request attribute under which we store the key size being used for - * this SSL connection (as an object of type java.lang.Integer). - */ - public static final String KEY_SIZE_ATTR = - "javax.servlet.request.key_size"; - - /** - * The request attribute under which we store the session id being used - * for this SSL connection (as an object of type java.lang.String). - */ - public static final String SSL_SESSION_ID_ATTR = - "javax.servlet.request.ssl_session"; - - - /** - * The request attribute key for the session manager. - * This one is a Tomcat extension to the Servlet spec. - */ - public static final String SSL_SESSION_MGR_ATTR = - "javax.servlet.request.ssl_session_mgr"; - - - /** - * The servlet context attribute under which the managed bean Registry - * will be stored for privileged contexts (if enabled). - */ - public static final String MBEAN_REGISTRY_ATTR = - "org.apache.catalina.Registry"; - - - /** - * The servlet context attribute under which the MBeanServer will be stored - * for privileged contexts (if enabled). - */ - public static final String MBEAN_SERVER_ATTR = - "org.apache.catalina.MBeanServer"; - - - /** - * The request attribute under which we store the servlet name on a - * named dispatcher request. - */ - public static final String NAMED_DISPATCHER_ATTR = - "org.apache.catalina.NAMED"; - - - /** - * Platform specific new line sequence. - */ - public static final String NEWLINE = System.getProperty("line.separator"); - - - /** - * The request attribute under which the request URI of the included - * servlet is stored on an included dispatcher request. - */ - public static final String INCLUDE_REQUEST_URI_ATTR = - "javax.servlet.include.request_uri"; - - - /** - * The request attribute under which the context path of the included - * servlet is stored on an included dispatcher request. - */ - public static final String INCLUDE_CONTEXT_PATH_ATTR = - "javax.servlet.include.context_path"; - - - /** - * The request attribute under which the path info of the included - * servlet is stored on an included dispatcher request. - */ - public static final String INCLUDE_PATH_INFO_ATTR = - "javax.servlet.include.path_info"; - - - /** - * The request attribute under which the servlet path of the included - * servlet is stored on an included dispatcher request. - */ - public static final String INCLUDE_SERVLET_PATH_ATTR = - "javax.servlet.include.servlet_path"; - - - /** - * The request attribute under which the query string of the included - * servlet is stored on an included dispatcher request. - */ - public static final String INCLUDE_QUERY_STRING_ATTR = - "javax.servlet.include.query_string"; - - - /** - * The request attribute under which the original request URI is stored - * on an forwarded dispatcher request. - */ - public static final String FORWARD_REQUEST_URI_ATTR = - "javax.servlet.forward.request_uri"; - - - /** - * The request attribute under which the original context path is stored - * on an forwarded dispatcher request. - */ - public static final String FORWARD_CONTEXT_PATH_ATTR = - "javax.servlet.forward.context_path"; - - - /** - * The request attribute under which the original path info is stored - * on an forwarded dispatcher request. - */ - public static final String FORWARD_PATH_INFO_ATTR = - "javax.servlet.forward.path_info"; - - - /** - * The request attribute under which the original servlet path is stored - * on an forwarded dispatcher request. - */ - public static final String FORWARD_SERVLET_PATH_ATTR = - "javax.servlet.forward.servlet_path"; - - - /** - * The request attribute under which the original query string is stored - * on an forwarded dispatcher request. - */ - public static final String FORWARD_QUERY_STRING_ATTR = - "javax.servlet.forward.query_string"; - - - /** - * The request attribute under which we forward a servlet name to - * an error page. - */ - public static final String SERVLET_NAME_ATTR = - "javax.servlet.error.servlet_name"; - - - /** - * The servlet context attribute under which we store a flag used - * to mark this request as having been processed by the SSIServlet. - * We do this because of the pathInfo mangling happening when using - * the CGIServlet in conjunction with the SSI servlet. (value stored - * as an object of type String) - */ - public static final String SSI_FLAG_ATTR = - "org.apache.catalina.ssi.SSIServlet"; - - - /** - * The request attribute under which we forward an HTTP status code - * (as an object of type Integer) to an error page. - */ - public static final String STATUS_CODE_ATTR = - "javax.servlet.error.status_code"; - - - /** - * The subject under which the AccessControlContext is running. - */ - public static final String SUBJECT_ATTR = - "javax.security.auth.subject"; - - - /** - * The servlet context attribute under which we record the set of - * welcome files (as an object of type String[]) for this application. - */ - public static final String WELCOME_FILES_ATTR = - "org.apache.catalina.WELCOME_FILES"; - - - /** - * The master flag which controls strict servlet specification - * compliance. - */ - public static final boolean STRICT_SERVLET_COMPLIANCE = - Boolean.valueOf(System.getProperty("org.apache.catalina.STRICT_SERVLET_COMPLIANCE", "false")).booleanValue(); - - - /** - * Has security been turned on? - */ - public static final boolean IS_SECURITY_ENABLED = - (System.getSecurityManager() != null); - - /** - * - */ - public static final String ASYNC_SUPPORTED_ATTR = - "org.apache.catalina.ASYNC_SUPPORTED"; - - - /** - * Default domain for MBeans if none can be determined - */ - public static final String DEFAULT_MBEAN_DOMAIN = "Catalina"; - - /** - * Name of the system property containing - * the tomcat product installation path - */ - public static final String CATALINA_HOME_PROP = "catalina.home"; - - /** - * Name of the system property containing - * the tomcat instance installation path - */ - public static final String CATALINA_BASE_PROP = "catalina.base"; -} diff --git a/scm-web-api/src/main/java/org/apache/catalina/servlets/CGIServlet.java b/scm-web-api/src/main/java/org/apache/catalina/servlets/CGIServlet.java deleted file mode 100644 index 77daa70051..0000000000 --- a/scm-web-api/src/main/java/org/apache/catalina/servlets/CGIServlet.java +++ /dev/null @@ -1,1918 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package org.apache.catalina.servlets; - -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.Date; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Locale; -import java.util.StringTokenizer; -import java.util.Vector; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -import org.apache.catalina.Globals; -import sonia.scm.util.Util; - - -/** - * 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). - *

- *

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

- *

- * - * <web-app>/cgi-bin/* - * - *

- *

- * then the following request: - *

- *

- * - * http://localhost:8080/<web-app>/cgi-bin/dir1/script/pathinfo1 - * - *

- *

- * would result in the execution of the script - *

- *

- * - * <web-app-root>/WEB-INF/cgi/dir1/script - * - *

- *

- * 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. - *

- *

- * 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). - *

- *

- * 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 [dev@tomcat.apache.org] - * with amendments. - * - *

- *

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

- *  AUTH_TYPE
- *  CONTENT_LENGTH
- *  CONTENT_TYPE
- *  GATEWAY_INTERFACE
- *  PATH_INFO
- *  PATH_TRANSLATED
- *  QUERY_STRING
- *  REMOTE_ADDR
- *  REMOTE_HOST
- *  REMOTE_IDENT
- *  REMOTE_USER
- *  REQUEST_METHOD
- *  SCRIPT_NAME
- *  SERVER_NAME
- *  SERVER_PORT
- *  SERVER_PROTOCOL
- *  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].) - *

- * [end excerpt] - * - *

- *

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 of your webapps' cgi-handling servlet - * to be - *

- *

- * - * 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. - *

- *

- * 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 is located at - * http://cgi-spec.golux.com. - * - *

- *

- *

TODO:

- * - *

- * - * @author Martin T Dengler [root@martindengler.com] - * @author Amy Roh - * @version $Id$ - * @since Tomcat 4.0 - * - */ - - -public final class CGIServlet extends HttpServlet { - - /* some vars below copied from Craig R. McClanahan's InvokerServlet */ - - private static final long serialVersionUID = 1L; - - /** the debugging detail level for this servlet. */ - private int debug = 0; - - /** - * The CGI search path will start at - * webAppRootDir + File.separator + cgiPathPrefix - * (or webAppRootDir alone if cgiPathPrefix is - * null) - */ - private String cgiPathPrefix = null; - - /** the executable to use with the script */ - private String cgiExecutable = "perl"; - - /** the encoding to use for parameters */ - private String parameterEncoding = - System.getProperty("file.encoding", "UTF-8"); - - /** - * The time (in milliseconds) to wait for the reading of stderr to complete - * before terminating the CGI process. - */ - private long stderrTimeout = 2000; - - /** object used to ensure multiple threads don't try to expand same file */ - static Object expandFileLock = new Object(); - - /** the shell environment variables to be passed to the CGI script */ - static Hashtable shellEnv = new Hashtable(); - - /** - * Sets instance variables. - *

- * Modified from Craig R. McClanahan's InvokerServlet - *

- * - * @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 - */ - @Override - public void init(ServletConfig config) throws ServletException { - - super.init(config); - - // Set our properties from the initialization parameters - if (getServletConfig().getInitParameter("debug") != null) - debug = Integer.parseInt(getServletConfig().getInitParameter("debug")); - cgiPathPrefix = getServletConfig().getInitParameter("cgiPathPrefix"); - boolean passShellEnvironment = - Boolean.valueOf(getServletConfig().getInitParameter("passShellEnvironment")).booleanValue(); - - if (passShellEnvironment) { - shellEnv.putAll(System.getenv()); - } - - if (getServletConfig().getInitParameter("executable") != null) { - cgiExecutable = getServletConfig().getInitParameter("executable"); - } - - if (getServletConfig().getInitParameter("parameterEncoding") != null) { - parameterEncoding = getServletConfig().getInitParameter("parameterEncoding"); - } - - if (getServletConfig().getInitParameter("stderrTimeout") != null) { - stderrTimeout = Long.parseLong(getServletConfig().getInitParameter( - "stderrTimeout")); - } - - } - - public void setCgiExecutable(String cgiExecutable) - { - this.cgiExecutable = cgiExecutable; - } - - /** - * Prints out important Servlet API and container information - * - *

- * Copied from SnoopAllServlet by Craig R. McClanahan - *

- * - * @param out ServletOutputStream as target of the information - * @param req HttpServletRequest object used as source of information - * @param res HttpServletResponse object currently not used but could - * provide future information - * - * @exception IOException if a write operation exception occurs - * - */ - protected void printServletEnvironment(ServletOutputStream out, - HttpServletRequest req, - @SuppressWarnings("unused") HttpServletResponse res) - throws IOException { - - // Document the properties from ServletRequest - out.println("

ServletRequest Properties

"); - out.println(""); - out.println("
"); - - // Document the properties from HttpServletRequest - out.println("

HttpServletRequest Properties

"); - out.println(""); - out.println("
"); - - // Document the servlet request attributes - out.println("

ServletRequest Attributes

"); - out.println(""); - out.println("
"); - - // Process the current session (if there is one) - HttpSession session = req.getSession(false); - if (session != null) { - - // Document the session properties - out.println("

HttpSession Properties

"); - out.println(""); - out.println("
"); - - // Document the session attributes - out.println("

HttpSession Attributes

"); - out.println(""); - out.println("
"); - - } - - // Document the servlet configuration properties - out.println("

ServletConfig Properties

"); - out.println(""); - out.println("
"); - - // Document the servlet configuration initialization parameters - out.println("

ServletConfig Initialization Parameters

"); - out.println(""); - out.println("
"); - - // Document the servlet context properties - out.println("

ServletContext Properties

"); - out.println(""); - out.println("
"); - - // Document the servlet context initialization parameters - out.println("

ServletContext Initialization Parameters

"); - out.println(""); - out.println("
"); - - // Document the servlet context attributes - out.println("

ServletContext Attributes

"); - out.println(""); - out.println("
"); - - - } - - - /** - * Provides CGI Gateway service -- delegates to doGet - * - * @param req HttpServletRequest passed in by servlet container - * @param res HttpServletResponse passed in by servlet container - * - * @exception ServletException if a servlet-specific exception occurs - * @exception IOException if a read/write exception occurs - * - * @see javax.servlet.http.HttpServlet - * - */ - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse res) - throws IOException, ServletException { - doGet(req, res); - } - - - /** - * Provides CGI Gateway service - * - * @param req HttpServletRequest passed in by servlet container - * @param res HttpServletResponse passed in by servlet container - * - * @exception ServletException if a servlet-specific exception occurs - * @exception IOException if a read/write exception occurs - * - * @see javax.servlet.http.HttpServlet - * - */ - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse res) - throws ServletException, IOException { - - CGIEnvironment cgiEnv = new CGIEnvironment(req, getServletContext()); - - if (cgiEnv.isValid()) { - CGIRunner cgi = new CGIRunner(cgiEnv.getCommand(), - cgiEnv.getEnvironment(), - cgiEnv.getWorkingDirectory(), - cgiEnv.getParameters()); - //if POST, we need to cgi.setInput - //REMIND: how does this interact with Servlet API 2.3's Filters?! - if ("POST".equals(req.getMethod())) { - cgi.setInput(req.getInputStream()); - } - cgi.setResponse(res); - cgi.run(); - } - - if (!cgiEnv.isValid()) { - res.setStatus(404); - } - - if (debug >= 10) { - - ServletOutputStream out = res.getOutputStream(); - out.println("$Name$"); - out.println("$Header$

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

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

"); - out.println("

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

"); - out.println("

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

"); - - } - - printServletEnvironment(out, req, res); - - out.println(""); - - } - - - } //doGet - - - /** - * Encapsulates the CGI environment and rules to derive - * that environment from the servlet container and request information. - * - *

- *

- * - * @version $Id$ - * @since Tomcat 4.0 - * - */ - protected class CGIEnvironment { - - - /** context of the enclosing servlet */ - private ServletContext context = null; - - /** context path of enclosing servlet */ - private String contextPath = null; - - /** servlet URI of the enclosing servlet */ - private String servletPath = null; - - /** pathInfo for the current request */ - private String pathInfo = null; - - /** real file system directory of the enclosing servlet's web app */ - private String webAppRootDir = null; - - /** tempdir for context - used to expand scripts in unexpanded wars */ - private File tmpDir = null; - - /** derived cgi environment */ - private Hashtable env = null; - - /** cgi command to be invoked */ - private String command = null; - - /** cgi command's desired working directory */ - private File workingDirectory = null; - - /** cgi command's command line parameters */ - private ArrayList cmdLineParameters = new ArrayList(); - - /** whether or not this object is valid or not */ - private boolean valid = false; - - - /** - * 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 - * - */ - protected CGIEnvironment(HttpServletRequest req, - ServletContext context) throws IOException { - setupFromContext(context); - setupFromRequest(req); - - this.valid = setCGIEnvironment(req); - - if (this.valid) { - workingDirectory = new File(command.substring(0, - command.lastIndexOf(File.separator))); - } - - } - - - /** - * Uses the ServletContext to set some CGI variables - * - * @param context ServletContext for information provided by the - * Servlet API - */ - protected void setupFromContext(ServletContext context) { - this.context = context; - this.webAppRootDir = context.getRealPath("/"); - this.tmpDir = (File) context.getAttribute(System.getProperty("java.io.tmpdir")); - } - - - /** - * Uses the HttpServletRequest to set most CGI variables - * - * @param req HttpServletRequest for information provided by - * the Servlet API - * @throws UnsupportedEncodingException - */ - protected void setupFromRequest(HttpServletRequest req) - throws UnsupportedEncodingException { - - boolean isIncluded = false; - - // Look to see if this request is an include - if (req.getAttribute(Globals.INCLUDE_REQUEST_URI_ATTR) != null) { - isIncluded = true; - } - if (isIncluded) { - this.contextPath = (String) req.getAttribute( - Globals.INCLUDE_CONTEXT_PATH_ATTR); - this.servletPath = (String) req.getAttribute( - Globals.INCLUDE_SERVLET_PATH_ATTR); - this.pathInfo = (String) req.getAttribute( - Globals.INCLUDE_PATH_INFO_ATTR); - } else { - this.contextPath = req.getContextPath(); - this.servletPath = req.getServletPath(); - this.pathInfo = req.getPathInfo(); - } - // If getPathInfo() returns null, must be using extension mapping - // In this case, pathInfo should be same as servletPath - if (this.pathInfo == null) { - this.pathInfo = this.servletPath; - } - - // If the request method is GET, POST or HEAD and the query string - // does not contain an unencoded "=" this is an indexed query. - // The parsed query string becomes the command line parameters - // for the cgi command. - if (req.getMethod().equals("GET") - || req.getMethod().equals("POST") - || req.getMethod().equals("HEAD")) { - String qs; - if (isIncluded) { - qs = (String) req.getAttribute( - Globals.INCLUDE_QUERY_STRING_ATTR); - } else { - qs = req.getQueryString(); - } - if (qs != null && qs.indexOf("=") == -1) { - StringTokenizer qsTokens = new StringTokenizer(qs, "+"); - while ( qsTokens.hasMoreTokens() ) { - cmdLineParameters.add(URLDecoder.decode(qsTokens.nextToken(), - parameterEncoding)); - } - } - } - } - - - /** - * Resolves core information about the cgi script. - * - *

- * Example URI: - *

 /servlet/cgigateway/dir1/realCGIscript/pathinfo1 
- *
    - *
  • path = $CATALINA_HOME/mywebapp/dir1/realCGIscript - *
  • scriptName = /servlet/cgigateway/dir1/realCGIscript - *
  • cgiName = /dir1/realCGIscript - *
  • 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. - *

- *

- * 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 - * - *

- * - * @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 - *
- * - * @since Tomcat 4.0 - */ - protected String[] findCGI(String pathInfo, String webAppRootDir, - String contextPath, String servletPath, - String cgiPathPrefix) { - String path = null; - String name = null; - String scriptname = null; - String cginame = ""; - - if ((webAppRootDir != null) - && (webAppRootDir.lastIndexOf(File.separator) == - (webAppRootDir.length() - 1))) { - //strip the trailing "/" from the webAppRootDir - webAppRootDir = - webAppRootDir.substring(0, (webAppRootDir.length() - 1)); - } - - if (cgiPathPrefix != null) { - webAppRootDir = webAppRootDir + File.separator - + cgiPathPrefix; - } - - if (debug >= 2) { - log("findCGI: path=" + pathInfo + ", " + webAppRootDir); - } - - File currentLocation = new File(webAppRootDir); - StringTokenizer dirWalker = - new StringTokenizer(pathInfo, "/"); - if (debug >= 3) { - log("findCGI: currentLoc=" + currentLocation); - } - while (!currentLocation.isFile() && dirWalker.hasMoreElements()) { - if (debug >= 3) { - log("findCGI: currentLoc=" + currentLocation); - } - String nextElement = (String) dirWalker.nextElement(); - currentLocation = new File(currentLocation, nextElement); - cginame = cginame + "/" + nextElement; - } - if (!currentLocation.isFile()) { - return new String[] { null, null, null, null }; - } - - if (debug >= 2) { - log("findCGI: FOUND cgi at " + currentLocation); - } - path = currentLocation.getAbsolutePath(); - name = currentLocation.getName(); - - if (".".equals(contextPath)) { - scriptname = servletPath; - } else { - scriptname = contextPath + servletPath; - } - if (!servletPath.equals(cginame)) { - scriptname = scriptname + cginame; - } - - if (debug >= 1) { - log("findCGI calc: name=" + name + ", path=" + path - + ", scriptname=" + scriptname + ", cginame=" + cginame); - } - return new String[] { path, scriptname, cginame, name }; - } - - /** - * 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 - * - * @return true if environment was set OK, false if there - * was a problem and no environment was set - */ - 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) - */ - - Hashtable envp = new Hashtable(); - - // Add the shell environment variables (if any) - envp.putAll(shellEnv); - - // Add the CGI environment variables - String sPathInfoOrig = null; - String sPathInfoCGI = null; - String sPathTranslatedCGI = null; - String sCGIFullPath = null; - String sCGIScriptName = null; - String sCGIFullName = null; - String sCGIName = null; - String[] sCGINames; - - - sPathInfoOrig = this.pathInfo; - sPathInfoOrig = sPathInfoOrig == null ? "" : sPathInfoOrig; - - if (webAppRootDir == null ) { - // The app has not been deployed in exploded form - webAppRootDir = tmpDir.toString(); - expandCGIScript(); - } - - sCGINames = findCGI(sPathInfoOrig, - webAppRootDir, - contextPath, - servletPath, - cgiPathPrefix); - - sCGIFullPath = sCGINames[0]; - sCGIScriptName = sCGINames[1]; - sCGIFullName = sCGINames[2]; - sCGIName = sCGINames[3]; - - if (sCGIFullPath == null - || sCGIScriptName == null - || sCGIFullName == null - || sCGIName == null) { - return false; - } - - envp.put("SERVER_SOFTWARE", "TOMCAT"); - - envp.put("SERVER_NAME", nullsToBlanks(req.getServerName())); - - envp.put("GATEWAY_INTERFACE", "CGI/1.1"); - - envp.put("SERVER_PROTOCOL", nullsToBlanks(req.getProtocol())); - - int port = req.getServerPort(); - Integer iPort = - (port == 0 ? Integer.valueOf(-1) : Integer.valueOf(port)); - envp.put("SERVER_PORT", iPort.toString()); - - envp.put("REQUEST_METHOD", nullsToBlanks(req.getMethod())); - - envp.put("REQUEST_URI", nullsToBlanks(req.getRequestURI())); - - - /*- - * PATH_INFO should be determined by using sCGIFullName: - * 1) Let sCGIFullName not end in a "/" (see method findCGI) - * 2) Let sCGIFullName equal the pathInfo fragment which - * corresponds to the actual cgi script. - * 3) Thus, PATH_INFO = request.getPathInfo().substring( - * sCGIFullName.length()) - * - * (see method findCGI, where the real work is done) - * - */ - if (pathInfo == null - || (pathInfo.substring(sCGIFullName.length()).length() <= 0)) { - sPathInfoCGI = ""; - } else { - sPathInfoCGI = pathInfo.substring(sCGIFullName.length()); - } - envp.put("PATH_INFO", sPathInfoCGI); - - - /*- - * PATH_TRANSLATED must be determined after PATH_INFO (and the - * implied real cgi-script) has been taken into account. - * - * The following example demonstrates: - * - * servlet info = /servlet/cgigw/dir1/dir2/cgi1/trans1/trans2 - * cgifullpath = /servlet/cgigw/dir1/dir2/cgi1 - * path_info = /trans1/trans2 - * webAppRootDir = servletContext.getRealPath("/") - * - * path_translated = servletContext.getRealPath("/trans1/trans2") - * - * That is, PATH_TRANSLATED = webAppRootDir + sPathInfoCGI - * (unless sPathInfoCGI is null or blank, then the CGI - * specification dictates that the PATH_TRANSLATED metavariable - * SHOULD NOT be defined. - * - */ - if (sPathInfoCGI != null && !("".equals(sPathInfoCGI))) { - sPathTranslatedCGI = context.getRealPath(sPathInfoCGI); - } - if (sPathTranslatedCGI == null || "".equals(sPathTranslatedCGI)) { - //NOOP - } else { - envp.put("PATH_TRANSLATED", nullsToBlanks(sPathTranslatedCGI)); - } - - - envp.put("SCRIPT_NAME", nullsToBlanks(sCGIScriptName)); - - envp.put("QUERY_STRING", nullsToBlanks(req.getQueryString())); - - envp.put("REMOTE_HOST", nullsToBlanks(req.getRemoteHost())); - - envp.put("REMOTE_ADDR", nullsToBlanks(req.getRemoteAddr())); - - envp.put("AUTH_TYPE", nullsToBlanks(req.getAuthType())); - - envp.put("REMOTE_USER", nullsToBlanks(req.getRemoteUser())); - - 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. - */ - int contentLength = req.getContentLength(); - String sContentLength = (contentLength <= 0 ? "" : - (Integer.valueOf(contentLength)).toString()); - envp.put("CONTENT_LENGTH", sContentLength); - - - Enumeration headers = req.getHeaderNames(); - String header = null; - 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 - if ("AUTHORIZATION".equalsIgnoreCase(header) || - "PROXY_AUTHORIZATION".equalsIgnoreCase(header)) { - //NOOP per CGI specification section 11.2 - } else { - envp.put("HTTP_" + header.replace('-', '_'), - req.getHeader(header)); - } - } - - File fCGIFullPath = new File(sCGIFullPath); - command = fCGIFullPath.getCanonicalPath(); - - envp.put("X_TOMCAT_SCRIPT_PATH", command); //for kicks - - envp.put("SCRIPT_FILENAME", command); //for PHP - - this.env = envp; - - return true; - - } - - /** - * 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(); - StringBuilder destPath = new StringBuilder(); - InputStream is = null; - - // paths depend on mapping - if (cgiPathPrefix == null ) { - srcPath.append(pathInfo); - is = context.getResourceAsStream(srcPath.toString()); - destPath.append(tmpDir); - destPath.append(pathInfo); - } else { - // essentially same search algorithm as findCGI() - srcPath.append(cgiPathPrefix); - StringTokenizer pathWalker = - new StringTokenizer (pathInfo, "/"); - // start with first element - while (pathWalker.hasMoreElements() && (is == null)) { - srcPath.append("/"); - srcPath.append(pathWalker.nextElement()); - is = context.getResourceAsStream(srcPath.toString()); - } - destPath.append(tmpDir); - destPath.append("/"); - destPath.append(srcPath); - } - - if (is == null) { - // didn't find anything, give up now - if (debug >= 2) { - log("expandCGIScript: source '" + srcPath + "' not found"); - } - return; - } - - File f = new File(destPath.toString()); - if (f.exists()) { - // Don't need to expand if it already exists - return; - } - - // create directories - String dirPath = destPath.toString().substring( - 0,destPath.toString().lastIndexOf("/")); - File dir = new File(dirPath); - if (!dir.mkdirs() && debug >= 2) { - log("expandCGIScript: failed to create directories for '" + - dir.getAbsolutePath() + "'"); - return; - } - - try { - synchronized (expandFileLock) { - // make sure file doesn't exist - if (f.exists()) { - return; - } - - // create file - if (!f.createNewFile()) { - return; - } - FileOutputStream fos = new FileOutputStream(f); - - // copy data - Util.copy(is, fos); - is.close(); - fos.close(); - if (debug >= 2) { - log("expandCGIScript: expanded '" + srcPath + "' to '" + destPath + "'"); - } - } - } catch (IOException ioe) { - // delete in case file is corrupted - if (f.exists()) { - if (!f.delete() && debug >= 2) { - log("expandCGIScript: failed to delete '" + - f.getAbsolutePath() + "'"); - } - } - } - } - - - /** - * Print important CGI environment information in a easy-to-read HTML - * table - * - * @return HTML string containing CGI environment info - * - */ - @Override - public String toString() { - - StringBuilder sb = new StringBuilder(); - - sb.append(""); - - sb.append(""); - - sb.append(""); - - sb.append(""); - - if (isValid()) { - Enumeration envk = env.keys(); - while (envk.hasMoreElements()) { - String s = envk.nextElement(); - sb.append(""); - } - } - - sb.append(""); - - sb.append(""); - - sb.append(""); - - sb.append(""); - - sb.append("
"); - sb.append("CGIEnvironment Info
Debug Level"); - sb.append(debug); - sb.append("
Validity:"); - sb.append(isValid()); - sb.append("
"); - sb.append(s); - sb.append(""); - sb.append(blanksToString(env.get(s), - "[will be set to blank]")); - sb.append("

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

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

"); - } - sb.append("

end."); - - return sb.toString(); - } - - - /** - * Gets derived command string - * - * @return command string - * - */ - protected String getCommand() { - return command; - } - - - /** - * Gets derived CGI working directory - * - * @return working directory - * - */ - protected File getWorkingDirectory() { - return workingDirectory; - } - - - /** - * Gets derived CGI environment - * - * @return CGI environment - * - */ - protected Hashtable getEnvironment() { - return env; - } - - - /** - * Gets derived CGI query parameters - * - * @return CGI query parameters - * - */ - protected ArrayList getParameters() { - return cmdLineParameters; - } - - - /** - * Gets validity status - * - * @return true if this environment is valid, false - * otherwise - * - */ - protected boolean isValid() { - return valid; - } - - - /** - * 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 - */ - protected String nullsToBlanks(String s) { - return nullsToString(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 - */ - protected String nullsToString(String couldBeNull, - String subForNulls) { - return (couldBeNull == null ? subForNulls : 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 ("") - */ - protected String blanksToString(String couldBeBlank, - String subForBlanks) { - return (("".equals(couldBeBlank) || couldBeBlank == null) - ? subForBlanks - : couldBeBlank); - } - - - } //class CGIEnvironment - - - /** - * 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. - * - *

- *

- * - * 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. - *

- * - * @version $Id$ - */ - - protected class CGIRunner { - - /** script/command to be executed */ - private String command = null; - - /** environment used when invoking the cgi script */ - private Hashtable env = null; - - /** working directory used when invoking the cgi script */ - private File wd = null; - - /** command line parameters to be passed to the invoked script */ - private ArrayList params = null; - - /** stdin to be passed to cgi script */ - private InputStream stdin = null; - - /** response object used to set headers & get output stream */ - private HttpServletResponse response = null; - - /** boolean tracking whether this object has enough info to run() */ - private boolean readyToRun = false; - - - /** - * 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 Hashtable 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, Hashtable env, - File wd, ArrayList params) { - this.command = command; - this.env = env; - this.wd = wd; - this.params = params; - updateReadyStatus(); - } - - - /** - * Checks & sets ready status - */ - protected void updateReadyStatus() { - if (command != null - && env != null - && wd != null - && params != null - && response != null) { - readyToRun = true; - } else { - readyToRun = false; - } - } - - - /** - * Gets ready status - * - * @return false if not ready (run will throw - * an exception), true if ready - */ - protected boolean isReady() { - return readyToRun; - } - - - /** - * 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; - updateReadyStatus(); - } - - - /** - * Sets standard input to be passed on to the invoked cgi script - * - * @param stdin InputStream to be used - * - */ - protected void setInput(InputStream stdin) { - this.stdin = stdin; - updateReadyStatus(); - } - - - /** - * Converts a Hashtable to a String array by converting each - * key/value pair in the Hashtable to a String in the form - * "key=value" (hashkey + "=" + hash.get(hashkey).toString()) - * - * @param h Hashtable to convert - * - * @return converted string array - * - * @exception NullPointerException if a hash key has a null value - * - */ - protected String[] hashToStringArray(Hashtable h) - throws NullPointerException { - Vector v = new Vector(); - Enumeration e = h.keys(); - while (e.hasMoreElements()) { - String k = e.nextElement(); - v.add(k + "=" + h.get(k).toString()); - } - String[] strArr = new String[v.size()]; - v.copyInto(strArr); - return strArr; - } - - - /** - * Executes a CGI script with the desired environment, current working - * directory, and input/output streams - * - *

- * This implements the following CGI specification recommedations: - *

    - *
  • Servers SHOULD provide the "query" component of - * the script-URI as command-line arguments to scripts if it - * 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; - *
    • 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) - */ - protected void run() throws IOException { - - /* - * REMIND: this method feels too big; should it be re-written? - */ - - if (!isReady()) { - throw new IOException(this.getClass().getName() - + ": not ready to run."); - } - - if (debug >= 1 ) { - log("runCGI(envp=[" + env + "], command=" + command + ")"); - } - - if ((command.indexOf(File.separator + "." + File.separator) >= 0) - || (command.indexOf(File.separator + "..") >= 0) - || (command.indexOf(".." + File.separator) >= 0)) { - 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 - */ - Runtime rt = null; - BufferedReader cgiHeaderReader = null; - InputStream cgiOutput = null; - BufferedReader commandsStdErr = null; - Thread errReaderThread = null; - BufferedOutputStream commandsStdIn = null; - Process proc = null; - int bufRead = -1; - - //create query arguments - StringBuilder cmdAndArgs = new StringBuilder(); - if (command.indexOf(" ") < 0) { - cmdAndArgs.append(command); - } else { - // Spaces used as delimiter, so need to use quotes - cmdAndArgs.append("\""); - cmdAndArgs.append(command); - cmdAndArgs.append("\""); - } - - for (int i=0; i < params.size(); i++) { - cmdAndArgs.append(" "); - String param = params.get(i); - if (param.indexOf(" ") < 0) { - cmdAndArgs.append(param); - } else { - // Spaces used as delimiter, so need to use quotes - cmdAndArgs.append("\""); - cmdAndArgs.append(param); - cmdAndArgs.append("\""); - } - } - - StringBuilder command = new StringBuilder(cgiExecutable); - command.append(" "); - command.append(cmdAndArgs.toString()); - cmdAndArgs = command; - - try { - rt = Runtime.getRuntime(); - proc = rt.exec(cmdAndArgs.toString(), hashToStringArray(env), wd); - - String sContentLength = env.get("CONTENT_LENGTH"); - - if(!"".equals(sContentLength)) { - commandsStdIn = new BufferedOutputStream(proc.getOutputStream()); - Util.copy(stdin, commandsStdIn); - commandsStdIn.flush(); - commandsStdIn.close(); - } - - /* 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 ; - - errReaderThread = new Thread() { - @Override - public void run () { - sendToLog(stdErrRdr) ; - } - }; - errReaderThread.start(); - - InputStream cgiHeaderStream = - new HTTPHeaderInputStream(proc.getInputStream()); - cgiHeaderReader = - new BufferedReader(new InputStreamReader(cgiHeaderStream)); - - while (isRunning) { - try { - //set headers - String line = null; - while (((line = cgiHeaderReader.readLine()) != null) - && !("".equals(line))) { - if (debug >= 2) { - log("runCGI: addHeader(\"" + line + "\")"); - } - if (line.startsWith("HTTP")) { - response.setStatus(getSCFromHttpStatusLine(line)); - } else if (line.indexOf(":") >= 0) { - String header = - line.substring(0, line.indexOf(":")).trim(); - String value = - line.substring(line.indexOf(":") + 1).trim(); - if (header.equalsIgnoreCase("status")) { - response.setStatus(getSCFromCGIStatusHeader(value)); - } else { - response.addHeader(header , value); - } - } else { - log("runCGI: bad header line \"" + line + "\""); - } - } - - //write output - byte[] bBuf = new byte[2048]; - - OutputStream out = response.getOutputStream(); - cgiOutput = proc.getInputStream(); - - try { - while ((bufRead = cgiOutput.read(bBuf)) != -1) { - if (debug >= 4) { - log("runCGI: output " + bufRead + - " bytes of data"); - } - out.write(bBuf, 0, bufRead); - } - } finally { - // Attempt to consume any leftover byte if something bad happens, - // such as a socket disconnect on the servlet side; otherwise, the - // external process could hang - if (bufRead != -1) { - while ((bufRead = cgiOutput.read(bBuf)) != -1) { - // NOOP - just read the data - } - } - } - - proc.exitValue(); // Throws exception if alive - - isRunning = false; - - } catch (IllegalThreadStateException e) { - try { - Thread.sleep(500); - } catch (InterruptedException ignored) { - // Ignore - } - } - } //replacement for Process.waitFor() - - } - catch (IOException e){ - log ("Caught exception " + e); - throw e; - } - finally{ - // Close the header reader - if (cgiHeaderReader != null) { - try { - cgiHeaderReader.close(); - } catch (IOException ioe) { - log ("Exception closing header reader " + ioe); - } - } - // Close the output stream if used - if (cgiOutput != null) { - try { - cgiOutput.close(); - } catch (IOException ioe) { - log ("Exception closing output stream " + ioe); - } - } - // Make sure the error stream reader has finished - if (errReaderThread != null) { - try { - errReaderThread.join(stderrTimeout); - } catch (InterruptedException e) { - log ("Interupted waiting for stderr reader thread"); - } - } - if (debug > 4) { - log ("Running finally block"); - } - if (proc != null){ - proc.destroy(); - proc = null; - } - } - } - - /** - * 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. - */ - private int getSCFromHttpStatusLine(String line) { - int statusStart = line.indexOf(' ') + 1; - - if (statusStart < 1 || line.length() < statusStart + 3) { - // Not a valid HTTP Status-Line - log ("runCGI: invalid HTTP Status-Line:" + line); - return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; - } - - String status = line.substring(statusStart, statusStart + 3); - - int statusCode; - try { - statusCode = Integer.parseInt(status); - } catch (NumberFormatException nfe) { - // Not a valid status code - log ("runCGI: invalid status code:" + status); - return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; - } - - return statusCode; - } - - /** - * Parses the CGI Status Header value and extracts the status code. - * - * @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. - */ - private int getSCFromCGIStatusHeader(String value) { - if (value.length() < 3) { - // Not a valid status value - log ("runCGI: invalid status value:" + value); - return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; - } - - String status = value.substring(0, 3); - - int statusCode; - try { - statusCode = Integer.parseInt(status); - } catch (NumberFormatException nfe) { - // Not a valid status code - log ("runCGI: invalid status code:" + status); - return HttpServletResponse.SC_INTERNAL_SERVER_ERROR; - } - - return statusCode; - } - - private void sendToLog(BufferedReader rdr) { - String line = null; - int lineCount = 0 ; - try { - while ((line = rdr.readLine()) != null) { - log("runCGI (stderr):" + line) ; - lineCount++ ; - } - } catch (IOException e) { - log("sendToLog error", e) ; - } finally { - try { - rdr.close() ; - } catch (IOException ce) { - log("sendToLog error", ce) ; - } - } - if ( lineCount > 0 && debug > 2) { - log("runCGI: " + lineCount + " lines received on stderr") ; - } - } - } //class CGIRunner - - /** - * This is an input stream specifically for reading HTTP headers. It reads - * upto 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; - private static final int STATE_FIRST_CR = 1; - private static final int STATE_FIRST_LF = 2; - private static final int STATE_SECOND_CR = 3; - private static final int STATE_HEADER_END = 4; - - private InputStream input; - private int state; - - HTTPHeaderInputStream(InputStream theInput) { - input = theInput; - state = STATE_CHARACTER; - } - - /** - * @see java.io.InputStream#read() - */ - @Override - public int read() throws IOException { - if (state == STATE_HEADER_END) { - return -1; - } - - int i = input.read(); - - // Update the state - // State machine looks like this - // - // -------->-------- - // | (CR) | - // | | - // CR1--->--- | - // | | | - // ^(CR) |(LF) | - // | | | - // CHAR--->--LF1--->--EOH - // (LF) | (LF) | - // |(CR) ^(LF) - // | | - // (CR2)-->--- - - if (i == 10) { - // LF - switch(state) { - case STATE_CHARACTER: - state = STATE_FIRST_LF; - break; - case STATE_FIRST_CR: - state = STATE_FIRST_LF; - break; - case STATE_FIRST_LF: - case STATE_SECOND_CR: - state = STATE_HEADER_END; - break; - } - - } else if (i == 13) { - // CR - switch(state) { - case STATE_CHARACTER: - state = STATE_FIRST_CR; - break; - case STATE_FIRST_CR: - state = STATE_HEADER_END; - break; - case STATE_FIRST_LF: - state = STATE_SECOND_CR; - break; - } - - } else { - state = STATE_CHARACTER; - } - - return i; - } - } // class HTTPHeaderInputStream - -} //class CGIServlet