/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package com.xpn.xwiki.web;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.struts.Globals;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.util.RequestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xwiki.component.util.DefaultParameterizedType;
import org.xwiki.resource.ResourceReferenceResolver;
import org.xwiki.resource.ResourceType;
import org.xwiki.resource.ResourceTypeResolver;
import org.xwiki.resource.entity.EntityResourceReference;
import org.xwiki.url.ExtendedURL;
/**
* @version $Id: 5cca171ed4d89fa05abda93a21d3421ad6362230 $
*/
public class XWikiRequestProcessor extends org.apache.struts.action.RequestProcessor
{
protected static final Logger LOGGER = LoggerFactory.getLogger(XWikiRequestProcessor.class);
private ResourceTypeResolver<ExtendedURL> typeResolver =
Utils.getComponent(new DefaultParameterizedType(null, ResourceTypeResolver.class, ExtendedURL.class));
private ResourceReferenceResolver<ExtendedURL> resolver =
Utils.getComponent(new DefaultParameterizedType(null, ResourceReferenceResolver.class, ExtendedURL.class));
/**
* Remove request parameters containing special characters having a meaning for BeanUtils since otherwise BeanUtils
* throws an exception if they are not used properly (for example if there's an open {@code (} but without a closing
* {@code )} it'll report a "missing end delimiter" exception. FTR BeanUtils uses those to represent Nested
* properties, Indexed properties, Mapped properties and Combined properties, see
* http://commons.apache.org/proper/commons-beanutils/apidocs/org/apache/commons/beanutils/package-summary.html#standard
*/
public class RequestProcessorServletRequestWrapper extends HttpServletRequestWrapper
{
private final char[] FORBIDDEN = new char[] {'(', ')', '[', ']', '.'};
public RequestProcessorServletRequestWrapper(HttpServletRequest request)
{
super(request);
}
@Override
public String getParameter(String name)
{
if (!StringUtils.containsAny(name, FORBIDDEN)) {
return super.getParameter(name);
} else {
return null;
}
}
@Override
public Map<String, String[]> getParameterMap()
{
// Remove all forbidden names
Map<String, String[]> newParameterMap = new HashMap<>();
for (Map.Entry<String, String[]> entry : super.getParameterMap().entrySet()) {
if (!StringUtils.containsAny(entry.getKey(), FORBIDDEN)) {
newParameterMap.put(entry.getKey(), entry.getValue());
}
}
return Collections.unmodifiableMap(newParameterMap);
}
@Override
public Enumeration<String> getParameterNames()
{
return Collections.enumeration(getParameterMap().keySet());
}
@Override
public String[] getParameterValues(String name)
{
if (!StringUtils.containsAny(name, FORBIDDEN)) {
return super.getParameterValues(name);
} else {
return null;
}
}
}
@Override
protected String processPath(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
throws IOException
{
String url = httpServletRequest.getRequestURL().toString();
try {
ExtendedURL extendedURL = new ExtendedURL(new URL(url), httpServletRequest.getContextPath());
ResourceType type = this.typeResolver.resolve(extendedURL, Collections.<String, Object>emptyMap());
EntityResourceReference entityResourceReference = (EntityResourceReference) this.resolver.resolve(
extendedURL, type, Collections.<String, Object>emptyMap());
return "/" + entityResourceReference.getAction().getActionName() + "/";
} catch (Exception e) {
throw new IOException(String.format("Failed to extract the Entity Action from URL [%s]", url), e);
}
}
/**
* Override the implementation from Struts in order to remove request parameter keys using special BeanUtils syntax
* that we don't use and that can causes errors to be raised, see {@link RequestProcessorServletRequestWrapper}.
*/
@Override
protected void processPopulate(HttpServletRequest request, HttpServletResponse response, ActionForm form,
ActionMapping mapping) throws ServletException
{
if (form == null) {
return;
}
form.setServlet(this.servlet);
form.reset(mapping, request);
if (mapping.getMultipartClass() != null) {
request.setAttribute(Globals.MULTIPART_KEY,
mapping.getMultipartClass());
}
RequestUtils.populate(form, mapping.getPrefix(), mapping.getSuffix(),
new RequestProcessorServletRequestWrapper(request));
// Set the cancellation request attribute if appropriate
if ((request.getParameter(Globals.CANCEL_PROPERTY) != null)
|| (request.getParameter(Globals.CANCEL_PROPERTY_X) != null)) {
request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
}
}
}