--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"\r
+ "http://www.w3.org/TR/REC-html40/strict.dtd">\r
+<html>\r
+ <head>\r
+ <title>CgiFrame.java</title>\r
+ <meta name="Author" content="Benoit Mahe">\r
+ <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">\r
+ <meta name="Generator" content="*emacs: emacs-css">\r
+\r
+ <link rel="Stylesheet" media="screen" type="text/css" href="default-html.css">\r
+ </head>\r
+ <body>\r
+\r
+ <pre>\r
+<span class="comment">// CgiFrame.java\r
+// $Id: CgiFrame.html,v 1.1 2010/06/15 12:20:01 smhuang Exp $\r
+// (c) COPYRIGHT MIT and INRIA, 1996.\r
+// Please first read the full copyright statement in file COPYRIGHT.html\r
+</span>\r
+<span class="keyword">package</span> <span class="reference">org</span>.<span class="reference">w3c</span>.<span class="reference">jigsaw</span>.<span class="type">frames</span> ;\r
+\r
+<span class="keyword">import</span> <span class="reference">java</span>.<span class="reference">io</span>.* ;\r
+<span class="keyword">import</span> <span class="reference">java</span>.<span class="reference">util</span>.*;\r
+<span class="keyword">import</span> <span class="reference">java</span>.<span class="reference">net</span>.*;\r
+\r
+<span class="keyword">import</span> <span class="reference">org</span>.<span class="reference">w3c</span>.<span class="reference">tools</span>.<span class="reference">resources</span>.*;\r
+<span class="keyword">import</span> <span class="reference">org</span>.<span class="reference">w3c</span>.<span class="reference">tools</span>.<span class="reference">resources</span>.<span class="type">ProtocolException</span>;\r
+<span class="keyword">import</span> <span class="reference">org</span>.<span class="reference">w3c</span>.<span class="reference">www</span>.<span class="reference">mime</span>.* ;\r
+<span class="keyword">import</span> <span class="reference">org</span>.<span class="reference">w3c</span>.<span class="reference">jigsaw</span>.<span class="reference">http</span>.* ;\r
+<span class="keyword">import</span> <span class="reference">org</span>.<span class="reference">w3c</span>.<span class="reference">www</span>.<span class="reference">http</span>.*;\r
+<span class="keyword">import</span> <span class="reference">org</span>.<span class="reference">w3c</span>.<span class="reference">jigsaw</span>.<span class="reference">auth</span>.<span class="type">AuthFilter</span>;\r
+\r
+<span class="comment">/**\r
+ * Parsing the CGI output - The CGIHeaderHolder, to hold CGI headers.\r
+ */</span>\r
+<span class="keyword">class</span> <span class="function-name">CGIHeaderHolder</span> <span class="keyword">implements</span> <span class="type">MimeHeaderHolder</span> {\r
+ <span class="comment">// Status and Location deserve special treatments:\r
+</span> <span class="type">String</span> <span class="variable-name">status</span> = <span class="keyword">null</span>;\r
+ <span class="type">String</span> <span class="variable-name">location</span> = <span class="keyword">null</span>;\r
+ <span class="comment">// Anyway, he is going to pay for using CGI\r
+</span> <span class="type">Hashtable</span> <span class="variable-name">headers</span> = <span class="keyword">null</span>;\r
+ <span class="comment">// The MIME parse we are attached to:\r
+</span> <span class="type">MimeParser</span> <span class="variable-name">parser</span> = <span class="keyword">null</span>;\r
+\r
+ <span class="comment">/**\r
+ * The parsing is now about to start, take any appropriate action.\r
+ * This hook can return a <strong>true</strong> boolean value to enforce\r
+ * the MIME parser into transparent mode (eg the parser will <em>not</em>\r
+ * try to parse any headers.\r
+ * <p>This hack is primarily defined for HTTP/0.9 support, it might\r
+ * also be usefull for other hacks.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">parser</span><span class="comment"> The Mime parser.\r
+ * </span><span class="keyword">@return </span><span class="comment">A boolean <strong>true</strong> if the MimeParser shouldn't\r
+ * continue the parsing, <strong>false</strong> otherwise.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">boolean</span> <span class="function-name">notifyBeginParsing</span>(<span class="type">MimeParser</span> <span class="variable-name">parser</span>)\r
+ <span class="keyword">throws</span> <span class="type">IOException</span>\r
+ {\r
+ <span class="keyword">return</span> <span class="keyword">false</span>;\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * All the headers have been parsed, take any appropriate actions.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">parser</span><span class="comment"> The Mime parser.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">void</span> <span class="function-name">notifyEndParsing</span>(<span class="type">MimeParser</span> <span class="variable-name">parser</span>)\r
+ <span class="keyword">throws</span> <span class="type">IOException</span>\r
+ {\r
+ <span class="keyword">return</span> ;\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * A new header has been emited by the script.\r
+ * If the script is not an NPH, then it <strong>must</strong> at least\r
+ * emit one header, so we are safe here, although people may not be safe \r
+ * against the spec.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">name</span><span class="comment"> The header name.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">buf</span><span class="comment"> The header bytes.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">off</span><span class="comment"> The begining of the value bytes in above buffer.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">len</span><span class="comment"> The length of the value bytes in above buffer.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">void</span> <span class="function-name">notifyHeader</span>(<span class="type">String</span> <span class="variable-name">name</span>, <span class="type">byte</span> <span class="variable-name">buf</span>[], <span class="type">int</span> <span class="variable-name">off</span>, <span class="type">int</span> <span class="variable-name">len</span>)\r
+ <span class="keyword">throws</span> <span class="type">MimeParserException</span>\r
+ {\r
+ <span class="keyword">if</span> ( name.equalsIgnoreCase("<span class="string">status</span>") ) {\r
+ status = <span class="keyword">new</span> <span class="type">String</span>(buf, 0, off, len);\r
+ } <span class="keyword">else</span> <span class="keyword">if</span> ( name.equalsIgnoreCase("<span class="string">location</span>") ) {\r
+ location = <span class="keyword">new</span> <span class="type">String</span>(buf, 0, off, len);\r
+ } <span class="keyword">else</span> {\r
+ <span class="type">String</span> <span class="variable-name">extraval</span> = <span class="keyword">new</span> <span class="type">String</span>(buf, 0, off, len);\r
+ <span class="keyword">if</span> ( headers == <span class="keyword">null</span> ) {\r
+ headers = <span class="keyword">new</span> <span class="type">Hashtable</span>(11);\r
+ } <span class="keyword">else</span> {\r
+ <span class="type">String</span> <span class="variable-name">val</span> = (<span class="type">String</span>) headers.get(name.toLowerCase());\r
+ <span class="keyword">if</span> ( val != <span class="keyword">null</span> )\r
+ extraval = val + "<span class="string">,</span>" + extraval;\r
+ }\r
+ headers.put(name.toLowerCase(), extraval);\r
+ }\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Get the status emited by the script.\r
+ */</span>\r
+ \r
+ <span class="reference">public</span> <span class="type">String</span> <span class="function-name">getStatus</span>() {\r
+ <span class="keyword">return</span> status;\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Get the location header value emited by the script.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">String</span> <span class="function-name">getLocation</span>() {\r
+ <span class="keyword">return</span> location;\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Get any header value (except status and location).\r
+ * </span><span class="keyword">@param </span><span class="variable-name">name</span><span class="comment"> The name of the header to fetch.\r
+ * </span><span class="keyword">@return </span><span class="comment">The string value of requested header, or <strong>null</strong>\r
+ * if header was not defined.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">String</span> <span class="function-name">getValue</span>(<span class="type">String</span> <span class="variable-name">name</span>) {\r
+ <span class="keyword">return</span> (headers == <span class="keyword">null</span>) ? <span class="keyword">null</span> : (<span class="type">String</span>) headers.get(name);\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Enumerate the headers defined by the holder.\r
+ * </span><span class="keyword">@return </span><span class="comment">A enumeration of header names, or <strong>null</strong> if no\r
+ * header is defined.\r
+ */</span>\r
+ \r
+ <span class="reference">public</span> <span class="type">Enumeration</span> <span class="function-name">enumerateHeaders</span>() {\r
+ <span class="keyword">if</span> ( headers == <span class="keyword">null</span> )\r
+ <span class="keyword">return</span> <span class="keyword">null</span>;\r
+ <span class="keyword">return</span> headers.keys();\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Get the remaining output of the stream.\r
+ * This should be called only once header parsing is done.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">InputStream</span> <span class="function-name">getInputStream</span>() {\r
+ <span class="keyword">return</span> parser.getInputStream();\r
+ }\r
+\r
+ <span class="function-name">CGIHeaderHolder</span>(<span class="type">MimeParser</span> <span class="variable-name">parser</span>) {\r
+ <span class="reference">this</span>.parser = parser;\r
+ }\r
+\r
+}\r
+\r
+<span class="comment">/**\r
+ * Parsing the CGI output - Always create a CGIHeaderHolder.\r
+ */</span>\r
+\r
+<span class="keyword">class</span> <span class="function-name">CGIHeaderHolderFactory</span> <span class="keyword">implements</span> <span class="type">MimeParserFactory</span> {\r
+ \r
+ <span class="comment">/**\r
+ * Create a new header holder to hold the parser's result.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">parser</span><span class="comment"> The parser that has something to parse.\r
+ * </span><span class="keyword">@return </span><span class="comment">A MimeParserHandler compliant object.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">MimeHeaderHolder</span> <span class="function-name">createHeaderHolder</span>(<span class="type">MimeParser</span> <span class="variable-name">parser</span>) {\r
+ <span class="keyword">return</span> <span class="keyword">new</span> <span class="type">CGIHeaderHolder</span>(parser);\r
+ }\r
+\r
+ <span class="function-name">CGIHeaderHolderFactory</span>() {\r
+ }\r
+\r
+}\r
+\r
+<span class="comment">/**\r
+ * A simple process feeder class.\r
+ */</span>\r
+\r
+<span class="keyword">class</span> <span class="function-name">ProcessFeeder</span> <span class="keyword">extends</span> <span class="type">Thread</span> {\r
+ <span class="type">Process</span> <span class="variable-name">proc</span> = <span class="keyword">null</span> ;\r
+ <span class="type">OutputStream</span> <span class="variable-name">out</span> = <span class="keyword">null</span> ;\r
+ <span class="type">InputStream</span> <span class="variable-name">in</span> = <span class="keyword">null</span> ;\r
+ <span class="type">int</span> <span class="variable-name">count</span> = -1 ;\r
+\r
+ <span class="reference">public</span> <span class="type">void</span> <span class="function-name">run</span> () {\r
+ <span class="keyword">try</span> {\r
+ <span class="type">byte</span> <span class="variable-name">buffer</span>[] = <span class="keyword">new</span> <span class="type">byte</span>[4096] ;\r
+ <span class="type">int</span> <span class="variable-name">got</span> = -1 ;\r
+ \r
+ <span class="comment">// Send the data to the target process:\r
+</span> <span class="keyword">if</span> ( count >= 0 ) {\r
+ <span class="keyword">while</span> ( (count > 0) && ((got = in.read(buffer)) > 0) ) {\r
+ out.write (buffer, 0, got) ;\r
+ count -= got ;\r
+ }\r
+ } <span class="keyword">else</span> {\r
+ <span class="keyword">while</span> ( (got = in.read(buffer)) > 0 ) {\r
+ out.write (buffer, 0, got) ;\r
+ }\r
+ }\r
+ } <span class="keyword">catch</span> (<span class="type">Exception</span> <span class="variable-name">e</span>) {\r
+ System.out.println ("<span class="string">ProcessFeeder: caught exception !</span>") ;\r
+ e.printStackTrace() ;\r
+ } <span class="keyword">finally</span> {\r
+ <span class="comment">// Clean up the process:\r
+</span> <span class="keyword">try</span> { out.flush() ; } <span class="keyword">catch</span> (<span class="type">IOException</span> <span class="variable-name">ex</span>) {}\r
+ <span class="keyword">try</span> { out.close() ; } <span class="keyword">catch</span> (<span class="type">IOException</span> <span class="variable-name">ex</span>) {}\r
+ <span class="keyword">try</span> { proc.waitFor() ; } <span class="keyword">catch</span> (<span class="type">Exception</span> <span class="variable-name">ex</span>) {}\r
+ }\r
+ }\r
+ \r
+ <span class="function-name">ProcessFeeder</span> (<span class="type">Process</span> <span class="variable-name">proc</span>, <span class="type">InputStream</span> <span class="variable-name">in</span>) {\r
+ <span class="reference">this</span> (proc, in, -1) ;\r
+ }\r
+ \r
+ <span class="function-name">ProcessFeeder</span> (<span class="type">Process</span> <span class="variable-name">proc</span>, <span class="type">InputStream</span> <span class="variable-name">in</span>, <span class="type">int</span> <span class="variable-name">count</span>) {\r
+ <span class="reference">this</span>.proc = proc ;\r
+ <span class="reference">this</span>.out = proc.getOutputStream() ;\r
+ <span class="reference">this</span>.in = in ;\r
+ <span class="reference">this</span>.count = count ;\r
+ }\r
+}\r
+\r
+<span class="comment">/**\r
+ * Handle CGI scripts.\r
+ */</span>\r
+<span class="reference">public</span> <span class="keyword">class</span> <span class="function-name">CgiFrame</span> <span class="keyword">extends</span> <span class="type">HTTPFrame</span> {\r
+\r
+ <span class="string">private</span> <span class="type">final</span> <span class="type">static</span> \r
+ <span class="type">String</span> <span class="variable-name">STATE_EXTRA_PATH</span> = "<span class="doc-string">org.w3c.jigsaw.frames.CgiFrame.extraPath</span>";\r
+\r
+ <span class="comment">/**\r
+ * Attribute index - The interpreter to use, if any.\r
+ */</span>\r
+ <span class="preprocessor">protected</span> <span class="type">static</span> <span class="type">int</span> <span class="variable-name">ATTR_INTERPRETER</span> = -1;\r
+ <span class="comment">/**\r
+ * Attribute index - The array of string that makes the command to run.\r
+ */</span>\r
+ <span class="preprocessor">protected</span> <span class="type">static</span> <span class="type">int</span> <span class="variable-name">ATTR_COMMAND</span> = -1 ;\r
+ <span class="comment">/**\r
+ * Attribute index - Does the script takes care of its headers ?\r
+ */</span>\r
+ <span class="preprocessor">protected</span> <span class="type">static</span> <span class="type">int</span> <span class="variable-name">ATTR_NOHEADER</span> = -1 ;\r
+ <span class="comment">/**\r
+ * Attribute index - Does the script generates the form on GET ?\r
+ */</span>\r
+ <span class="preprocessor">protected</span> <span class="type">static</span> <span class="type">int</span> <span class="variable-name">ATTR_GENERATES_FORM</span> = -1 ;\r
+ <span class="comment">/**\r
+ * Attribute index - Do DNS, to fill in REMOTE_HOST env var.\r
+ */</span>\r
+ <span class="preprocessor">protected</span> <span class="type">static</span> <span class="type">int</span> <span class="variable-name">ATTR_REMOTE_HOST</span> = -1;\r
+ <span class="comment">/**\r
+ * Attribute index - Turn the script in debug mode.\r
+ */</span>\r
+ <span class="preprocessor">protected</span> <span class="type">static</span> <span class="type">int</span> <span class="variable-name">ATTR_CGI_DEBUG</span> = -1;\r
+\r
+ <span class="type">static</span> {\r
+ <span class="type">Attribute</span> <span class="variable-name">a</span> = <span class="keyword">null</span> ;\r
+ <span class="type">Class</span> <span class="variable-name">cls</span> = <span class="keyword">null</span> ;\r
+ <span class="keyword">try</span> {\r
+ cls = Class.forName("<span class="string">org.w3c.jigsaw.frames.CgiFrame</span>") ;\r
+ } <span class="keyword">catch</span> (<span class="type">Exception</span> <span class="variable-name">ex</span>) {\r
+ ex.printStackTrace() ;\r
+ System.exit(1) ;\r
+ }\r
+ <span class="comment">// The interpreter attribute:\r
+</span> a = <span class="keyword">new</span> <span class="type">StringAttribute</span>("<span class="string">interpreter</span>"\r
+ , <span class="keyword">null</span>\r
+ , Attribute.EDITABLE);\r
+ ATTR_INTERPRETER = AttributeRegistry.registerAttribute(cls, a);\r
+ <span class="comment">// The command attribute:\r
+</span> a = <span class="keyword">new</span> <span class="type">StringArrayAttribute</span>("<span class="string">command</span>"\r
+ , <span class="keyword">null</span>\r
+ , Attribute.MANDATORY|Attribute.EDITABLE);\r
+ ATTR_COMMAND = AttributeRegistry.registerAttribute(cls, a) ;\r
+ <span class="comment">// The noheader attribute:\r
+</span> a = <span class="keyword">new</span> <span class="type">BooleanAttribute</span>("<span class="string">noheader</span>"\r
+ , <span class="reference">Boolean</span>.<span class="type">FALSE</span>\r
+ , Attribute.EDITABLE) ;\r
+ ATTR_NOHEADER = AttributeRegistry.registerAttribute(cls, a) ;\r
+ <span class="comment">// The generates form attribute\r
+</span> a = <span class="keyword">new</span> <span class="type">BooleanAttribute</span>("<span class="string">generates-form</span>"\r
+ , <span class="reference">Boolean</span>.<span class="type">TRUE</span>\r
+ , Attribute.EDITABLE) ;\r
+ ATTR_GENERATES_FORM = AttributeRegistry.registerAttribute(cls, a);\r
+ <span class="comment">// Registerr the DODNS attribute.\r
+</span> a = <span class="keyword">new</span> <span class="type">BooleanAttribute</span>("<span class="string">remote-host</span>"\r
+ , <span class="keyword">null</span>\r
+ , Attribute.EDITABLE);\r
+ ATTR_REMOTE_HOST = AttributeRegistry.registerAttribute(cls, a);\r
+ <span class="comment">// Register the debug mode flag:\r
+</span> a = <span class="keyword">new</span> <span class="type">BooleanAttribute</span>("<span class="string">cgi-debug</span>"\r
+ , <span class="reference">Boolean</span>.<span class="type">FALSE</span>\r
+ , Attribute.EDITABLE);\r
+ ATTR_CGI_DEBUG = AttributeRegistry.registerAttribute(cls, a);\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Get the interpreter to use to execute the script.\r
+ * This is most usefull for operating systems that don't have a\r
+ * <code>!#</code> convention ala UNIX.\r
+ * </span><span class="keyword">@return </span><span class="comment">The interpreter to run the script.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">String</span> <span class="function-name">getInterpreter</span>() {\r
+ <span class="keyword">return</span> getString(ATTR_INTERPRETER, <span class="keyword">null</span>);\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Get the command string array.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">String</span>[] <span class="function-name">getCommand</span>() {\r
+ <span class="keyword">return</span> (<span class="type">String</span>[]) getValue(ATTR_COMMAND, <span class="keyword">null</span>) ;\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Get the noheader flag.\r
+ * </span><span class="keyword">@return </span><span class="comment">The boolean value of the noheader flag.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">boolean</span> <span class="function-name">checkNoheaderFlag</span>() {\r
+ <span class="keyword">return</span> getBoolean(ATTR_NOHEADER, <span class="keyword">false</span>) ;\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Get the generates form flag.\r
+ * </span><span class="keyword">@return </span><span class="comment">The boolean value of the generates form flag.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">boolean</span> <span class="function-name">checkGeneratesFormFlag</span>() {\r
+ <span class="keyword">return</span> getBoolean(ATTR_GENERATES_FORM, <span class="keyword">true</span>) ;\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Get the remote host attribute value.\r
+ * If turned on, this flag will enable the REMOTE_HOST env var computation.\r
+ * </span><span class="keyword">@return </span><span class="comment">A boolean.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">boolean</span> <span class="function-name">checkRemoteHost</span>() {\r
+ <span class="keyword">return</span> getBoolean(ATTR_REMOTE_HOST, <span class="keyword">false</span>);\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Get the CGI debug flag.\r
+ * </span><span class="keyword">@return </span><span class="comment">The boolean value of the CGI debug flag.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">boolean</span> <span class="function-name">checkCgiDebug</span>() {\r
+ <span class="keyword">return</span> getBoolean(ATTR_CGI_DEBUG, <span class="keyword">false</span>);\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Turn the given header name into it's env var canonical name.\r
+ * This guy is crazy enough to run CGI scripts, he can pay for that \r
+ * overhead.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">name</span><span class="comment"> The header name.\r
+ * </span><span class="keyword">@return </span><span class="comment">A String giving the official env variable name for that header.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">String</span> <span class="function-name">getEnvName</span>(<span class="type">String</span> <span class="variable-name">name</span>) {\r
+ <span class="type">int</span> <span class="variable-name">sl</span> = name.length();\r
+ <span class="type">StringBuffer</span> <span class="variable-name">sb</span> = <span class="keyword">new</span> <span class="type">StringBuffer</span>(5+sl);\r
+ sb.append("<span class="string">HTTP_</span>");\r
+ <span class="keyword">for</span> (<span class="type">int</span> <span class="variable-name">i</span> = 0 ; i < sl ; i++) {\r
+ <span class="type">char</span> <span class="variable-name">ch</span> = name.charAt(i);\r
+ sb.append((ch == '<span class="string">-</span>') ? '<span class="string">_</span>' : Character.toUpperCase(ch));\r
+ }\r
+ <span class="keyword">return</span> sb.toString();\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Handle the CGI script output.\r
+ * This methods handles the CGI script output. Depending on the\r
+ * value of the <strong>noheader</strong> attribute it either:\r
+ * <ul>\r
+ * <li>Sends back the script output directly,</li>\r
+ * <li>Parses the script output, looking for a status header or a \r
+ * location header, or a content-length header, or any combination\r
+ * of those three.\r
+ * </ul>\r
+ * </span><span class="keyword">@param </span><span class="variable-name">process</span><span class="comment"> The underlying CGI process.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">request</span><span class="comment"> The processed request.\r
+ * </span><span class="keyword">@exception </span><span class="type">ProtocolException</span><span class="comment"> If an HTTP error should be sent back \r
+ * to the client.\r
+ */</span>\r
+\r
+ <span class="preprocessor">protected</span> <span class="type">Reply</span> <span class="function-name">handleCGIOutput</span> (<span class="type">Process</span> <span class="variable-name">process</span>, <span class="type">Request</span> <span class="variable-name">request</span>) \r
+ <span class="keyword">throws</span> <span class="type">ProtocolException</span>\r
+ {\r
+ <span class="comment">// No header script don't deserve attention:\r
+</span> <span class="keyword">if</span> ( checkNoheaderFlag() ) {\r
+ <span class="type">Reply</span> <span class="variable-name">reply</span> = request.makeReply(HTTP.NOHEADER) ;\r
+ reply.setStream (process.getInputStream()) ;\r
+ <span class="keyword">return</span> reply ;\r
+ }\r
+ <span class="comment">// Check for debugging mode:\r
+</span> <span class="keyword">if</span> ( checkCgiDebug() ) {\r
+ <span class="type">Reply</span> <span class="variable-name">reply</span> = request.makeReply(HTTP.OK);\r
+ reply.setContentType(MimeType.TEXT_PLAIN);\r
+ reply.setStream(process.getInputStream());\r
+ <span class="keyword">return</span> reply;\r
+ }\r
+ <span class="comment">// We MUST parse at least one header:\r
+</span> <span class="type">MimeParser</span> <span class="variable-name">p</span> = <span class="keyword">new</span> <span class="type">MimeParser</span>(process.getInputStream(),\r
+ <span class="keyword">new</span> <span class="type">CGIHeaderHolderFactory</span>());\r
+ <span class="type">Reply</span> <span class="variable-name">reply</span> = <span class="keyword">null</span> ;\r
+ <span class="keyword">try</span> {\r
+ <span class="type">CGIHeaderHolder</span> <span class="variable-name">h</span> = (<span class="type">CGIHeaderHolder</span>) p.parse();\r
+ <span class="comment">// Check for a status code:\r
+</span> <span class="type">String</span> <span class="variable-name">svalue</span> = h.getStatus();\r
+ <span class="type">String</span> <span class="variable-name">location</span> = h.getLocation();\r
+ <span class="keyword">if</span> ( svalue != <span class="keyword">null</span> ) {\r
+ <span class="type">int</span> <span class="variable-name">status</span> = -1;\r
+ <span class="keyword">try</span> {\r
+ status = Integer.parseInt(svalue);\r
+ } <span class="keyword">catch</span> (<span class="type">Exception</span> <span class="variable-name">ex</span>) {\r
+ <span class="comment">// This script has emited an invalid status line:\r
+</span> <span class="type">String</span> <span class="variable-name">msg</span> = ("<span class="string">Emited an invalid status line [</span>"+\r
+ svalue + "<span class="string">].</span>");\r
+ getServer().errlog(<span class="reference">this</span>, msg);\r
+ <span class="comment">// Throw an HTTPException:\r
+</span> reply = request.makeReply(HTTP.INTERNAL_SERVER_ERROR);\r
+ reply.setContent("<span class="string">CGI script emited invalid status.</span>");\r
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="type">HTTPException</span>(reply);\r
+ }\r
+ reply = request.makeReply(status);\r
+ } <span class="keyword">else</span> {\r
+ <span class="comment">// No status code available, any location header ?\r
+</span> reply = request.makeReply((location == <span class="keyword">null</span>)\r
+ ? <span class="reference">HTTP</span>.<span class="type">OK</span>\r
+ : HTTP.FOUND);\r
+ }\r
+ <span class="comment">// Set up the location header if needed:\r
+</span> <span class="keyword">if</span> ( location != <span class="keyword">null</span> ) {\r
+ <span class="keyword">try</span> {\r
+ reply.setLocation(<span class="keyword">new</span> <span class="type">URL</span>(getURL(request), location));\r
+ } <span class="keyword">catch</span> (<span class="type">MalformedURLException</span> <span class="variable-name">ex</span>) {\r
+ <span class="comment">// This should really not happen:\r
+</span> getServer().errlog(<span class="reference">this</span>, "<span class="string">unable to create location url </span>"+\r
+ location+\r
+ "<span class="string"> in base </span>"+getURL(request));\r
+ }\r
+ }\r
+ <span class="comment">// And then, the remaining headers:\r
+</span> <span class="type">Enumeration</span> <span class="variable-name">e</span> = h.enumerateHeaders();\r
+ <span class="keyword">if</span> ( e != <span class="keyword">null</span> ) {\r
+ <span class="keyword">while</span> ( e.hasMoreElements() ) {\r
+ <span class="type">String</span> <span class="variable-name">hname</span> = (<span class="type">String</span>) e.nextElement();\r
+ reply.setValue(hname, (<span class="type">String</span>) h.getValue(hname));\r
+ }\r
+ }\r
+ reply.setStream(p.getInputStream()) ;\r
+ } <span class="keyword">catch</span> (<span class="type">IOException</span> <span class="variable-name">ex</span>) {\r
+ ex.printStackTrace();\r
+ } <span class="keyword">catch</span> (<span class="type">MimeParserException</span> <span class="variable-name">ex</span>) {\r
+ <span class="comment">// This script has generated invalid output:\r
+</span> <span class="type">String</span> <span class="variable-name">msg</span> = (getURL(request)\r
+ +"<span class="string">: emited invalid output [</span>"+\r
+ ex.getMessage() +"<span class="string">]</span>");\r
+ getServer().errlog(<span class="reference">this</span>, msg);\r
+ <span class="comment">// Throw an HTTPException:\r
+</span> <span class="type">Reply</span> <span class="variable-name">error</span> = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;\r
+ error.setContent("<span class="string">CGI error: unable to parse script headers.</span>") ;\r
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="type">HTTPException</span> (error) ;\r
+ }\r
+ <span class="keyword">return</span> reply ;\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Add an enviornment binding to the given vector.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">name</span><span class="comment"> The name of the enviornment variable.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">val</span><span class="comment"> Its value.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">into</span><span class="comment"> The vector to which accumulate bindings.\r
+ */</span>\r
+\r
+ <span class="string">private</span> <span class="type">void</span> <span class="function-name">addEnv</span> (<span class="type">String</span> <span class="variable-name">name</span>, <span class="type">String</span> <span class="variable-name">val</span>, <span class="type">Vector</span> <span class="variable-name">into</span>) {\r
+ into.addElement (name+"<span class="string">=</span>"+val) ;\r
+ }\r
+ \r
+ <span class="comment">/**\r
+ * Prepare the command to run for this CGI script, and run it.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">request</span><span class="comment"> The request to handle.\r
+ * </span><span class="keyword">@return </span><span class="comment">The running CGI process object.\r
+ * </span><span class="keyword">@exception </span><span class="type">HTTPException</span><span class="comment"> If we weren't able to build the command or\r
+ * the environment.\r
+ */</span>\r
+\r
+ <span class="preprocessor">protected</span> <span class="type">Process</span> <span class="function-name">makeCgiCommand</span> (<span class="type">Request</span> <span class="variable-name">request</span>) \r
+ <span class="keyword">throws</span> <span class="type">ProtocolException</span>, <span class="type">IOException</span> \r
+ {\r
+ <span class="comment">// Check the command attribute first:\r
+</span> <span class="type">String</span> <span class="variable-name">query</span> = <span class="keyword">null</span>;\r
+ <span class="type">String</span> <span class="variable-name">command</span>[] = getCommand() ;\r
+ <span class="keyword">if</span> ( command == <span class="keyword">null</span> ) {\r
+ <span class="type">Reply</span> <span class="variable-name">error</span> = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;\r
+ error.setContent("<span class="string">CgiResource mis-configured: it doesn't have a </span>"\r
+ + "<span class="string"> command attribute</span>");\r
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="type">HTTPException</span>(error);\r
+ }\r
+ <span class="comment">// Ok:\r
+</span> <span class="type">Vector</span> <span class="variable-name">env</span> = <span class="keyword">new</span> <span class="type">Vector</span>(32) ;\r
+ httpd server = request.getClient().getServer() ;\r
+ <span class="type">InetAddress</span> <span class="variable-name">sadr</span> = server.getInetAddress() ;\r
+ <span class="comment">// Specified environment variables:\r
+</span> <span class="comment">// We do not handle the following variables:\r
+</span> <span class="comment">// - PATH_TRANSLATED: I don't understand it\r
+</span> <span class="comment">// - REMOTE_IDENT: would require usage of IDENT protocol.\r
+</span> <span class="comment">// Authentification type, if any:\r
+</span> <span class="type">String</span> <span class="variable-name">svalue</span> = (<span class="type">String</span>) request.getState(AuthFilter.STATE_AUTHTYPE);\r
+ <span class="keyword">if</span> ( svalue != <span class="keyword">null</span> )\r
+ addEnv("<span class="string">AUTH_TYPE</span>", svalue, env);\r
+ <span class="comment">// Content length, if available:\r
+</span> svalue = request.getValue("<span class="string">content-length</span>");\r
+ <span class="keyword">if</span> ( svalue != <span class="keyword">null</span> )\r
+ addEnv("<span class="string">CONTENT_LENGTH</span>", svalue, env);\r
+ <span class="comment">// Content type, if available:\r
+</span> svalue = request.getValue("<span class="string">content-type</span>");\r
+ <span class="keyword">if</span> ( svalue != <span class="keyword">null</span> )\r
+ addEnv("<span class="string">CONTENT_TYPE</span>", svalue, env);\r
+ <span class="comment">// The gateway interface, hopefully 1.1 !\r
+</span> addEnv ("<span class="string">GATEWAY_INTERFACE</span>", "<span class="string">CGI/1.1</span>", env) ;\r
+ <span class="comment">// The PATH_INFO, which I am afraid I still don't understand:\r
+</span> svalue = (<span class="type">String</span>) request.getState(STATE_EXTRA_PATH);\r
+ <span class="keyword">if</span> ( svalue == <span class="keyword">null</span> )\r
+ addEnv ("<span class="string">PATH_INFO</span>", "<span class="string">/</span>", env) ; \r
+ <span class="keyword">else</span>\r
+ addEnv ("<span class="string">PATH_INFO</span>", svalue, env) ; \r
+ <span class="comment">// The query string:\r
+</span> query = request.getQueryString();\r
+ <span class="keyword">if</span> ( query != <span class="keyword">null</span> ) \r
+ addEnv("<span class="string">QUERY_STRING</span>", query, env) ;\r
+ <span class="comment">// The remote client IP address:\r
+</span> svalue = request.getClient().getInetAddress().toString();\r
+ addEnv ("<span class="string">REMOTE_ADDR</span>", svalue, env);\r
+ <span class="comment">// Authentified user:\r
+</span> svalue = (<span class="type">String</span>) request.getState(AuthFilter.STATE_AUTHUSER);\r
+ <span class="keyword">if</span> ( svalue != <span class="keyword">null</span> )\r
+ addEnv("<span class="string">REMOTE_USER</span>", svalue, env);\r
+ <span class="comment">// Remote host name, if allowed:\r
+</span> <span class="keyword">if</span> ( checkRemoteHost() ) {\r
+ <span class="type">String</span> <span class="variable-name">host</span> = request.getClient().getInetAddress().getHostName();\r
+ addEnv("<span class="string">REMOTE_HOST</span>", host, env);\r
+ }\r
+ <span class="comment">// The request method:\r
+</span> addEnv ("<span class="string">REQUEST_METHOD</span>", request.getMethod(), env) ;\r
+ <span class="comment">// The script name :\r
+</span> addEnv("<span class="string">SCRIPT_NAME</span>", getURLPath(), env);\r
+ <span class="comment">// Server name:\r
+</span> addEnv ("<span class="string">SERVER_NAME</span>", getServer().getHost(), env) ;\r
+ <span class="comment">// Server port:\r
+</span> svalue = Integer.toString(getServer().getLocalPort());\r
+ addEnv ("<span class="string">SERVER_PORT</span>", svalue, env);\r
+ <span class="comment">// Server protocol:\r
+</span> addEnv ("<span class="string">SERVER_PROTOCOL</span>", request.getVersion(), env) ;\r
+ <span class="comment">// Server software:\r
+</span> addEnv ("<span class="string">SERVER_SOFTWARE</span>", server.getSoftware(), env) ;\r
+ <span class="comment">// All other request fields, yeah, let's lose even more time:\r
+</span> <span class="type">Enumeration</span> <span class="variable-name">e</span> = request.enumerateHeaderDescriptions(<span class="keyword">false</span>);\r
+ <span class="keyword">while</span> ( e.hasMoreElements() ) {\r
+ <span class="type">HeaderDescription</span> <span class="variable-name">d</span> = (<span class="type">HeaderDescription</span>) e.nextElement();\r
+ addEnv(getEnvName(d.getName())\r
+ , request.getHeaderValue(d).toString()\r
+ , env);\r
+ }\r
+ <span class="comment">// Command line:\r
+</span> <span class="keyword">if</span> ( query != <span class="keyword">null</span> ) {\r
+ <span class="type">String</span> <span class="variable-name">querycmd</span>[] = <span class="keyword">new</span> <span class="type">String</span>[command.length+1] ;\r
+ System.arraycopy(command, 0, querycmd, 0, command.length) ;\r
+ querycmd[command.length] = query ;\r
+ command = querycmd ;\r
+ }\r
+ <span class="type">String</span> <span class="variable-name">aenv</span>[] = <span class="keyword">new</span> <span class="type">String</span>[env.size()] ;\r
+ env.copyInto (aenv) ;\r
+ <span class="comment">// Run the process:\r
+</span> <span class="keyword">if</span> ( getInterpreter() != <span class="keyword">null</span> ) {\r
+ <span class="type">String</span> <span class="variable-name">run</span>[] = <span class="keyword">new</span> <span class="type">String</span>[command.length+1];\r
+ run[0] = getInterpreter();\r
+ System.arraycopy(command, 0, run, 1, command.length);\r
+ <span class="keyword">return</span> Runtime.getRuntime().exec (run, aenv) ;\r
+ } <span class="keyword">else</span> {\r
+ <span class="keyword">return</span> Runtime.getRuntime().exec (command, aenv) ;\r
+ }\r
+ }\r
+\r
+ <span class="comment">/**\r
+ * Lookup sub-resources.\r
+ * Accumulate the remaning path in some special state of the request.\r
+ * <p>This allows us to implement the <code>PATH_INFO</code> \r
+ * CGI variable properly.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">ls</span><span class="comment"> Current lookup state.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">lr</span><span class="comment"> Lookup result under construction.\r
+ * </span><span class="keyword">@return </span><span class="comment">A boolean <strong>true</strong> if lookup should continue,\r
+ * <strong>false</strong> otherwise.\r
+ */</span>\r
+\r
+ <span class="reference">public</span> <span class="type">boolean</span> <span class="function-name">lookup</span>(<span class="type">LookupState</span> <span class="variable-name">ls</span>, <span class="type">LookupResult</span> <span class="variable-name">lr</span>) \r
+ <span class="keyword">throws</span> <span class="type">ProtocolException</span> \r
+ {\r
+ <span class="comment">// Get the extra path information:\r
+</span> <span class="type">String</span> <span class="variable-name">extraPath</span> = ls.getRemainingPath(<span class="keyword">true</span>);\r
+ <span class="keyword">if</span> ((extraPath == <span class="keyword">null</span>) || extraPath.equals(""))\r
+ extraPath = "<span class="string">/</span>";\r
+ <span class="comment">// Keep this path info into the request, if possible:\r
+</span> <span class="type">Request</span> <span class="variable-name">request</span> = (<span class="type">Request</span>) ls.getRequest();\r
+ <span class="keyword">if</span> ( request != <span class="keyword">null</span> )\r
+ request.setState(STATE_EXTRA_PATH, extraPath);\r
+ lr.setTarget(getResource().getResourceReference());\r
+ <span class="keyword">return</span> <span class="reference">super</span>.lookup(ls, lr);\r
+ }\r
+ \r
+ <span class="comment">/** \r
+ * GET method implementation.\r
+ * this method is splitted into two cases:\r
+ * <p>If the resource is able to generates its form, than run the script\r
+ * to emit the form. Otherwsie, use our super class (FileResource) ability\r
+ * to send the file that contains the form.\r
+ * <p>Note that there is no need to feed the underlying process with\r
+ * data in the GET case.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">request</span><span class="comment"> The request to handle.\r
+ * </span><span class="keyword">@exception </span><span class="type">ProtocolException</span><span class="comment"> If processing the request failed.\r
+ */</span>\r
+ <span class="reference">public</span> <span class="type">Reply</span> <span class="function-name">get</span>(<span class="type">Request</span> <span class="variable-name">request</span>)\r
+ <span class="keyword">throws</span> <span class="type">ProtocolException</span>, <span class="type">NotAProtocolException</span>\r
+ {\r
+ <span class="keyword">if</span> ( ! checkGeneratesFormFlag() )\r
+ <span class="keyword">return</span> <span class="reference">super</span>.get (request) ;\r
+ <span class="type">Process</span> <span class="variable-name">process</span> = <span class="keyword">null</span> ;\r
+ <span class="keyword">try</span> {\r
+ process = makeCgiCommand (request) ;\r
+ } <span class="keyword">catch</span> (<span class="type">IOException</span> <span class="variable-name">e</span>) {\r
+ <span class="type">Reply</span> <span class="variable-name">error</span> = request.makeReply(HTTP.NOT_FOUND) ;\r
+ error.setContent("<span class="string">The resource's script wasn't found.</span>") ;\r
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="type">HTTPException</span> (error) ;\r
+ }\r
+ <span class="keyword">return</span> handleCGIOutput (process, request) ;\r
+ }\r
+\r
+\r
+ <span class="comment">/**\r
+ * Handle the POST method according to CGI/1.1 specification.\r
+ * The request body is sent back to the launched CGI script, as is, and\r
+ * the script output is handled by the handleCGIOutput method.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">request</span><span class="comment"> The request to process.\r
+ * </span><span class="keyword">@exception </span><span class="type">ProtocolException</span><span class="comment"> If the processing failed.\r
+ */</span>\r
+ <span class="reference">public</span> <span class="type">Reply</span> <span class="function-name">post</span>(<span class="type">Request</span> <span class="variable-name">request</span>)\r
+ <span class="keyword">throws</span> <span class="type">ProtocolException</span>, <span class="type">NotAProtocolException</span>\r
+ {\r
+ <span class="type">Process</span> <span class="variable-name">process</span> = <span class="keyword">null</span> ;\r
+ <span class="comment">// Launch the CGI process:\r
+</span> <span class="keyword">try</span> {\r
+ process = makeCgiCommand(request) ;\r
+ } <span class="keyword">catch</span> (<span class="type">IOException</span> <span class="variable-name">ex</span>) {\r
+ <span class="comment">// The process wasn't executable, emit a errlog message:\r
+</span> <span class="type">String</span> <span class="variable-name">msg</span> = ("<span class="string">The process </span>"+\r
+ getCommand()[0] +"<span class="string"> couldn't be executed [</span>"+\r
+ ex.getMessage() + "<span class="string">]</span>");\r
+ getServer().errlog(<span class="reference">this</span>, msg);\r
+ <span class="comment">// Throw an internal server error:\r
+</span> <span class="type">Reply</span> <span class="variable-name">error</span> = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;\r
+ error.setContent("<span class="string">CGI script is misconfigured.</span>");\r
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="type">HTTPException</span> (error) ;\r
+ }\r
+ <span class="comment">// Now feed the process:\r
+</span> <span class="keyword">try</span> {\r
+ <span class="comment">// Send the 100 status code:\r
+</span> <span class="type">Client</span> <span class="variable-name">client</span> = request.getClient();\r
+ <span class="keyword">if</span> ( client != <span class="keyword">null</span> ) \r
+ client.sendContinue();\r
+ <span class="type">InputStream</span> <span class="variable-name">in</span> = request.getInputStream();\r
+ <span class="keyword">if</span> ( in == <span class="keyword">null</span> ) {\r
+ <span class="comment">// There was no input to that CCI, close process stream\r
+</span> process.getOutputStream().close();\r
+ } <span class="keyword">else</span> {\r
+ <span class="comment">// Some input to feed the process with:\r
+</span> (<span class="keyword">new</span> <span class="type">ProcessFeeder</span>(process, in)).start();\r
+ }\r
+ } <span class="keyword">catch</span> (<span class="type">IOException</span> <span class="variable-name">ex</span>) {\r
+ <span class="comment">// This is most probably a bad request:\r
+</span> <span class="type">Reply</span> <span class="variable-name">error</span> = request.makeReply(HTTP.BAD_REQUEST);\r
+ error.setContent("<span class="string">The request didn't have a valid input.</span>");\r
+ <span class="keyword">throw</span> <span class="keyword">new</span> <span class="type">HTTPException</span>(error);\r
+ }\r
+ <span class="keyword">return</span> handleCGIOutput(process, request);\r
+ }\r
+ \r
+ <span class="comment">/**\r
+ * At register time, if no command, use a suitable default.\r
+ * THis method will set the command to the identifier, if it is not\r
+ * provided.\r
+ * </span><span class="keyword">@param </span><span class="variable-name">values</span><span class="comment"> Default attribute values.\r
+ */</span>\r
+ \r
+ <span class="reference">public</span> <span class="type">void</span> <span class="function-name">registerResource</span>(<span class="type">FramedResource</span> <span class="variable-name">resource</span>) {\r
+ <span class="reference">super</span>.registerResource(resource);\r
+ <span class="comment">//if no command is specified look for a file resource attached\r
+</span> <span class="comment">//and get its File absolute path if available.\r
+</span> <span class="keyword">if</span> (getCommand() == <span class="keyword">null</span>) {\r
+ <span class="keyword">if</span> (getFileResource() != <span class="keyword">null</span>) {\r
+ <span class="keyword">if</span> (getFileResource().getFile() != <span class="keyword">null</span>) {\r
+ <span class="type">String</span> <span class="variable-name">cmd</span>[] = <span class="keyword">new</span> <span class="type">String</span>[1];\r
+ cmd[0] = getFileResource().getFile().getAbsolutePath();\r
+ setValue(ATTR_COMMAND, cmd);\r
+ }\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+ </pre>\r
+ </body>\r
+</html>\r