mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 01:54:22 +00:00
[DS-1061] Filenames and BitstreamFormat detection break on filenames with equal signs in them
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.xmlui.cocoon.servlet.multipart;
|
||||
|
||||
import org.apache.cocoon.configuration.Settings;
|
||||
import org.apache.cocoon.servlet.RequestUtil;
|
||||
import org.apache.cocoon.servlet.ServletSettings;
|
||||
import org.apache.cocoon.servlet.multipart.MultipartConfigurationHelper;
|
||||
import org.apache.cocoon.servlet.multipart.MultipartHttpServletRequest;
|
||||
import org.apache.cocoon.util.AbstractLogEnabled;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Servlet filter for handling multi part MIME uploads
|
||||
*
|
||||
* @version $Id: MultipartFilter.java 588803 2007-10-26 23:40:14Z vgritsenko $
|
||||
*/
|
||||
public class DSpaceMultipartFilter extends AbstractLogEnabled
|
||||
implements Filter{
|
||||
|
||||
/**
|
||||
* The RequestFactory is responsible for wrapping multipart-encoded
|
||||
* forms and for handing the file payload of incoming requests
|
||||
*/
|
||||
protected DSpaceRequestFactory requestFactory;
|
||||
|
||||
/** Root Cocoon Bean Factory. */
|
||||
protected BeanFactory cocoonBeanFactory;
|
||||
|
||||
/** The root settings. */
|
||||
protected Settings settings;
|
||||
|
||||
/** The special servlet settings. */
|
||||
protected ServletSettings servletSettings;
|
||||
|
||||
/** The servlet context. */
|
||||
protected ServletContext servletContext;
|
||||
|
||||
/**
|
||||
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
|
||||
*/
|
||||
public void init(FilterConfig config) throws ServletException {
|
||||
this.servletContext = config.getServletContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.Filter#destroy()
|
||||
*/
|
||||
public void destroy() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
protected synchronized void configure() {
|
||||
if (this.cocoonBeanFactory == null) {
|
||||
this.cocoonBeanFactory = WebApplicationContextUtils.getRequiredWebApplicationContext(this.servletContext);
|
||||
this.settings = (Settings) this.cocoonBeanFactory.getBean(Settings.ROLE);
|
||||
this.servletSettings = new ServletSettings(this.settings);
|
||||
String containerEncoding;
|
||||
final String encoding = this.settings.getContainerEncoding();
|
||||
if (encoding == null) {
|
||||
containerEncoding = "ISO-8859-1";
|
||||
} else {
|
||||
containerEncoding = encoding;
|
||||
}
|
||||
|
||||
final MultipartConfigurationHelper config = new MultipartConfigurationHelper();
|
||||
config.configure(this.settings, getLogger());
|
||||
|
||||
this.requestFactory = new DSpaceRequestFactory(config.isAutosaveUploads(),
|
||||
new File(config.getUploadDirectory()),
|
||||
config.isAllowOverwrite(),
|
||||
config.isSilentlyRename(),
|
||||
config.getMaxUploadSize(),
|
||||
containerEncoding);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
|
||||
*/
|
||||
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
|
||||
throws IOException, ServletException {
|
||||
if (this.cocoonBeanFactory == null) {
|
||||
this.configure();
|
||||
}
|
||||
|
||||
// get the request (wrapped if contains multipart-form data)
|
||||
HttpServletRequest request = (HttpServletRequest) req;
|
||||
HttpServletResponse response = (HttpServletResponse) res;
|
||||
try{
|
||||
|
||||
try {
|
||||
request = this.requestFactory.getServletRequest(request);
|
||||
} catch (Exception e) {
|
||||
if (getLogger().isErrorEnabled()) {
|
||||
getLogger().error("Problem in multipart filter. Unable to create request.", e);
|
||||
}
|
||||
|
||||
RequestUtil.manageException(request, response, null, null,
|
||||
HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||
"Problem in creating the Request",
|
||||
null, null, e, this.servletSettings, getLogger(), this);
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
|
||||
} finally {
|
||||
try {
|
||||
if (request instanceof MultipartHttpServletRequest) {
|
||||
if (getLogger().isDebugEnabled()) {
|
||||
getLogger().debug("Deleting uploaded file(s).");
|
||||
}
|
||||
((MultipartHttpServletRequest) request).cleanup();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
getLogger().error("MultipartFilter got an exception while trying to cleanup the uploaded files.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,437 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.xmlui.cocoon.servlet.multipart;
|
||||
|
||||
import org.apache.cocoon.servlet.multipart.*;
|
||||
import org.apache.cocoon.util.NullOutputStream;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.*;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* This class is used to implement a multipart request wrapper.
|
||||
* It will parse the http post stream and and fill its hashtable with values.
|
||||
*
|
||||
* This class has been adjusted by DSpace to allow the uploading of files with an = sign in it
|
||||
*
|
||||
* The hashtable will contain:
|
||||
* Vector: inline part values
|
||||
* FilePart: file part
|
||||
*
|
||||
* @version $Id: MultipartParser.java 638211 2008-03-18 04:41:23Z joerg $
|
||||
*/
|
||||
public class DSpaceMultipartParser {
|
||||
|
||||
public static final String UPLOAD_STATUS_SESSION_ATTR = "org.apache.cocoon.servlet.multipartparser.status";
|
||||
|
||||
private final static int FILE_BUFFER_SIZE = 4096;
|
||||
|
||||
private static final int MAX_BOUNDARY_SIZE = 128;
|
||||
|
||||
private boolean saveUploadedFilesToDisk;
|
||||
|
||||
private File uploadDirectory = null;
|
||||
|
||||
private boolean allowOverwrite;
|
||||
|
||||
private boolean silentlyRename;
|
||||
|
||||
private int maxUploadSize;
|
||||
|
||||
private String characterEncoding;
|
||||
|
||||
private Hashtable parts;
|
||||
|
||||
private boolean oversized = false;
|
||||
|
||||
private int contentLength;
|
||||
|
||||
private HttpSession session;
|
||||
|
||||
private boolean hasSession;
|
||||
|
||||
private Hashtable uploadStatus;
|
||||
|
||||
/**
|
||||
* Constructor, parses given request
|
||||
*
|
||||
* @param saveUploadedFilesToDisk Write fileparts to the uploadDirectory. If true the corresponding object
|
||||
* in the hashtable will contain a FilePartFile, if false a FilePartArray
|
||||
* @param uploadDirectory The directory to write to if saveUploadedFilesToDisk is true.
|
||||
* @param allowOverwrite Allow existing files to be overwritten.
|
||||
* @param silentlyRename If file exists rename file (using filename+number).
|
||||
* @param maxUploadSize The maximum content length accepted.
|
||||
* @param characterEncoding The character encoding to be used.
|
||||
*/
|
||||
public DSpaceMultipartParser(boolean saveUploadedFilesToDisk,
|
||||
File uploadDirectory,
|
||||
boolean allowOverwrite,
|
||||
boolean silentlyRename,
|
||||
int maxUploadSize,
|
||||
String characterEncoding)
|
||||
{
|
||||
this.saveUploadedFilesToDisk = saveUploadedFilesToDisk;
|
||||
this.uploadDirectory = uploadDirectory;
|
||||
this.allowOverwrite = allowOverwrite;
|
||||
this.silentlyRename = silentlyRename;
|
||||
this.maxUploadSize = maxUploadSize;
|
||||
this.characterEncoding = characterEncoding;
|
||||
}
|
||||
|
||||
private void parseParts(int contentLength, String contentType, InputStream requestStream)
|
||||
throws IOException, MultipartException {
|
||||
this.contentLength = contentLength;
|
||||
if (contentLength > this.maxUploadSize) {
|
||||
this.oversized = true;
|
||||
}
|
||||
|
||||
BufferedInputStream bufferedStream = new BufferedInputStream(requestStream);
|
||||
PushbackInputStream pushbackStream = new PushbackInputStream(bufferedStream, MAX_BOUNDARY_SIZE);
|
||||
DSpaceTokenStream stream = new DSpaceTokenStream(pushbackStream);
|
||||
|
||||
parseMultiPart(stream, getBoundary(contentType));
|
||||
}
|
||||
|
||||
public Hashtable getParts(int contentLength, String contentType, InputStream requestStream)
|
||||
throws IOException, MultipartException {
|
||||
this.parts = new Hashtable();
|
||||
parseParts(contentLength, contentType, requestStream);
|
||||
return this.parts;
|
||||
}
|
||||
|
||||
public Hashtable getParts(HttpServletRequest request) throws IOException, MultipartException {
|
||||
this.parts = new Hashtable();
|
||||
|
||||
// Copy all parameters coming from the request URI to the parts table.
|
||||
// This happens when a form's action attribute has some parameters
|
||||
Enumeration names = request.getParameterNames();
|
||||
while(names.hasMoreElements()) {
|
||||
String name = (String)names.nextElement();
|
||||
String[] values = request.getParameterValues(name);
|
||||
Vector v = new Vector(values.length);
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
v.add(values[i]);
|
||||
}
|
||||
this.parts.put(name, v);
|
||||
}
|
||||
|
||||
// upload progress bar support
|
||||
this.session = request.getSession();
|
||||
this.hasSession = this.session != null;
|
||||
if (this.hasSession) {
|
||||
this.uploadStatus = new Hashtable();
|
||||
this.uploadStatus.put("started", Boolean.FALSE);
|
||||
this.uploadStatus.put("finished", Boolean.FALSE);
|
||||
this.uploadStatus.put("sent", new Integer(0));
|
||||
this.uploadStatus.put("total", new Integer(request.getContentLength()));
|
||||
this.uploadStatus.put("filename", "");
|
||||
this.uploadStatus.put("error", Boolean.FALSE);
|
||||
this.uploadStatus.put("uploadsdone", new Integer(0));
|
||||
this.session.setAttribute(UPLOAD_STATUS_SESSION_ATTR, this.uploadStatus);
|
||||
}
|
||||
|
||||
parseParts(request.getContentLength(), request.getContentType(), request.getInputStream());
|
||||
|
||||
if (this.hasSession) {
|
||||
this.uploadStatus.put("finished", Boolean.TRUE);
|
||||
}
|
||||
|
||||
return this.parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a multipart block
|
||||
*
|
||||
* @param ts
|
||||
* @param boundary
|
||||
*
|
||||
* @throws java.io.IOException
|
||||
* @throws org.apache.cocoon.servlet.multipart.MultipartException
|
||||
*/
|
||||
private void parseMultiPart(DSpaceTokenStream ts, String boundary)
|
||||
throws IOException, MultipartException {
|
||||
|
||||
ts.setBoundary(boundary.getBytes());
|
||||
ts.read(); // read first boundary away
|
||||
ts.setBoundary(("\r\n" + boundary).getBytes());
|
||||
|
||||
while (ts.getState() == DSpaceTokenStream.STATE_NEXTPART) {
|
||||
ts.nextPart();
|
||||
parsePart(ts);
|
||||
}
|
||||
|
||||
if (ts.getState() != DSpaceTokenStream.STATE_ENDMULTIPART) { // sanity check
|
||||
throw new MultipartException("Malformed stream");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single part
|
||||
*
|
||||
* @param ts
|
||||
*
|
||||
* @throws java.io.IOException
|
||||
* @throws org.apache.cocoon.servlet.multipart.MultipartException
|
||||
*/
|
||||
private void parsePart(DSpaceTokenStream ts)
|
||||
throws IOException, MultipartException {
|
||||
|
||||
Hashtable headers = new Hashtable();
|
||||
headers = readHeaders(ts);
|
||||
try {
|
||||
if (headers.containsKey("filename")) {
|
||||
if (!"".equals(headers.get("filename"))) {
|
||||
parseFilePart(ts, headers);
|
||||
} else {
|
||||
// IE6 sends an empty part with filename="" for
|
||||
// empty upload fields. Just parse away the part
|
||||
byte[] buf = new byte[32];
|
||||
while(ts.getState() == DSpaceTokenStream.STATE_READING)
|
||||
ts.read(buf);
|
||||
}
|
||||
} else if (((String) headers.get("content-disposition"))
|
||||
.toLowerCase().equals("form-data")) {
|
||||
parseInlinePart(ts, headers);
|
||||
}
|
||||
|
||||
// FIXME: multipart/mixed parts are untested.
|
||||
else if (((String) headers.get("content-disposition")).toLowerCase()
|
||||
.indexOf("multipart") > -1) {
|
||||
parseMultiPart(new DSpaceTokenStream(ts, MAX_BOUNDARY_SIZE),
|
||||
"--" + (String) headers.get("boundary"));
|
||||
ts.read(); // read past boundary
|
||||
} else {
|
||||
throw new MultipartException("Unknown part type");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new MultipartException("Malformed stream: " + e.getMessage());
|
||||
} catch (NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
throw new MultipartException("Malformed header");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a file part
|
||||
*
|
||||
* @param in
|
||||
* @param headers
|
||||
*
|
||||
* @throws java.io.IOException
|
||||
* @throws org.apache.cocoon.servlet.multipart.MultipartException
|
||||
*/
|
||||
private void parseFilePart(DSpaceTokenStream in, Hashtable headers)
|
||||
throws IOException, MultipartException {
|
||||
|
||||
byte[] buf = new byte[FILE_BUFFER_SIZE];
|
||||
OutputStream out;
|
||||
File file = null;
|
||||
|
||||
if (oversized) {
|
||||
out = new NullOutputStream();
|
||||
} else if (!saveUploadedFilesToDisk) {
|
||||
out = new ByteArrayOutputStream();
|
||||
} else {
|
||||
String fileName = (String) headers.get("filename");
|
||||
if(File.separatorChar == '\\')
|
||||
fileName = fileName.replace('/','\\');
|
||||
else
|
||||
fileName = fileName.replace('\\','/');
|
||||
|
||||
String filePath = uploadDirectory.getPath() + File.separator;
|
||||
fileName = new File(fileName).getName();
|
||||
file = new File(filePath + fileName);
|
||||
|
||||
if (!allowOverwrite && !file.createNewFile()) {
|
||||
if (silentlyRename) {
|
||||
int c = 0;
|
||||
do {
|
||||
file = new File(filePath + c++ + "_" + fileName);
|
||||
} while (!file.createNewFile());
|
||||
} else {
|
||||
throw new MultipartException("Duplicate file '" + file.getName()
|
||||
+ "' in '" + file.getParent() + "'");
|
||||
}
|
||||
}
|
||||
|
||||
out = new FileOutputStream(file);
|
||||
}
|
||||
|
||||
if (hasSession) { // upload widget support
|
||||
this.uploadStatus.put("finished", Boolean.FALSE);
|
||||
this.uploadStatus.put("started", Boolean.TRUE);
|
||||
this.uploadStatus.put("widget", headers.get("name"));
|
||||
this.uploadStatus.put("filename", headers.get("filename"));
|
||||
}
|
||||
|
||||
int length = 0; // Track length for OversizedPart
|
||||
try {
|
||||
int read = 0;
|
||||
while (in.getState() == DSpaceTokenStream.STATE_READING) {
|
||||
// read data
|
||||
read = in.read(buf);
|
||||
length += read;
|
||||
out.write(buf, 0, read);
|
||||
|
||||
if (this.hasSession) {
|
||||
this.uploadStatus.put("sent",
|
||||
new Integer(((Integer)this.uploadStatus.get("sent")).intValue() + read)
|
||||
);
|
||||
}
|
||||
}
|
||||
if (this.hasSession) { // upload widget support
|
||||
this.uploadStatus.put("uploadsdone",
|
||||
new Integer(((Integer)this.uploadStatus.get("uploadsdone")).intValue() + 1)
|
||||
);
|
||||
this.uploadStatus.put("error", Boolean.FALSE);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// don't let incomplete file uploads pile up in the upload dir.
|
||||
// this usually happens with aborted form submits containing very large files.
|
||||
out.close();
|
||||
out = null;
|
||||
if ( file!=null ) file.delete();
|
||||
if (this.hasSession) { // upload widget support
|
||||
this.uploadStatus.put("error", Boolean.TRUE);
|
||||
}
|
||||
throw ioe;
|
||||
} finally {
|
||||
if ( out!=null ) out.close();
|
||||
}
|
||||
|
||||
String name = (String)headers.get("name");
|
||||
if (oversized) {
|
||||
this.parts.put(name, new RejectedPart(headers, length, this.contentLength, this.maxUploadSize));
|
||||
} else if (file == null) {
|
||||
byte[] bytes = ((ByteArrayOutputStream) out).toByteArray();
|
||||
this.parts.put(name, new PartInMemory(headers, bytes));
|
||||
} else {
|
||||
this.parts.put(name, new PartOnDisk(headers, file));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an inline part
|
||||
*
|
||||
* @param in
|
||||
* @param headers
|
||||
*
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
private void parseInlinePart(DSpaceTokenStream in, Hashtable headers)
|
||||
throws IOException {
|
||||
|
||||
// Buffer incoming bytes for proper string decoding (there can be multibyte chars)
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
|
||||
while (in.getState() == DSpaceTokenStream.STATE_READING) {
|
||||
int c = in.read();
|
||||
if (c != -1) bos.write(c);
|
||||
}
|
||||
|
||||
String field = (String) headers.get("name");
|
||||
Vector v = (Vector) this.parts.get(field);
|
||||
|
||||
if (v == null) {
|
||||
v = new Vector();
|
||||
this.parts.put(field, v);
|
||||
}
|
||||
|
||||
v.add(new String(bos.toByteArray(), this.characterEncoding));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read part headers
|
||||
*
|
||||
* @param in
|
||||
*
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
private Hashtable readHeaders(DSpaceTokenStream in) throws IOException {
|
||||
|
||||
Hashtable headers = new Hashtable();
|
||||
String hdrline = readln(in);
|
||||
|
||||
while (!"".equals(hdrline)) {
|
||||
String name = StringUtils.substringBefore(hdrline, ": ").toLowerCase();
|
||||
String value;
|
||||
if(hdrline.contains(";")){
|
||||
value = StringUtils.substringBetween(hdrline, ": ", "; ");
|
||||
}else{
|
||||
value = StringUtils.substringAfter(hdrline, ": ");
|
||||
}
|
||||
|
||||
headers.put(name, value);
|
||||
|
||||
hdrline = StringUtils.substringAfter(hdrline, ";");
|
||||
|
||||
// The extra tokenizer.hasMoreTokens() hdrline headers.put
|
||||
// handles the filename="" case IE6 submits for an empty
|
||||
// upload field.
|
||||
while (0 < hdrline.length()) {
|
||||
name = StringUtils.substringBetween(hdrline, " ", "=");
|
||||
if(hdrline.contains("; ")){
|
||||
value = StringUtils.substringBetween(hdrline, "=\"", "\";");
|
||||
hdrline = StringUtils.substringAfter(hdrline, ";");
|
||||
}else{
|
||||
value = StringUtils.substringBetween(hdrline, "=\"", "\"");
|
||||
hdrline = "";
|
||||
}
|
||||
|
||||
headers.put(name, value);
|
||||
}
|
||||
|
||||
hdrline = readln(in);
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get boundary from contentheader
|
||||
*/
|
||||
private String getBoundary(String hdr) {
|
||||
|
||||
int start = hdr.toLowerCase().indexOf("boundary=");
|
||||
if (start > -1) {
|
||||
return "--" + hdr.substring(start + 9);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read string until newline or end of stream
|
||||
*
|
||||
* @param in
|
||||
*
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
private String readln(DSpaceTokenStream in) throws IOException {
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
|
||||
int b = in.read();
|
||||
|
||||
while ((b != -1) && (b != '\r')) {
|
||||
bos.write(b);
|
||||
b = in.read();
|
||||
}
|
||||
|
||||
if (b == '\r') {
|
||||
in.read(); // read '\n'
|
||||
}
|
||||
|
||||
return new String(bos.toByteArray(), this.characterEncoding);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.xmlui.cocoon.servlet.multipart;
|
||||
|
||||
import org.apache.cocoon.servlet.multipart.MultipartException;
|
||||
import org.apache.cocoon.servlet.multipart.MultipartHttpServletRequest;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
* This is the interface of Request Wrapper in Cocoon.
|
||||
*
|
||||
* @version $Id: RequestFactory.java 587750 2007-10-24 02:35:22Z vgritsenko $
|
||||
*/
|
||||
public class DSpaceRequestFactory {
|
||||
|
||||
private boolean saveUploadedFilesToDisk;
|
||||
|
||||
private File uploadDirectory;
|
||||
|
||||
private boolean allowOverwrite;
|
||||
|
||||
private boolean silentlyRename;
|
||||
|
||||
private String defaultCharEncoding;
|
||||
|
||||
private int maxUploadSize;
|
||||
|
||||
public DSpaceRequestFactory(boolean saveUploadedFilesToDisk,
|
||||
File uploadDirectory,
|
||||
boolean allowOverwrite,
|
||||
boolean silentlyRename,
|
||||
int maxUploadSize,
|
||||
String defaultCharEncoding) {
|
||||
this.saveUploadedFilesToDisk = saveUploadedFilesToDisk;
|
||||
this.uploadDirectory = uploadDirectory;
|
||||
this.allowOverwrite = allowOverwrite;
|
||||
this.silentlyRename = silentlyRename;
|
||||
this.maxUploadSize = maxUploadSize;
|
||||
this.defaultCharEncoding = defaultCharEncoding;
|
||||
|
||||
if (saveUploadedFilesToDisk) {
|
||||
// Empty the contents of the upload directory
|
||||
File[] files = uploadDirectory.listFiles();
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
files[i].delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the request includes a "multipart/form-data", then wrap it with
|
||||
* methods that allow easier connection to those objects since the servlet
|
||||
* API doesn't provide those methods directly.
|
||||
*/
|
||||
public HttpServletRequest getServletRequest(HttpServletRequest request) throws IOException, MultipartException {
|
||||
HttpServletRequest req = request;
|
||||
String contentType = request.getContentType();
|
||||
|
||||
if ((contentType != null) && (contentType.toLowerCase().indexOf("multipart/form-data") > -1)) {
|
||||
|
||||
String charEncoding = request.getCharacterEncoding();
|
||||
if (charEncoding == null || charEncoding.equals("")) {
|
||||
charEncoding = this.defaultCharEncoding;
|
||||
}
|
||||
|
||||
DSpaceMultipartParser parser = new DSpaceMultipartParser(
|
||||
this.saveUploadedFilesToDisk,
|
||||
this.uploadDirectory,
|
||||
this.allowOverwrite,
|
||||
this.silentlyRename,
|
||||
this.maxUploadSize,
|
||||
charEncoding);
|
||||
|
||||
Hashtable parts = parser.getParts(request);
|
||||
|
||||
req = new MultipartHttpServletRequest(request,parts);
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,242 @@
|
||||
/**
|
||||
* The contents of this file are subject to the license and copyright
|
||||
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||
* tree and available online at
|
||||
*
|
||||
* http://www.dspace.org/license/
|
||||
*/
|
||||
package org.dspace.app.xmlui.cocoon.servlet.multipart;
|
||||
|
||||
import org.apache.cocoon.servlet.multipart.MultipartException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PushbackInputStream;
|
||||
|
||||
/**
|
||||
* Utility class for MultipartParser. Divides the inputstream into parts
|
||||
* separated by a given boundary.
|
||||
*
|
||||
* A newline is espected after each boundary and is parsed away.
|
||||
* @version $Id: TokenStream.java 587750 2007-10-24 02:35:22Z vgritsenko $
|
||||
*/
|
||||
class DSpaceTokenStream extends PushbackInputStream {
|
||||
|
||||
/**
|
||||
* Initial state, no boundary has been set.
|
||||
*/
|
||||
public static final int STATE_NOBOUNDARY = -1;
|
||||
|
||||
/**
|
||||
* Fully read a part, now at the beginning of a new part
|
||||
*/
|
||||
public static final int STATE_NEXTPART = -2;
|
||||
|
||||
/**
|
||||
* Read last boundary, end of multipart block
|
||||
*/
|
||||
public static final int STATE_ENDMULTIPART = -3;
|
||||
|
||||
/**
|
||||
* End of stream, this should not happen
|
||||
*/
|
||||
public static final int STATE_ENDOFSTREAM = -4;
|
||||
|
||||
/**
|
||||
* Currently reading a part
|
||||
*/
|
||||
public static final int STATE_READING = -5;
|
||||
|
||||
/** Field in */
|
||||
private PushbackInputStream in = null;
|
||||
|
||||
/** Field boundary */
|
||||
private byte[] boundary = null;
|
||||
|
||||
/** Field state */
|
||||
private int state = STATE_NOBOUNDARY;
|
||||
|
||||
/**
|
||||
* Creates a new pushback token stream from in.
|
||||
*
|
||||
* @param in The input stream
|
||||
*/
|
||||
public DSpaceTokenStream(PushbackInputStream in) {
|
||||
this(in,1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new pushback token stream from in.
|
||||
*
|
||||
* @param in The input stream
|
||||
* @param size Size (in bytes) of the pushback buffer
|
||||
*/
|
||||
public DSpaceTokenStream(PushbackInputStream in, int size) {
|
||||
super(in,size);
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the boundary to scan for
|
||||
*
|
||||
* @param boundary A byte array containg the boundary
|
||||
*
|
||||
* @throws org.apache.cocoon.servlet.multipart.MultipartException
|
||||
*/
|
||||
public void setBoundary(byte[] boundary) throws MultipartException {
|
||||
this.boundary = boundary;
|
||||
if (state == STATE_NOBOUNDARY) {
|
||||
state = STATE_READING;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start reading the next part in the stream. This method may only be called
|
||||
* if state is STATE_NEXTPART. It will throw a MultipartException if not.
|
||||
*
|
||||
* @throws org.apache.cocoon.servlet.multipart.MultipartException
|
||||
*/
|
||||
public void nextPart() throws MultipartException {
|
||||
if (state != STATE_NEXTPART) {
|
||||
throw new MultipartException("Illegal state");
|
||||
}
|
||||
state = STATE_READING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the stream state
|
||||
*
|
||||
*/
|
||||
public int getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the ouput buffer until either it's full, the boundary has been reached or
|
||||
* the end of the inputstream has been reached.
|
||||
* When a boundary is reached it is entirely read away including trailing \r's and \n's.
|
||||
* It will not be written to the output buffer.
|
||||
* The stream state is updated after each call.
|
||||
*
|
||||
* @param out The output buffer
|
||||
*
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
private int readToBoundary(byte[] out) throws IOException {
|
||||
if (state != STATE_READING) {
|
||||
return 0;
|
||||
}
|
||||
int boundaryIndex = 0;
|
||||
int written = 0;
|
||||
int b = in.read();
|
||||
|
||||
while (true) {
|
||||
while ((byte) b != boundary[0]) {
|
||||
if (b == -1) {
|
||||
state = STATE_ENDOFSTREAM;
|
||||
return written;
|
||||
}
|
||||
out[written++] = (byte) b;
|
||||
|
||||
if (written == out.length) {
|
||||
return written;
|
||||
}
|
||||
b = in.read();
|
||||
}
|
||||
boundaryIndex = 0; // we know the first byte matched
|
||||
// check for boundary
|
||||
while ((boundaryIndex < boundary.length)
|
||||
&& ((byte) b == boundary[boundaryIndex])) {
|
||||
b = in.read();
|
||||
boundaryIndex++;
|
||||
}
|
||||
|
||||
if (boundaryIndex == boundary.length) { // matched boundary
|
||||
if (b != -1) {
|
||||
if (b == '\r') { // newline, another part follows
|
||||
state = STATE_NEXTPART;
|
||||
in.read();
|
||||
} else if (b == '-') { // hyphen, end of multipart
|
||||
state = STATE_ENDMULTIPART;
|
||||
in.read(); // read next hyphen
|
||||
in.read(); // read \r
|
||||
in.read(); // read \n
|
||||
} else { // something else, error
|
||||
throw new IOException(
|
||||
"Unexpected character after boundary");
|
||||
}
|
||||
} else { // nothing after boundary, this shouldn't happen either
|
||||
state = STATE_ENDOFSTREAM;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
// did not match boundary
|
||||
// bytes skipped, write first skipped byte, push back the rest
|
||||
if (b != -1) { // b may be -1
|
||||
in.unread(b); // the non-matching byte
|
||||
}
|
||||
in.unread(boundary, 1,
|
||||
boundaryIndex - 1); // unread skipped boundary data
|
||||
out[written++] = boundary[0];
|
||||
if (written == out.length) {
|
||||
return written;
|
||||
}
|
||||
b = in.read();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.InputStream#read(byte[])
|
||||
*
|
||||
* @param out
|
||||
*
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public int read(byte[] out) throws IOException {
|
||||
if (state != STATE_READING) {
|
||||
return 0;
|
||||
}
|
||||
return readToBoundary(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.InputStream#read(byte[],int,int)
|
||||
*
|
||||
* @param out
|
||||
* @param off
|
||||
* @param len
|
||||
*
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public int read(byte[] out, int off, int len) throws IOException {
|
||||
if ((off < 0) || (off >= out.length)) {
|
||||
throw new IOException("Buffer offset outside buffer");
|
||||
}
|
||||
if (off + len >= out.length) {
|
||||
throw new IOException("Buffer end outside buffer");
|
||||
}
|
||||
if (len < 0) {
|
||||
throw new IOException("Length must be a positive integer");
|
||||
}
|
||||
byte[] buf = new byte[len];
|
||||
int read = read(buf);
|
||||
if (read > 0) {
|
||||
System.arraycopy(buf, 0, out, off, read);
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.InputStream#read()
|
||||
*
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public int read() throws IOException {
|
||||
byte[] buf = new byte[1];
|
||||
int read = read(buf);
|
||||
|
||||
if (read == 0) {
|
||||
return -1;
|
||||
}
|
||||
return buf[0];
|
||||
}
|
||||
}
|
@@ -71,7 +71,7 @@
|
||||
<description>Multipart MIME handling filter for Cocoon</description>
|
||||
<display-name>Cocoon multipart filter</display-name>
|
||||
<filter-name>CocoonMultipartFilter</filter-name>
|
||||
<filter-class>org.apache.cocoon.servlet.multipart.MultipartFilter</filter-class>
|
||||
<filter-class>org.dspace.app.xmlui.cocoon.servlet.multipart.DSpaceMultipartFilter</filter-class>
|
||||
</filter>
|
||||
|
||||
<!--
|
||||
|
@@ -19,6 +19,7 @@
|
||||
<groupId>org.dspace</groupId>
|
||||
<artifactId>dspace-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Custom build DSpace cocoon -->
|
||||
<dependency>
|
||||
<groupId>jdom</groupId>
|
||||
|
Reference in New Issue
Block a user