001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.catalina.ssi;
017:
018: import java.io.IOException;
019: import java.io.PrintWriter;
020: import java.io.Reader;
021: import java.io.StringWriter;
022: import java.util.Date;
023: import java.util.HashMap;
024: import java.util.StringTokenizer;
025:
026: import org.apache.catalina.util.IOTools;
027:
028: /**
029: * The entry point to SSI processing. This class does the actual parsing, delegating to the SSIMediator, SSICommand, and
030: * SSIExternalResolver as necessary[
031: *
032: * @author Dan Sandberg
033: * @version $Revision: 1.3 $, $Date: 2004/02/27 14:58:47 $
034: *
035: */
036: public class SSIProcessor {
037: /** The start pattern */
038: protected final static String COMMAND_START = "<!--#";
039:
040: /** The end pattern */
041: protected final static String COMMAND_END = "-->";
042: protected final static int BUFFER_SIZE = 4096;
043:
044: protected SSIExternalResolver ssiExternalResolver;
045: protected HashMap commands = new HashMap();
046: protected int debug;
047:
048: public SSIProcessor(SSIExternalResolver ssiExternalResolver,
049: int debug) {
050: this .ssiExternalResolver = ssiExternalResolver;
051: this .debug = debug;
052: addBuiltinCommands();
053: }
054:
055: protected void addBuiltinCommands() {
056: addCommand("config", new SSIConfig());
057: addCommand("echo", new SSIEcho());
058: addCommand("exec", new SSIExec());
059: addCommand("include", new SSIInclude());
060: addCommand("flastmod", new SSIFlastmod());
061: addCommand("fsize", new SSIFsize());
062: addCommand("printenv", new SSIPrintenv());
063: addCommand("set", new SSISet());
064: }
065:
066: public void addCommand(String name, SSICommand command) {
067: commands.put(name, command);
068: }
069:
070: /**
071: * Process a file with server-side commands, reading from reader and writing the processed
072: * version to writer.
073: *
074: * NOTE: We really should be doing this in a streaming way rather than converting it to an array first.
075: *
076: * @param reader the reader to read the file containing SSIs from
077: * @param writer the writer to write the file with the SSIs processed.
078: * @throws IOException when things go horribly awry. Should be unlikely since
079: * the SSICommand usually catches 'normal' IOExceptions.
080: */
081: public void process(Reader reader, Date lastModifiedDate,
082: PrintWriter writer) throws IOException {
083: SSIMediator ssiMediator = new SSIMediator(ssiExternalResolver,
084: lastModifiedDate, debug);
085:
086: StringWriter stringWriter = new StringWriter();
087: IOTools.flow(reader, stringWriter);
088: String fileContents = stringWriter.toString();
089: stringWriter = null;
090:
091: int index = 0;
092: boolean inside = false;
093: StringBuffer command = new StringBuffer();
094: try {
095: while (index < fileContents.length()) {
096: char c = fileContents.charAt(index);
097: if (!inside) {
098: if (c == COMMAND_START.charAt(0)
099: && charCmp(fileContents, index,
100: COMMAND_START)) {
101: inside = true;
102: index += COMMAND_START.length();
103: command.setLength(0); //clear the command string
104: } else {
105: writer.write(c);
106: index++;
107: }
108: } else {
109: if (c == COMMAND_END.charAt(0)
110: && charCmp(fileContents, index, COMMAND_END)) {
111: inside = false;
112: index += COMMAND_END.length();
113: String strCmd = parseCmd(command);
114: if (debug > 0) {
115: ssiExternalResolver.log(
116: "SSIProcessor.process -- processing command: "
117: + strCmd, null);
118: }
119: String[] paramNames = parseParamNames(command,
120: strCmd.length());
121: String[] paramValues = parseParamValues(
122: command, strCmd.length());
123:
124: //We need to fetch this value each time, since it may change during the loop
125: String configErrMsg = ssiMediator
126: .getConfigErrMsg();
127: SSICommand ssiCommand = (SSICommand) commands
128: .get(strCmd.toLowerCase());
129: if (ssiCommand != null) {
130: if (paramNames.length == paramValues.length) {
131: ssiCommand
132: .process(ssiMediator,
133: paramNames,
134: paramValues, writer);
135: } else {
136: ssiExternalResolver.log(
137: "Parameter names count does not match parameter values count on command: "
138: + strCmd, null);
139: writer.write(configErrMsg);
140: }
141: } else {
142: ssiExternalResolver.log("Unknown command: "
143: + strCmd, null);
144: writer.write(configErrMsg);
145: }
146: } else {
147: command.append(c);
148: index++;
149: }
150: }
151: }
152: } catch (SSIStopProcessingException e) {
153: //If we are here, then we have already stopped processing, so all is good
154: }
155: }
156:
157: /**
158: * Parse a StringBuffer and take out the param type token.
159: * Called from <code>requestHandler</code>
160: * @param cmd a value of type 'StringBuffer'
161: * @return a value of type 'String[]'
162: */
163: protected String[] parseParamNames(StringBuffer cmd, int start) {
164: int bIdx = start;
165: int i = 0;
166: int quotes = 0;
167: boolean inside = false;
168: StringBuffer retBuf = new StringBuffer();
169:
170: while (bIdx < cmd.length()) {
171: if (!inside) {
172: while (bIdx < cmd.length() && isSpace(cmd.charAt(bIdx)))
173: bIdx++;
174:
175: if (bIdx >= cmd.length())
176: break;
177:
178: inside = !inside;
179: } else {
180: while (bIdx < cmd.length() && cmd.charAt(bIdx) != '=') {
181: retBuf.append(cmd.charAt(bIdx));
182: bIdx++;
183: }
184:
185: retBuf.append('"');
186: inside = !inside;
187: quotes = 0;
188:
189: while (bIdx < cmd.length() && quotes != 2) {
190: if (cmd.charAt(bIdx) == '"')
191: quotes++;
192:
193: bIdx++;
194: }
195: }
196: }
197:
198: StringTokenizer str = new StringTokenizer(retBuf.toString(),
199: "\"");
200: String[] retString = new String[str.countTokens()];
201:
202: while (str.hasMoreTokens()) {
203: retString[i++] = str.nextToken().trim();
204: }
205:
206: return retString;
207: }
208:
209: /**
210: * Parse a StringBuffer and take out the param token.
211: * Called from <code>requestHandler</code>
212: * @param cmd a value of type 'StringBuffer'
213: * @return a value of type 'String[]'
214: */
215: protected String[] parseParamValues(StringBuffer cmd, int start) {
216: int bIdx = start;
217: int i = 0;
218: int quotes = 0;
219: boolean inside = false;
220: StringBuffer retBuf = new StringBuffer();
221:
222: while (bIdx < cmd.length()) {
223: if (!inside) {
224: while (bIdx < cmd.length() && cmd.charAt(bIdx) != '"')
225: bIdx++;
226:
227: if (bIdx >= cmd.length())
228: break;
229:
230: inside = !inside;
231: } else {
232: while (bIdx < cmd.length() && cmd.charAt(bIdx) != '"') {
233: retBuf.append(cmd.charAt(bIdx));
234: bIdx++;
235: }
236:
237: retBuf.append('"');
238: inside = !inside;
239: }
240:
241: bIdx++;
242: }
243:
244: StringTokenizer str = new StringTokenizer(retBuf.toString(),
245: "\"");
246: String[] retString = new String[str.countTokens()];
247:
248: while (str.hasMoreTokens()) {
249: retString[i++] = str.nextToken();
250: }
251:
252: return retString;
253: }
254:
255: /**
256: * Parse a StringBuffer and take out the command token.
257: * Called from <code>requestHandler</code>
258: * @param cmd a value of type 'StringBuffer'
259: * @return a value of type 'String', or null if there is none
260: */
261: private String parseCmd(StringBuffer cmd) {
262: int firstLetter = -1;
263: int lastLetter = -1;
264: for (int i = 0; i < cmd.length(); i++) {
265: char c = cmd.charAt(i);
266: if (Character.isLetter(c)) {
267: if (firstLetter == -1) {
268: firstLetter = i;
269: }
270: lastLetter = i;
271: } else if (isSpace(c)) {
272: if (lastLetter > -1) {
273: break;
274: }
275: } else {
276: break;
277: }
278: }
279:
280: String command = null;
281: if (firstLetter != -1) {
282: command = cmd.substring(firstLetter, lastLetter + 1);
283: }
284: return command;
285: }
286:
287: protected boolean charCmp(String buf, int index, String command) {
288: return buf.regionMatches(index, command, 0, command.length());
289: }
290:
291: protected boolean isSpace(char c) {
292: return c == ' ' || c == '\n' || c == '\t' || c == '\r';
293: }
294: }
|