001: /*
002: * CGIProcessEnvironment.java $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/util/CGIProcessEnvironment.java,v 1.3 2001/08/14 18:50:10 pier Exp $
003: * $Revision: 1.3 $, $Date: 2001/08/14 18:50:10 $
004: *
005: * ====================================================================
006: *
007: * The Apache Software License, Version 1.1
008: *
009: * Copyright (c) 1999 The Apache Software Foundation. All rights
010: * reserved.
011: *
012: * Redistribution and use in source and binary forms, with or without
013: * modification, are permitted provided that the following conditions
014: * are met:
015: *
016: * 1. Redistributions of source code must retain the above copyright
017: * notice, this list of conditions and the following disclaimer.
018: *
019: * 2. Redistributions in binary form must reproduce the above copyright
020: * notice, this list of conditions and the following disclaimer in
021: * the documentation and/or other materials provided with the
022: * distribution.
023: *
024: * 3. The end-user documentation included with the redistribution, if
025: * any, must include the following acknowlegement:
026: * "This product includes software developed by the
027: * Apache Software Foundation (http://www.apache.org/)."
028: * Alternately, this acknowlegement may appear in the software itself,
029: * if and wherever such third-party acknowlegements normally appear.
030: *
031: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
032: * Foundation" must not be used to endorse or promote products derived
033: * from this software without prior written permission. For written
034: * permission, please contact apache@apache.org.
035: *
036: * 5. Products derived from this software may not be called "Apache"
037: * nor may "Apache" appear in their names without prior written
038: * permission of the Apache Group.
039: *
040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
043: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
044: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
045: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
046: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
051: * SUCH DAMAGE.
052: * ====================================================================
053: *
054: * This software consists of voluntary contributions made by many
055: * individuals on behalf of the Apache Software Foundation. For more
056: * information on the Apache Software Foundation, please see
057: * <http://www.apache.org/>.
058: *
059: *
060: */
061:
062: package org.apache.catalina.util;
063:
064: import java.lang.Process;
065: import java.io.File;
066: import java.io.Writer;
067: import java.io.Reader;
068: import java.io.PrintWriter;
069: import java.io.BufferedWriter;
070: import java.io.BufferedReader;
071: import java.io.InputStream;
072: import java.io.OutputStream;
073: import java.io.InputStreamReader;
074: import java.io.OutputStreamWriter;
075: import java.io.BufferedInputStream;
076: import java.io.BufferedOutputStream;
077: import java.io.IOException;
078: import java.net.URLEncoder;
079: import java.util.Hashtable;
080: import java.util.Vector;
081: import java.util.Enumeration;
082: import java.util.StringTokenizer;
083: import java.util.Locale;
084: import java.util.Date;
085: import javax.servlet.ServletException;
086: import javax.servlet.ServletOutputStream;
087: import javax.servlet.ServletContext;
088: import javax.servlet.ServletConfig;
089: import javax.servlet.http.HttpServlet;
090: import javax.servlet.http.HttpServletRequest;
091: import javax.servlet.http.HttpServletResponse;
092: import javax.servlet.http.HttpSession;
093: import javax.servlet.http.Cookie;
094: import org.apache.catalina.Context;
095: import org.apache.catalina.Wrapper;
096:
097: // import org.apache.catalina.util.StringManager;
098:
099: /**
100: * Encapsulates the CGI Process' environment and rules to derive
101: * that environment from the servlet container and request information.
102: * @author Martin Dengler [root@martindengler.com]
103: * @version $Revision: 1.3 $, $Date: 2001/08/14 18:50:10 $
104: * @since Tomcat 4.0
105: */
106:
107: public class CGIProcessEnvironment extends ProcessEnvironment {
108:
109: /** cgi command's query parameters */
110: private Hashtable queryParameters = null;
111:
112: /**
113: * The CGI search path will start at
114: * webAppRootDir + File.separator + cgiPathPrefix
115: * (or webAppRootDir alone if cgiPathPrefix is
116: * null)
117: */
118: private String cgiPathPrefix = null;
119:
120: /**
121: * Creates a ProcessEnvironment and derives the necessary environment,
122: * working directory, command, etc. The cgi path prefix is initialized
123: * to "" (the empty string).
124: *
125: * @param req HttpServletRequest for information provided by
126: * the Servlet API
127: * @param context ServletContext for information provided by
128: * the Servlet API
129: */
130: public CGIProcessEnvironment(HttpServletRequest req,
131: ServletContext context) {
132: this (req, context, "");
133: }
134:
135: /**
136: * Creates a ProcessEnvironment and derives the necessary environment,
137: * working directory, command, etc.
138: * @param req HttpServletRequest for information provided by
139: * the Servlet API
140: * @param context ServletContext for information provided by
141: * the Servlet API
142: * @param cgiPathPrefix subdirectory of webAppRootDir below which the
143: * web app's CGIs may be stored; can be null or "".
144: */
145: public CGIProcessEnvironment(HttpServletRequest req,
146: ServletContext context, String cgiPathPrefix) {
147: this (req, context, cgiPathPrefix, 0);
148: }
149:
150: /**
151: * Creates a ProcessEnvironment and derives the necessary environment,
152: * working directory, command, etc.
153: * @param req HttpServletRequest for information provided by
154: * the Servlet API
155: * @param context ServletContext for information provided by
156: * the Servlet API
157: * @param debug int debug level (0 == none, 6 == lots)
158: */
159: public CGIProcessEnvironment(HttpServletRequest req,
160: ServletContext context, int debug) {
161: this (req, context, "", 0);
162: }
163:
164: /**
165: * Creates a ProcessEnvironment and derives the necessary environment,
166: * working directory, command, etc.
167: * @param req HttpServletRequest for information provided by
168: * the Servlet API
169: * @param context ServletContext for information provided by
170: * the Servlet API
171: * @param cgiPathPrefix subdirectory of webAppRootDir below which the
172: * web app's CGIs may be stored; can be null or "".
173: * @param debug int debug level (0 == none, 6 == lots)
174: */
175: public CGIProcessEnvironment(HttpServletRequest req,
176: ServletContext context, String cgiPathPrefix, int debug) {
177: super (req, context, debug);
178: this .cgiPathPrefix = cgiPathPrefix;
179: queryParameters = new Hashtable();
180: Enumeration paramNames = req.getParameterNames();
181: while (paramNames != null && paramNames.hasMoreElements()) {
182: String param = paramNames.nextElement().toString();
183: if (param != null) {
184: queryParameters.put(param, URLEncoder.encode(req
185: .getParameter(param)));
186: }
187: }
188: this .valid = deriveProcessEnvironment(req);
189: }
190:
191: /**
192: * Constructs the CGI environment to be supplied to the invoked CGI
193: * script; relies heavliy on Servlet API methods and findCGI
194: * @param HttpServletRequest request associated with the CGI invokation
195: * @return true if environment was set OK, false if there was a problem
196: * and no environment was set
197: */
198: protected boolean deriveProcessEnvironment(HttpServletRequest req) {
199: /*
200: * This method is slightly ugly; c'est la vie.
201: * "You cannot stop [ugliness], you can only hope to contain [it]"
202: * (apologies to Marv Albert regarding MJ)
203: */
204:
205: Hashtable envp;
206: super .deriveProcessEnvironment(req);
207: envp = getEnvironment();
208:
209: String sPathInfoOrig = null;
210: String sPathTranslatedOrig = null;
211: String sPathInfoCGI = null;
212: String sPathTranslatedCGI = null;
213: String sCGIFullPath = null;
214: String sCGIScriptName = null;
215: String sCGIFullName = null;
216: String sCGIName = null;
217: String[] sCGINames;
218: sPathInfoOrig = this .pathInfo;
219: sPathInfoOrig = sPathInfoOrig == null ? "" : sPathInfoOrig;
220: sPathTranslatedOrig = req.getPathTranslated();
221: sPathTranslatedOrig = sPathTranslatedOrig == null ? ""
222: : sPathTranslatedOrig;
223: sCGINames = findCGI(sPathInfoOrig, getWebAppRootDir(),
224: getContextPath(), getServletPath(), cgiPathPrefix);
225: sCGIFullPath = sCGINames[0];
226: sCGIScriptName = sCGINames[1];
227: sCGIFullName = sCGINames[2];
228: sCGIName = sCGINames[3];
229: if (sCGIFullPath == null || sCGIScriptName == null
230: || sCGIFullName == null || sCGIName == null) {
231: return false;
232: }
233: envp.put("SERVER_SOFTWARE", "TOMCAT");
234: envp.put("SERVER_NAME", nullsToBlanks(req.getServerName()));
235: envp.put("GATEWAY_INTERFACE", "CGI/1.1");
236: envp.put("SERVER_PROTOCOL", nullsToBlanks(req.getProtocol()));
237: int port = req.getServerPort();
238: Integer iPort = (port == 0 ? new Integer(-1)
239: : new Integer(port));
240: envp.put("SERVER_PORT", iPort.toString());
241: envp.put("REQUEST_METHOD", nullsToBlanks(req.getMethod()));
242:
243: /*-
244: * PATH_INFO should be determined by using sCGIFullName:
245: * 1) Let sCGIFullName not end in a "/" (see method findCGI)
246: * 2) Let sCGIFullName equal the pathInfo fragment which
247: * corresponds to the actual cgi script.
248: * 3) Thus, PATH_INFO = request.getPathInfo().substring(
249: * sCGIFullName.length())
250: *
251: * (see method findCGI, where the real work is done)
252: *
253: */
254:
255: if (pathInfo == null
256: || (pathInfo.substring(sCGIFullName.length()).length() <= 0)) {
257: sPathInfoCGI = "";
258: } else {
259: sPathInfoCGI = pathInfo.substring(sCGIFullName.length());
260: }
261: envp.put("PATH_INFO", sPathInfoCGI);
262:
263: /*-
264: * PATH_TRANSLATED must be determined after PATH_INFO (and the
265: * implied real cgi-script) has been taken into account.
266: *
267: * The following example demonstrates:
268: *
269: * servlet info = /servlet/cgigw/dir1/dir2/cgi1/trans1/trans2
270: * cgifullpath = /servlet/cgigw/dir1/dir2/cgi1
271: * path_info = /trans1/trans2
272: * webAppRootDir = servletContext.getRealPath("/")
273: *
274: * path_translated = servletContext.getRealPath("/trans1/trans2")
275: *
276: * That is, PATH_TRANSLATED = webAppRootDir + sPathInfoCGI
277: * (unless sPathInfoCGI is null or blank, then the CGI
278: * specification dictates that the PATH_TRANSLATED metavariable
279: * SHOULD NOT be defined.
280: *
281: */
282:
283: if (sPathInfoCGI != null && !("".equals(sPathInfoCGI))) {
284: sPathTranslatedCGI = getContext().getRealPath(sPathInfoCGI);
285: } else {
286: sPathTranslatedCGI = null;
287: }
288: if (sPathTranslatedCGI == null || "".equals(sPathTranslatedCGI)) {
289: //NOOP
290: } else {
291: envp.put("PATH_TRANSLATED",
292: nullsToBlanks(sPathTranslatedCGI));
293: }
294: envp.put("SCRIPT_NAME", nullsToBlanks(sCGIScriptName));
295: envp.put("QUERY_STRING", nullsToBlanks(req.getQueryString()));
296: envp.put("REMOTE_HOST", nullsToBlanks(req.getRemoteHost()));
297: envp.put("REMOTE_ADDR", nullsToBlanks(req.getRemoteAddr()));
298: envp.put("AUTH_TYPE", nullsToBlanks(req.getAuthType()));
299: envp.put("REMOTE_USER", nullsToBlanks(req.getRemoteUser()));
300: envp.put("REMOTE_IDENT", ""); //not necessary for full compliance
301: envp.put("CONTENT_TYPE", nullsToBlanks(req.getContentType()));
302:
303: /* Note CGI spec says CONTENT_LENGTH must be NULL ("") or undefined
304: * if there is no content, so we cannot put 0 or -1 in as per the
305: * Servlet API spec.
306: */
307:
308: int contentLength = req.getContentLength();
309: String sContentLength = (contentLength <= 0 ? ""
310: : (new Integer(contentLength)).toString());
311: envp.put("CONTENT_LENGTH", sContentLength);
312: Enumeration headers = req.getHeaderNames();
313: String header = null;
314: while (headers.hasMoreElements()) {
315: header = null;
316: header = ((String) headers.nextElement()).toUpperCase();
317: //REMIND: rewrite multiple headers as if received as single
318: //REMIND: change character set
319: //REMIND: I forgot what the previous REMIND means
320: if ("AUTHORIZATION".equalsIgnoreCase(header)
321: || "PROXY_AUTHORIZATION".equalsIgnoreCase(header)) {
322: //NOOP per CGI specification section 11.2
323: } else if ("HOST".equalsIgnoreCase(header)) {
324: String host = req.getHeader(header);
325: envp.put("HTTP_" + header.replace('-', '_'), host
326: .substring(0, host.indexOf(":")));
327: } else {
328: envp.put("HTTP_" + header.replace('-', '_'), req
329: .getHeader(header));
330: }
331: }
332: command = sCGIFullPath;
333: workingDirectory = new File(command.substring(0, command
334: .lastIndexOf(File.separator)));
335: envp.put("X_TOMCAT_COMMAND_PATH", command); //for kicks
336: this .setEnvironment(envp);
337: return true;
338: }
339:
340: /**
341: * Resolves core information about the cgi script. <p> Example URI:
342: * <PRE> /servlet/cgigateway/dir1/realCGIscript/pathinfo1 </PRE> <ul>
343: * <LI><b>path</b> = $CATALINA_HOME/mywebapp/dir1/realCGIscript
344: * <LI><b>scriptName</b> = /servlet/cgigateway/dir1/realCGIscript</LI>
345: * <LI><b>cgiName</b> = /dir1/realCGIscript
346: * <LI><b>name</b> = realCGIscript
347: * </ul>
348: * </p>
349: * <p>
350: * CGI search algorithm: search the real path below
351: * <my-webapp-root> and find the first non-directory in
352: * the getPathTranslated("/"), reading/searching from left-to-right.
353: * </p>
354: * <p>
355: * The CGI search path will start at
356: * webAppRootDir + File.separator + cgiPathPrefix (or webAppRootDir
357: * alone if cgiPathPrefix is null).
358: * </p>
359: * <p>
360: * cgiPathPrefix is usually set by the calling servlet to the servlet's
361: * cgiPathPrefix init parameter
362: * </p>
363: *
364: * @param pathInfo String from HttpServletRequest.getPathInfo()
365: * @param webAppRootDir String from context.getRealPath("/")
366: * @param contextPath String as from HttpServletRequest.getContextPath()
367: * @param servletPath String as from HttpServletRequest.getServletPath()
368: * @param cgiPathPrefix subdirectory of webAppRootDir below which the
369: * web app's CGIs may be stored; can be null.
370: * @return
371: * <ul> <li> <code>path</code> - full file-system path to valid cgi
372: * script, or null if no cgi was found
373: * <li> <code>scriptName</code> - CGI variable SCRIPT_NAME; the full
374: * URL path to valid cgi script or
375: * null if no cgi was found
376: * <li> <code>cgiName</code> - servlet pathInfo fragment
377: * corresponding to the cgi script
378: * itself, or null if not found
379: * <li> <code>name</code> - simple name (no directories) of
380: * the cgi script, or null if no cgi
381: * was found
382: * </ul>
383: * @author Martin Dengler [root@martindengler.com]
384: * @since Tomcat 4.0
385: */
386: protected String[] findCGI(String pathInfo, String webAppRootDir,
387: String contextPath, String servletPath, String cgiPathPrefix) {
388: String path = null;
389: String name = null;
390: String scriptname = null;
391: String cginame = null;
392: if ((webAppRootDir != null)
393: && (webAppRootDir.lastIndexOf("/") == (webAppRootDir
394: .length() - 1))) {
395: //strip the trailing "/" from the webAppRootDir
396: webAppRootDir = webAppRootDir.substring(0, (webAppRootDir
397: .length() - 1));
398: }
399: if (cgiPathPrefix != null) {
400: webAppRootDir = webAppRootDir + File.separator
401: + cgiPathPrefix;
402: }
403: if (debug >= 2) {
404: log("findCGI: start = [" + webAppRootDir
405: + "], pathInfo = [" + pathInfo + "] ");
406: }
407: File currentLocation = new File(webAppRootDir);
408: StringTokenizer dirWalker = new StringTokenizer(pathInfo, "/");
409: while (!currentLocation.isFile() && dirWalker.hasMoreElements()) {
410: currentLocation = new File(currentLocation,
411: (String) dirWalker.nextElement());
412: if (debug >= 3) {
413: log("findCGI: traversing to [" + currentLocation + "]");
414: }
415: }
416: if (!currentLocation.isFile()) {
417: return new String[] { null, null, null, null };
418: } else {
419: if (debug >= 2) {
420: log("findCGI: FOUND cgi at [" + currentLocation + "]");
421: }
422: path = currentLocation.getAbsolutePath();
423: name = currentLocation.getName();
424: cginame = currentLocation.getParent().substring(
425: webAppRootDir.length())
426: + File.separator + name;
427: if (".".equals(contextPath)) {
428: scriptname = servletPath + cginame;
429: } else {
430: scriptname = contextPath + servletPath + cginame;
431: }
432: }
433: if (debug >= 1) {
434: log("findCGI calc: name=" + name + ", path=" + path
435: + ", scriptname=" + scriptname + ", cginame="
436: + cginame);
437: }
438: return new String[] { path, scriptname, cginame, name };
439: }
440:
441: /**
442: * Print important CGI environment information in an
443: * easy-to-read HTML table
444: * @return HTML string containing CGI environment info
445: */
446: public String toString() {
447: StringBuffer sb = new StringBuffer();
448: sb.append("<TABLE border=2>");
449: sb.append("<tr><th colspan=2 bgcolor=grey>");
450: sb.append("ProcessEnvironment Info</th></tr>");
451: sb.append("<tr><td>Debug Level</td><td>");
452: sb.append(debug);
453: sb.append("</td></tr>");
454: sb.append("<tr><td>Validity:</td><td>");
455: sb.append(isValid());
456: sb.append("</td></tr>");
457: if (isValid()) {
458: Enumeration envk = env.keys();
459: while (envk.hasMoreElements()) {
460: String s = (String) envk.nextElement();
461: sb.append("<tr><td>");
462: sb.append(s);
463: sb.append("</td><td>");
464: sb.append(blanksToString((String) env.get(s),
465: "[will be set to blank]"));
466: sb.append("</td></tr>");
467: }
468: }
469: sb.append("<tr><td colspan=2><HR></td></tr>");
470: sb.append("<tr><td>Derived Command</td><td>");
471: sb.append(nullsToBlanks(command));
472: sb.append("</td></tr>");
473: sb.append("<tr><td>Working Directory</td><td>");
474: if (workingDirectory != null) {
475: sb.append(workingDirectory.toString());
476: }
477: sb.append("</td></tr>");
478: sb.append("<tr><td colspan=2>Query Params</td></tr>");
479: Enumeration paramk = queryParameters.keys();
480: while (paramk.hasMoreElements()) {
481: String s = paramk.nextElement().toString();
482: sb.append("<tr><td>");
483: sb.append(s);
484: sb.append("</td><td>");
485: sb.append(queryParameters.get(s).toString());
486: sb.append("</td></tr>");
487: }
488:
489: sb.append("</TABLE><p>end.");
490: return sb.toString();
491: }
492:
493: /**
494: * Gets process' derived query parameters
495: * @return process' query parameters
496: */
497: public Hashtable getParameters() {
498: return queryParameters;
499: }
500:
501: }
|