// CgiServlet - runs CGI programs // import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; /** Runs CGI program. *

*/ public class CgiServlet extends HttpServlet { protected String basePath = "WEB-INF/cgi-bin/"; protected boolean debug = false; protected boolean secure = false; public void init(ServletConfig cfig) throws ServletException { super.init(cfig); String npath = getInitParameter("basePath"); if ( npath != null ) basePath = npath; if ( getInitParameter("secure") != null ) secure = true; if ( getInitParameter("debug") != null ) debug = true; System.err.println("FSN CgiServlet: '"+basePath+"' debug="+debug); } public void doPost ( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException { // XXX This is not OK, is it??? doGet( req, res ); } public void doGet( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException { if ( secure && ! req.isSecure() ) res.sendError( HttpServletResponse.SC_FORBIDDEN ); String path = req.getPathInfo(); if ( path == null || path.charAt( 0 ) != '/' ) { res.sendError( HttpServletResponse.SC_BAD_REQUEST ); return; } if ( path.indexOf( "/../" ) != -1 || path.endsWith( "/.." ) ) { res.sendError( HttpServletResponse.SC_FORBIDDEN ); return; } // Make a version without the leading /. String pathname = path.substring( 1 ); if ( pathname.length() == 0 ) pathname = "./"; String filename, pathinfo; if ( false ) { filename = pathname.replace( '/', File.separatorChar ); if ( filename.charAt( filename.length() - 1 ) == File.separatorChar ) filename = filename.substring( 0, filename.length() - 1 ); pathinfo = path; } else { int i = pathname.indexOf('/'); if ( i > -1 ) { filename = pathname.substring(0,i); pathinfo = pathname.substring(i); } else { filename = pathname; pathinfo = ""; } if ( debug ) { System.err.println("filename="+filename); System.err.println("pathname="+pathname); System.err.println("pathinfo="+pathinfo); } } filename = getServletContext().getRealPath( basePath + filename ); File file = new File( filename ); if ( !file.exists() || !file.isFile() ) res.sendError( HttpServletResponse.SC_NOT_FOUND ); String queryString = req.getQueryString(); int contentLength = req.getContentLength(); int c; // Make argument list. Vector argVec = new Vector(); argVec.addElement( filename ); if ( queryString != null && queryString.indexOf( "=" ) == -1 ) { Enumeration enum = new StringTokenizer( queryString, "+" ); while ( enum.hasMoreElements() ) argVec.addElement( (String) enum.nextElement() ); } String argList[] = vector2Array( argVec ); // Make environment list. Vector envVec = new Vector(); envVec.addElement( makeEnv( "PATH", "/usr/local/bin:/usr/ucb:/bin:/usr/bin" ) ); envVec.addElement( makeEnv( "GATEWAY_INTERFACE", "CGI/1.1" ) ); envVec.addElement( makeEnv( "SERVER_SOFTWARE", getServletContext().getServerInfo() ) ); envVec.addElement( makeEnv( "SERVER_NAME", req.getServerName() ) ); envVec.addElement( makeEnv( "SERVER_PORT", Integer.toString( req.getServerPort() ) ) ); envVec.addElement( makeEnv( "REMOTE_ADDR", req.getRemoteAddr() ) ); envVec.addElement( makeEnv( "REMOTE_HOST", req.getRemoteHost() ) ); envVec.addElement( makeEnv( "REQUEST_METHOD", req.getMethod() ) ); if ( contentLength != -1 ) envVec.addElement( makeEnv( "CONTENT_LENGTH", Integer.toString( contentLength ) ) ); if ( req.getContentType() != null ) envVec.addElement( makeEnv( "CONTENT_TYPE", req.getContentType() ) ); // if ( debug ) { System.err.println("req.getServletPath="+req.getServletPath()); System.err.println("req.getPathInfo="+req.getPathInfo()); System.err.println("req.getPathTranslated="+req.getPathTranslated()); } if ( false ) { envVec.addElement( makeEnv( "SCRIPT_NAME", req.getServletPath() ) ); if ( req.getPathInfo() != null ) envVec.addElement( makeEnv( "PATH_INFO", req.getPathInfo() ) ); } else { int i = req.getPathInfo().indexOf("/",1); if ( i == -1 ) i = req.getPathInfo().length(); envVec.addElement( makeEnv("SCRIPT_NAME", req.getServletPath() +req.getPathInfo().substring(0,i) ) ); envVec.addElement( makeEnv("PATH_INFO", pathinfo ) ); } if ( req.getPathTranslated() != null ) envVec.addElement( makeEnv( "PATH_TRANSLATED", req.getPathTranslated() ) ); // if ( queryString != null ) envVec.addElement( makeEnv( "QUERY_STRING", queryString ) ); envVec.addElement( makeEnv( "SERVER_PROTOCOL", req.getProtocol() ) ); if ( req.getRemoteUser() != null ) envVec.addElement( makeEnv( "REMOTE_USER", req.getRemoteUser() ) ); if ( req.getAuthType() != null ) envVec.addElement( makeEnv( "AUTH_TYPE", req.getAuthType() ) ); Enumeration enum = req.getHeaderNames(); while ( enum.hasMoreElements() ) { String name = (String) enum.nextElement(); String value = req.getHeader( name ); if ( debug ) System.err.println("*** up: "+name+" "+value); if ( value == null ) value = ""; envVec.addElement( makeEnv( "HTTP_" + name.toUpperCase().replace( '-', '_' ), value ) ); } String envList[] = vector2Array( envVec ); // Start the command. Process proc = Runtime.getRuntime().exec( argList, envList ); InputStream procIn = new BufferedInputStream(proc.getInputStream()); try { // Now read the response from the process. // If it's a POST, copy the request data to the process. if ( req.getMethod().equalsIgnoreCase( "post" ) ) { InputStream reqIn = req.getInputStream(); OutputStream procOut = proc.getOutputStream(); for ( int i = 0; i < contentLength; ++i ) { c = reqIn.read(); if ( c == -1 ) break; procOut.write( c ); } procOut.close(); } OutputStream resOut = res.getOutputStream(); // Some of the headers have to be intercepted and handled. boolean firstLine = true; while ( true ) { String line = readLine(procIn); if ( line == null ) break; line = line.trim(); if ( line.equals( "" ) ) break; int colon = line.indexOf( ":" ); if ( colon == -1 ) { // No colon. If it's the first line, parse it for status. if ( firstLine ) { StringTokenizer tok = new StringTokenizer( line, " " ); try { switch( tok.countTokens() ) { case 2: tok.nextToken(); res.setStatus( Integer.parseInt( tok.nextToken() ) ); break; case 3: tok.nextToken(); res.setStatus( Integer.parseInt( tok.nextToken() ), tok.nextToken() ); break; } } catch ( NumberFormatException ignore ) {} } else { // No colon and it's not the first line? Ignore. } } else { // There's a colon. Check for certain special headers. String name = line.substring( 0, colon ); String value = line.substring( colon + 1 ).trim(); if ( name.equalsIgnoreCase( "Status" ) ) { StringTokenizer tok = new StringTokenizer( value, " " ); try { switch( tok.countTokens() ) { case 1: res.setStatus( Integer.parseInt( tok.nextToken() ) ); break; case 2: res.setStatus( Integer.parseInt( tok.nextToken() ), tok.nextToken() ); break; } } catch ( NumberFormatException ignore ) {} } else if ( name.equalsIgnoreCase( "Content-type" ) ) { res.setContentType( value ); } else if ( name.equalsIgnoreCase( "Content-length" ) ) { try { res.setContentLength( Integer.parseInt( value ) ); } catch ( NumberFormatException ignore ) {} } else if ( name.equalsIgnoreCase( "Location" ) ) { res.setStatus( HttpServletResponse.SC_MOVED_TEMPORARILY ); res.setHeader( name, value ); } else { // Not a special header. Just set it. res.setHeader( name, value ); } } } // Copy the rest of the data uninterpreted. int b; while ( ( b = procIn.read() ) != -1 ) { resOut.write(b); } procIn.close(); resOut.close(); } catch ( IOException e ) { //res.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR ); // There's some weird bug in Java, when reading from a Process // you get a spurious IOException. We have to ignore it. } } private static String makeEnv( String name, String value ) { return name + "=" + value; } private static String[] vector2Array( Vector vec ) { String list[] = new String[vec.size()]; vec.copyInto(list); return list; } /** * Read a RFC2616 line from an InputStream: */ public static String readLine(InputStream in) throws IOException { byte[] b = new byte[4096]; int l = readLine(in, b); return new String(b, 0, l); } /** * Read a RFC2616 line from an InputStream: */ public static int readLine(InputStream in, byte[] b ) throws IOException { int off2 = 0; while ( off2 < b.length ) { int r = in.read(); if ( r == -1 ) { if (off2 == 0 ) return -1; break; } if ( r == 13 ) continue; if ( r == 10 ) break; b[off2] = (byte) r; ++off2; } return off2; } }