001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/util/tags/sakai_2-4-1/util-util/util/src/java/org/sakaiproject/util/Web.java $
003: * $Id: Web.java 6832 2006-03-21 20:43:34Z ggolden@umich.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.util;
021:
022: import java.io.ByteArrayOutputStream;
023: import java.io.PrintWriter;
024: import java.util.Enumeration;
025:
026: import javax.servlet.ServletConfig;
027: import javax.servlet.http.HttpServletRequest;
028:
029: import org.apache.commons.logging.Log;
030: import org.apache.commons.logging.LogFactory;
031:
032: /**
033: * <p>
034: * Web is a web (html, http, etc) technlogies collection of helper methods.
035: * </p>
036: */
037: public class Web {
038: /** Our log (commons). */
039: private static Log M_log = LogFactory.getLog(Web.class);
040:
041: /** These characters are escaped when making a URL */
042: protected static final String ESCAPE_URL = "$&+,:;=?@ '\"<>#%{}|\\^~[]`";
043:
044: /** These can't be encoded in URLs safely even using %nn notation, so encode them using our own custom URL encoding, which the ParameterParser decodes */
045: protected static final String ESCAPE_URL_SPECIAL = "^?;";
046:
047: protected static void displayStringChars(PrintWriter out, String str) {
048: if (str == null) {
049: out.print("null");
050: } else
051: for (int i = 0; i < str.length(); i++) {
052: int c = (int) str.charAt(i);
053: out.print(Integer.toHexString(c) + " ");
054: }
055: out.println();
056: }
057:
058: /**
059: * Escape a plaintext string so that it can be output as part of an HTML document. Amperstand, greater-than, less-than, newlines, etc, will be escaped so that they display (instead of being interpreted as formatting).
060: *
061: * @param value
062: * The string to escape.
063: * @return value fully escaped for HTML.
064: */
065: public static String escapeHtml(String value) {
066: return FormattedText.escapeHtml(value, true);
067: }
068:
069: /**
070: * Escape HTML-formatted text in preparation to include it in an HTML document.
071: *
072: * @param value
073: * The string to escape.
074: * @return value escaped for HTML.
075: */
076: public static String escapeHtmlFormattedText(String value) {
077: return FormattedText.escapeHtmlFormattedText(value);
078: }
079:
080: /**
081: * Return a string based on value that is safe to place into a javascript / html identifier: anything not alphanumeric change to 'x'. If the first character is not alphabetic, a letter 'i' is prepended.
082: *
083: * @param value
084: * The string to escape.
085: * @return value fully escaped using javascript / html identifier rules.
086: */
087: public static String escapeJavascript(String value) {
088: if (value == null || value == "")
089: return "";
090: try {
091: StringBuffer buf = new StringBuffer();
092:
093: // prepend 'i' if first character is not a letter
094: if (!java.lang.Character.isLetter(value.charAt(0))) {
095: buf.append("i");
096: }
097:
098: // change non-alphanumeric characters to 'x'
099: for (int i = 0; i < value.length(); i++) {
100: char c = value.charAt(i);
101: if (!java.lang.Character.isLetterOrDigit(c)) {
102: buf.append("x");
103: } else {
104: buf.append(c);
105: }
106: }
107:
108: String rv = buf.toString();
109: return rv;
110: } catch (Exception e) {
111: M_log.warn("escapeJavascript: ", e);
112: return value;
113: }
114: }
115:
116: /**
117: * Return a string based on value that is safe to place into a javascript value that is in single quiotes.
118: *
119: * @param value
120: * The string to escape.
121: * @return value escaped.
122: */
123: public static String escapeJsQuoted(String value) {
124: if (value == null)
125: return "";
126: try {
127: StringBuffer buf = new StringBuffer();
128: for (int i = 0; i < value.length(); i++) {
129: char c = value.charAt(i);
130:
131: // a single quote must be escaped with a leading backslash
132: if (c == '\'') {
133: buf.append("\\'");
134: }
135:
136: // a backslash must be escaped with another backslash
137: else if (c == '\\') {
138: buf.append("\\\\");
139: }
140:
141: else {
142: buf.append(c);
143: }
144: }
145:
146: String rv = buf.toString();
147: return rv;
148: } catch (Exception e) {
149: M_log.warn("escapeJsQuoted: ", e);
150: return value;
151: }
152: }
153:
154: /**
155: * Return a string based on id that is fully escaped using URL rules, using a UTF-8 underlying encoding.
156: *
157: * @param id
158: * The string to escape.
159: * @return id fully escaped using URL rules.
160: */
161: public static String escapeUrl(String id) {
162: if (id == null)
163: return "";
164: try {
165: // convert the string to bytes in UTF-8
166: byte[] bytes = id.getBytes("UTF-8");
167:
168: StringBuffer buf = new StringBuffer();
169: for (int i = 0; i < bytes.length; i++) {
170: byte b = bytes[i];
171: // escape ascii control characters, ascii high bits, specials
172: if (ESCAPE_URL_SPECIAL.indexOf((char) b) != -1) {
173: buf.append("^^x"); // special funky way to encode bad URL characters - ParameterParser will decode it
174: buf.append(toHex(b));
175: buf.append('^');
176: } else if ((ESCAPE_URL.indexOf((char) b) != -1)
177: || (b <= 0x1F) || (b == 0x7F) || (b >= 0x80)) {
178: buf.append("%");
179: buf.append(toHex(b));
180: } else {
181: buf.append((char) b);
182: }
183: }
184:
185: String rv = buf.toString();
186: return rv;
187: } catch (Exception e) {
188: M_log.warn("escapeUrl: ", e);
189: return id;
190: }
191:
192: } // escapeUrl
193:
194: /**
195: * Returns the hex digit cooresponding to a number between 0 and 15.
196: *
197: * @param i
198: * The number to get the hex digit for.
199: * @return The hex digit cooresponding to that number.
200: * @exception java.lang.IllegalArgumentException
201: * If supplied digit is not between 0 and 15 inclusive.
202: */
203: protected static final char hexDigit(int i) {
204: switch (i) {
205: case 0:
206: return '0';
207: case 1:
208: return '1';
209: case 2:
210: return '2';
211: case 3:
212: return '3';
213: case 4:
214: return '4';
215: case 5:
216: return '5';
217: case 6:
218: return '6';
219: case 7:
220: return '7';
221: case 8:
222: return '8';
223: case 9:
224: return '9';
225: case 10:
226: return 'A';
227: case 11:
228: return 'B';
229: case 12:
230: return 'C';
231: case 13:
232: return 'D';
233: case 14:
234: return 'E';
235: case 15:
236: return 'F';
237: }
238:
239: throw new IllegalArgumentException("Invalid digit:" + i);
240: }
241:
242: /**
243: * Form a path string from the parts of the array starting at index start to the end, each with a '/' in front.
244: *
245: * @param parts
246: * The parts strings
247: * @param start
248: * The index of the first part to use
249: * @param end
250: * The index past the last part to use
251: * @return a path string from the parts of the array starting at index start to the end, each with a '/' in front.
252: */
253: public static String makePath(String[] parts, int start, int end) {
254: StringBuffer buf = new StringBuffer();
255: for (int i = start; i < end; i++) {
256: buf.append('/');
257: buf.append(parts[i]);
258: }
259:
260: if (buf.length() > 0)
261: return buf.toString();
262:
263: return null;
264: }
265:
266: protected static void print(PrintWriter out, String name, int value) {
267: out.print(" " + name + ": ");
268: if (value == -1) {
269: out.println("none");
270: } else {
271: out.println(value);
272: }
273: }
274:
275: protected static void print(PrintWriter out, String name,
276: String value) {
277: out.print(" " + name + ": ");
278: out.println(value == null ? "none" : value);
279: }
280:
281: /**
282: * Compute the URL that would return to this servlet based on the current request, with the optional path and parameters
283: *
284: * @param req
285: * The request.
286: * @return The URL back to this servlet based on the current request.
287: */
288: public static String returnUrl(HttpServletRequest req, String path) {
289: StringBuffer url = new StringBuffer();
290: url.append(serverUrl(req));
291: url.append(req.getContextPath());
292: url.append(req.getServletPath());
293:
294: if (path != null)
295: url.append(path);
296:
297: // TODO: params
298:
299: return url.toString();
300: }
301:
302: /**
303: * Send the HTML / Javascript to invoke an automatic update
304: *
305: * @param out
306: * @param req
307: * The request.
308: * @param placementId
309: * The tool's placement id / presence location / part of the delivery address
310: * @param updateTime
311: * The time (seconds) between courier checks
312: */
313: public static void sendAutoUpdate(PrintWriter out,
314: HttpServletRequest req, String placementId, int updateTime) {
315: out
316: .println("<script type=\"text/javascript\" language=\"JavaScript\">");
317: out.println("updateTime = " + updateTime + "000;");
318: out.println("updateUrl = \"" + serverUrl(req) + "/courier/"
319: + placementId + "\";");
320: out.println("scheduleUpdate();");
321: out.println("</script>");
322: }
323:
324: /**
325: * Compute the URL that would return to this server based on the current request. Note: this method is duplicated in the kernel/request RequestFilter.java
326: *
327: * @param req
328: * The request.
329: * @return The URL back to this server based on the current request.
330: */
331: public static String serverUrl(HttpServletRequest req) {
332: String transport = null;
333: int port = 0;
334: boolean secure = false;
335:
336: // if force.url.secure is set (to a https port number), use https and this port
337: String forceSecure = System
338: .getProperty("sakai.force.url.secure");
339: if (forceSecure != null) {
340: transport = "https";
341: port = Integer.parseInt(forceSecure);
342: secure = true;
343: }
344:
345: // otherwise use the request scheme and port
346: else {
347: transport = req.getScheme();
348: port = req.getServerPort();
349: secure = req.isSecure();
350: }
351:
352: StringBuffer url = new StringBuffer();
353: url.append(transport);
354: url.append("://");
355: url.append(req.getServerName());
356: if (((port != 80) && (!secure)) || ((port != 443) && secure)) {
357: url.append(":");
358: url.append(port);
359: }
360:
361: return url.toString();
362: }
363:
364: public static String snoop(PrintWriter out, boolean html,
365: ServletConfig config, HttpServletRequest req) {
366: // if no out, send to system out
367: ByteArrayOutputStream ostream = null;
368: if (out == null) {
369: ostream = new ByteArrayOutputStream();
370: out = new PrintWriter(ostream);
371: html = false;
372: }
373:
374: String h1 = "";
375: String h1x = "";
376: String pre = "";
377: String prex = "";
378: String b = "";
379: String bx = "";
380: String p = "";
381: if (html) {
382: h1 = "<h1>";
383: h1x = "</h1>";
384: pre = "<pre>";
385: prex = "</pre>";
386: b = "<b>";
387: bx = "</b>";
388: p = "<p>";
389: }
390:
391: Enumeration e = null;
392:
393: out.println(h1 + "Snoop for request" + h1x);
394: out.println(req.toString());
395:
396: if (config != null) {
397: e = config.getInitParameterNames();
398: if (e != null) {
399: boolean first = true;
400: while (e.hasMoreElements()) {
401: if (first) {
402: out.println(h1 + "Init Parameters" + h1x);
403: out.println(pre);
404: first = false;
405: }
406: String param = (String) e.nextElement();
407: out.println(" " + param + ": "
408: + config.getInitParameter(param));
409: }
410: out.println(prex);
411: }
412: }
413:
414: out.println(h1 + "Request information:" + h1x);
415: out.println(pre);
416:
417: print(out, "Request method", req.getMethod());
418: String requestUri = req.getRequestURI();
419: print(out, "Request URI", requestUri);
420: displayStringChars(out, requestUri);
421: print(out, "Request protocol", req.getProtocol());
422: String servletPath = req.getServletPath();
423: print(out, "Servlet path", servletPath);
424: displayStringChars(out, servletPath);
425: String contextPath = req.getContextPath();
426: print(out, "Context path", contextPath);
427: displayStringChars(out, contextPath);
428: String pathInfo = req.getPathInfo();
429: print(out, "Path info", pathInfo);
430: displayStringChars(out, pathInfo);
431: print(out, "Path translated", req.getPathTranslated());
432: print(out, "Query string", req.getQueryString());
433: print(out, "Content length", req.getContentLength());
434: print(out, "Content type", req.getContentType());
435: print(out, "Server name", req.getServerName());
436: print(out, "Server port", req.getServerPort());
437: print(out, "Remote user", req.getRemoteUser());
438: print(out, "Remote address", req.getRemoteAddr());
439: // print(out, "Remote host", req.getRemoteHost());
440: print(out, "Authorization scheme", req.getAuthType());
441:
442: out.println(prex);
443:
444: e = req.getHeaderNames();
445: if (e.hasMoreElements()) {
446: out.println(h1 + "Request headers:" + h1x);
447: out.println(pre);
448: while (e.hasMoreElements()) {
449: String name = (String) e.nextElement();
450: out.println(" " + name + ": " + req.getHeader(name));
451: }
452: out.println(prex);
453: }
454:
455: e = req.getParameterNames();
456: if (e.hasMoreElements()) {
457: out.println(h1 + "Servlet parameters (Single Value style):"
458: + h1x);
459: out.println(pre);
460: while (e.hasMoreElements()) {
461: String name = (String) e.nextElement();
462: out
463: .println(" " + name + " = "
464: + req.getParameter(name));
465: }
466: out.println(prex);
467: }
468:
469: e = req.getParameterNames();
470: if (e.hasMoreElements()) {
471: out.println(h1
472: + "Servlet parameters (Multiple Value style):"
473: + h1x);
474: out.println(pre);
475: while (e.hasMoreElements()) {
476: String name = (String) e.nextElement();
477: String vals[] = (String[]) req.getParameterValues(name);
478: if (vals != null) {
479: out.print(b + " " + name + " = " + bx);
480: out.println(vals[0]);
481: for (int i = 1; i < vals.length; i++)
482: out.println(" " + vals[i]);
483: }
484: out.println(p);
485: }
486: out.println(prex);
487: }
488:
489: e = req.getAttributeNames();
490: if (e.hasMoreElements()) {
491: out.println(h1 + "Request attributes:" + h1x);
492: out.println(pre);
493: while (e.hasMoreElements()) {
494: String name = (String) e.nextElement();
495: out.println(" " + name + ": " + req.getAttribute(name));
496: }
497: out.println(prex);
498: }
499:
500: if (ostream != null) {
501: out.flush();
502: return ostream.toString();
503: }
504:
505: return "";
506: }
507:
508: /**
509: * Returns a hex representation of a byte.
510: *
511: * @param b
512: * The byte to convert to hex.
513: * @return The 2-digit hex value of the supplied byte.
514: */
515: protected static final String toHex(byte b) {
516:
517: char ret[] = new char[2];
518:
519: ret[0] = hexDigit((b >>> 4) & (byte) 0x0F);
520: ret[1] = hexDigit((b >>> 0) & (byte) 0x0F);
521:
522: return new String(ret);
523: }
524:
525: }
|