[DS-1061] Filenames and BitstreamFormat detection break on filenames with equal signs in them

This commit is contained in:
KevinVdV
2012-06-22 18:27:18 +02:00
parent 7ef850c8c7
commit 36a90c6313
6 changed files with 904 additions and 1 deletions

View File

@@ -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);
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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];
}
}

View File

@@ -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>
<!--

View File

@@ -19,6 +19,7 @@
<groupId>org.dspace</groupId>
<artifactId>dspace-api</artifactId>
</dependency>
<!-- Custom build DSpace cocoon -->
<dependency>
<groupId>jdom</groupId>