/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-06 Wolfgang M. Meier
* wolfgang@exist-db.org
* http://exist.sourceforge.net
*
* This program 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
* of the License, or (at your option) any later version.
*
* This program 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 program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package org.exist.http.servlets;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FilenameUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* A wrapper for requests processed by a servlet. All parameters, submitted as part of
* the URL and via the http POST body (application/x-www-form-urlencoded and
* multipart/form-data encoded) are made available transparently.
*
* @author Wolfgang Meier <wolfgang@exist-db.org>
* @author Pierrick Brihaye <pierrick.brihaye@free.fr>
* @author Dannes Wessels <dannes@exist-db.org>
*/
public class HttpRequestWrapper implements RequestWrapper {
private static Logger LOG = LogManager.getLogger(HttpRequestWrapper.class.getName());
private HttpServletRequest servletRequest;
private String formEncoding = null;
private String containerEncoding = null;
private String pathInfo = null;
private String servletPath = null;
private boolean isMultipartContent=false;
// Use linkedhashmap to preserver order
// Object can be a single object, or a List of objects
private Map<String, Object> params = new LinkedHashMap<String, Object>();
// flag to administer wether multi-part formdata was processed
private boolean isFormDataParsed = false;
/**
* Constructs a wrapper for the given servlet request. multipart/form-data
* will be parsed when available upon indication.
*
* Defaults to UTF-8 encoding
*
* @param servletRequest The request as viewed by the servlet
*/
public HttpRequestWrapper(HttpServletRequest servletRequest) {
this(servletRequest, "UTF-8", "UTF-8");
}
/**
* Constructs a wrapper for the given servlet request. multipart/form-data
* will be parsed when available upon indication.
*
* @param servletRequest The request as viewed by the servlet
* @param formEncoding The encoding of the request's forms
* @param containerEncoding The encoding of the servlet
*/
public HttpRequestWrapper(HttpServletRequest servletRequest, String formEncoding,
String containerEncoding) {
this(servletRequest, formEncoding, containerEncoding, true);
}
/**
* Constructs a wrapper for the given servlet request.
*
* @param servletRequest The request as viewed by the servlet
* @param formEncoding The encoding of the request's forms
* @param containerEncoding The encoding of the servlet
* @param parseMultipart Set to TRUE to enable parse multipart/form-data when available.
*/
public HttpRequestWrapper(HttpServletRequest servletRequest, String formEncoding,
String containerEncoding, boolean parseMultipart) {
this.servletRequest = servletRequest;
this.formEncoding = formEncoding;
this.containerEncoding = containerEncoding;
this.pathInfo = servletRequest.getPathInfo();
this.servletPath = servletRequest.getServletPath();
// Get url-encoded parameters (url-ecoded from http GET and POST)
parseParameters();
// Determine if request is a multipart
isMultipartContent=ServletFileUpload.isMultipartContent(servletRequest);
// Get multi-part formdata parameters when it is a mpfd request
// and when instructed to do so
if (parseMultipart && isMultipartContent) {
// Formdata is actually parsed
isFormDataParsed = true;
// Get multi-part formdata
parseMultipartContent();
}
LOG.debug("Retrieved "+params.size() + " parameters.");
}
@Override
public Object getAttribute(String name) {
return servletRequest.getAttribute(name);
}
@Override
public Enumeration getAttributeNames() {
return servletRequest.getAttributeNames();
}
/**
* Returns an array of Cookies
*/
@Override
public Cookie[] getCookies() {
return servletRequest.getCookies();
}
private static void addParameter(Map<String, Object> map, String paramName, Object value) {
final Object original = map.get(paramName);
if (original != null) {
// Check if original value was already a List
if (original instanceof List) {
// Add value to existing List
((List) original).add(value);
} else {
// Single value already detected, convert to List and add both items
final ArrayList<Object> list = new ArrayList<Object>();
list.add(original);
list.add(value);
map.put(paramName, list);
}
} else {
// Parameter did not exist yet, add single value
map.put(paramName, value);
}
}
/**
* Parses multi-part requests in order to set the parameters.
*/
private void parseMultipartContent() {
// Create a factory for disk-based file items
final DiskFileItemFactory factory = new DiskFileItemFactory();
// Dizzzz: Wonder why this should be zero
factory.setSizeThreshold(0);
// Create a new file upload handler
final ServletFileUpload upload = new ServletFileUpload(factory);
try {
final List<FileItem> items = upload.parseRequest(servletRequest);
// Iterate over all mult-part formdata items and
// add all data (field and files) to parmeters
for (final FileItem item : items) {
addParameter(params, item.getFieldName(), item);
}
} catch (final FileUploadException e) {
LOG.error(e);
}
}
/**
* Parses the url-encoded parameters
*/
private void parseParameters() {
final Map<String, String[]> map = servletRequest.getParameterMap();
for (final Map.Entry<String, String[]> param : map.entrySet()) {
// Write keys and values
for (final String value : param.getValue()) {
addParameter(params, param.getKey(), decode(value));
}
}
}
/**
* Convert object to FileItem, get FirstItem from list, or null
* if object or object in list is not a FileItem
*
* @param obj List or Fileitem
* @return First Fileitem in list or Fileitem.
*/
private List<FileItem> getFileItem(Object obj) {
final List<FileItem> fileList = new LinkedList<FileItem>();
if (obj instanceof List) {
// Cast
final List list = (List) obj;
// Return first FileItem object if present
for(final Object listObject : list) {
if(listObject instanceof FileItem && !((FileItem) listObject).isFormField()){
fileList.add((FileItem) listObject);
}
}
} else if(obj instanceof FileItem && !((FileItem) obj).isFormField()) {
// Cast and return
fileList.add((FileItem) obj);
}
// object did not represent a List of FileItem's or FileItem.
return fileList;
}
/**
* @param value
*/
private String decode(String value) {
if (formEncoding == null || value == null) {
return value;
}
if (containerEncoding == null) {
//TODO : use file.encoding system property ?
containerEncoding = "ISO-8859-1";
}
if (containerEncoding.equals(formEncoding)) {
return value;
}
try {
final byte[] bytes = value.getBytes(containerEncoding);
return new String(bytes, formEncoding);
} catch (final UnsupportedEncodingException e) {
LOG.warn(e);
return value;
}
}
/**
* @see javax.servlet.http.HttpServletRequest#getInputStream()
*/
@Override
public InputStream getInputStream() throws IOException {
return servletRequest.getInputStream();
}
/**
* @see javax.servlet.http.HttpServletRequest#getCharacterEncoding()
*/
@Override
public String getCharacterEncoding() {
return servletRequest.getCharacterEncoding();
}
/**
* @see javax.servlet.http.HttpServletRequest#getContentLength()
*/
@Override
public long getContentLength() {
long retval = servletRequest.getContentLength();
final String lenstr = servletRequest.getHeader("Content-Length");
if (lenstr != null) {
retval = Long.parseLong(lenstr);
}
return retval;
}
/**
* @see javax.servlet.http.HttpServletRequest#getContentType()
*/
@Override
public String getContentType() {
return servletRequest.getContentType();
}
/**
* @see javax.servlet.http.HttpServletRequest#getContextPath()
*/
@Override
public String getContextPath() {
return servletRequest.getContextPath();
}
/**
* @see javax.servlet.http.HttpServletRequest#getHeader(String)
*/
@Override
public String getHeader(String arg0) {
return servletRequest.getHeader(arg0);
}
/**
* @see javax.servlet.http.HttpServletRequest#getCharacterEncoding()
* @return An enumeration of header names
*/
@Override
public Enumeration getHeaderNames() {
return servletRequest.getHeaderNames();
}
/**
* @see javax.servlet.http.HttpServletRequest#getHeaders(String)
*/
@Override
public Enumeration getHeaders(String arg0) {
return servletRequest.getHeaders(arg0);
}
/**
* @see javax.servlet.http.HttpServletRequest#getMethod()
*/
@Override
public String getMethod() {
return servletRequest.getMethod();
}
/**
* @see javax.servlet.http.HttpServletRequest#getParameter(String)
*/
@Override
public String getParameter(String name) {
// Parameters
Object o = params.get(name);
if (o == null) {
return null;
}
// If Parameter is a List, get first entry. The data is used later on
if (o instanceof List) {
final List lst = ((List) o);
o = lst.get(0);
}
// If parameter is file item, convert to string
if (o instanceof FileItem) {
final FileItem fi = (FileItem) o;
if (formEncoding == null) {
return fi.getString();
} else {
try {
return fi.getString(formEncoding);
} catch (final UnsupportedEncodingException e) {
LOG.warn(e);
return null;
}
}
// Return just a simple value
} else if (o instanceof String) {
return (String) o;
}
return null;
}
/**
* @see javax.servlet.http.HttpServletRequest#getParameter(String)
*/
@Override
public List<File> getFileUploadParam(String name) {
if (!isFormDataParsed) {
return null;
}
final Object o = params.get(name);
if (o == null) {
return null;
}
final List<FileItem> items = getFileItem(o);
final List<File> files = new ArrayList<File>(items.size());
for (final FileItem item : items) {
files.add(((DiskFileItem) item).getStoreLocation());
}
return files;
}
/**
* @see javax.servlet.http.HttpServletRequest#getParameter(String)
*/
@Override
public List<String> getUploadedFileName(String name) {
if (!isFormDataParsed) {
return null;
}
final Object o = params.get(name);
if (o == null) {
return null;
}
final List<FileItem> items = getFileItem(o);
final List<String> files = new ArrayList<String>(items.size());
for (final FileItem item : items) {
files.add(FilenameUtils.normalize(item.getName()));
}
return files;
}
/**
* @see javax.servlet.http.HttpServletRequest#getParameterNames()
*/
@Override
public Enumeration getParameterNames() {
return Collections.enumeration(params.keySet());
}
/**
* @see javax.servlet.http.HttpServletRequest#getParameterValues(String)
*/
@Override
public String[] getParameterValues(String key) {
// params already retrieved
final Object obj = params.get(key);
// Fast return
if (obj == null) {
return null;
}
// Allocate return values
String[] values;
// If object is a List, retrieve data from list
if (obj instanceof List) {
// Cast to List
final List list = (List) obj;
// Reserve the right aboumt of elements
values = new String[list.size()];
// position in array
int position = 0;
// Iterate over list
for (final Object object : list) {
// Item is a FileItem
if (object instanceof FileItem) {
// Cast
final FileItem item = (FileItem) object;
// Get string representation of FileItem
try {
values[position] = formEncoding == null ? item.getString() : item.getString(formEncoding);
} catch (final UnsupportedEncodingException e) {
LOG.warn(e);
e.printStackTrace();
}
} else {
// Normal formfield
values[position] = (String) object;
}
position++;
}
} else {
// No list retrieve one element only
// Allocate space
values = new String[1];
// Item is a FileItem
if (obj instanceof FileItem) {
final FileItem item = (FileItem) obj;
try {
values[0] = formEncoding == null ? item.getString() : item.getString(formEncoding);
} catch (final UnsupportedEncodingException e) {
LOG.warn(e);
e.printStackTrace();
}
} else {
// Normal formfield
values[0] = (String) obj;
}
}
return values;
}
/**
* @see javax.servlet.http.HttpServletRequest#getPathInfo()
*/
@Override
public String getPathInfo() {
return pathInfo;
}
/**
* @see javax.servlet.http.HttpServletRequest#getPathTranslated()
*/
@Override
public String getPathTranslated() {
return servletRequest.getPathTranslated();
}
/**
* @see javax.servlet.http.HttpServletRequest#getProtocol()
*/
@Override
public String getProtocol() {
return servletRequest.getProtocol();
}
/**
* @see javax.servlet.http.HttpServletRequest#getQueryString()
*/
@Override
public String getQueryString() {
return servletRequest.getQueryString();
}
/**
* @see javax.servlet.http.HttpServletRequest#getRemoteAddr()
*/
@Override
public String getRemoteAddr() {
return servletRequest.getRemoteAddr();
}
/**
* @see javax.servlet.http.HttpServletRequest#getRemoteHost()
*/
@Override
public String getRemoteHost() {
return servletRequest.getRemoteHost();
}
/**
* @see javax.servlet.http.HttpServletRequest#getRemotePort()
*/
@Override
public int getRemotePort() {
return servletRequest.getRemotePort();
}
/**
* @see javax.servlet.http.HttpServletRequest#getRemoteUser()
*/
@Override
public String getRemoteUser() {
return servletRequest.getRemoteUser();
}
/**
* @see javax.servlet.http.HttpServletRequest#getRequestedSessionId()
*/
@Override
public String getRequestedSessionId() {
return servletRequest.getRequestedSessionId();
}
/**
* @see javax.servlet.http.HttpServletRequest#getRequestURI()
*/
@Override
public String getRequestURI() {
return servletRequest.getRequestURI();
}
/**
* @see javax.servlet.http.HttpServletRequest#getRequestURL()
*/
@Override
public StringBuffer getRequestURL() {
return servletRequest.getRequestURL();
}
/**
* @see javax.servlet.http.HttpServletRequest#getScheme()
*/
@Override
public String getScheme() {
return servletRequest.getScheme();
}
/**
* @see javax.servlet.http.HttpServletRequest#getServerName()
*/
@Override
public String getServerName() {
return servletRequest.getServerName();
}
/**
* @see javax.servlet.http.HttpServletRequest#getServerPort()
*/
@Override
public int getServerPort() {
return servletRequest.getServerPort();
}
/**
* @see javax.servlet.http.HttpServletRequest#getServletPath()
*/
@Override
public String getServletPath() {
return servletPath;
}
/**
* @see javax.servlet.http.HttpServletRequest#getSession()
*/
@Override
public SessionWrapper getSession() {
final HttpSession session = servletRequest.getSession();
if (session == null) {
return null;
} else {
return new HttpSessionWrapper(session);
}
}
/**
* @see javax.servlet.http.HttpServletRequest#getSession(boolean)
*/
@Override
public SessionWrapper getSession(boolean arg0) {
final HttpSession session = servletRequest.getSession(arg0);
if (session == null) {
return null;
} else {
return new HttpSessionWrapper(session);
}
}
/**
* @see javax.servlet.http.HttpServletRequest#getUserPrincipal()
*/
@Override
public Principal getUserPrincipal() {
return servletRequest.getUserPrincipal();
}
/**
* @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromCookie()
*/
@Override
public boolean isRequestedSessionIdFromCookie() {
return servletRequest.isRequestedSessionIdFromCookie();
}
/**
* @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromURL()
*/
@Override
public boolean isRequestedSessionIdFromURL() {
return servletRequest.isRequestedSessionIdFromURL();
}
/**
* @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdValid()
*/
@Override
public boolean isRequestedSessionIdValid() {
return servletRequest.isRequestedSessionIdValid();
}
/**
* @see javax.servlet.http.HttpServletRequest#isSecure()
*/
@Override
public boolean isSecure() {
return servletRequest.isSecure();
}
/**
* @see javax.servlet.http.HttpServletRequest#isUserInRole(String)
*/
@Override
public boolean isUserInRole(String arg0) {
return servletRequest.isUserInRole(arg0);
}
/**
* @see javax.servlet.http.HttpServletRequest#removeAttribute(String)
*/
@Override
public void removeAttribute(String arg0) {
servletRequest.removeAttribute(arg0);
}
/**
* @see javax.servlet.http.HttpServletRequest#setAttribute(String, Object)
*/
@Override
public void setAttribute(String arg0, Object arg1) {
servletRequest.setAttribute(arg0, arg1);
}
/**
* @see javax.servlet.http.HttpServletRequest#setCharacterEncoding(String)
*/
@Override
public void setCharacterEncoding(String arg0) throws UnsupportedEncodingException {
servletRequest.setCharacterEncoding(arg0);
}
public void setPathInfo(String arg0) {
pathInfo = arg0;
}
public void setServletPath(String arg0) {
servletPath = arg0;
}
/**
* Indicate if a form is processed.
*
* @return TRUE if a form is processed else FALSE.
*/
public boolean isFormDataParsed() {
return isFormDataParsed;
}
/**
* Indicate if the request is a multi-part formdata request
*
* @return TRUE if request is multi-part/formdata request, else FALSE.
*/
@Override
public boolean isMultipartContent() {
return isMultipartContent;
}
}