/container/catalina/src/share/org/apache/catalina/servlets/CGIServlet.java
Java | 1982 lines | 959 code | 282 blank | 741 comment | 180 complexity | 4cdfbc68fdaf41eea4852fdb7d156f50 MD5 | raw file
Possible License(s): Apache-2.0, CPL-1.0
Large files files are truncated, but you can click here to view the full file
- /*
- * 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.UnavailableException;
- 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 org.apache.catalina.util.IOTools;
- /**
- * 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.
- *
- * <p>
- * <i>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).</i>
- * </p>
- * <p>
- *
- * <b>Example</b>:<br>
- * If an instance of this servlet was mapped (using
- * <code><web-app>/WEB-INF/web.xml</code>) to:
- * </p>
- * <p>
- * <code>
- * <web-app>/cgi-bin/*
- * </code>
- * </p>
- * <p>
- * then the following request:
- * </p>
- * <p>
- * <code>
- * http://localhost:8080/<web-app>/cgi-bin/dir1/script/pathinfo1
- * </code>
- * </p>
- * <p>
- * would result in the execution of the script
- * </p>
- * <p>
- * <code>
- * <web-app-root>/WEB-INF/cgi/dir1/script
- * </code>
- * </p>
- * <p>
- * with the script's <code>PATH_INFO</code> set to <code>/pathinfo1</code>.
- * </p>
- * <p>
- * Recommendation: House all your CGI scripts under
- * <code><webapp>/WEB-INF/cgi</code>. 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.
- * </p>
- * <p>
- * The default CGI location is mentioned above. You have the flexibility to
- * put CGIs wherever you want, however:
- * </p>
- * <p>
- * The CGI search path will start at
- * webAppRootDir + File.separator + cgiPathPrefix
- * (or webAppRootDir alone if cgiPathPrefix is
- * null).
- * </p>
- * <p>
- * cgiPathPrefix is defined by setting
- * this servlet's cgiPathPrefix init parameter
- * </p>
- *
- * <p>
- *
- * <B>CGI Specification</B>:<br> derived from
- * <a href="http://cgi-spec.golux.com">http://cgi-spec.golux.com</a>.
- * 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
- * <a href="http://tomcat.apache.org/lists.html#tomcat-dev">Tomcat group [dev@tomcat.apache.org]</a>
- * with amendments.
- *
- * </p>
- * <p>
- *
- * <b>Canonical metavariables</b>:<br>
- * The CGI specification defines the following canonical metavariables:
- * <br>
- * [excerpt from CGI specification]
- * <PRE>
- * 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
- * </PRE>
- * <p>
- * Metavariables with names beginning with the protocol name (<EM>e.g.</EM>,
- * "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].)
- * </p>
- * [end excerpt]
- *
- * </p>
- * <h2> Implementation notes</h2>
- * <p>
- *
- * <b>standard input handling</b>: 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).
- * <br>
- * If you find your cgi scripts are timing out receiving input, you can set
- * the init parameter <code></code> of your webapps' cgi-handling servlet
- * to be
- * </p>
- * <p>
- *
- * <b>Metavariable Values</b>: According to the CGI specificion,
- * 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.
- *
- * </p>
- * <p>
- *
- * <b>NPH -- Non-parsed-header implementation</b>: This implementation does
- * not support the CGI NPH concept, whereby server ensures that the data
- * supplied to the script are preceisely as supplied by the client and
- * unaltered by the server.
- * </p>
- * <p>
- * 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.
- * </p>
- * <p>
- * 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.
- * </p>
- * <p>
- *
- * The CGI specification is located at
- * <a href="http://cgi-spec.golux.com">http://cgi-spec.golux.com</a>.
- *
- * </p>
- * <p>
- * <h3>TODO:</h3>
- * <ul>
- * <li> Support for setting headers (for example, Location headers don't work)
- * <li> Support for collapsing multiple header lines (per RFC 2616)
- * <li> Ensure handling of POST method does not interfere with 2.3 Filters
- * <li> Refactor some debug code out of core
- * <li> Ensure header handling preserves encoding
- * <li> Possibly rewrite CGIRunner.run()?
- * <li> Possibly refactor CGIRunner and CGIEnvironment as non-inner classes?
- * <li> Document handling of cgi stdin when there is no stdin
- * <li> Revisit IOException handling in CGIRunner.run()
- * <li> Better documentation
- * <li> Confirm use of ServletInputStream.available() in CGIRunner.run() is
- * not needed
- * <li> Make checking for "." and ".." in servlet & cgi PATH_INFO less
- * draconian
- * <li> [add more to this TODO list]
- * </ul>
- * </p>
- *
- * @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 */
- /** 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");
- /** 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.
- * <P>
- * Modified from Craig R. McClanahan's InvokerServlet
- * </P>
- *
- * @param config a <code>ServletConfig</code> object
- * containing the servlet's
- * configuration and initialization
- * parameters
- *
- * @exception ServletException if an exception has occurred that
- * interferes with the servlet's normal
- * operation
- */
- public void init(ServletConfig config) throws ServletException {
- super.init(config);
- // Verify that we were not accessed using the invoker servlet
- String servletName = getServletConfig().getServletName();
- if (servletName == null)
- servletName = "";
- if (servletName.startsWith("org.apache.catalina.INVOKER."))
- throw new UnavailableException
- ("Cannot invoke CGIServlet through the invoker");
- boolean passShellEnvironment = false;
-
- // Set our properties from the initialization parameters
- String value = null;
- try {
- value = getServletConfig().getInitParameter("debug");
- debug = Integer.parseInt(value);
- cgiPathPrefix =
- getServletConfig().getInitParameter("cgiPathPrefix");
- value = getServletConfig().getInitParameter("passShellEnvironment");
- passShellEnvironment = Boolean.valueOf(value).booleanValue();
- } catch (Throwable t) {
- //NOOP
- }
- if (passShellEnvironment) {
- try {
- shellEnv.putAll(getShellEnvironment());
- } catch (IOException ioe) {
- ServletException e = new ServletException(
- "Unable to read shell environment variables", ioe);
- throw e;
- }
- }
- value = getServletConfig().getInitParameter("executable");
- if (value != null) {
- cgiExecutable = value;
- }
- value = getServletConfig().getInitParameter("parameterEncoding");
- if (value != null) {
- parameterEncoding = value;
- }
- }
- /**
- * Prints out important Servlet API and container information
- *
- * <p>
- * Copied from SnoopAllServlet by Craig R. McClanahan
- * </p>
- *
- * @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, HttpServletResponse res) throws IOException {
- // Document the properties from ServletRequest
- out.println("<h1>ServletRequest Properties</h1>");
- out.println("<ul>");
- Enumeration attrs = req.getAttributeNames();
- while (attrs.hasMoreElements()) {
- String attr = (String) attrs.nextElement();
- out.println("<li><b>attribute</b> " + attr + " = " +
- req.getAttribute(attr));
- }
- out.println("<li><b>characterEncoding</b> = " +
- req.getCharacterEncoding());
- out.println("<li><b>contentLength</b> = " +
- req.getContentLength());
- out.println("<li><b>contentType</b> = " +
- req.getContentType());
- Enumeration locales = req.getLocales();
- while (locales.hasMoreElements()) {
- Locale locale = (Locale) locales.nextElement();
- out.println("<li><b>locale</b> = " + locale);
- }
- Enumeration params = req.getParameterNames();
- while (params.hasMoreElements()) {
- String param = (String) params.nextElement();
- String values[] = req.getParameterValues(param);
- for (int i = 0; i < values.length; i++)
- out.println("<li><b>parameter</b> " + param + " = " +
- values[i]);
- }
- out.println("<li><b>protocol</b> = " + req.getProtocol());
- out.println("<li><b>remoteAddr</b> = " + req.getRemoteAddr());
- out.println("<li><b>remoteHost</b> = " + req.getRemoteHost());
- out.println("<li><b>scheme</b> = " + req.getScheme());
- out.println("<li><b>secure</b> = " + req.isSecure());
- out.println("<li><b>serverName</b> = " + req.getServerName());
- out.println("<li><b>serverPort</b> = " + req.getServerPort());
- out.println("</ul>");
- out.println("<hr>");
- // Document the properties from HttpServletRequest
- out.println("<h1>HttpServletRequest Properties</h1>");
- out.println("<ul>");
- out.println("<li><b>authType</b> = " + req.getAuthType());
- out.println("<li><b>contextPath</b> = " +
- req.getContextPath());
- Cookie cookies[] = req.getCookies();
- if (cookies!=null) {
- for (int i = 0; i < cookies.length; i++)
- out.println("<li><b>cookie</b> " + cookies[i].getName() +" = " +cookies[i].getValue());
- }
- Enumeration headers = req.getHeaderNames();
- while (headers.hasMoreElements()) {
- String header = (String) headers.nextElement();
- out.println("<li><b>header</b> " + header + " = " +
- req.getHeader(header));
- }
- out.println("<li><b>method</b> = " + req.getMethod());
- out.println("<li><a name=\"pathInfo\"><b>pathInfo</b></a> = "
- + req.getPathInfo());
- out.println("<li><b>pathTranslated</b> = " +
- req.getPathTranslated());
- out.println("<li><b>queryString</b> = " +
- req.getQueryString());
- out.println("<li><b>remoteUser</b> = " +
- req.getRemoteUser());
- out.println("<li><b>requestedSessionId</b> = " +
- req.getRequestedSessionId());
- out.println("<li><b>requestedSessionIdFromCookie</b> = " +
- req.isRequestedSessionIdFromCookie());
- out.println("<li><b>requestedSessionIdFromURL</b> = " +
- req.isRequestedSessionIdFromURL());
- out.println("<li><b>requestedSessionIdValid</b> = " +
- req.isRequestedSessionIdValid());
- out.println("<li><b>requestURI</b> = " +
- req.getRequestURI());
- out.println("<li><b>servletPath</b> = " +
- req.getServletPath());
- out.println("<li><b>userPrincipal</b> = " +
- req.getUserPrincipal());
- out.println("</ul>");
- out.println("<hr>");
- // Document the servlet request attributes
- out.println("<h1>ServletRequest Attributes</h1>");
- out.println("<ul>");
- attrs = req.getAttributeNames();
- while (attrs.hasMoreElements()) {
- String attr = (String) attrs.nextElement();
- out.println("<li><b>" + attr + "</b> = " +
- req.getAttribute(attr));
- }
- out.println("</ul>");
- out.println("<hr>");
- // Process the current session (if there is one)
- HttpSession session = req.getSession(false);
- if (session != null) {
- // Document the session properties
- out.println("<h1>HttpSession Properties</h1>");
- out.println("<ul>");
- out.println("<li><b>id</b> = " +
- session.getId());
- out.println("<li><b>creationTime</b> = " +
- new Date(session.getCreationTime()));
- out.println("<li><b>lastAccessedTime</b> = " +
- new Date(session.getLastAccessedTime()));
- out.println("<li><b>maxInactiveInterval</b> = " +
- session.getMaxInactiveInterval());
- out.println("</ul>");
- out.println("<hr>");
- // Document the session attributes
- out.println("<h1>HttpSession Attributes</h1>");
- out.println("<ul>");
- attrs = session.getAttributeNames();
- while (attrs.hasMoreElements()) {
- String attr = (String) attrs.nextElement();
- out.println("<li><b>" + attr + "</b> = " +
- session.getAttribute(attr));
- }
- out.println("</ul>");
- out.println("<hr>");
- }
- // Document the servlet configuration properties
- out.println("<h1>ServletConfig Properties</h1>");
- out.println("<ul>");
- out.println("<li><b>servletName</b> = " +
- getServletConfig().getServletName());
- out.println("</ul>");
- out.println("<hr>");
- // Document the servlet configuration initialization parameters
- out.println("<h1>ServletConfig Initialization Parameters</h1>");
- out.println("<ul>");
- params = getServletConfig().getInitParameterNames();
- while (params.hasMoreElements()) {
- String param = (String) params.nextElement();
- String value = getServletConfig().getInitParameter(param);
- out.println("<li><b>" + param + "</b> = " + value);
- }
- out.println("</ul>");
- out.println("<hr>");
- // Document the servlet context properties
- out.println("<h1>ServletContext Properties</h1>");
- out.println("<ul>");
- out.println("<li><b>majorVersion</b> = " +
- getServletContext().getMajorVersion());
- out.println("<li><b>minorVersion</b> = " +
- getServletContext().getMinorVersion());
- out.println("<li><b>realPath('/')</b> = " +
- getServletContext().getRealPath("/"));
- out.println("<li><b>serverInfo</b> = " +
- getServletContext().getServerInfo());
- out.println("</ul>");
- out.println("<hr>");
- // Document the servlet context initialization parameters
- out.println("<h1>ServletContext Initialization Parameters</h1>");
- out.println("<ul>");
- params = getServletContext().getInitParameterNames();
- while (params.hasMoreElements()) {
- String param = (String) params.nextElement();
- String value = getServletContext().getInitParameter(param);
- out.println("<li><b>" + param + "</b> = " + value);
- }
- out.println("</ul>");
- out.println("<hr>");
- // Document the servlet context attributes
- out.println("<h1>ServletContext Attributes</h1>");
- out.println("<ul>");
- attrs = getServletContext().getAttributeNames();
- while (attrs.hasMoreElements()) {
- String attr = (String) attrs.nextElement();
- out.println("<li><b>" + attr + "</b> = " +
- getServletContext().getAttribute(attr));
- }
- out.println("</ul>");
- out.println("<hr>");
- }
- /**
- * Provides CGI Gateway service -- delegates to <code>doGet</code>
- *
- * @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
- *
- */
- 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
- *
- */
- protected void doGet(HttpServletRequest req, HttpServletResponse res)
- throws ServletException, IOException {
- // Verify that we were not accessed using the invoker servlet
- if (req.getAttribute(Globals.INVOKED_ATTR) != null)
- throw new UnavailableException
- ("Cannot invoke CGIServlet through the invoker");
- 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) {
- try {
- ServletOutputStream out = res.getOutputStream();
- out.println("<HTML><HEAD><TITLE>$Name$</TITLE></HEAD>");
- out.println("<BODY>$Header$<p>");
- if (cgiEnv.isValid()) {
- out.println(cgiEnv.toString());
- } else {
- out.println("<H3>");
- out.println("CGI script not found or not specified.");
- out.println("</H3>");
- out.println("<H4>");
- out.println("Check the <b>HttpServletRequest ");
- out.println("<a href=\"#pathInfo\">pathInfo</a></b> ");
- 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("</H4>");
- out.println("<H4>");
- out.println("For a good discussion of how CGI scripts ");
- out.println("work and what their environment variables ");
- out.println("mean, please visit the <a ");
- out.println("href=\"http://cgi-spec.golux.com\">CGI ");
- out.println("Specification page</a>.");
- out.println("</H4>");
- }
- printServletEnvironment(out, req, res);
- out.println("</BODY></HTML>");
- } catch (IOException ignored) {
- }
- } //debugging
- } //doGet
- /** For future testing use only; does nothing right now */
- public static void main(String[] args) {
- System.out.println("$Header$");
- }
- /**
- * Get all shell environment variables. Have to do it this rather ugly way
- * as the API to obtain is not available in 1.4 and earlier APIs.
- *
- * See <a href="http://www.rgagnon.com/javadetails/java-0150.html">Read environment
- * variables from an application</a> for original source and article.
- */
- private Hashtable getShellEnvironment() throws IOException {
- Hashtable envVars = new Hashtable();
- Process p = null;
- Runtime r = Runtime.getRuntime();
- String OS = System.getProperty("os.name").toLowerCase();
- boolean ignoreCase;
- if (OS.indexOf("windows 9") > -1) {
- p = r.exec( "command.com /c set" );
- ignoreCase = true;
- } else if ((OS.indexOf("nt") > -1) || (OS.indexOf("windows") > -1) ) {
- // thanks to JuanFran for the xp fix!
- p = r.exec( "cmd.exe /c set" );
- ignoreCase = true;
- } else {
- // our last hope, we assume Unix (thanks to H. Ware for the fix)
- p = r.exec( "env" );
- ignoreCase = false;
- }
- BufferedReader br = new BufferedReader
- ( new InputStreamReader( p.getInputStream() ) );
- String line;
- while( (line = br.readLine()) != null ) {
- int idx = line.indexOf( '=' );
- String key = line.substring( 0, idx );
- String value = line.substring( idx+1 );
- if (ignoreCase) {
- key = key.toUpperCase();
- }
- envVars.put(key, value);
- }
- return envVars;
- }
- /**
- * Encapsulates the CGI environment and rules to derive
- * that environment from the servlet container and request information.
- *
- * <p>
- * </p>
- *
- * @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(Globals.WORK_DIR_ATTR);
- }
- /**
- * 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.
- *
- * <p>
- * Example URI:
- * <PRE> /servlet/cgigateway/dir1/realCGIscript/pathinfo1 </PRE>
- * <ul>
- * <LI><b>path</b> = $CATALINA_HOME/mywebapp/dir1/realCGIscript
- * <LI><b>scriptName</b> = /servlet/cgigateway/dir1/realCGIscript
- * <LI><b>cgiName</b> = /dir1/realCGIscript
- * <LI><b>name</b> = realCGIscript
- * </ul>
- * </p>
- * <p>
- * 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.
- *</p>
- *<p>
- * The CGI search path will start at
- * webAppRootDir + File.separator + cgiPathPrefix
- * (or webAppRootDir alone if cgiPathPrefix is
- * null).
- *</p>
- *<p>
- * cgiPathPrefix is defined by setting
- * this servlet's cgiPathPrefix init parameter
- *
- *</p>
- *
- * @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
- * <ul>
- * <li>
- * <code>path</code> - full file-system path to valid cgi script,
- * or null if no cgi was found
- * <li>
- * <code>scriptName</code> -
- * CGI variable SCRIPT_NAME; the full URL path
- * to valid cgi script or null if no cgi was
- * found
- * <li>
- * <code>cgiName</code> - servlet pathInfo fragment corresponding to
- * the cgi script itself, or null if not found
- * <li>
- * <code>name</code> - simple name (no directories) of the
- * cgi script, or null if no cgi was found
- * </ul>
- *
- * @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 };
- } else {
- 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 heavliy on Servlet API methods and findCGI
- *
- * @param req request associated with the CGI
- * invokation
- *
- * @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 sPathTranslatedOrig = 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;
- sPathTranslatedOrig = req.getPathTranslated();
- sPathTranslatedOrig =
- sPathTranslatedOrig == null ? "" : sPathTranslatedOrig;
- 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 ? new Integer(-1) : new Integer(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);
- } else {
- sPathTranslatedCGI = null;
- }
- 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 ? "" :
- (new Integer(contentLength)).toString());
- envp.put("CONTENT_LENGTH", sContentLength);
- Enumeration headers = req.getHeaderNames();
- String header = null;
- while (headers.hasMoreElements()) {
- header = null;
- header = ((String) headers.nextElement()).toUpperCase();
- //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() {
- StringBuffer srcPath = new StringBuffer();
- StringBuffer destPath = new StringBuffer();
- 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 = new String (destPath.toString().substring(
- 0,destPath.toString().lastIndexOf("/")));
- File dir = new File(dirPath);
- dir.mkdirs();
- 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
- IOTools.flow(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()) {
- f.delete();
- }
- }
- }
- /**
- * Print important CGI environment information in a easy-to-read HTML
- * table
- *
- * @return HTML string containing CGI environment info
- *
- */
- public String toString() {
- StringBuffer sb = new StringBuffer();
- sb.append("<TABLE border=2>");
- sb.append("<tr><th colspan=2 bgcolor=grey>");
- sb.append("CGIEnvironment Info</th></tr>");
- sb.append("<tr><td>Debug Level</td><td>");
- sb.append(debug);
- sb.append("</td></tr>");
- sb.append("<tr><td>Validity:</td><td>");
- sb.append(isValid());
- sb.append("</td></tr>");
- if (isValid()) {
- Enumeration envk = env.keys();
- while (envk.hasMoreElements()) {
- String s = (String) envk.nextElement();
- sb.append("<tr><td>");
- sb.append(s);
- sb.append("</td><td>");
- sb.append(blanksToString((String) env.get(s),
- "[will be set to blank]"));
- sb.append("</td></tr>");
- }
- }
- sb.append("<tr><td colspan=2><HR></td></tr>");
- sb.append("<tr><td>Derived Command</td><td>");
- sb.append(nullsToBlanks(command));
- sb.append("</td></tr>");
- sb.append("<tr><td>Working Directory</td><td>");
- if (workingDirectory != null) {
- sb.append(workingDirectory.toString());
- }
- sb.append("</td></tr>");
- sb.append("<tr><td>Command Line Params</td><td>");
- for (int i=0; i < cmdLineParameters.size(); i++) {
- String param = (String) cmdLineParameters.get(i);
- sb.append("<p>");
- sb.append(param);
- sb.append("</p>");
- }
- sb.append("</td></tr>");
- sb.append("</TABLE><p>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() {
- r…
Large files files are truncated, but you can click here to view the full file