| /* |
| * Copyright 1999,2004 The Apache Software Foundation. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.catalina.ssi; |
| |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.io.Reader; |
| import java.io.StringWriter; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.StringTokenizer; |
| |
| import org.apache.catalina.util.IOTools; |
| |
| /** |
| * The entry point to SSI processing. This class does the actual parsing, delegating to the SSIMediator, SSICommand, and |
| * SSIExternalResolver as necessary[ |
| * |
| * @author Dan Sandberg |
| * @version $Revision$, $Date$ |
| * |
| */ |
| public class SSIProcessor { |
| /** The start pattern */ |
| protected final static String COMMAND_START = "<!--#"; |
| |
| /** The end pattern */ |
| protected final static String COMMAND_END = "-->"; |
| protected final static int BUFFER_SIZE = 4096; |
| |
| protected SSIExternalResolver ssiExternalResolver; |
| protected HashMap commands = new HashMap(); |
| protected int debug; |
| |
| public SSIProcessor( SSIExternalResolver ssiExternalResolver, int debug ) { |
| this.ssiExternalResolver = ssiExternalResolver; |
| this.debug = debug; |
| addBuiltinCommands(); |
| } |
| |
| protected void addBuiltinCommands() { |
| addCommand( "config", new SSIConfig() ); |
| addCommand( "echo", new SSIEcho() ); |
| addCommand( "exec", new SSIExec() ); |
| addCommand( "include", new SSIInclude() ); |
| addCommand( "flastmod", new SSIFlastmod() ); |
| addCommand( "fsize", new SSIFsize() ); |
| addCommand( "printenv", new SSIPrintenv() ); |
| addCommand( "set", new SSISet() ); |
| } |
| |
| public void addCommand( String name, SSICommand command ) { |
| commands.put( name, command ); |
| } |
| |
| /** |
| * Process a file with server-side commands, reading from reader and writing the processed |
| * version to writer. |
| * |
| * NOTE: We really should be doing this in a streaming way rather than converting it to an array first. |
| * |
| * @param reader the reader to read the file containing SSIs from |
| * @param writer the writer to write the file with the SSIs processed. |
| * @throws IOException when things go horribly awry. Should be unlikely since |
| * the SSICommand usually catches 'normal' IOExceptions. |
| */ |
| public void process( Reader reader, Date lastModifiedDate, PrintWriter writer ) throws IOException { |
| SSIMediator ssiMediator = new SSIMediator( ssiExternalResolver, |
| lastModifiedDate, |
| debug ); |
| |
| StringWriter stringWriter = new StringWriter(); |
| IOTools.flow( reader, stringWriter ); |
| String fileContents = stringWriter.toString(); |
| stringWriter = null; |
| |
| int index = 0; |
| boolean inside = false; |
| StringBuffer command = new StringBuffer(); |
| try { |
| while (index < fileContents.length()) { |
| char c = fileContents.charAt( index ); |
| if ( !inside ) { |
| if ( c == COMMAND_START.charAt( 0 ) && charCmp( fileContents, index, COMMAND_START ) ) { |
| inside = true; |
| index += COMMAND_START.length(); |
| command.setLength( 0 ); //clear the command string |
| } else { |
| writer.write( c ); |
| index++; |
| } |
| } else { |
| if ( c == COMMAND_END.charAt( 0 ) && charCmp( fileContents, index, COMMAND_END ) ) { |
| inside = false; |
| index += COMMAND_END.length(); |
| String strCmd = parseCmd(command); |
| if ( debug > 0 ) { |
| ssiExternalResolver.log( "SSIProcessor.process -- processing command: " + strCmd, null ); |
| } |
| String[] paramNames = parseParamNames(command, strCmd.length()); |
| String[] paramValues = parseParamValues(command, strCmd.length()); |
| |
| //We need to fetch this value each time, since it may change during the loop |
| String configErrMsg = ssiMediator.getConfigErrMsg(); |
| SSICommand ssiCommand = (SSICommand) commands.get(strCmd.toLowerCase()); |
| if ( ssiCommand != null ) { |
| if ( paramNames.length==paramValues.length ) { |
| ssiCommand.process( ssiMediator, paramNames, paramValues, writer ); |
| } else { |
| ssiExternalResolver.log( "Parameter names count does not match parameter values count on command: " + strCmd, null ); |
| writer.write( configErrMsg ); |
| } |
| } else { |
| ssiExternalResolver.log( "Unknown command: " + strCmd, null); |
| writer.write( configErrMsg ); |
| } |
| } else { |
| command.append( c ); |
| index++; |
| } |
| } |
| } |
| } catch ( SSIStopProcessingException e ) { |
| //If we are here, then we have already stopped processing, so all is good |
| } |
| } |
| |
| /** |
| * Parse a StringBuffer and take out the param type token. |
| * Called from <code>requestHandler</code> |
| * @param cmd a value of type 'StringBuffer' |
| * @return a value of type 'String[]' |
| */ |
| protected String[] parseParamNames(StringBuffer cmd, int start) { |
| int bIdx = start; |
| int i = 0; |
| int quotes = 0; |
| boolean inside = false; |
| StringBuffer retBuf = new StringBuffer(); |
| |
| while(bIdx < cmd.length()) { |
| if(!inside) { |
| while(bIdx < cmd.length()&&isSpace(cmd.charAt(bIdx))) |
| bIdx++; |
| |
| if(bIdx>=cmd.length()) |
| break; |
| |
| inside=!inside; |
| } else { |
| while(bIdx < cmd.length()&&cmd.charAt(bIdx)!='=') { |
| retBuf.append(cmd.charAt(bIdx)); |
| bIdx++; |
| } |
| |
| retBuf.append('"'); |
| inside=!inside; |
| quotes=0; |
| |
| while(bIdx < cmd.length()&"es!=2) { |
| if(cmd.charAt(bIdx)=='"') |
| quotes++; |
| |
| bIdx++; |
| } |
| } |
| } |
| |
| StringTokenizer str = new StringTokenizer(retBuf.toString(), "\""); |
| String[] retString = new String[str.countTokens()]; |
| |
| while(str.hasMoreTokens()) { |
| retString[i++] = str.nextToken().trim(); |
| } |
| |
| return retString; |
| } |
| |
| /** |
| * Parse a StringBuffer and take out the param token. |
| * Called from <code>requestHandler</code> |
| * @param cmd a value of type 'StringBuffer' |
| * @return a value of type 'String[]' |
| */ |
| protected String[] parseParamValues(StringBuffer cmd, int start) { |
| int bIdx = start; |
| int i = 0; |
| int quotes = 0; |
| boolean inside = false; |
| StringBuffer retBuf = new StringBuffer(); |
| |
| while(bIdx < cmd.length()) { |
| if(!inside) { |
| while(bIdx < cmd.length()&& |
| cmd.charAt(bIdx)!='"') |
| bIdx++; |
| |
| if(bIdx>=cmd.length()) |
| break; |
| |
| inside=!inside; |
| } else { |
| while(bIdx < cmd.length() && cmd.charAt(bIdx)!='"') { |
| retBuf.append(cmd.charAt(bIdx)); |
| bIdx++; |
| } |
| |
| retBuf.append('"'); |
| inside=!inside; |
| } |
| |
| bIdx++; |
| } |
| |
| StringTokenizer str = new StringTokenizer(retBuf.toString(), "\""); |
| String[] retString = new String[str.countTokens()]; |
| |
| while(str.hasMoreTokens()) { |
| retString[i++] = str.nextToken(); |
| } |
| |
| return retString; |
| } |
| |
| /** |
| * Parse a StringBuffer and take out the command token. |
| * Called from <code>requestHandler</code> |
| * @param cmd a value of type 'StringBuffer' |
| * @return a value of type 'String', or null if there is none |
| */ |
| private String parseCmd(StringBuffer cmd) { |
| int firstLetter = -1; |
| int lastLetter = -1; |
| for ( int i=0; i < cmd.length(); i++ ) { |
| char c = cmd.charAt( i ); |
| if ( Character.isLetter( c ) ) { |
| if ( firstLetter == -1 ) { |
| firstLetter = i; |
| } |
| lastLetter = i; |
| } else if ( isSpace( c ) ) { |
| if ( lastLetter > -1 ) { |
| break; |
| } |
| } else { |
| break; |
| } |
| } |
| |
| String command = null; |
| if ( firstLetter != -1 ) { |
| command = cmd.substring( firstLetter, lastLetter + 1 ); |
| } |
| return command; |
| } |
| |
| protected boolean charCmp(String buf, int index, String command) { |
| return buf.regionMatches( index, command, 0, command.length() ); |
| } |
| |
| protected boolean isSpace(char c) { |
| return c==' '||c=='\n'||c=='\t'||c=='\r'; |
| } |
| } |