/**
* Copyright 2005-2010 hdiv.org
*
* 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.hdiv.util;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hdiv.config.HDIVConfig;
import org.hdiv.dataComposer.IDataComposer;
public class HDIVRequestUtils {
/**
* Commons Logging instance.
*/
private static Log log = LogFactory.getLog(HDIVRequestUtils.class);
/** <p>Valid characters in a scheme.</p>
* <p>RFC 1738 says the following:</p>
* <blockquote>
* Scheme names consist of a sequence of characters. The lower
* case letters "a"--"z", digits, and the characters plus ("+"),
* period ("."), and hyphen ("-") are allowed. For resiliency,
* programs interpreting URLs should treat upper case letters as
* equivalent to lower case in scheme names (e.g., allow "HTTP" as
* well as "http").
* </blockquote>
* <p>We treat as absolute any URL that begins with such a scheme name,
* followed by a colon.</p>
*/
public static final String VALID_SCHEME_CHARS =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+.-";
/**
* <p>
* It generates a new encoded values for the <code>url</code> parameters.
* </p>
* <p>
* The returned values guarantees the confidentiality in the encoded and
* memory strategies if confidentiality indicator defined by user is true.
* </p>
*
* @param url request url
* @param questionIndex index of the first question occurrence in
* <code>url</code> string
* @param charEncoding character encoding
* @return url with encoded values
*/
public static String composeAction(String url, int questionIndex, String charEncoding) {
IDataComposer dataComposer = HDIVUtil.getDataComposer();
String composed = HDIVRequestUtils.composeAction(url, questionIndex, charEncoding, dataComposer);
return composed;
}
/**
* <p>
* It generates a new encoded values for the <code>url</code> parameters.
* </p>
* <p>
* The returned values guarantees the confidentiality in the encoded and
* memory strategies if confidentiality indicator defined by user is true.
* </p>
*
* @param url request url
* @param questionIndex index of the first question occurrence in
* <code>url</code> string
* @param charEncoding character encoding
* @param dataComposer the dataComposer
* @return url with encoded values
*/
public static String composeAction(String url, int questionIndex, String charEncoding, IDataComposer dataComposer) {
String value = url;
value = value.substring(questionIndex + 1);
value = value.replaceAll("&", "&");
String token = null;
String urlAction = HDIVUtil.getActionMappingName(url);
StringTokenizer st = new StringTokenizer(value, "&");
while (st.hasMoreTokens()) {
token = st.nextToken();
String param = token.substring(0, token.indexOf("="));
String val = token.substring(token.indexOf("=") + 1);
String encodedValue = dataComposer.compose(urlAction, param, val, false, true, charEncoding);
value = value.replaceFirst(HDIVUtil.protectCharacters(token), param + "=" + encodedValue);
}
return url.substring(0, questionIndex + 1) + value;
}
/**
* It creates a new state to store all the parameters and values of the
* <code>request</code> and it generates a new encoded values for the
* <code>request</code> parameters and adds the HDIV parameter.
*
* @param request HTTP request
* @param finalLocation the location to redirect to
* @return URL with encoded parameters and HDIV parameter
*/
private static String composeURL(HttpServletRequest request, String finalLocation) {
String encodedURL = finalLocation;
IDataComposer dataComposer = HDIVUtil.getDataComposer(request);//TODO Can you pass as a parameter?
dataComposer.beginRequest(HDIVUtil.getActionMappingName(finalLocation));
int question = finalLocation.indexOf("?");
if (question > 0) {
// generate a new encoded values for the url parameters
encodedURL = HDIVRequestUtils.composeAction(finalLocation, question, "UTF-8", dataComposer);
}
return HDIVRequestUtils.addExtraParameters(request, dataComposer, encodedURL);
}
/**
* Adds the HDIV parameter, depending on the strategy defined by the user,
* to validate the request <code>encodedURL</code>.
*
* @param request HTTP request
* @param dataComposer HDIV's data composer
* @param encodedURL URL encoded
* @return <code>url</code> with the HDIV state added as a new parameters.
* @see org.hdiv.composer.IDataComposer
* @since HDIV 2.0.3
*/
public static String addExtraParameters(HttpServletRequest request, IDataComposer dataComposer, String encodedURL) {
String hdivParameter = (String) request.getSession().getAttribute("HDIVParameter");
return addHDIVState(dataComposer, hdivParameter, encodedURL);
}
/**
* Adds the HDIV parameter, depending on the strategy defined by the user,
* to validate the request <code>encodedURL</code>.
*
* @param dataComposer HDIV's data composer
* @param hdivParameter HDIV's parameter name
* @param encodedURL URL encoded
* @return <code>url</code> with the HDIV state added as a new parameter
* @see org.hdiv.composer.IDataComposer
*/
public static String addHDIVState(IDataComposer dataComposer, String hdivParameter, String encodedURL) {
String separator = "";
String requestId = dataComposer.endRequest();
if ((requestId.length() <= 0) || (encodedURL.startsWith("javascript:"))) {
return encodedURL;
}
// we check if the url contains parameters
separator = (encodedURL.indexOf("?") > 0) ? "&" : "?";
StringBuffer sb = new StringBuffer();
sb.append(encodedURL)
.append(separator)
.append(hdivParameter)
.append("=")
.append(requestId);
return sb.toString();
}
/**
* Adds HDIV state as a parameters if
* <code>url</code> references our application.
*
* @param request HTTP request
* @param url URL
* @param validationInUrlsWithoutParamsActivated
* @return <code>url</code> with the HDIV state added as a new parameters
*/
public static String addHDIVParameterIfNecessary(HttpServletRequest request,
String url,
boolean validationInUrlsWithoutParamsActivated) {
// if url has not got parameters, we do not have to include HDIV's state
if (!validationInUrlsWithoutParamsActivated && !(url.indexOf("?")>0)) {
return url;
}
if (!HDIVRequestUtils.isAbsoluteUrl(url)) {
url = HDIVRequestUtils.getContextRelativePath(request, url);
if (url.indexOf(request.getContextPath()) != -1) {
url = HDIVRequestUtils.composeURL(request, url);
}
} else { // URL is absolute
if (url.indexOf(request.getContextPath()) != -1) {
url = url.substring(url.indexOf(request.getContextPath()));
url = HDIVRequestUtils.composeURL(request, url);
}
}
return url;
}
/**
* Returns <tt>true</tt> if our current URL is absolute,
* <tt>false</tt> otherwise.
*/
public static boolean isAbsoluteUrl(String url) {
// a null URL is not absolute, by our definition
if (url == null)
return false;
// do a fast, simple check first
int colonPos;
if ((colonPos = url.indexOf(":")) == -1)
return false;
// if we DO have a colon, make sure that every character
// leading up to it is a valid scheme character
for (int i = 0; i < colonPos; i++)
if (VALID_SCHEME_CHARS.indexOf(url.charAt(i)) == -1)
return false;
// if so, we've got an absolute url
return true;
}
public static String getContextRelativePath(ServletRequest request, String relativePath) {
String returnValue = null;
if (relativePath.startsWith("/")) {
returnValue = relativePath;
} else if (!(request instanceof HttpServletRequest)) {
returnValue = relativePath;
} else { // relative path
HttpServletRequest hrequest = (HttpServletRequest) request;
returnValue = hrequest.getContextPath() + "/" + relativePath;
}
return removeRelativePaths(returnValue);
}
/**
* Removes from <code>url<code> references to relative paths.
*
* @param url url
* @return returns <code>url</code> without relative paths.
* @since HDIV 2.0.3
*/
public static String removeRelativePaths(String url) {
String urlWithoutRelativePath = url;
// .. is illegal in an absolute path according to the Servlet Spec and
// will cause known problems on Orion application servers.
if (url.indexOf("..") != -1) {
Stack stack = new Stack();
StringTokenizer pathParts = new StringTokenizer(urlWithoutRelativePath.replace('\\', '/'), "/");
while (pathParts.hasMoreTokens()) {
String part = pathParts.nextToken();
if (!part.equals(".")) {
if (part.equals("..")) {
stack.pop();
} else {
stack.push(part);
}
}
}
StringBuffer flatPathBuffer = new StringBuffer();
for (int i = 0; i < stack.size(); i++) {
flatPathBuffer.append("/").append(stack.elementAt(i));
}
urlWithoutRelativePath = flatPathBuffer.toString();
}
return urlWithoutRelativePath;
}
/**
* Checks if <code>path</code> has an action extension or a jsp page
* extension.
*
* @param path path
* @param extensions extensions table
* @return True if <code>path</code> is an action or references a jsp
* page.
*/
public static boolean hasActionOrServletExtension(String path, Hashtable extensions) {
if (path.indexOf("?") > 0) {
path = path.substring(0, path.indexOf("?"));
}
if (path.charAt(path.length() - 1) == '/') {
return true;
}
int pound = path.indexOf("#");
if (pound >= 0) {
path = path.substring(0, pound);
}
// strip a servlet session ID from
path = HDIVUtil.stripSession(path);
if (path.endsWith(".jsp")) {
return true;
}
if (extensions != null) {
for (Enumeration extensionsIds = extensions.elements(); extensionsIds.hasMoreElements();) {
Pattern extensionPattern = (Pattern) extensionsIds.nextElement();
Matcher m = extensionPattern.matcher(path);
if (m.matches()) {
return true;
}
}
}
return (!path.startsWith("/")) && (path.indexOf(".") == -1);
}
public static boolean hasExtensionToExclude(String path, List extensions)
{
if (path.indexOf("?") > 0) {
path = path.substring(0, path.indexOf("?"));
}
if (path.charAt(path.length() - 1) == '/') {
return false;
}
int pound = path.indexOf("#");
if (pound >= 0) {
path = path.substring(0, pound);
}
// strip a servlet session ID from
path = HDIVUtil.stripSession(path);
if (extensions != null) {
for (Iterator iter = extensions.iterator(); iter.hasNext();) {
String extension = (String) iter.next();
if (path.endsWith(extension)) {
return true;
}
}
}
return false;
}
public static String composeLinkUrl(String url, HttpServletRequest request){
ServletContext servletContext = request.getSession().getServletContext();
HDIVConfig hdivConfig = HDIVUtil.getHDIVConfig(servletContext);
String anchor = HDIVRequestUtils.getAnchorFromUrl(url);
url = HDIVRequestUtils.removeAnchorFromUrl(url);
if (!HDIVRequestUtils.hasExtensionToExclude(url, hdivConfig.getExcludedURLExtensions()))
{
if (HDIVRequestUtils.hasActionOrServletExtension(url, hdivConfig.getProtectedURLPatterns())) {
url = HDIVRequestUtils.addHDIVParameterIfNecessary(request,
url, hdivConfig.isValidationInUrlsWithoutParamsActivated());
}
}
return HDIVRequestUtils.appendAnchorToUrl(url, anchor);
}
public static String appendAnchorToUrl(String url, String anchor){
return url + "#" + anchor;
}
public static String getAnchorFromUrl(String url){
String anchor = null;
if(url.indexOf('#')>0){
anchor = url.substring(url.indexOf('#')+1);
}
return anchor;
}
public static String removeAnchorFromUrl(String url){
if(url.indexOf('#')>0){
return url.substring(0, url.indexOf('#'));
}else{
return url;
}
}
}