/*
* Copyright (c) JForum Team
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* 1) Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
* 2) Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* 3) Neither the name of "Rafael Steil" nor
* the names of its contributors may be used to endorse
* or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
*
* This file creation date: Mar 16, 2003 / 1:31:30 AM
* The JForum Project
* http://www.jforum.net
*/
package net.jforum.context.web;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import net.jforum.UrlPattern;
import net.jforum.UrlPatternCollection;
import net.jforum.context.RequestContext;
import net.jforum.context.SessionContext;
import net.jforum.exceptions.MultipartHandlingException;
import net.jforum.util.legacy.commons.fileupload.FileItem;
import net.jforum.util.legacy.commons.fileupload.FileUploadException;
import net.jforum.util.legacy.commons.fileupload.disk.DiskFileItemFactory;
import net.jforum.util.legacy.commons.fileupload.servlet.ServletFileUpload;
import net.jforum.util.legacy.commons.fileupload.servlet.ServletRequestContext;
import net.jforum.util.preferences.ConfigKeys;
import net.jforum.util.preferences.SystemGlobals;
import org.apache.commons.lang.StringUtils;
/**
* @author Rafael Steil
* @version $Id: WebRequestContext.java,v 1.15 2007/10/15 15:16:15 andowson Exp $
*/
public class WebRequestContext extends HttpServletRequestWrapper implements RequestContext
{
private Map query;
/**
* Default constructor.
*
* @param superRequest Original <code>HttpServletRequest</code> instance
* @throws IOException
*/
public WebRequestContext(HttpServletRequest superRequest) throws IOException
{
super(superRequest);
this.query = new HashMap();
boolean isMultipart = false;
String requestType = superRequest.getMethod().toUpperCase();
String contextPath = superRequest.getContextPath();
String requestUri = this.extractRequestUri(superRequest.getRequestURI(), contextPath);
String encoding = SystemGlobals.getValue(ConfigKeys.ENCODING);
String servletExtension = SystemGlobals.getValue(ConfigKeys.SERVLET_EXTENSION);
boolean isPost = "POST".equals(requestType);
boolean isGet = !isPost;
boolean isQueryStringEmpty = (superRequest.getQueryString() == null
|| superRequest.getQueryString().length() == 0);
if (isGet && isQueryStringEmpty && requestUri.endsWith(servletExtension)) {
superRequest.setCharacterEncoding(encoding);
this.parseFriendlyURL(requestUri, servletExtension);
}
else if (isPost) {
isMultipart = ServletFileUpload.isMultipartContent(new ServletRequestContext(superRequest));
if (isMultipart) {
this.handleMultipart(superRequest, encoding);
}
}
if (!isMultipart) {
boolean isAjax = "XMLHttpRequest".equals(superRequest.getHeader("X-Requested-With"));
if (!isAjax) {
superRequest.setCharacterEncoding(encoding);
}
else {
// Ajax requests are *usually* sent using application/x-www-form-urlencoded; charset=UTF-8.
// In JForum, we assume this as always true.
superRequest.setCharacterEncoding("UTF-8");
}
String containerEncoding = SystemGlobals.getValue(ConfigKeys.DEFAULT_CONTAINER_ENCODING);
if (isPost) {
containerEncoding = encoding;
}
for (Enumeration e = superRequest.getParameterNames(); e.hasMoreElements(); ) {
String name = (String)e.nextElement();
String[] values = superRequest.getParameterValues(name);
if (values != null && values.length > 1) {
for (int i = 0; i < values.length; i++) {
this.addParameter(name, new String(values[i].getBytes(containerEncoding), encoding));
}
}
else {
this.addParameter(name, new String(superRequest.getParameter(name).getBytes(containerEncoding), encoding));
}
}
if (this.getModule() == null && this.getAction() == null) {
int index = requestUri.indexOf('?');
if (index > -1) {
requestUri = requestUri.substring(0, index);
}
this.parseFriendlyURL(requestUri, servletExtension);
}
}
}
/**
* @param requestUri
* @param servletExtension
*/
private void parseFriendlyURL(String requestUri, String servletExtension)
{
requestUri = requestUri.substring(0, requestUri.length() - servletExtension.length());
String[] urlModel = requestUri.split("/");
int moduleIndex = 1;
int actionIndex = 2;
int baseLen = 3;
UrlPattern url = null;
if (urlModel.length >= baseLen) {
// <moduleName>.<actionName>.<numberOfParameters>
StringBuffer sb = new StringBuffer(64)
.append(urlModel[moduleIndex])
.append('.')
.append(urlModel[actionIndex])
.append('.')
.append(urlModel.length - baseLen);
url = UrlPatternCollection.findPattern(sb.toString());
}
if (url != null) {
if (url.getSize() >= urlModel.length - baseLen) {
for (int i = 0; i < url.getSize(); i++) {
this.addParameter(url.getVars()[i], urlModel[i + baseLen]);
}
}
this.addOrReplaceParameter("module", urlModel[moduleIndex]);
this.addParameter("action", urlModel[actionIndex]);
}
else {
this.addOrReplaceParameter("module", null);
this.addParameter("action", null);
}
}
public SessionContext getSessionContext(boolean create) {
return new WebSessionContext(this.getSession(true));
}
public SessionContext getSessionContext() {
return new WebSessionContext(this.getSession());
}
/**
* @param superRequest HttpServletRequest
* @param encoding String
* @throws UnsupportedEncodingException
*/
private void handleMultipart(HttpServletRequest superRequest, String encoding) throws UnsupportedEncodingException
{
String tmpPath = new StringBuffer(256)
.append(SystemGlobals.getApplicationPath())
.append('/')
.append(SystemGlobals.getValue(ConfigKeys.TMP_DIR))
.toString();
File tmpDir = new File(tmpPath);
boolean success = false;
try {
if (!tmpDir.exists()) {
tmpDir.mkdirs();
success = true;
}
}
catch (Exception e) {
// We won't log it because the directory
// creation failed for some reason - a SecurityException
// or something else. We don't care about it, as the
// code below tries to use java.io.tmpdir
}
if (!success) {
tmpPath = System.getProperty("java.io.tmpdir");
tmpDir = new File(tmpPath);
}
ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory(100 * 1024, tmpDir));
upload.setHeaderEncoding(encoding);
try {
List items = upload.parseRequest(superRequest);
for (Iterator iter = items.iterator(); iter.hasNext(); ) {
FileItem item = (FileItem)iter.next();
if (item.isFormField()) {
this.addParameter(item.getFieldName(), item.getString(encoding));
}
else {
if (item.getSize() > 0) {
// We really don't want to call addParameter(), as
// there should not be possible to have multiple
// values for a InputStream data
this.query.put(item.getFieldName(), item);
}
}
}
}
catch (FileUploadException e) {
throw new MultipartHandlingException("Error while processing multipart content: " + e);
}
}
/**
* @see javax.servlet.ServletRequestWrapper#getParameterValues(java.lang.String)
*/
public String[] getParameterValues(String name)
{
Object value = this.getObjectParameter(name);
if (value instanceof String) {
return new String[] { (String)value };
}
List l = (List)value;
return l == null
? super.getParameterValues(name)
: (String[])l.toArray(new String[0]);
}
private String extractRequestUri(String requestUri, String contextPath)
{
// First, remove the context path from the requestUri,
// so we can work only with the important stuff
if (contextPath != null && contextPath.length() > 0) {
requestUri = requestUri.substring(contextPath.length(), requestUri.length());
}
// Remove the "jsessionid" (or similar) from the URI
// Probably this is not the right way to go, since we're
// discarding the value...
int index = requestUri.indexOf(';');
if (index > -1) {
int lastIndex = requestUri.indexOf('?', index);
if (lastIndex == -1) {
lastIndex = requestUri.indexOf('&', index);
}
if (lastIndex == -1) {
requestUri = requestUri.substring(0, index);
}
else {
String part1 = requestUri.substring(0, index);
requestUri = part1 + requestUri.substring(lastIndex);
}
}
return requestUri;
}
/**
* @see javax.servlet.ServletRequest#getParameter(java.lang.String)
*/
public String getParameter(String parameter)
{
return (String)this.query.get(parameter);
}
/**
* Gets an parameter that is a number.
* A call to <code>Integer#parseInt(String)</code> is made
* to do the conversion
* @param parameter The parameter name to get the value
* @return int
*/
public int getIntParameter(String parameter)
{
return Integer.parseInt(this.getParameter(parameter));
}
/**
* Gets some request parameter as <code>Object</code>.
* This method may be used when you have to get some value
* of a <i>multipart/form-data</i> request, like a image
* of file. <br>
*
* @param parameter String
* @return Object
*/
public Object getObjectParameter(String parameter)
{
return this.query.get(parameter);
}
public void addParameter(String name, Object value)
{
if (!this.query.containsKey(name)) {
this.query.put(name, value);
}
else {
Object currentValue = this.getObjectParameter(name);
List l;
if (!(currentValue instanceof List)) {
l = new ArrayList();
l.add(currentValue);
}
else {
l = (List)currentValue;
}
l.add(value);
this.query.put(name, l);
}
}
public void addOrReplaceParameter(String name, Object value)
{
this.query.put(name, value);
}
/**
* Gets the <i>action</i> of the current request.
*
* An <i>Action</i> is the parameter name which specifies
* what next action should be done by the system. It may be
* add or edit a post, editing the groups, whatever. In the URL, the
* Action can the represented in two forms:
* <p>
* <blockquote>
* <code>
* http://www.host.com/webapp/servletName?module=groups&action=list
* </code>
* </blockquote>
* <p>
* or
* <p>
* <blockquote>
* <code>
* http://www.host.com/webapp/servletName/groups/list
* </code>
* </blockquote>
* <p>
* In both situations, the action's name is "list".
*
* @return String representing the action name
*/
public String getAction()
{
return this.getParameter("action");
}
public void changeAction(String newAction)
{
if (this.query.containsKey("action")) {
this.query.remove("action");
this.query.put("action", newAction);
}
else {
this.addParameter("action", newAction);
}
}
/**
* Gets the <i>module</i> of the current request.
*
* A <i>Module</i> is the parameter name which specifies
* what module the user is requesting. It may be the group
* administration, the topics or anything else configured module.
*In the URL, the Module can the represented in two forms:
* <p>
* <blockquote>
* <code>
* http://www.host.com/webapp/servletName?module=groups&action=list
* </code>
* </blockquote>
* <p>
* or
* <p>
* <blockquote>
* <code>
* http://www.host.com/webapp/servletName/groups/list
* </code>
* </blockquote>
* <p>
* In both situations, the module's name is "groups".
*
* @return String representing the module name
*/
public String getModule()
{
return this.getParameter("module");
}
public Object getObjectRequestParameter(String parameter)
{
return this.query.get(parameter);
}
/**
* @see javax.servlet.http.HttpServletRequestWrapper#getContextPath()
*/
public String getContextPath()
{
String contextPath = super.getContextPath();
String proxiedContextPath = SystemGlobals.getValue(ConfigKeys.PROXIED_CONTEXT_PATH);
if (!StringUtils.isEmpty(proxiedContextPath)) {
contextPath = proxiedContextPath;
}
return contextPath;
}
/**
* @see javax.servlet.ServletRequestWrapper#getRemoteAddr()
*/
public String getRemoteAddr()
{
// We look if the request is forwarded
// If it is not call the older function.
String ip = super.getHeader("x-forwarded-for");
if (ip == null) {
ip = super.getRemoteAddr();
}
else {
// Process the IP to keep the last IP (real ip of the computer on the net)
StringTokenizer tokenizer = new StringTokenizer(ip, ",");
// Ignore all tokens, except the last one
for (int i = 0; i < tokenizer.countTokens() -1 ; i++) {
tokenizer.nextElement();
}
ip = tokenizer.nextToken().trim();
if (ip.equals("")) {
ip = null;
}
}
// If the ip is still null, we put 0.0.0.0 to avoid null values
if (ip == null) {
ip = "0.0.0.0";
}
return ip;
}
}