001: // DefaultCommandRegistry.java
002: // $Id: DefaultCommandRegistry.java,v 1.3 2000/08/16 21:37:47 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.jigsaw.ssi.commands;
007:
008: import java.util.Date;
009: import java.util.Dictionary;
010: import java.util.Hashtable;
011: import java.io.IOException;
012: import java.io.InputStream;
013: import java.io.StringBufferInputStream;
014: import org.w3c.www.http.HeaderValue;
015: import org.w3c.util.ArrayDictionary;
016: import org.w3c.jigsaw.http.Client;
017: import org.w3c.jigsaw.http.Request;
018: import org.w3c.www.mime.MimeType;
019: import org.w3c.jigsaw.ssi.SSIFrame;
020:
021: import org.w3c.jigsaw.forms.URLDecoder;
022: import org.w3c.jigsaw.forms.URLDecoderException;
023:
024: /**
025: * <P>This class provides the most general and commonly used SSI commands.
026: * Compatibility with the NCSA-style directive set has been maintained
027: * as much as it made sense to, and new functionality adequate to Jigsaw
028: * has been added.</P>
029: *
030: * <P>In the description that follows, please refer to
031: * <A HREF="http://hoohoo.ncsa.uiuc.edu/docs/tutorials/includes.html">
032: * the NCSA server-side includes tutorial
033: * </a> for comparison.</P>
034: *
035: * <P>The full set of commands of the DefaultCommandRegistry is:
036: * <DL>
037: * <DT> <A name="cmd-config"><code>config</code></A>
038: * <DD> The <code>errmsg</code> tag is not implemented.
039: *
040: * <DT> <A name="cmd-include"><code>include</code></A>
041: * <DD>
042: * The <code>file</code> and <code>virtual</code> tags are handled in
043: * the same way. Both originate an internal request to the URL given
044: * as the value of the tag. There is no provision for including a file
045: * that is not indexed by Jigsaw. This command can be used to include
046: * the content of <em>any</em> resource. This includes the SSIFrame.
047: * <P> In addition, the following tags are admissible:
048: * <DL>
049: * <DT> <code>ifheader</code>
050: * <DD>
051: * Its value is interpreted as a header name. It causes the
052: * resource to be included only if that header was defined in the
053: * original (client) request.
054: * <DT> <code>else</code>
055: * <DD>
056: * Used in conjunction with <code>ifheader</code>, it specifies a
057: * URL to be included in case the header is <em>not</em> defined.
058: * </DL>
059: *
060: * <DT> <A name="cmd-echo"><code>echo</code></A>
061: * <DD>
062: * In addition to the <code>var</code> tag, which has the NCSA
063: * behavior, the following tags are admissible:
064: * <DL>
065: * <DT> <code>reqstate</code>
066: * <DD>
067: * Its value is interpreted as a Jigsaw request state, and
068: * is expanded as the value of the state. For instance, the
069: * command <BR> <code><!--#echo
070: * reqstate="org.w3c.jigsaw.filters.CounterFilter.count"--></code><BR>
071: * will print the current hit-count, assuming a
072: * CounterFilter exists for the resource.
073: * <DT> <code>reqheader</code>
074: * <DD>
075: * Its value is interpreted as a header in the request, and is
076: * expanded as the value of the header.
077: * <DT> <code>here</code>
078: * If this tag is present, command is expanded as interpreted relative
079: * to the innermost internal request. By default, it is interpreted
080: * relative to the original (client) request.
081: * </DL>
082: *
083: * <DT> <A name="cmd-fsize"><code>fsize</code></A>
084: * <DD>
085: * Behaves like its NCSA counterpart, except that it also
086: * recognizes the tag <code>here</code>. If present, this tag
087: * indicates to include the file size of the innermost included
088: * file. Normally, it includes the file size of the topmost
089: * SSI-parsed file requested by the client. It honors the
090: * <code>sizefmt</code> variable, as set by
091: * <code>config</code>.
092: *
093: * <DT> <A name="cmd-flastmod"><code>flastmod</code></A>
094: * <DD>
095: * In addition to NCSA behavior, it honors the
096: * <code>here</code> tag, which indicates to include the time
097: * stamp of the innermost included file.
098: *
099: * <DT> <A name="cmd-exec"><code>exec</code></A>
100: * <DD>
101: * It accepts <em>only</em> the <code>cmd</code> tag. Given
102: * that the <code>include</code> command can include
103: * CgiResources, the <code>cgi</code> tag is superfluous.
104: * <P> If the SSIFrame <code>secure</code> attribute is set,
105: * this command will be inoperative.
106: *
107: * <DT> <A name="cmd-params"><code>params</code></A>
108: * <DD>
109: * This command expands to an HTML unordered list of the
110: * parameters that it was called with. Provided mainly for instructional
111: * purposes.
112: *
113: * <DT> <A name="cmd-count"><code>count</code></A>
114: * <DD>
115: * Expands to the access count reported by the CounterFilter.
116: * (This may or may not mean the access count of the document,
117: * depending on the way the CounterFilter is set up)
118: *
119: * </DL>
120: *
121: * @author Antonio Ramirez <anto@mit.edu>
122: *
123: */
124:
125: public class DefaultCommandRegistry extends BasicCommandRegistry {
126: private static final boolean[] needsEscape = new boolean[256];
127:
128: private static MimeType type = MimeType.APPLICATION_X_WWW_FORM_URLENCODED;
129:
130: private static Command[] cmds = { new ConfigCommand(),
131: new IncludeCommand(), new EchoCommand(),
132: new FSizeCommand(), new FLastModCommand(),
133: new ExecCommand(), new SampleCommand(),
134: new CounterCommand(), new CountCommand(),
135: new CookieCommand(),
136: new org.w3c.jigsaw.ssi.jdbc.jdbcCommand(),
137: new LoopCommand(), new EndloopCommand(),
138: new ExitloopCommand(), new IfCommand(), new ElseCommand(),
139: new EndifCommand(),
140: new org.w3c.jigsaw.ssi.servlets.ServletCommand() };
141:
142: static {
143: for (int i = 0; i < 256; i++)
144: needsEscape[i] = false;
145:
146: needsEscape[(int) '&'] = needsEscape[(int) ';'] = needsEscape[(int) '\''] = needsEscape[(int) '`'] = needsEscape[(int) '"'] = needsEscape[(int) '|'] = needsEscape[(int) '*'] = needsEscape[(int) '?'] = needsEscape[(int) '~'] = needsEscape[(int) '<'] = needsEscape[(int) '>'] = needsEscape[(int) '^'] = needsEscape[(int) '('] = needsEscape[(int) ')'] = needsEscape[(int) '['] = needsEscape[(int) ']'] = needsEscape[(int) '{'] = needsEscape[(int) '}'] = needsEscape[(int) '$'] = needsEscape[(int) '\\'] = true;
147: }
148:
149: private String unescape(String str) {
150: if (str == null)
151: return null;
152: StringBuffer buf = new StringBuffer(str.length() + 10);
153: for (int i = 0; i < str.length(); i++) {
154: if (needsEscape[(int) ((byte) str.charAt(i))])
155: buf.append('\\');
156: buf.append(str.charAt(i));
157: }
158: return buf.toString();
159: }
160:
161: public DefaultCommandRegistry() {
162: for (int i = 0; i < cmds.length; i++) {
163: registerCommand(cmds[i]);
164: }
165: }
166:
167: public Dictionary initVariables(SSIFrame ssiframe, Request request,
168: Dictionary variables) {
169: variables = super .initVariables(ssiframe, request, variables);
170: if (variables == null)
171: variables = new Hashtable(5);
172:
173: // Format variables:
174: safePut(variables, "sizefmt", "abbrev"); // Abbreviated file sizes
175: safePut(variables, "datefmt", "%c"); // Locale's format
176: ArrayDictionary ssiVars = new ArrayDictionary(22);
177:
178: // CGI/SSI variables:
179: // This weird nesting of dictionaries is so that the set of
180: // variables that is exposed via the echo comand is clearly
181: // delimited.
182: safePut(ssiVars, "DOCUMENT_NAME", ssiframe.getFileResource()
183: .getFilename());
184:
185: safePut(ssiVars, "DOCUMENT_URI", request.getURL().toString());
186:
187: safePut(ssiVars, "QUERY_STRING_UNESCAPED", unescape(request
188: .getQueryString()));
189:
190: safePut(ssiVars, "SERVER_SOFTWARE", ssiframe.getFileResource()
191: .getServer().getSoftware());
192:
193: safePut(ssiVars, "SERVER_NAME", "jigsaw");
194:
195: safePut(ssiVars, "GATEWAY_INTERFACE",
196: "org.w3c.jigsaw.ssi.SSIFrame"); // (?)
197:
198: safePut(ssiVars, "SERVER_PROTOCOL", request.getVersion());
199:
200: safePut(ssiVars, "SERVER_PORT", String.valueOf(ssiframe
201: .getFileResource().getServer().getLocalPort()));
202:
203: safePut(ssiVars, "REQUEST_METHOD", request.getMethod());
204:
205: safePut(ssiVars, "PATH_INFO", "");
206:
207: safePut(ssiVars, "PATH_TRANSLATED", "");
208:
209: safePut(ssiVars, "SCRIPT_NAME", "org.w3c.jigsaw.ssi.SSIFrame"); // (?)
210:
211: String queryString = request.getQueryString();
212: safePut(ssiVars, "QUERY_STRING", queryString);
213:
214: java.net.InetAddress addr = request.getClient()
215: .getInetAddress();
216:
217: safePut(ssiVars, "REMOTE_HOST", addr.getHostName());
218:
219: // This should be simpler (why not addr.getIPAddress() ???)
220: String s = addr.toString();
221: byte[] ip = addr.getAddress();
222: int idx = s.indexOf('/');
223: safePut(ssiVars, "REMOTE_ADDR", (idx == -1) ? s : s
224: .substring(idx + 1));
225:
226: safePut(
227: ssiVars,
228: "REMOTE_USER",
229: request
230: .getState(org.w3c.jigsaw.auth.AuthFilter.STATE_AUTHUSER));
231:
232: safePut(
233: ssiVars,
234: "AUTH_TYPE",
235: request
236: .getState(org.w3c.jigsaw.auth.AuthFilter.STATE_AUTHTYPE));
237:
238: safePut(ssiVars, "REMOTE_IDENT", "");
239:
240: safePut(ssiVars, "CONTENT_TYPE", request.getContentType());
241:
242: int cl = request.getContentLength();
243: if (cl != -1)
244: ssiVars.put("CONTENT_LENGTH", String.valueOf(cl));
245:
246: HeaderValue hval = request.getHeaderValue(Request.H_ACCEPT);
247: if (hval != null)
248: ssiVars.put("HTTP_ACCEPT", hval.toExternalForm());
249:
250: safePut(ssiVars, "HTTP_USER_AGENT", request.getUserAgent());
251:
252: safePut(ssiVars, "X_LAST_MODIFIED", new Date(ssiframe
253: .getFileResource().getFile().lastModified()));
254:
255: safePut(ssiVars, "X_FILE_SIZE", new Long(ssiframe
256: .getFileResource().getFile().length()));
257:
258: variables.put("ssiVars", ssiVars);
259:
260: if (variables.get("topSsiVars") == null)
261: variables.put("topSsiVars", ssiVars);
262:
263: // Check to see if there's a query (either through POST
264: // or through getQueryString).
265: // If so, throw an URLDecoder to the mix.
266: // FIXME!!! URLDecoder strangeness
267:
268: try {
269: InputStream qDataStream = null;
270: if (request.getMethod().equals("POST")
271: && type.match(request.getContentType()) >= 0) {
272: // Notify the client that we are willing to continue:
273: Client client = request.getClient();
274: if (client != null)
275: client.sendContinue();
276: qDataStream = request.getInputStream();
277: } else if (queryString != null) {
278: qDataStream = new StringBufferInputStream(queryString);
279: }
280: if (qDataStream != null) {
281: URLDecoder formData = new URLDecoder(qDataStream);
282: formData.parse();
283: variables.put("formData", formData);
284: }
285: } catch (IOException ex) {
286: // nil
287: } catch (URLDecoderException ex) {
288: // nil
289: }
290:
291: return variables;
292: }
293:
294: private static final void safePut(Dictionary dict, Object key,
295: Object value) {
296: if (key == null || value == null)
297: return;
298: dict.put(key, value);
299: }
300: }
|