PageRenderTime 56ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/container/catalina/src/share/org/apache/catalina/servlets/CGIServlet.java

https://github.com/apache/tomcat55
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

  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package org.apache.catalina.servlets;
  18. import java.io.BufferedOutputStream;
  19. import java.io.BufferedReader;
  20. import java.io.File;
  21. import java.io.FileOutputStream;
  22. import java.io.IOException;
  23. import java.io.InputStream;
  24. import java.io.InputStreamReader;
  25. import java.io.OutputStream;
  26. import java.io.UnsupportedEncodingException;
  27. import java.net.URLDecoder;
  28. import java.util.ArrayList;
  29. import java.util.Date;
  30. import java.util.Enumeration;
  31. import java.util.Hashtable;
  32. import java.util.Locale;
  33. import java.util.StringTokenizer;
  34. import java.util.Vector;
  35. import javax.servlet.ServletConfig;
  36. import javax.servlet.ServletContext;
  37. import javax.servlet.ServletException;
  38. import javax.servlet.ServletOutputStream;
  39. import javax.servlet.UnavailableException;
  40. import javax.servlet.http.Cookie;
  41. import javax.servlet.http.HttpServlet;
  42. import javax.servlet.http.HttpServletRequest;
  43. import javax.servlet.http.HttpServletResponse;
  44. import javax.servlet.http.HttpSession;
  45. import org.apache.catalina.Globals;
  46. import org.apache.catalina.util.IOTools;
  47. /**
  48. * CGI-invoking servlet for web applications, used to execute scripts which
  49. * comply to the Common Gateway Interface (CGI) specification and are named
  50. * in the path-info used to invoke this servlet.
  51. *
  52. * <p>
  53. * <i>Note: This code compiles and even works for simple CGI cases.
  54. * Exhaustive testing has not been done. Please consider it beta
  55. * quality. Feedback is appreciated to the author (see below).</i>
  56. * </p>
  57. * <p>
  58. *
  59. * <b>Example</b>:<br>
  60. * If an instance of this servlet was mapped (using
  61. * <code>&lt;web-app&gt;/WEB-INF/web.xml</code>) to:
  62. * </p>
  63. * <p>
  64. * <code>
  65. * &lt;web-app&gt;/cgi-bin/*
  66. * </code>
  67. * </p>
  68. * <p>
  69. * then the following request:
  70. * </p>
  71. * <p>
  72. * <code>
  73. * http://localhost:8080/&lt;web-app&gt;/cgi-bin/dir1/script/pathinfo1
  74. * </code>
  75. * </p>
  76. * <p>
  77. * would result in the execution of the script
  78. * </p>
  79. * <p>
  80. * <code>
  81. * &lt;web-app-root&gt;/WEB-INF/cgi/dir1/script
  82. * </code>
  83. * </p>
  84. * <p>
  85. * with the script's <code>PATH_INFO</code> set to <code>/pathinfo1</code>.
  86. * </p>
  87. * <p>
  88. * Recommendation: House all your CGI scripts under
  89. * <code>&lt;webapp&gt;/WEB-INF/cgi</code>. This will ensure that you do not
  90. * accidentally expose your cgi scripts' code to the outside world and that
  91. * your cgis will be cleanly ensconced underneath the WEB-INF (i.e.,
  92. * non-content) area.
  93. * </p>
  94. * <p>
  95. * The default CGI location is mentioned above. You have the flexibility to
  96. * put CGIs wherever you want, however:
  97. * </p>
  98. * <p>
  99. * The CGI search path will start at
  100. * webAppRootDir + File.separator + cgiPathPrefix
  101. * (or webAppRootDir alone if cgiPathPrefix is
  102. * null).
  103. * </p>
  104. * <p>
  105. * cgiPathPrefix is defined by setting
  106. * this servlet's cgiPathPrefix init parameter
  107. * </p>
  108. *
  109. * <p>
  110. *
  111. * <B>CGI Specification</B>:<br> derived from
  112. * <a href="http://cgi-spec.golux.com">http://cgi-spec.golux.com</a>.
  113. * A work-in-progress & expired Internet Draft. Note no actual RFC describing
  114. * the CGI specification exists. Where the behavior of this servlet differs
  115. * from the specification cited above, it is either documented here, a bug,
  116. * or an instance where the specification cited differs from Best
  117. * Community Practice (BCP).
  118. * Such instances should be well-documented here. Please email the
  119. * <a href="http://tomcat.apache.org/lists.html#tomcat-dev">Tomcat group [dev@tomcat.apache.org]</a>
  120. * with amendments.
  121. *
  122. * </p>
  123. * <p>
  124. *
  125. * <b>Canonical metavariables</b>:<br>
  126. * The CGI specification defines the following canonical metavariables:
  127. * <br>
  128. * [excerpt from CGI specification]
  129. * <PRE>
  130. * AUTH_TYPE
  131. * CONTENT_LENGTH
  132. * CONTENT_TYPE
  133. * GATEWAY_INTERFACE
  134. * PATH_INFO
  135. * PATH_TRANSLATED
  136. * QUERY_STRING
  137. * REMOTE_ADDR
  138. * REMOTE_HOST
  139. * REMOTE_IDENT
  140. * REMOTE_USER
  141. * REQUEST_METHOD
  142. * SCRIPT_NAME
  143. * SERVER_NAME
  144. * SERVER_PORT
  145. * SERVER_PROTOCOL
  146. * SERVER_SOFTWARE
  147. * </PRE>
  148. * <p>
  149. * Metavariables with names beginning with the protocol name (<EM>e.g.</EM>,
  150. * "HTTP_ACCEPT") are also canonical in their description of request header
  151. * fields. The number and meaning of these fields may change independently
  152. * of this specification. (See also section 6.1.5 [of the CGI specification].)
  153. * </p>
  154. * [end excerpt]
  155. *
  156. * </p>
  157. * <h2> Implementation notes</h2>
  158. * <p>
  159. *
  160. * <b>standard input handling</b>: If your script accepts standard input,
  161. * then the client must start sending input within a certain timeout period,
  162. * otherwise the servlet will assume no input is coming and carry on running
  163. * the script. The script's the standard input will be closed and handling of
  164. * any further input from the client is undefined. Most likely it will be
  165. * ignored. If this behavior becomes undesirable, then this servlet needs
  166. * to be enhanced to handle threading of the spawned process' stdin, stdout,
  167. * and stderr (which should not be too hard).
  168. * <br>
  169. * If you find your cgi scripts are timing out receiving input, you can set
  170. * the init parameter <code></code> of your webapps' cgi-handling servlet
  171. * to be
  172. * </p>
  173. * <p>
  174. *
  175. * <b>Metavariable Values</b>: According to the CGI specificion,
  176. * implementations may choose to represent both null or missing values in an
  177. * implementation-specific manner, but must define that manner. This
  178. * implementation chooses to always define all required metavariables, but
  179. * set the value to "" for all metavariables whose value is either null or
  180. * undefined. PATH_TRANSLATED is the sole exception to this rule, as per the
  181. * CGI Specification.
  182. *
  183. * </p>
  184. * <p>
  185. *
  186. * <b>NPH -- Non-parsed-header implementation</b>: This implementation does
  187. * not support the CGI NPH concept, whereby server ensures that the data
  188. * supplied to the script are preceisely as supplied by the client and
  189. * unaltered by the server.
  190. * </p>
  191. * <p>
  192. * The function of a servlet container (including Tomcat) is specifically
  193. * designed to parse and possible alter CGI-specific variables, and as
  194. * such makes NPH functionality difficult to support.
  195. * </p>
  196. * <p>
  197. * The CGI specification states that compliant servers MAY support NPH output.
  198. * It does not state servers MUST support NPH output to be unconditionally
  199. * compliant. Thus, this implementation maintains unconditional compliance
  200. * with the specification though NPH support is not present.
  201. * </p>
  202. * <p>
  203. *
  204. * The CGI specification is located at
  205. * <a href="http://cgi-spec.golux.com">http://cgi-spec.golux.com</a>.
  206. *
  207. * </p>
  208. * <p>
  209. * <h3>TODO:</h3>
  210. * <ul>
  211. * <li> Support for setting headers (for example, Location headers don't work)
  212. * <li> Support for collapsing multiple header lines (per RFC 2616)
  213. * <li> Ensure handling of POST method does not interfere with 2.3 Filters
  214. * <li> Refactor some debug code out of core
  215. * <li> Ensure header handling preserves encoding
  216. * <li> Possibly rewrite CGIRunner.run()?
  217. * <li> Possibly refactor CGIRunner and CGIEnvironment as non-inner classes?
  218. * <li> Document handling of cgi stdin when there is no stdin
  219. * <li> Revisit IOException handling in CGIRunner.run()
  220. * <li> Better documentation
  221. * <li> Confirm use of ServletInputStream.available() in CGIRunner.run() is
  222. * not needed
  223. * <li> Make checking for "." and ".." in servlet & cgi PATH_INFO less
  224. * draconian
  225. * <li> [add more to this TODO list]
  226. * </ul>
  227. * </p>
  228. *
  229. * @author Martin T Dengler [root@martindengler.com]
  230. * @author Amy Roh
  231. * @version $Id$
  232. * @since Tomcat 4.0
  233. *
  234. */
  235. public final class CGIServlet extends HttpServlet {
  236. /* some vars below copied from Craig R. McClanahan's InvokerServlet */
  237. /** the debugging detail level for this servlet. */
  238. private int debug = 0;
  239. /**
  240. * The CGI search path will start at
  241. * webAppRootDir + File.separator + cgiPathPrefix
  242. * (or webAppRootDir alone if cgiPathPrefix is
  243. * null)
  244. */
  245. private String cgiPathPrefix = null;
  246. /** the executable to use with the script */
  247. private String cgiExecutable = "perl";
  248. /** the encoding to use for parameters */
  249. private String parameterEncoding = System.getProperty("file.encoding",
  250. "UTF-8");
  251. /** object used to ensure multiple threads don't try to expand same file */
  252. static Object expandFileLock = new Object();
  253. /** the shell environment variables to be passed to the CGI script */
  254. static Hashtable shellEnv = new Hashtable();
  255. /**
  256. * Sets instance variables.
  257. * <P>
  258. * Modified from Craig R. McClanahan's InvokerServlet
  259. * </P>
  260. *
  261. * @param config a <code>ServletConfig</code> object
  262. * containing the servlet's
  263. * configuration and initialization
  264. * parameters
  265. *
  266. * @exception ServletException if an exception has occurred that
  267. * interferes with the servlet's normal
  268. * operation
  269. */
  270. public void init(ServletConfig config) throws ServletException {
  271. super.init(config);
  272. // Verify that we were not accessed using the invoker servlet
  273. String servletName = getServletConfig().getServletName();
  274. if (servletName == null)
  275. servletName = "";
  276. if (servletName.startsWith("org.apache.catalina.INVOKER."))
  277. throw new UnavailableException
  278. ("Cannot invoke CGIServlet through the invoker");
  279. boolean passShellEnvironment = false;
  280. // Set our properties from the initialization parameters
  281. String value = null;
  282. try {
  283. value = getServletConfig().getInitParameter("debug");
  284. debug = Integer.parseInt(value);
  285. cgiPathPrefix =
  286. getServletConfig().getInitParameter("cgiPathPrefix");
  287. value = getServletConfig().getInitParameter("passShellEnvironment");
  288. passShellEnvironment = Boolean.valueOf(value).booleanValue();
  289. } catch (Throwable t) {
  290. //NOOP
  291. }
  292. if (passShellEnvironment) {
  293. try {
  294. shellEnv.putAll(getShellEnvironment());
  295. } catch (IOException ioe) {
  296. ServletException e = new ServletException(
  297. "Unable to read shell environment variables", ioe);
  298. throw e;
  299. }
  300. }
  301. value = getServletConfig().getInitParameter("executable");
  302. if (value != null) {
  303. cgiExecutable = value;
  304. }
  305. value = getServletConfig().getInitParameter("parameterEncoding");
  306. if (value != null) {
  307. parameterEncoding = value;
  308. }
  309. }
  310. /**
  311. * Prints out important Servlet API and container information
  312. *
  313. * <p>
  314. * Copied from SnoopAllServlet by Craig R. McClanahan
  315. * </p>
  316. *
  317. * @param out ServletOutputStream as target of the information
  318. * @param req HttpServletRequest object used as source of information
  319. * @param res HttpServletResponse object currently not used but could
  320. * provide future information
  321. *
  322. * @exception IOException if a write operation exception occurs
  323. *
  324. */
  325. protected void printServletEnvironment(ServletOutputStream out,
  326. HttpServletRequest req, HttpServletResponse res) throws IOException {
  327. // Document the properties from ServletRequest
  328. out.println("<h1>ServletRequest Properties</h1>");
  329. out.println("<ul>");
  330. Enumeration attrs = req.getAttributeNames();
  331. while (attrs.hasMoreElements()) {
  332. String attr = (String) attrs.nextElement();
  333. out.println("<li><b>attribute</b> " + attr + " = " +
  334. req.getAttribute(attr));
  335. }
  336. out.println("<li><b>characterEncoding</b> = " +
  337. req.getCharacterEncoding());
  338. out.println("<li><b>contentLength</b> = " +
  339. req.getContentLength());
  340. out.println("<li><b>contentType</b> = " +
  341. req.getContentType());
  342. Enumeration locales = req.getLocales();
  343. while (locales.hasMoreElements()) {
  344. Locale locale = (Locale) locales.nextElement();
  345. out.println("<li><b>locale</b> = " + locale);
  346. }
  347. Enumeration params = req.getParameterNames();
  348. while (params.hasMoreElements()) {
  349. String param = (String) params.nextElement();
  350. String values[] = req.getParameterValues(param);
  351. for (int i = 0; i < values.length; i++)
  352. out.println("<li><b>parameter</b> " + param + " = " +
  353. values[i]);
  354. }
  355. out.println("<li><b>protocol</b> = " + req.getProtocol());
  356. out.println("<li><b>remoteAddr</b> = " + req.getRemoteAddr());
  357. out.println("<li><b>remoteHost</b> = " + req.getRemoteHost());
  358. out.println("<li><b>scheme</b> = " + req.getScheme());
  359. out.println("<li><b>secure</b> = " + req.isSecure());
  360. out.println("<li><b>serverName</b> = " + req.getServerName());
  361. out.println("<li><b>serverPort</b> = " + req.getServerPort());
  362. out.println("</ul>");
  363. out.println("<hr>");
  364. // Document the properties from HttpServletRequest
  365. out.println("<h1>HttpServletRequest Properties</h1>");
  366. out.println("<ul>");
  367. out.println("<li><b>authType</b> = " + req.getAuthType());
  368. out.println("<li><b>contextPath</b> = " +
  369. req.getContextPath());
  370. Cookie cookies[] = req.getCookies();
  371. if (cookies!=null) {
  372. for (int i = 0; i < cookies.length; i++)
  373. out.println("<li><b>cookie</b> " + cookies[i].getName() +" = " +cookies[i].getValue());
  374. }
  375. Enumeration headers = req.getHeaderNames();
  376. while (headers.hasMoreElements()) {
  377. String header = (String) headers.nextElement();
  378. out.println("<li><b>header</b> " + header + " = " +
  379. req.getHeader(header));
  380. }
  381. out.println("<li><b>method</b> = " + req.getMethod());
  382. out.println("<li><a name=\"pathInfo\"><b>pathInfo</b></a> = "
  383. + req.getPathInfo());
  384. out.println("<li><b>pathTranslated</b> = " +
  385. req.getPathTranslated());
  386. out.println("<li><b>queryString</b> = " +
  387. req.getQueryString());
  388. out.println("<li><b>remoteUser</b> = " +
  389. req.getRemoteUser());
  390. out.println("<li><b>requestedSessionId</b> = " +
  391. req.getRequestedSessionId());
  392. out.println("<li><b>requestedSessionIdFromCookie</b> = " +
  393. req.isRequestedSessionIdFromCookie());
  394. out.println("<li><b>requestedSessionIdFromURL</b> = " +
  395. req.isRequestedSessionIdFromURL());
  396. out.println("<li><b>requestedSessionIdValid</b> = " +
  397. req.isRequestedSessionIdValid());
  398. out.println("<li><b>requestURI</b> = " +
  399. req.getRequestURI());
  400. out.println("<li><b>servletPath</b> = " +
  401. req.getServletPath());
  402. out.println("<li><b>userPrincipal</b> = " +
  403. req.getUserPrincipal());
  404. out.println("</ul>");
  405. out.println("<hr>");
  406. // Document the servlet request attributes
  407. out.println("<h1>ServletRequest Attributes</h1>");
  408. out.println("<ul>");
  409. attrs = req.getAttributeNames();
  410. while (attrs.hasMoreElements()) {
  411. String attr = (String) attrs.nextElement();
  412. out.println("<li><b>" + attr + "</b> = " +
  413. req.getAttribute(attr));
  414. }
  415. out.println("</ul>");
  416. out.println("<hr>");
  417. // Process the current session (if there is one)
  418. HttpSession session = req.getSession(false);
  419. if (session != null) {
  420. // Document the session properties
  421. out.println("<h1>HttpSession Properties</h1>");
  422. out.println("<ul>");
  423. out.println("<li><b>id</b> = " +
  424. session.getId());
  425. out.println("<li><b>creationTime</b> = " +
  426. new Date(session.getCreationTime()));
  427. out.println("<li><b>lastAccessedTime</b> = " +
  428. new Date(session.getLastAccessedTime()));
  429. out.println("<li><b>maxInactiveInterval</b> = " +
  430. session.getMaxInactiveInterval());
  431. out.println("</ul>");
  432. out.println("<hr>");
  433. // Document the session attributes
  434. out.println("<h1>HttpSession Attributes</h1>");
  435. out.println("<ul>");
  436. attrs = session.getAttributeNames();
  437. while (attrs.hasMoreElements()) {
  438. String attr = (String) attrs.nextElement();
  439. out.println("<li><b>" + attr + "</b> = " +
  440. session.getAttribute(attr));
  441. }
  442. out.println("</ul>");
  443. out.println("<hr>");
  444. }
  445. // Document the servlet configuration properties
  446. out.println("<h1>ServletConfig Properties</h1>");
  447. out.println("<ul>");
  448. out.println("<li><b>servletName</b> = " +
  449. getServletConfig().getServletName());
  450. out.println("</ul>");
  451. out.println("<hr>");
  452. // Document the servlet configuration initialization parameters
  453. out.println("<h1>ServletConfig Initialization Parameters</h1>");
  454. out.println("<ul>");
  455. params = getServletConfig().getInitParameterNames();
  456. while (params.hasMoreElements()) {
  457. String param = (String) params.nextElement();
  458. String value = getServletConfig().getInitParameter(param);
  459. out.println("<li><b>" + param + "</b> = " + value);
  460. }
  461. out.println("</ul>");
  462. out.println("<hr>");
  463. // Document the servlet context properties
  464. out.println("<h1>ServletContext Properties</h1>");
  465. out.println("<ul>");
  466. out.println("<li><b>majorVersion</b> = " +
  467. getServletContext().getMajorVersion());
  468. out.println("<li><b>minorVersion</b> = " +
  469. getServletContext().getMinorVersion());
  470. out.println("<li><b>realPath('/')</b> = " +
  471. getServletContext().getRealPath("/"));
  472. out.println("<li><b>serverInfo</b> = " +
  473. getServletContext().getServerInfo());
  474. out.println("</ul>");
  475. out.println("<hr>");
  476. // Document the servlet context initialization parameters
  477. out.println("<h1>ServletContext Initialization Parameters</h1>");
  478. out.println("<ul>");
  479. params = getServletContext().getInitParameterNames();
  480. while (params.hasMoreElements()) {
  481. String param = (String) params.nextElement();
  482. String value = getServletContext().getInitParameter(param);
  483. out.println("<li><b>" + param + "</b> = " + value);
  484. }
  485. out.println("</ul>");
  486. out.println("<hr>");
  487. // Document the servlet context attributes
  488. out.println("<h1>ServletContext Attributes</h1>");
  489. out.println("<ul>");
  490. attrs = getServletContext().getAttributeNames();
  491. while (attrs.hasMoreElements()) {
  492. String attr = (String) attrs.nextElement();
  493. out.println("<li><b>" + attr + "</b> = " +
  494. getServletContext().getAttribute(attr));
  495. }
  496. out.println("</ul>");
  497. out.println("<hr>");
  498. }
  499. /**
  500. * Provides CGI Gateway service -- delegates to <code>doGet</code>
  501. *
  502. * @param req HttpServletRequest passed in by servlet container
  503. * @param res HttpServletResponse passed in by servlet container
  504. *
  505. * @exception ServletException if a servlet-specific exception occurs
  506. * @exception IOException if a read/write exception occurs
  507. *
  508. * @see javax.servlet.http.HttpServlet
  509. *
  510. */
  511. protected void doPost(HttpServletRequest req, HttpServletResponse res)
  512. throws IOException, ServletException {
  513. doGet(req, res);
  514. }
  515. /**
  516. * Provides CGI Gateway service
  517. *
  518. * @param req HttpServletRequest passed in by servlet container
  519. * @param res HttpServletResponse passed in by servlet container
  520. *
  521. * @exception ServletException if a servlet-specific exception occurs
  522. * @exception IOException if a read/write exception occurs
  523. *
  524. * @see javax.servlet.http.HttpServlet
  525. *
  526. */
  527. protected void doGet(HttpServletRequest req, HttpServletResponse res)
  528. throws ServletException, IOException {
  529. // Verify that we were not accessed using the invoker servlet
  530. if (req.getAttribute(Globals.INVOKED_ATTR) != null)
  531. throw new UnavailableException
  532. ("Cannot invoke CGIServlet through the invoker");
  533. CGIEnvironment cgiEnv = new CGIEnvironment(req, getServletContext());
  534. if (cgiEnv.isValid()) {
  535. CGIRunner cgi = new CGIRunner(cgiEnv.getCommand(),
  536. cgiEnv.getEnvironment(),
  537. cgiEnv.getWorkingDirectory(),
  538. cgiEnv.getParameters());
  539. //if POST, we need to cgi.setInput
  540. //REMIND: how does this interact with Servlet API 2.3's Filters?!
  541. if ("POST".equals(req.getMethod())) {
  542. cgi.setInput(req.getInputStream());
  543. }
  544. cgi.setResponse(res);
  545. cgi.run();
  546. }
  547. if (!cgiEnv.isValid()) {
  548. res.setStatus(404);
  549. }
  550. if (debug >= 10) {
  551. try {
  552. ServletOutputStream out = res.getOutputStream();
  553. out.println("<HTML><HEAD><TITLE>$Name$</TITLE></HEAD>");
  554. out.println("<BODY>$Header$<p>");
  555. if (cgiEnv.isValid()) {
  556. out.println(cgiEnv.toString());
  557. } else {
  558. out.println("<H3>");
  559. out.println("CGI script not found or not specified.");
  560. out.println("</H3>");
  561. out.println("<H4>");
  562. out.println("Check the <b>HttpServletRequest ");
  563. out.println("<a href=\"#pathInfo\">pathInfo</a></b> ");
  564. out.println("property to see if it is what you meant ");
  565. out.println("it to be. You must specify an existant ");
  566. out.println("and executable file as part of the ");
  567. out.println("path-info.");
  568. out.println("</H4>");
  569. out.println("<H4>");
  570. out.println("For a good discussion of how CGI scripts ");
  571. out.println("work and what their environment variables ");
  572. out.println("mean, please visit the <a ");
  573. out.println("href=\"http://cgi-spec.golux.com\">CGI ");
  574. out.println("Specification page</a>.");
  575. out.println("</H4>");
  576. }
  577. printServletEnvironment(out, req, res);
  578. out.println("</BODY></HTML>");
  579. } catch (IOException ignored) {
  580. }
  581. } //debugging
  582. } //doGet
  583. /** For future testing use only; does nothing right now */
  584. public static void main(String[] args) {
  585. System.out.println("$Header$");
  586. }
  587. /**
  588. * Get all shell environment variables. Have to do it this rather ugly way
  589. * as the API to obtain is not available in 1.4 and earlier APIs.
  590. *
  591. * See <a href="http://www.rgagnon.com/javadetails/java-0150.html">Read environment
  592. * variables from an application</a> for original source and article.
  593. */
  594. private Hashtable getShellEnvironment() throws IOException {
  595. Hashtable envVars = new Hashtable();
  596. Process p = null;
  597. Runtime r = Runtime.getRuntime();
  598. String OS = System.getProperty("os.name").toLowerCase();
  599. boolean ignoreCase;
  600. if (OS.indexOf("windows 9") > -1) {
  601. p = r.exec( "command.com /c set" );
  602. ignoreCase = true;
  603. } else if ((OS.indexOf("nt") > -1) || (OS.indexOf("windows") > -1) ) {
  604. // thanks to JuanFran for the xp fix!
  605. p = r.exec( "cmd.exe /c set" );
  606. ignoreCase = true;
  607. } else {
  608. // our last hope, we assume Unix (thanks to H. Ware for the fix)
  609. p = r.exec( "env" );
  610. ignoreCase = false;
  611. }
  612. BufferedReader br = new BufferedReader
  613. ( new InputStreamReader( p.getInputStream() ) );
  614. String line;
  615. while( (line = br.readLine()) != null ) {
  616. int idx = line.indexOf( '=' );
  617. String key = line.substring( 0, idx );
  618. String value = line.substring( idx+1 );
  619. if (ignoreCase) {
  620. key = key.toUpperCase();
  621. }
  622. envVars.put(key, value);
  623. }
  624. return envVars;
  625. }
  626. /**
  627. * Encapsulates the CGI environment and rules to derive
  628. * that environment from the servlet container and request information.
  629. *
  630. * <p>
  631. * </p>
  632. *
  633. * @version $Id$
  634. * @since Tomcat 4.0
  635. *
  636. */
  637. protected class CGIEnvironment {
  638. /** context of the enclosing servlet */
  639. private ServletContext context = null;
  640. /** context path of enclosing servlet */
  641. private String contextPath = null;
  642. /** servlet URI of the enclosing servlet */
  643. private String servletPath = null;
  644. /** pathInfo for the current request */
  645. private String pathInfo = null;
  646. /** real file system directory of the enclosing servlet's web app */
  647. private String webAppRootDir = null;
  648. /** tempdir for context - used to expand scripts in unexpanded wars */
  649. private File tmpDir = null;
  650. /** derived cgi environment */
  651. private Hashtable env = null;
  652. /** cgi command to be invoked */
  653. private String command = null;
  654. /** cgi command's desired working directory */
  655. private File workingDirectory = null;
  656. /** cgi command's command line parameters */
  657. private ArrayList cmdLineParameters = new ArrayList();
  658. /** whether or not this object is valid or not */
  659. private boolean valid = false;
  660. /**
  661. * Creates a CGIEnvironment and derives the necessary environment,
  662. * query parameters, working directory, cgi command, etc.
  663. *
  664. * @param req HttpServletRequest for information provided by
  665. * the Servlet API
  666. * @param context ServletContext for information provided by the
  667. * Servlet API
  668. *
  669. */
  670. protected CGIEnvironment(HttpServletRequest req,
  671. ServletContext context) throws IOException {
  672. setupFromContext(context);
  673. setupFromRequest(req);
  674. this.valid = setCGIEnvironment(req);
  675. if (this.valid) {
  676. workingDirectory = new File(command.substring(0,
  677. command.lastIndexOf(File.separator)));
  678. }
  679. }
  680. /**
  681. * Uses the ServletContext to set some CGI variables
  682. *
  683. * @param context ServletContext for information provided by the
  684. * Servlet API
  685. */
  686. protected void setupFromContext(ServletContext context) {
  687. this.context = context;
  688. this.webAppRootDir = context.getRealPath("/");
  689. this.tmpDir = (File) context.getAttribute(Globals.WORK_DIR_ATTR);
  690. }
  691. /**
  692. * Uses the HttpServletRequest to set most CGI variables
  693. *
  694. * @param req HttpServletRequest for information provided by
  695. * the Servlet API
  696. * @throws UnsupportedEncodingException
  697. */
  698. protected void setupFromRequest(HttpServletRequest req)
  699. throws UnsupportedEncodingException {
  700. boolean isIncluded = false;
  701. // Look to see if this request is an include
  702. if (req.getAttribute(Globals.INCLUDE_REQUEST_URI_ATTR) != null) {
  703. isIncluded = true;
  704. }
  705. if (isIncluded) {
  706. this.contextPath = (String) req.getAttribute(
  707. Globals.INCLUDE_CONTEXT_PATH_ATTR);
  708. this.servletPath = (String) req.getAttribute(
  709. Globals.INCLUDE_SERVLET_PATH_ATTR);
  710. this.pathInfo = (String) req.getAttribute(
  711. Globals.INCLUDE_PATH_INFO_ATTR);
  712. } else {
  713. this.contextPath = req.getContextPath();
  714. this.servletPath = req.getServletPath();
  715. this.pathInfo = req.getPathInfo();
  716. }
  717. // If getPathInfo() returns null, must be using extension mapping
  718. // In this case, pathInfo should be same as servletPath
  719. if (this.pathInfo == null) {
  720. this.pathInfo = this.servletPath;
  721. }
  722. // If the request method is GET, POST or HEAD and the query string
  723. // does not contain an unencoded "=" this is an indexed query.
  724. // The parsed query string becomes the command line parameters
  725. // for the cgi command.
  726. if (req.getMethod().equals("GET")
  727. || req.getMethod().equals("POST")
  728. || req.getMethod().equals("HEAD")) {
  729. String qs;
  730. if (isIncluded) {
  731. qs = (String) req.getAttribute(
  732. Globals.INCLUDE_QUERY_STRING_ATTR);
  733. } else {
  734. qs = req.getQueryString();
  735. }
  736. if (qs != null && qs.indexOf("=") == -1) {
  737. StringTokenizer qsTokens = new StringTokenizer(qs, "+");
  738. while ( qsTokens.hasMoreTokens() ) {
  739. cmdLineParameters.add(URLDecoder.decode(qsTokens.nextToken(),
  740. parameterEncoding));
  741. }
  742. }
  743. }
  744. }
  745. /**
  746. * Resolves core information about the cgi script.
  747. *
  748. * <p>
  749. * Example URI:
  750. * <PRE> /servlet/cgigateway/dir1/realCGIscript/pathinfo1 </PRE>
  751. * <ul>
  752. * <LI><b>path</b> = $CATALINA_HOME/mywebapp/dir1/realCGIscript
  753. * <LI><b>scriptName</b> = /servlet/cgigateway/dir1/realCGIscript
  754. * <LI><b>cgiName</b> = /dir1/realCGIscript
  755. * <LI><b>name</b> = realCGIscript
  756. * </ul>
  757. * </p>
  758. * <p>
  759. * CGI search algorithm: search the real path below
  760. * &lt;my-webapp-root&gt; and find the first non-directory in
  761. * the getPathTranslated("/"), reading/searching from left-to-right.
  762. *</p>
  763. *<p>
  764. * The CGI search path will start at
  765. * webAppRootDir + File.separator + cgiPathPrefix
  766. * (or webAppRootDir alone if cgiPathPrefix is
  767. * null).
  768. *</p>
  769. *<p>
  770. * cgiPathPrefix is defined by setting
  771. * this servlet's cgiPathPrefix init parameter
  772. *
  773. *</p>
  774. *
  775. * @param pathInfo String from HttpServletRequest.getPathInfo()
  776. * @param webAppRootDir String from context.getRealPath("/")
  777. * @param contextPath String as from
  778. * HttpServletRequest.getContextPath()
  779. * @param servletPath String as from
  780. * HttpServletRequest.getServletPath()
  781. * @param cgiPathPrefix subdirectory of webAppRootDir below which
  782. * the web app's CGIs may be stored; can be null.
  783. * The CGI search path will start at
  784. * webAppRootDir + File.separator + cgiPathPrefix
  785. * (or webAppRootDir alone if cgiPathPrefix is
  786. * null). cgiPathPrefix is defined by setting
  787. * the servlet's cgiPathPrefix init parameter.
  788. *
  789. *
  790. * @return
  791. * <ul>
  792. * <li>
  793. * <code>path</code> - full file-system path to valid cgi script,
  794. * or null if no cgi was found
  795. * <li>
  796. * <code>scriptName</code> -
  797. * CGI variable SCRIPT_NAME; the full URL path
  798. * to valid cgi script or null if no cgi was
  799. * found
  800. * <li>
  801. * <code>cgiName</code> - servlet pathInfo fragment corresponding to
  802. * the cgi script itself, or null if not found
  803. * <li>
  804. * <code>name</code> - simple name (no directories) of the
  805. * cgi script, or null if no cgi was found
  806. * </ul>
  807. *
  808. * @since Tomcat 4.0
  809. */
  810. protected String[] findCGI(String pathInfo, String webAppRootDir,
  811. String contextPath, String servletPath,
  812. String cgiPathPrefix) {
  813. String path = null;
  814. String name = null;
  815. String scriptname = null;
  816. String cginame = "";
  817. if ((webAppRootDir != null)
  818. && (webAppRootDir.lastIndexOf(File.separator) ==
  819. (webAppRootDir.length() - 1))) {
  820. //strip the trailing "/" from the webAppRootDir
  821. webAppRootDir =
  822. webAppRootDir.substring(0, (webAppRootDir.length() - 1));
  823. }
  824. if (cgiPathPrefix != null) {
  825. webAppRootDir = webAppRootDir + File.separator
  826. + cgiPathPrefix;
  827. }
  828. if (debug >= 2) {
  829. log("findCGI: path=" + pathInfo + ", " + webAppRootDir);
  830. }
  831. File currentLocation = new File(webAppRootDir);
  832. StringTokenizer dirWalker =
  833. new StringTokenizer(pathInfo, "/");
  834. if (debug >= 3) {
  835. log("findCGI: currentLoc=" + currentLocation);
  836. }
  837. while (!currentLocation.isFile() && dirWalker.hasMoreElements()) {
  838. if (debug >= 3) {
  839. log("findCGI: currentLoc=" + currentLocation);
  840. }
  841. String nextElement = (String) dirWalker.nextElement();
  842. currentLocation = new File(currentLocation, nextElement);
  843. cginame = cginame + "/" + nextElement;
  844. }
  845. if (!currentLocation.isFile()) {
  846. return new String[] { null, null, null, null };
  847. } else {
  848. if (debug >= 2) {
  849. log("findCGI: FOUND cgi at " + currentLocation);
  850. }
  851. path = currentLocation.getAbsolutePath();
  852. name = currentLocation.getName();
  853. if (".".equals(contextPath)) {
  854. scriptname = servletPath;
  855. } else {
  856. scriptname = contextPath + servletPath;
  857. }
  858. if (!servletPath.equals(cginame)) {
  859. scriptname = scriptname + cginame;
  860. }
  861. }
  862. if (debug >= 1) {
  863. log("findCGI calc: name=" + name + ", path=" + path
  864. + ", scriptname=" + scriptname + ", cginame=" + cginame);
  865. }
  866. return new String[] { path, scriptname, cginame, name };
  867. }
  868. /**
  869. * Constructs the CGI environment to be supplied to the invoked CGI
  870. * script; relies heavliy on Servlet API methods and findCGI
  871. *
  872. * @param req request associated with the CGI
  873. * invokation
  874. *
  875. * @return true if environment was set OK, false if there
  876. * was a problem and no environment was set
  877. */
  878. protected boolean setCGIEnvironment(HttpServletRequest req) throws IOException {
  879. /*
  880. * This method is slightly ugly; c'est la vie.
  881. * "You cannot stop [ugliness], you can only hope to contain [it]"
  882. * (apologies to Marv Albert regarding MJ)
  883. */
  884. Hashtable envp = new Hashtable();
  885. // Add the shell environment variables (if any)
  886. envp.putAll(shellEnv);
  887. // Add the CGI environment variables
  888. String sPathInfoOrig = null;
  889. String sPathTranslatedOrig = null;
  890. String sPathInfoCGI = null;
  891. String sPathTranslatedCGI = null;
  892. String sCGIFullPath = null;
  893. String sCGIScriptName = null;
  894. String sCGIFullName = null;
  895. String sCGIName = null;
  896. String[] sCGINames;
  897. sPathInfoOrig = this.pathInfo;
  898. sPathInfoOrig = sPathInfoOrig == null ? "" : sPathInfoOrig;
  899. sPathTranslatedOrig = req.getPathTranslated();
  900. sPathTranslatedOrig =
  901. sPathTranslatedOrig == null ? "" : sPathTranslatedOrig;
  902. if (webAppRootDir == null ) {
  903. // The app has not been deployed in exploded form
  904. webAppRootDir = tmpDir.toString();
  905. expandCGIScript();
  906. }
  907. sCGINames = findCGI(sPathInfoOrig,
  908. webAppRootDir,
  909. contextPath,
  910. servletPath,
  911. cgiPathPrefix);
  912. sCGIFullPath = sCGINames[0];
  913. sCGIScriptName = sCGINames[1];
  914. sCGIFullName = sCGINames[2];
  915. sCGIName = sCGINames[3];
  916. if (sCGIFullPath == null
  917. || sCGIScriptName == null
  918. || sCGIFullName == null
  919. || sCGIName == null) {
  920. return false;
  921. }
  922. envp.put("SERVER_SOFTWARE", "TOMCAT");
  923. envp.put("SERVER_NAME", nullsToBlanks(req.getServerName()));
  924. envp.put("GATEWAY_INTERFACE", "CGI/1.1");
  925. envp.put("SERVER_PROTOCOL", nullsToBlanks(req.getProtocol()));
  926. int port = req.getServerPort();
  927. Integer iPort = (port == 0 ? new Integer(-1) : new Integer(port));
  928. envp.put("SERVER_PORT", iPort.toString());
  929. envp.put("REQUEST_METHOD", nullsToBlanks(req.getMethod()));
  930. envp.put("REQUEST_URI", nullsToBlanks(req.getRequestURI()));
  931. /*-
  932. * PATH_INFO should be determined by using sCGIFullName:
  933. * 1) Let sCGIFullName not end in a "/" (see method findCGI)
  934. * 2) Let sCGIFullName equal the pathInfo fragment which
  935. * corresponds to the actual cgi script.
  936. * 3) Thus, PATH_INFO = request.getPathInfo().substring(
  937. * sCGIFullName.length())
  938. *
  939. * (see method findCGI, where the real work is done)
  940. *
  941. */
  942. if (pathInfo == null
  943. || (pathInfo.substring(sCGIFullName.length()).length() <= 0)) {
  944. sPathInfoCGI = "";
  945. } else {
  946. sPathInfoCGI = pathInfo.substring(sCGIFullName.length());
  947. }
  948. envp.put("PATH_INFO", sPathInfoCGI);
  949. /*-
  950. * PATH_TRANSLATED must be determined after PATH_INFO (and the
  951. * implied real cgi-script) has been taken into account.
  952. *
  953. * The following example demonstrates:
  954. *
  955. * servlet info = /servlet/cgigw/dir1/dir2/cgi1/trans1/trans2
  956. * cgifullpath = /servlet/cgigw/dir1/dir2/cgi1
  957. * path_info = /trans1/trans2
  958. * webAppRootDir = servletContext.getRealPath("/")
  959. *
  960. * path_translated = servletContext.getRealPath("/trans1/trans2")
  961. *
  962. * That is, PATH_TRANSLATED = webAppRootDir + sPathInfoCGI
  963. * (unless sPathInfoCGI is null or blank, then the CGI
  964. * specification dictates that the PATH_TRANSLATED metavariable
  965. * SHOULD NOT be defined.
  966. *
  967. */
  968. if (sPathInfoCGI != null && !("".equals(sPathInfoCGI))) {
  969. sPathTranslatedCGI = context.getRealPath(sPathInfoCGI);
  970. } else {
  971. sPathTranslatedCGI = null;
  972. }
  973. if (sPathTranslatedCGI == null || "".equals(sPathTranslatedCGI)) {
  974. //NOOP
  975. } else {
  976. envp.put("PATH_TRANSLATED", nullsToBlanks(sPathTranslatedCGI));
  977. }
  978. envp.put("SCRIPT_NAME", nullsToBlanks(sCGIScriptName));
  979. envp.put("QUERY_STRING", nullsToBlanks(req.getQueryString()));
  980. envp.put("REMOTE_HOST", nullsToBlanks(req.getRemoteHost()));
  981. envp.put("REMOTE_ADDR", nullsToBlanks(req.getRemoteAddr()));
  982. envp.put("AUTH_TYPE", nullsToBlanks(req.getAuthType()));
  983. envp.put("REMOTE_USER", nullsToBlanks(req.getRemoteUser()));
  984. envp.put("REMOTE_IDENT", ""); //not necessary for full compliance
  985. envp.put("CONTENT_TYPE", nullsToBlanks(req.getContentType()));
  986. /* Note CGI spec says CONTENT_LENGTH must be NULL ("") or undefined
  987. * if there is no content, so we cannot put 0 or -1 in as per the
  988. * Servlet API spec.
  989. */
  990. int contentLength = req.getContentLength();
  991. String sContentLength = (contentLength <= 0 ? "" :
  992. (new Integer(contentLength)).toString());
  993. envp.put("CONTENT_LENGTH", sContentLength);
  994. Enumeration headers = req.getHeaderNames();
  995. String header = null;
  996. while (headers.hasMoreElements()) {
  997. header = null;
  998. header = ((String) headers.nextElement()).toUpperCase();
  999. //REMIND: rewrite multiple headers as if received as single
  1000. //REMIND: change character set
  1001. //REMIND: I forgot what the previous REMIND means
  1002. if ("AUTHORIZATION".equalsIgnoreCase(header) ||
  1003. "PROXY_AUTHORIZATION".equalsIgnoreCase(header)) {
  1004. //NOOP per CGI specification section 11.2
  1005. } else {
  1006. envp.put("HTTP_" + header.replace('-', '_'),
  1007. req.getHeader(header));
  1008. }
  1009. }
  1010. File fCGIFullPath = new File(sCGIFullPath);
  1011. command = fCGIFullPath.getCanonicalPath();
  1012. envp.put("X_TOMCAT_SCRIPT_PATH", command); //for kicks
  1013. envp.put("SCRIPT_FILENAME", command); // for PHP
  1014. this.env = envp;
  1015. return true;
  1016. }
  1017. /**
  1018. * Extracts requested resource from web app archive to context work
  1019. * directory to enable CGI script to be executed.
  1020. */
  1021. protected void expandCGIScript() {
  1022. StringBuffer srcPath = new StringBuffer();
  1023. StringBuffer destPath = new StringBuffer();
  1024. InputStream is = null;
  1025. // paths depend on mapping
  1026. if (cgiPathPrefix == null ) {
  1027. srcPath.append(pathInfo);
  1028. is = context.getResourceAsStream(srcPath.toString());
  1029. destPath.append(tmpDir);
  1030. destPath.append(pathInfo);
  1031. } else {
  1032. // essentially same search algorithm as findCGI()
  1033. srcPath.append(cgiPathPrefix);
  1034. StringTokenizer pathWalker =
  1035. new StringTokenizer (pathInfo, "/");
  1036. // start with first element
  1037. while (pathWalker.hasMoreElements() && (is == null)) {
  1038. srcPath.append("/");
  1039. srcPath.append(pathWalker.nextElement());
  1040. is = context.getResourceAsStream(srcPath.toString());
  1041. }
  1042. destPath.append(tmpDir);
  1043. destPath.append("/");
  1044. destPath.append(srcPath);
  1045. }
  1046. if (is == null) {
  1047. // didn't find anything, give up now
  1048. if (debug >= 2) {
  1049. log("expandCGIScript: source '" + srcPath + "' not found");
  1050. }
  1051. return;
  1052. }
  1053. File f = new File(destPath.toString());
  1054. if (f.exists()) {
  1055. // Don't need to expand if it already exists
  1056. return;
  1057. }
  1058. // create directories
  1059. String dirPath = new String (destPath.toString().substring(
  1060. 0,destPath.toString().lastIndexOf("/")));
  1061. File dir = new File(dirPath);
  1062. dir.mkdirs();
  1063. try {
  1064. synchronized (expandFileLock) {
  1065. // make sure file doesn't exist
  1066. if (f.exists()) {
  1067. return;
  1068. }
  1069. // create file
  1070. if (!f.createNewFile()) {
  1071. return;
  1072. }
  1073. FileOutputStream fos = new FileOutputStream(f);
  1074. // copy data
  1075. IOTools.flow(is, fos);
  1076. is.close();
  1077. fos.close();
  1078. if (debug >= 2) {
  1079. log("expandCGIScript: expanded '" + srcPath + "' to '" + destPath + "'");
  1080. }
  1081. }
  1082. } catch (IOException ioe) {
  1083. // delete in case file is corrupted
  1084. if (f.exists()) {
  1085. f.delete();
  1086. }
  1087. }
  1088. }
  1089. /**
  1090. * Print important CGI environment information in a easy-to-read HTML
  1091. * table
  1092. *
  1093. * @return HTML string containing CGI environment info
  1094. *
  1095. */
  1096. public String toString() {
  1097. StringBuffer sb = new StringBuffer();
  1098. sb.append("<TABLE border=2>");
  1099. sb.append("<tr><th colspan=2 bgcolor=grey>");
  1100. sb.append("CGIEnvironment Info</th></tr>");
  1101. sb.append("<tr><td>Debug Level</td><td>");
  1102. sb.append(debug);
  1103. sb.append("</td></tr>");
  1104. sb.append("<tr><td>Validity:</td><td>");
  1105. sb.append(isValid());
  1106. sb.append("</td></tr>");
  1107. if (isValid()) {
  1108. Enumeration envk = env.keys();
  1109. while (envk.hasMoreElements()) {
  1110. String s = (String) envk.nextElement();
  1111. sb.append("<tr><td>");
  1112. sb.append(s);
  1113. sb.append("</td><td>");
  1114. sb.append(blanksToString((String) env.get(s),
  1115. "[will be set to blank]"));
  1116. sb.append("</td></tr>");
  1117. }
  1118. }
  1119. sb.append("<tr><td colspan=2><HR></td></tr>");
  1120. sb.append("<tr><td>Derived Command</td><td>");
  1121. sb.append(nullsToBlanks(command));
  1122. sb.append("</td></tr>");
  1123. sb.append("<tr><td>Working Directory</td><td>");
  1124. if (workingDirectory != null) {
  1125. sb.append(workingDirectory.toString());
  1126. }
  1127. sb.append("</td></tr>");
  1128. sb.append("<tr><td>Command Line Params</td><td>");
  1129. for (int i=0; i < cmdLineParameters.size(); i++) {
  1130. String param = (String) cmdLineParameters.get(i);
  1131. sb.append("<p>");
  1132. sb.append(param);
  1133. sb.append("</p>");
  1134. }
  1135. sb.append("</td></tr>");
  1136. sb.append("</TABLE><p>end.");
  1137. return sb.toString();
  1138. }
  1139. /**
  1140. * Gets derived command string
  1141. *
  1142. * @return command string
  1143. *
  1144. */
  1145. protected String getCommand() {
  1146. return command;
  1147. }
  1148. /**
  1149. * Gets derived CGI working directory
  1150. *
  1151. * @return working directory
  1152. *
  1153. */
  1154. protected File getWorkingDirectory() {
  1155. return workingDirectory;
  1156. }
  1157. /**
  1158. * Gets derived CGI environment
  1159. *
  1160. * @return CGI environment
  1161. *
  1162. */
  1163. protected Hashtable getEnvironment() {
  1164. return env;
  1165. }
  1166. /**
  1167. * Gets derived CGI query parameters
  1168. *
  1169. * @return CGI query parameters
  1170. *
  1171. */
  1172. protected ArrayList getParameters() {
  1173. r

Large files files are truncated, but you can click here to view the full file