KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > barracuda > core > helper > servlet > ScriptDetector


1 /*
2  * Copyright (C) 2003 Christian Cryder [christianc@granitepeaks.com]
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * $Id: ScriptDetector.java,v 1.18 2004/02/01 05:16:28 christianc Exp $
19  */

20 package org.enhydra.barracuda.core.helper.servlet;
21
22 import java.io.*;
23 import javax.servlet.*;
24 import javax.servlet.http.*;
25
26 import org.apache.log4j.*;
27
28 import org.w3c.dom.*;
29 import org.w3c.dom.html.*;
30
31 import org.enhydra.barracuda.core.comp.*;
32 import org.enhydra.barracuda.core.comp.scripting.*;
33 import org.enhydra.barracuda.core.view.*;
34
35
36 /**
37  * This class simply detects whether or not the
38  * client has scripting enabled.
39  *
40  * @author Christian Cryder [christianc@granitepeaks.com]
41  * @author Jacob Kjome <hoju@visi.com>
42  * @version %I%, %G%
43  * @since 1.0.1 (2001-10-22)
44  */

45 public class ScriptDetector {
46     //public vars...eventually, these should probably be final
47
protected static final Logger logger = Logger.getLogger(ScriptDetector.class.getName());
48
49     /**
50      * Flag indicating whether or not to check for client-side
51      * scripting
52      */

53 //temporarily turned off...there seems to be a bug in IE 5.5 where the client side script rewriting
54
//hoses the URL...I need to think about how to handle this. If you need to override this value
55
//you can do so through the assembler xml file - csc_013002
56
// public static boolean DETECT_CLIENT_SCRIPTING_ENABLED = true;
57
public static boolean DETECT_CLIENT_SCRIPTING_ENABLED = false;
58
59     /**
60      * Flag added to URL's and Forms and sent with requests
61      * allowing the server to determine if the client
62      * supports scripting
63      */

64     public static final String JavaDoc SCRIPT_FLAG = "$csjs";
65
66     /**
67      * Flag added to URL's and Forms and sent with requests
68      * to avoid browser and proxy caching
69      */

70     private static final String JavaDoc UNIQUE_FLAG = "$u";
71
72     //-------------------- ScriptDetector ------------------------
73
/**
74      * This method checks an incoming request to see if it has
75      * a scripting flag which indicates whether or not the client
76      * supports scripting. If not, it writes a response that
77      * is sent back to the client which immediately causes the
78      * client to return again. However, this time, with the scripting flag
79      * set.
80      * <p>Right now, we only call this method if we are handling a GET
81      * request (the assumption being that if the client is POSTing data,
82      * it probably came from the server in the first place and, thus, should
83      * already have the script flag).</p>
84      *
85      * @param req the servlet request
86      * @param resp the servlet response
87      * @return <code>true</code> if we actually wrote a response
88      * @throws java.io.IOException
89      * @throws javax.servlet.ServletException
90      */

91     public static boolean checkClientReq(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
92         //the basic idea here is that we want to determine whether or
93
//not a client has scripting enabled. So...we write out a response
94
if (DETECT_CLIENT_SCRIPTING_ENABLED &&
95             req.getParameter(SCRIPT_FLAG)==null) {
96
97             //csc_061202.1 - added
98
//set the caching hdrs to prevent caching
99
resp.setHeader("Cache-Control","no-cache");
100             resp.setDateHeader("Expires", System.currentTimeMillis());
101
102             //build the response
103
resp.setContentType("text/html");
104             PrintWriter out = resp.getWriter();
105
106             //Note the following pseudo-code: decoded(getRequestURI)==decoded(getContextPath) + getServletPath + getPathInfo
107
//Notice that getRequestURI already includes getPathInfo. So, we don't need to append that to getRequestURI (comment based on old removed code)
108
String JavaDoc queryStr = req.getQueryString();
109             if (queryStr==null) queryStr = SCRIPT_FLAG;
110             else queryStr += "&"+SCRIPT_FLAG;
111             String JavaDoc url = new StringBuffer JavaDoc(60).append(req.getRequestURI()).append("?").append(queryStr).toString();
112
113             out.println("<html>");
114             out.println(" <head>");
115             out.println(" <title></title>");
116             out.print (" <script type=\"text/javascript\">location.replace('");out.print(url);out.println("=true');</script>");
117             out.print (" <noscript><meta http-equiv=\"REFRESH\" CONTENT=\"0; URL=");out.print(url);out.println("=false\"></noscript>");
118             out.println(" </head>");
119             out.println(" <body>");
120             out.print (" <h3>Redirecting...</h3><p>If you are not automatically redirected, please click <a HREF=\"");out.print(url);out.println("=false\">here</a></p>");
121             out.println(" </body>");
122             out.println("</html>");
123
124             //out.flush();
125
resp.flushBuffer(); //instead of out.flush for Servlet 2.2 compatibility (Craig McClanahan http://w4.metronet.com/~wjm/tomcat/2000/Nov/msg00174.html)
126
return true;
127         }
128         return false;
129     }
130
131     /**
132      * The purpose of this method is to prepare an outgoing <code>HTMLDocument</code>
133      * with a scripting flag. This ensures that, upon follow-up requests,
134      * client scripting support can be determined.
135      * <p>This method adjusts all links and forms with a flag marking the client
136      * as not supporting scripting. In the case of links it adds the flag
137      * to the query string and in the case of forms, a hidden field. When the page
138      * is rendered on the client, if the client supports scripting, a script is
139      * invoked which re-sets the value of flags to indicate that scripting is
140      * supported.</p>
141      * <p>Note that this method should not be called until AFTER all standard
142      * DOM manipulation has been completed (in other words, think of this as
143      * a filter that should be applied right before the final page gets sent back
144      * to the client).</p>
145      *
146      * @param doc the DOM <code>HTMLDocument</code> that we wish to process
147      * @param vc the <code>ViewContext</code> in which this doc is to be rendered
148      */

149     public static void prepareClientResp(HTMLDocument doc, ViewContext vc) {
150         if (DETECT_CLIENT_SCRIPTING_ENABLED) {
151             String JavaDoc unique = generateUniqueString(); //make the requests unique to eliminate caching problems
152

153             //first find the body element and add a script component to it
154
//this will cause the page to be checked when it loads on the client
155
HTMLElement bodyEl = doc.getBody();
156             if (bodyEl!=null) {
157                 BScript bsComp = new BScript(BScript.ON_LOAD, "sc_CheckPage();");
158                 bsComp.addResource(ResourceGateway.EXT_RESOURCE_ID+BScriptResource.JS_SCRIPTING_CHECK);
159                 bsComp.setView(new DefaultView(bodyEl));
160                 bsComp.initCycle();
161                 try {bsComp.render(vc);}
162                 catch (RenderException e) {logger.error("Fatal error rendering ScriptDetector code");}
163                 bsComp.destroyCycle();
164             }
165
166             //next find all the forms and add a SCRIPT_FLAG input element to each
167
HTMLCollection forms = doc.getForms();
168             for (int i=0; i<forms.getLength(); i++) {
169                 Node form = forms.item(i);
170
171                 //add a script flag
172
Element el = doc.createElement("input");
173                 el.setAttribute("name",SCRIPT_FLAG);
174                 el.setAttribute("value","false");
175                 el.setAttribute("type","hidden");
176                 form.appendChild(el);
177
178                 //add a unique flag (fixes browser caching problems)
179
//el = doc.createElement("input");
180
//el.setAttribute("name",UNIQUE_FLAG);
181
//el.setAttribute("value",unique);
182
//el.setAttribute("type","hidden");
183
//form.appendChild(el);
184
}
185
186             //finally adjust all the Links as well
187
HTMLCollection links = doc.getLinks(); //All HTMLAnchorElements and HTMLAreaElements which have a value for the "href" attribute
188
for (int i=0; i<links.getLength(); i++) {
189                 Element el = (Element) links.item(i);
190                 String JavaDoc href = el.getAttribute("href");
191                 el.setAttribute("href", getURLWithScriptFlag(href, unique, false, true));
192             }
193         }
194     }
195
196     /**
197      * The purpose of this method is to prepare an outgoing <code>WMLDocument</code>
198      * with a scripting flag. This ensures that, upon follow-up requests,
199      * client scripting support can be determined.
200      *
201      * @param doc the DOM <code>WMLDocument</code> that we wish to process
202      * @param vc the <code>ViewContext</code> in which this doc is to be rendered
203      */

204 // public static void prepareClientResp(WMLDocument doc, ViewContext vc) {
205
// TODO: add support for WML Documents
206
// }
207

208     /**
209      * The purpose of this method is to prepare an outgoing <code>Document</code>
210      * with a scripting flag. This ensures that, upon follow-up requests,
211      * client scripting support can be determined.
212      * <p>Note that it is preferable to send a specific type of document
213      * to this method such as an <code>HTMLDocument</code> or a
214      * <code>WMLDocument</code> since they provide the advantage of
215      * compile-time checking for the type where here, given a generic
216      * <code>Document</code>, we use <code>instanceof</code> to check the
217      * type of document and then cast <code>Document</code> to the appropriate
218      * document type. For instance, use the {@link #prepareClientResp(HTMLDocument, ViewContext)}
219      * form if you have an <code>HTMLDocument</code> to prepare</p>
220      * <p>This method claims to throw a DOMException for unsupported DOM's.
221      * However, since we don't have explicit support for WML, XHTML, or XML documents
222      * yet, we'll just let them pass unchanged rather than throw the exception.
223      * Leaving the throws clause here for future use to avoid modifying the interface.</p>
224      *
225      * @param doc the DOM <code>Document</code> that we wish to process
226      * @param vc the <code>ViewContext</code> in which this doc is to be rendered
227      * @throws org.w3c.dom.DOMException - if the document passed in cannot be
228      * cast to one of the currently supported types
229      */

230     public static void prepareClientResp(Document doc, ViewContext vc) throws DOMException {
231         if (DETECT_CLIENT_SCRIPTING_ENABLED) {
232             if (doc instanceof HTMLDocument) {
233                 prepareClientResp((HTMLDocument) doc, vc);
234 // } else if (doc instanceof WMLDocument) {
235
// //TODO: add support for WML Documents
236
} else {
237                 //throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "The document passed in is not currently supported");
238
//do nothing
239
}
240         }
241     }
242
243     /**
244      * This method prepares a URL for redirect by adding the appropriate
245      * client scripting flag.
246      *
247      * @param url the URL to redirect the client to
248      * @param vc the <code>ViewCapabilities</code> of the client
249      * @return a modified url if script detection is enabled, unmodified if not
250      */

251     public static String JavaDoc prepareRedirectURL(String JavaDoc url, ViewCapabilities vc) {
252         if (DETECT_CLIENT_SCRIPTING_ENABLED) {
253             String JavaDoc unique = generateUniqueString(); //make the requests unique to eliminate caching problems
254
boolean scriptingEnabled = !(vc.getScriptingType() instanceof ScriptingType.None);
255             return getURLWithScriptFlag(url, unique, scriptingEnabled);
256         } else {
257             return url;
258         }
259     }
260
261     /**
262      * This method evaluates a client request to see whether or not
263      * scripting is enabled (based on the presence of the SCRIPT_FLAG
264      * in the parameters). This function will either return true, false,
265      * or null (which means indeterminate).
266      *
267      * @param req the servlet request
268      * @return <code>true</code> if we can tell for sure that the client
269      * has scripting enabled, <code>false</code> if we know for
270      * sure that it does not, and <code>null</code> if we cannot
271      * determine for sure
272      */

273     public static Boolean JavaDoc scriptingEnabled(HttpServletRequest req) {
274         String JavaDoc s = req.getParameter(SCRIPT_FLAG);
275         if (s==null) return null;
276         else return new Boolean JavaDoc(s.equals("true"));
277     }
278
279     /**
280      * This method modifies URL Strings by adding a script flag. It allows for a
281      * special check for Strings derived from the href attribute of link elements
282      * including HTMLAnchorElement and HTMLAreaElement to make sure that only the
283      * appropriate href values get modified (ie... skipping values containing 'mailto:',
284      * 'javascript:', 'data:', or 'jar:' protocols as well as cases where the script
285      * flag already exists).
286      *
287      * @param url the url in which to embed the script flag
288      * @param unique a moderately random String to make the url unique
289      * (to avoid browser caching)
290      * @param scriptingEnabled the value to give to the <code>SCRIPT_FLAG</code>
291      * @param doHrefCheck whether or not to check for special cases where the
292      * <code>SCRIPT_FLAG</code> should not be applied
293      * @return a modified url (or unmodified if <code>doHrefCheck</code>
294      * is <code>true</code> and a special case is matched)
295      */

296     private static String JavaDoc getURLWithScriptFlag(String JavaDoc url, String JavaDoc unique, boolean scriptingEnabled, boolean doHrefCheck) {
297         if (doHrefCheck) {
298             if (!url.startsWith("mailto:") && !url.startsWith("javascript:")
299                                            && !url.startsWith("data:")
300                                            && !url.startsWith("jar:")
301                                            && !(url.indexOf(SCRIPT_FLAG)>-1)) { //&& url.indexOf(UNIQUE_FLAG)>-1)) {
302
return getURLWithScriptFlag(url, unique, scriptingEnabled);
303             } else {
304                 return url;
305             }
306         }
307         return getURLWithScriptFlag(url, unique, scriptingEnabled);
308     }
309
310     /**
311      * This method modifies URL Strings by adding a <code>SCRIPT_FLAG</code>.
312      *
313      * @param url the url in which to embed the script flag
314      * @param unique a moderately random String to make the URL unique
315      * (to avoid browser caching)
316      * @param scriptingEnabled the value to give to the <code>SCRIPT_FLAG</code>
317      * @return a modified url
318      */

319     private static String JavaDoc getURLWithScriptFlag(String JavaDoc url, String JavaDoc unique, boolean scriptingEnabled) {
320         String JavaDoc url2 = url;
321         String JavaDoc hash = "";
322         int hashPos = url.indexOf("#");
323         String JavaDoc sep = "?";
324         if (url.indexOf(sep)>-1) sep = "&";
325         if (hashPos>-1) {
326             url2 = url.substring(0,hashPos-1);
327             hash = url.substring(hashPos, url.length());
328         }
329         return new StringBuffer JavaDoc(60).append(url2).append(sep).append(SCRIPT_FLAG).append("=").append(scriptingEnabled).append(hash).toString(); //.append("&").append(UNIQUE_FLAG).append("=").append(unique)
330
}
331
332     /**
333      * This method generates a pseudo-random string which can be used to
334      * make http requests unique to eliminate browser and proxy
335      * caching problems.
336      *
337      * @return a unique string
338      */

339     private static String JavaDoc generateUniqueString() {
340         return String.valueOf(new Object JavaDoc().hashCode()); //avoid StringBuffer object creation from concatenation
341
}
342 }
343
Popular Tags