001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.catalina.util;
018:
019: import java.io.File;
020: import java.net.URLEncoder;
021: import java.util.Enumeration;
022: import java.util.Hashtable;
023: import java.util.StringTokenizer;
024:
025: import javax.servlet.ServletContext;
026: import javax.servlet.http.HttpServletRequest;
027:
028: // import org.apache.catalina.util.StringManager;
029:
030: /**
031: * Encapsulates the CGI Process' environment and rules to derive
032: * that environment from the servlet container and request information.
033: * @author Martin Dengler [root@martindengler.com]
034: * @version $Revision: 1.4 $, $Date: 2004/05/26 16:20:54 $
035: * @since Tomcat 4.0
036: */
037:
038: public class CGIProcessEnvironment extends ProcessEnvironment {
039:
040: /** cgi command's query parameters */
041: private Hashtable queryParameters = null;
042:
043: /**
044: * The CGI search path will start at
045: * webAppRootDir + File.separator + cgiPathPrefix
046: * (or webAppRootDir alone if cgiPathPrefix is
047: * null)
048: */
049: private String cgiPathPrefix = null;
050:
051: /**
052: * Creates a ProcessEnvironment and derives the necessary environment,
053: * working directory, command, etc. The cgi path prefix is initialized
054: * to "" (the empty string).
055: *
056: * @param req HttpServletRequest for information provided by
057: * the Servlet API
058: * @param context ServletContext for information provided by
059: * the Servlet API
060: */
061: public CGIProcessEnvironment(HttpServletRequest req,
062: ServletContext context) {
063: this (req, context, "");
064: }
065:
066: /**
067: * Creates a ProcessEnvironment and derives the necessary environment,
068: * working directory, command, etc.
069: * @param req HttpServletRequest for information provided by
070: * the Servlet API
071: * @param context ServletContext for information provided by
072: * the Servlet API
073: * @param cgiPathPrefix subdirectory of webAppRootDir below which the
074: * web app's CGIs may be stored; can be null or "".
075: */
076: public CGIProcessEnvironment(HttpServletRequest req,
077: ServletContext context, String cgiPathPrefix) {
078: this (req, context, cgiPathPrefix, 0);
079: }
080:
081: /**
082: * Creates a ProcessEnvironment and derives the necessary environment,
083: * working directory, command, etc.
084: * @param req HttpServletRequest for information provided by
085: * the Servlet API
086: * @param context ServletContext for information provided by
087: * the Servlet API
088: * @param debug int debug level (0 == none, 6 == lots)
089: */
090: public CGIProcessEnvironment(HttpServletRequest req,
091: ServletContext context, int debug) {
092: this (req, context, "", 0);
093: }
094:
095: /**
096: * Creates a ProcessEnvironment and derives the necessary environment,
097: * working directory, command, etc.
098: * @param req HttpServletRequest for information provided by
099: * the Servlet API
100: * @param context ServletContext for information provided by
101: * the Servlet API
102: * @param cgiPathPrefix subdirectory of webAppRootDir below which the
103: * web app's CGIs may be stored; can be null or "".
104: * @param debug int debug level (0 == none, 6 == lots)
105: */
106: public CGIProcessEnvironment(HttpServletRequest req,
107: ServletContext context, String cgiPathPrefix, int debug) {
108: super (req, context, debug);
109: this .cgiPathPrefix = cgiPathPrefix;
110: queryParameters = new Hashtable();
111: Enumeration paramNames = req.getParameterNames();
112: while (paramNames != null && paramNames.hasMoreElements()) {
113: String param = paramNames.nextElement().toString();
114: if (param != null) {
115: queryParameters.put(param, URLEncoder.encode(req
116: .getParameter(param)));
117: }
118: }
119: this .valid = deriveProcessEnvironment(req);
120: }
121:
122: /**
123: * Constructs the CGI environment to be supplied to the invoked CGI
124: * script; relies heavliy on Servlet API methods and findCGI
125: * @param req request associated with the CGI invokation
126: * @return true if environment was set OK, false if there was a problem
127: * and no environment was set
128: */
129: protected boolean deriveProcessEnvironment(HttpServletRequest req) {
130: /*
131: * This method is slightly ugly; c'est la vie.
132: * "You cannot stop [ugliness], you can only hope to contain [it]"
133: * (apologies to Marv Albert regarding MJ)
134: */
135:
136: Hashtable envp;
137: super .deriveProcessEnvironment(req);
138: envp = getEnvironment();
139:
140: String sPathInfoOrig = null;
141: String sPathTranslatedOrig = null;
142: String sPathInfoCGI = null;
143: String sPathTranslatedCGI = null;
144: String sCGIFullPath = null;
145: String sCGIScriptName = null;
146: String sCGIFullName = null;
147: String sCGIName = null;
148: String[] sCGINames;
149: sPathInfoOrig = this .pathInfo;
150: sPathInfoOrig = sPathInfoOrig == null ? "" : sPathInfoOrig;
151: sPathTranslatedOrig = req.getPathTranslated();
152: sPathTranslatedOrig = sPathTranslatedOrig == null ? ""
153: : sPathTranslatedOrig;
154: sCGINames = findCGI(sPathInfoOrig, getWebAppRootDir(),
155: getContextPath(), getServletPath(), cgiPathPrefix);
156: sCGIFullPath = sCGINames[0];
157: sCGIScriptName = sCGINames[1];
158: sCGIFullName = sCGINames[2];
159: sCGIName = sCGINames[3];
160: if (sCGIFullPath == null || sCGIScriptName == null
161: || sCGIFullName == null || sCGIName == null) {
162: return false;
163: }
164: envp.put("SERVER_SOFTWARE", "TOMCAT");
165: envp.put("SERVER_NAME", nullsToBlanks(req.getServerName()));
166: envp.put("GATEWAY_INTERFACE", "CGI/1.1");
167: envp.put("SERVER_PROTOCOL", nullsToBlanks(req.getProtocol()));
168: int port = req.getServerPort();
169: Integer iPort = (port == 0 ? new Integer(-1)
170: : new Integer(port));
171: envp.put("SERVER_PORT", iPort.toString());
172: envp.put("REQUEST_METHOD", nullsToBlanks(req.getMethod()));
173:
174: /*-
175: * PATH_INFO should be determined by using sCGIFullName:
176: * 1) Let sCGIFullName not end in a "/" (see method findCGI)
177: * 2) Let sCGIFullName equal the pathInfo fragment which
178: * corresponds to the actual cgi script.
179: * 3) Thus, PATH_INFO = request.getPathInfo().substring(
180: * sCGIFullName.length())
181: *
182: * (see method findCGI, where the real work is done)
183: *
184: */
185:
186: if (pathInfo == null
187: || (pathInfo.substring(sCGIFullName.length()).length() <= 0)) {
188: sPathInfoCGI = "";
189: } else {
190: sPathInfoCGI = pathInfo.substring(sCGIFullName.length());
191: }
192: envp.put("PATH_INFO", sPathInfoCGI);
193:
194: /*-
195: * PATH_TRANSLATED must be determined after PATH_INFO (and the
196: * implied real cgi-script) has been taken into account.
197: *
198: * The following example demonstrates:
199: *
200: * servlet info = /servlet/cgigw/dir1/dir2/cgi1/trans1/trans2
201: * cgifullpath = /servlet/cgigw/dir1/dir2/cgi1
202: * path_info = /trans1/trans2
203: * webAppRootDir = servletContext.getRealPath("/")
204: *
205: * path_translated = servletContext.getRealPath("/trans1/trans2")
206: *
207: * That is, PATH_TRANSLATED = webAppRootDir + sPathInfoCGI
208: * (unless sPathInfoCGI is null or blank, then the CGI
209: * specification dictates that the PATH_TRANSLATED metavariable
210: * SHOULD NOT be defined.
211: *
212: */
213:
214: if (sPathInfoCGI != null && !("".equals(sPathInfoCGI))) {
215: sPathTranslatedCGI = getContext().getRealPath(sPathInfoCGI);
216: } else {
217: sPathTranslatedCGI = null;
218: }
219: if (sPathTranslatedCGI == null || "".equals(sPathTranslatedCGI)) {
220: //NOOP
221: } else {
222: envp.put("PATH_TRANSLATED",
223: nullsToBlanks(sPathTranslatedCGI));
224: }
225: envp.put("SCRIPT_NAME", nullsToBlanks(sCGIScriptName));
226: envp.put("QUERY_STRING", nullsToBlanks(req.getQueryString()));
227: envp.put("REMOTE_HOST", nullsToBlanks(req.getRemoteHost()));
228: envp.put("REMOTE_ADDR", nullsToBlanks(req.getRemoteAddr()));
229: envp.put("AUTH_TYPE", nullsToBlanks(req.getAuthType()));
230: envp.put("REMOTE_USER", nullsToBlanks(req.getRemoteUser()));
231: envp.put("REMOTE_IDENT", ""); //not necessary for full compliance
232: envp.put("CONTENT_TYPE", nullsToBlanks(req.getContentType()));
233:
234: /* Note CGI spec says CONTENT_LENGTH must be NULL ("") or undefined
235: * if there is no content, so we cannot put 0 or -1 in as per the
236: * Servlet API spec.
237: */
238:
239: int contentLength = req.getContentLength();
240: String sContentLength = (contentLength <= 0 ? ""
241: : (new Integer(contentLength)).toString());
242: envp.put("CONTENT_LENGTH", sContentLength);
243: Enumeration headers = req.getHeaderNames();
244: String header = null;
245: while (headers.hasMoreElements()) {
246: header = null;
247: header = ((String) headers.nextElement()).toUpperCase();
248: //REMIND: rewrite multiple headers as if received as single
249: //REMIND: change character set
250: //REMIND: I forgot what the previous REMIND means
251: if ("AUTHORIZATION".equalsIgnoreCase(header)
252: || "PROXY_AUTHORIZATION".equalsIgnoreCase(header)) {
253: //NOOP per CGI specification section 11.2
254: } else if ("HOST".equalsIgnoreCase(header)) {
255: String host = req.getHeader(header);
256: envp.put("HTTP_" + header.replace('-', '_'), host
257: .substring(0, host.indexOf(":")));
258: } else {
259: envp.put("HTTP_" + header.replace('-', '_'), req
260: .getHeader(header));
261: }
262: }
263: command = sCGIFullPath;
264: workingDirectory = new File(command.substring(0, command
265: .lastIndexOf(File.separator)));
266: envp.put("X_TOMCAT_COMMAND_PATH", command); //for kicks
267: this .setEnvironment(envp);
268: return true;
269: }
270:
271: /**
272: * Resolves core information about the cgi script. <p> Example URI:
273: * <PRE> /servlet/cgigateway/dir1/realCGIscript/pathinfo1 </PRE> <ul>
274: * <LI><b>path</b> = $CATALINA_HOME/mywebapp/dir1/realCGIscript
275: * <LI><b>scriptName</b> = /servlet/cgigateway/dir1/realCGIscript</LI>
276: * <LI><b>cgiName</b> = /dir1/realCGIscript
277: * <LI><b>name</b> = realCGIscript
278: * </ul>
279: * </p>
280: * <p>
281: * CGI search algorithm: search the real path below
282: * <my-webapp-root> and find the first non-directory in
283: * the getPathTranslated("/"), reading/searching from left-to-right.
284: * </p>
285: * <p>
286: * The CGI search path will start at
287: * webAppRootDir + File.separator + cgiPathPrefix (or webAppRootDir
288: * alone if cgiPathPrefix is null).
289: * </p>
290: * <p>
291: * cgiPathPrefix is usually set by the calling servlet to the servlet's
292: * cgiPathPrefix init parameter
293: * </p>
294: *
295: * @param pathInfo String from HttpServletRequest.getPathInfo()
296: * @param webAppRootDir String from context.getRealPath("/")
297: * @param contextPath String as from HttpServletRequest.getContextPath()
298: * @param servletPath String as from HttpServletRequest.getServletPath()
299: * @param cgiPathPrefix subdirectory of webAppRootDir below which the
300: * web app's CGIs may be stored; can be null.
301: * @return
302: * <ul> <li> <code>path</code> - full file-system path to valid cgi
303: * script, or null if no cgi was found
304: * <li> <code>scriptName</code> - CGI variable SCRIPT_NAME; the full
305: * URL path to valid cgi script or
306: * null if no cgi was found
307: * <li> <code>cgiName</code> - servlet pathInfo fragment
308: * corresponding to the cgi script
309: * itself, or null if not found
310: * <li> <code>name</code> - simple name (no directories) of
311: * the cgi script, or null if no cgi
312: * was found
313: * </ul>
314: * @author Martin Dengler [root@martindengler.com]
315: * @since Tomcat 4.0
316: */
317: protected String[] findCGI(String pathInfo, String webAppRootDir,
318: String contextPath, String servletPath, String cgiPathPrefix) {
319: String path = null;
320: String name = null;
321: String scriptname = null;
322: String cginame = null;
323: if ((webAppRootDir != null)
324: && (webAppRootDir.lastIndexOf("/") == (webAppRootDir
325: .length() - 1))) {
326: //strip the trailing "/" from the webAppRootDir
327: webAppRootDir = webAppRootDir.substring(0, (webAppRootDir
328: .length() - 1));
329: }
330: if (cgiPathPrefix != null) {
331: webAppRootDir = webAppRootDir + File.separator
332: + cgiPathPrefix;
333: }
334: if (debug >= 2) {
335: log("findCGI: start = [" + webAppRootDir
336: + "], pathInfo = [" + pathInfo + "] ");
337: }
338: File currentLocation = new File(webAppRootDir);
339: StringTokenizer dirWalker = new StringTokenizer(pathInfo, "/");
340: while (!currentLocation.isFile() && dirWalker.hasMoreElements()) {
341: currentLocation = new File(currentLocation,
342: (String) dirWalker.nextElement());
343: if (debug >= 3) {
344: log("findCGI: traversing to [" + currentLocation + "]");
345: }
346: }
347: if (!currentLocation.isFile()) {
348: return new String[] { null, null, null, null };
349: } else {
350: if (debug >= 2) {
351: log("findCGI: FOUND cgi at [" + currentLocation + "]");
352: }
353: path = currentLocation.getAbsolutePath();
354: name = currentLocation.getName();
355: cginame = currentLocation.getParent().substring(
356: webAppRootDir.length())
357: + File.separator + name;
358: if (".".equals(contextPath)) {
359: scriptname = servletPath + cginame;
360: } else {
361: scriptname = contextPath + servletPath + cginame;
362: }
363: }
364: if (debug >= 1) {
365: log("findCGI calc: name=" + name + ", path=" + path
366: + ", scriptname=" + scriptname + ", cginame="
367: + cginame);
368: }
369: return new String[] { path, scriptname, cginame, name };
370: }
371:
372: /**
373: * Print important CGI environment information in an
374: * easy-to-read HTML table
375: * @return HTML string containing CGI environment info
376: */
377: public String toString() {
378: StringBuffer sb = new StringBuffer();
379: sb.append("<TABLE border=2>");
380: sb.append("<tr><th colspan=2 bgcolor=grey>");
381: sb.append("ProcessEnvironment Info</th></tr>");
382: sb.append("<tr><td>Debug Level</td><td>");
383: sb.append(debug);
384: sb.append("</td></tr>");
385: sb.append("<tr><td>Validity:</td><td>");
386: sb.append(isValid());
387: sb.append("</td></tr>");
388: if (isValid()) {
389: Enumeration envk = env.keys();
390: while (envk.hasMoreElements()) {
391: String s = (String) envk.nextElement();
392: sb.append("<tr><td>");
393: sb.append(s);
394: sb.append("</td><td>");
395: sb.append(blanksToString((String) env.get(s),
396: "[will be set to blank]"));
397: sb.append("</td></tr>");
398: }
399: }
400: sb.append("<tr><td colspan=2><HR></td></tr>");
401: sb.append("<tr><td>Derived Command</td><td>");
402: sb.append(nullsToBlanks(command));
403: sb.append("</td></tr>");
404: sb.append("<tr><td>Working Directory</td><td>");
405: if (workingDirectory != null) {
406: sb.append(workingDirectory.toString());
407: }
408: sb.append("</td></tr>");
409: sb.append("<tr><td colspan=2>Query Params</td></tr>");
410: Enumeration paramk = queryParameters.keys();
411: while (paramk.hasMoreElements()) {
412: String s = paramk.nextElement().toString();
413: sb.append("<tr><td>");
414: sb.append(s);
415: sb.append("</td><td>");
416: sb.append(queryParameters.get(s).toString());
417: sb.append("</td></tr>");
418: }
419:
420: sb.append("</TABLE><p>end.");
421: return sb.toString();
422: }
423:
424: /**
425: * Gets process' derived query parameters
426: * @return process' query parameters
427: */
428: public Hashtable getParameters() {
429: return queryParameters;
430: }
431:
432: }
|