This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,256 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
/**
* This class implements an output stream in which the data is
* written into a byte array. The buffer automatically grows as data
* is written to it.
* <p>
* The data can be retrieved using <code>toByteArray()</code> and
* <code>toString()</code>.
* <p>
* Closing a {@code ByteArrayOutputStream} has no effect. The methods in
* this class can be called after the stream has been closed without
* generating an {@code IOException}.
* <p>
* This is an alternative implementation of the {@link java.io.ByteArrayOutputStream}
* class. The original implementation only allocates 32 bytes at the beginning.
* As this class is designed for heavy duty it starts at 1024 bytes. In contrast
* to the original it doesn't reallocate the whole memory block but allocates
* additional buffers. This way no buffers need to be garbage collected and
* the contents don't have to be copied to the new buffer. This class is
* designed to behave exactly like the original. The only exception is the
* deprecated toString(int) method that has been ignored.
*/
public class ByteArrayOutputStream extends OutputStream {
static final int DEFAULT_SIZE = 1024;
/** A singleton empty byte array. */
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
/** The list of buffers, which grows and never reduces. */
private final List<byte[]> buffers = new ArrayList<>();
/** The index of the current buffer. */
private int currentBufferIndex;
/** The total count of bytes in all the filled buffers. */
private int filledBufferSum;
/** The current buffer. */
private byte[] currentBuffer;
/** The total count of bytes written. */
private int count;
/**
* Creates a new byte array output stream. The buffer capacity is
* initially 1024 bytes, though its size increases if necessary.
*/
public ByteArrayOutputStream() {
this(DEFAULT_SIZE);
}
/**
* Creates a new byte array output stream, with a buffer capacity of
* the specified size, in bytes.
*
* @param size the initial size
* @throws IllegalArgumentException if size is negative
*/
public ByteArrayOutputStream(final int size) {
if (size < 0) {
throw new IllegalArgumentException(
"Negative initial size: " + size);
}
synchronized (this) {
needNewBuffer(size);
}
}
/**
* Makes a new buffer available either by allocating
* a new one or re-cycling an existing one.
*
* @param newcount the size of the buffer if one is created
*/
private void needNewBuffer(final int newcount) {
if (currentBufferIndex < buffers.size() - 1) {
//Recycling old buffer
filledBufferSum += currentBuffer.length;
currentBufferIndex++;
currentBuffer = buffers.get(currentBufferIndex);
} else {
//Creating new buffer
int newBufferSize;
if (currentBuffer == null) {
newBufferSize = newcount;
filledBufferSum = 0;
} else {
newBufferSize = Math.max(
currentBuffer.length << 1,
newcount - filledBufferSum);
filledBufferSum += currentBuffer.length;
}
currentBufferIndex++;
currentBuffer = new byte[newBufferSize];
buffers.add(currentBuffer);
}
}
/**
* Write the bytes to byte array.
* @param b the bytes to write
* @param off The start offset
* @param len The number of bytes to write
*/
@Override
public void write(final byte[] b, final int off, final int len) {
if ((off < 0)
|| (off > b.length)
|| (len < 0)
|| ((off + len) > b.length)
|| ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
synchronized (this) {
final int newcount = count + len;
int remaining = len;
int inBufferPos = count - filledBufferSum;
while (remaining > 0) {
final int part = Math.min(remaining, currentBuffer.length - inBufferPos);
System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos, part);
remaining -= part;
if (remaining > 0) {
needNewBuffer(newcount);
inBufferPos = 0;
}
}
count = newcount;
}
}
/**
* Write a byte to byte array.
* @param b the byte to write
*/
@Override
public synchronized void write(final int b) {
int inBufferPos = count - filledBufferSum;
if (inBufferPos == currentBuffer.length) {
needNewBuffer(count + 1);
inBufferPos = 0;
}
currentBuffer[inBufferPos] = (byte) b;
count++;
}
/**
* Writes the entire contents of the specified input stream to this
* byte stream. Bytes from the input stream are read directly into the
* internal buffers of this streams.
*
* @param in the input stream to read from
* @return total number of bytes read from the input stream
* (and written to this stream)
* @throws IOException if an I/O error occurs while reading the input stream
* @since 1.4
*/
public synchronized int write(final InputStream in) throws IOException {
int readCount = 0;
int inBufferPos = count - filledBufferSum;
int n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
while (n != -1) {
readCount += n;
inBufferPos += n;
count += n;
if (inBufferPos == currentBuffer.length) {
needNewBuffer(currentBuffer.length);
inBufferPos = 0;
}
n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
}
return readCount;
}
/**
* Closing a {@code ByteArrayOutputStream} has no effect. The methods in
* this class can be called after the stream has been closed without
* generating an {@code IOException}.
*
* @throws IOException never (this method should not declare this exception
* but it has to now due to backwards compatibility)
*/
@Override
public void close() throws IOException {
//nop
}
/**
* Writes the entire contents of this byte stream to the
* specified output stream.
*
* @param out the output stream to write to
* @throws IOException if an I/O error occurs, such as if the stream is closed
* @see java.io.ByteArrayOutputStream#writeTo(OutputStream)
*/
public synchronized void writeTo(final OutputStream out) throws IOException {
int remaining = count;
for (final byte[] buf : buffers) {
final int c = Math.min(buf.length, remaining);
out.write(buf, 0, c);
remaining -= c;
if (remaining == 0) {
break;
}
}
}
/**
* Gets the current contents of this byte stream as a byte array.
* The result is independent of this stream.
*
* @return the current contents of this output stream, as a byte array
* @see java.io.ByteArrayOutputStream#toByteArray()
*/
public synchronized byte[] toByteArray() {
int remaining = count;
if (remaining == 0) {
return EMPTY_BYTE_ARRAY;
}
final byte newbuf[] = new byte[remaining];
int pos = 0;
for (final byte[] buf : buffers) {
final int c = Math.min(buf.length, remaining);
System.arraycopy(buf, 0, newbuf, pos, c);
pos += c;
remaining -= c;
if (remaining == 0) {
break;
}
}
return newbuf;
}
}

View File

@@ -0,0 +1,227 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* An output stream which will retain data in memory until a specified
* threshold is reached, and only then commit it to disk. If the stream is
* closed before the threshold is reached, the data will not be written to
* disk at all.
* <p>
* This class originated in FileUpload processing. In this use case, you do
* not know in advance the size of the file being uploaded. If the file is small
* you want to store it in memory (for speed), but if the file is large you want
* to store it to file (to avoid memory issues).
*/
public class DeferredFileOutputStream
extends ThresholdingOutputStream
{
// ----------------------------------------------------------- Data members
/**
* The output stream to which data will be written prior to the threshold
* being reached.
*/
private ByteArrayOutputStream memoryOutputStream;
/**
* The output stream to which data will be written at any given time. This
* will always be one of <code>memoryOutputStream</code> or
* <code>diskOutputStream</code>.
*/
private OutputStream currentOutputStream;
/**
* The file to which output will be directed if the threshold is exceeded.
*/
private File outputFile;
/**
* The temporary file prefix.
*/
private final String prefix;
/**
* The temporary file suffix.
*/
private final String suffix;
/**
* The directory to use for temporary files.
*/
private final File directory;
// ----------------------------------------------------------- Constructors
/**
* Constructs an instance of this class which will trigger an event at the
* specified threshold, and save data to a file beyond that point.
* The initial buffer size will default to 1024 bytes which is ByteArrayOutputStream's default buffer size.
*
* @param threshold The number of bytes at which to trigger an event.
* @param outputFile The file to which data is saved beyond the threshold.
*/
public DeferredFileOutputStream(final int threshold, final File outputFile)
{
this(threshold, outputFile, null, null, null, ByteArrayOutputStream.DEFAULT_SIZE);
}
/**
* Constructs an instance of this class which will trigger an event at the
* specified threshold, and save data either to a file beyond that point.
*
* @param threshold The number of bytes at which to trigger an event.
* @param outputFile The file to which data is saved beyond the threshold.
* @param prefix Prefix to use for the temporary file.
* @param suffix Suffix to use for the temporary file.
* @param directory Temporary file directory.
* @param initialBufferSize The initial size of the in memory buffer.
*/
private DeferredFileOutputStream(final int threshold, final File outputFile, final String prefix,
final String suffix, final File directory, final int initialBufferSize) {
super(threshold);
this.outputFile = outputFile;
this.prefix = prefix;
this.suffix = suffix;
this.directory = directory;
memoryOutputStream = new ByteArrayOutputStream(initialBufferSize);
currentOutputStream = memoryOutputStream;
}
// --------------------------------------- ThresholdingOutputStream methods
/**
* Returns the current output stream. This may be memory based or disk
* based, depending on the current state with respect to the threshold.
*
* @return The underlying output stream.
*
* @throws IOException if an error occurs.
*/
@Override
protected OutputStream getStream() throws IOException
{
return currentOutputStream;
}
/**
* Switches the underlying output stream from a memory based stream to one
* that is backed by disk. This is the point at which we realise that too
* much data is being written to keep in memory, so we elect to switch to
* disk-based storage.
*
* @throws IOException if an error occurs.
*/
@Override
protected void thresholdReached() throws IOException
{
if (prefix != null) {
outputFile = File.createTempFile(prefix, suffix, directory);
}
FileUtils.forceMkdirParent(outputFile);
final FileOutputStream fos = new FileOutputStream(outputFile);
try {
memoryOutputStream.writeTo(fos);
} catch (final IOException e){
fos.close();
throw e;
}
currentOutputStream = fos;
memoryOutputStream = null;
}
// --------------------------------------------------------- Public methods
/**
* Determines whether or not the data for this output stream has been
* retained in memory.
*
* @return {@code true} if the data is available in memory;
* {@code false} otherwise.
*/
public boolean isInMemory()
{
return !isThresholdExceeded();
}
/**
* Returns the data for this output stream as an array of bytes, assuming
* that the data has been retained in memory. If the data was written to
* disk, this method returns {@code null}.
*
* @return The data for this output stream, or {@code null} if no such
* data is available.
*/
public byte[] getData()
{
if (memoryOutputStream != null)
{
return memoryOutputStream.toByteArray();
}
return null;
}
/**
* Returns either the output file specified in the constructor or
* the temporary file created or null.
* <p>
* If the constructor specifying the file is used then it returns that
* same output file, even when threshold has not been reached.
* <p>
* If constructor specifying a temporary file prefix/suffix is used
* then the temporary file created once the threshold is reached is returned
* If the threshold was not reached then {@code null} is returned.
*
* @return The file for this output stream, or {@code null} if no such
* file exists.
*/
public File getFile()
{
return outputFile;
}
/**
* Closes underlying output stream, and mark this as closed
*
* @throws IOException if an error occurs.
*/
@Override
public void close() throws IOException
{
super.close();
}
}

View File

@@ -0,0 +1,204 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
/**
* <p> This class represents a file or form item that was received within a
* <code>multipart/form-data</code> POST request.
*
* <p> After retrieving an instance of this class from a {@link
* org.apache.tomcat.util.http.fileupload.FileUpload FileUpload} instance (see
* {@link org.apache.tomcat.util.http.fileupload.FileUpload
* #parseRequest(RequestContext)}), you may
* either request all contents of the file at once using {@link #get()} or
* request an {@link java.io.InputStream InputStream} with
* {@link #getInputStream()} and process the file without attempting to load
* it into memory, which may come handy with large files.
*
* <p> While this interface does not extend
* <code>javax.activation.DataSource</code> per se (to avoid a seldom used
* dependency), several of the defined methods are specifically defined with
* the same signatures as methods in that interface. This allows an
* implementation of this interface to also implement
* <code>javax.activation.DataSource</code> with minimal additional work.
*
* @since 1.3 additionally implements FileItemHeadersSupport
*/
public interface FileItem extends FileItemHeadersSupport {
// ------------------------------- Methods from javax.activation.DataSource
/**
* Returns an {@link java.io.InputStream InputStream} that can be
* used to retrieve the contents of the file.
*
* @return An {@link java.io.InputStream InputStream} that can be
* used to retrieve the contents of the file.
*
* @throws IOException if an error occurs.
*/
InputStream getInputStream() throws IOException;
/**
* Returns the content type passed by the browser or <code>null</code> if
* not defined.
*
* @return The content type passed by the browser or <code>null</code> if
* not defined.
*/
String getContentType();
/**
* Returns the original file name in the client's file system, as provided by
* the browser (or other client software). In most cases, this will be the
* base file name, without path information. However, some clients, such as
* the Opera browser, do include path information.
*
* @return The original file name in the client's file system.
* @throws InvalidFileNameException The file name contains a NUL character,
* which might be an indicator of a security attack. If you intend to
* use the file name anyways, catch the exception and use
* InvalidFileNameException#getName().
*/
String getName();
// ------------------------------------------------------- FileItem methods
/**
* Provides a hint as to whether or not the file contents will be read
* from memory.
*
* @return <code>true</code> if the file contents will be read from memory;
* <code>false</code> otherwise.
*/
boolean isInMemory();
/**
* Returns the size of the file item.
*
* @return The size of the file item, in bytes.
*/
long getSize();
/**
* Returns the contents of the file item as an array of bytes.
*
* @return The contents of the file item as an array of bytes.
*/
byte[] get();
/**
* Returns the contents of the file item as a String, using the specified
* encoding. This method uses {@link #get()} to retrieve the
* contents of the item.
*
* @param encoding The character encoding to use.
*
* @return The contents of the item, as a string.
*
* @throws UnsupportedEncodingException if the requested character
* encoding is not available.
*/
String getString(String encoding) throws UnsupportedEncodingException;
/**
* Returns the contents of the file item as a String, using the default
* character encoding. This method uses {@link #get()} to retrieve the
* contents of the item.
*
* @return The contents of the item, as a string.
*/
String getString();
/**
* A convenience method to write an uploaded item to disk. The client code
* is not concerned with whether or not the item is stored in memory, or on
* disk in a temporary location. They just want to write the uploaded item
* to a file.
* <p>
* This method is not guaranteed to succeed if called more than once for
* the same item. This allows a particular implementation to use, for
* example, file renaming, where possible, rather than copying all of the
* underlying data, thus gaining a significant performance benefit.
*
* @param file The <code>File</code> into which the uploaded item should
* be stored.
*
* @throws Exception if an error occurs.
*/
void write(File file) throws Exception;
/**
* Deletes the underlying storage for a file item, including deleting any
* associated temporary disk file. Although this storage will be deleted
* automatically when the <code>FileItem</code> instance is garbage
* collected, this method can be used to ensure that this is done at an
* earlier time, thus preserving system resources.
*/
void delete();
/**
* Returns the name of the field in the multipart form corresponding to
* this file item.
*
* @return The name of the form field.
*/
String getFieldName();
/**
* Sets the field name used to reference this file item.
*
* @param name The name of the form field.
*/
void setFieldName(String name);
/**
* Determines whether or not a <code>FileItem</code> instance represents
* a simple form field.
*
* @return <code>true</code> if the instance represents a simple form
* field; <code>false</code> if it represents an uploaded file.
*/
boolean isFormField();
/**
* Specifies whether or not a <code>FileItem</code> instance represents
* a simple form field.
*
* @param state <code>true</code> if the instance represents a simple form
* field; <code>false</code> if it represents an uploaded file.
*/
void setFormField(boolean state);
/**
* Returns an {@link java.io.OutputStream OutputStream} that can
* be used for storing the contents of the file.
*
* @return An {@link java.io.OutputStream OutputStream} that can be used
* for storing the contensts of the file.
*
* @throws IOException if an error occurs.
*/
OutputStream getOutputStream() throws IOException;
}

View File

@@ -0,0 +1,46 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
/**
* <p>A factory interface for creating {@link FileItem} instances. Factories
* can provide their own custom configuration, over and above that provided
* by the default file upload implementation.</p>
*/
public interface FileItemFactory {
/**
* Create a new {@link FileItem} instance from the supplied parameters and
* any local factory configuration.
*
* @param fieldName The name of the form field.
* @param contentType The content type of the form field.
* @param isFormField <code>true</code> if this is a plain form field;
* <code>false</code> otherwise.
* @param fileName The name of the uploaded file, if any, as supplied
* by the browser or other client.
*
* @return The newly created file item.
*/
FileItem createItem(
String fieldName,
String contentType,
boolean isFormField,
String fileName
);
}

View File

@@ -0,0 +1,74 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
import java.util.Iterator;
/**
* <p> This class provides support for accessing the headers for a file or form
* item that was received within a <code>multipart/form-data</code> POST
* request.</p>
*
* @since 1.2.1
*/
public interface FileItemHeaders {
/**
* Returns the value of the specified part header as a <code>String</code>.
*
* If the part did not include a header of the specified name, this method
* return <code>null</code>. If there are multiple headers with the same
* name, this method returns the first header in the item. The header
* name is case insensitive.
*
* @param name a <code>String</code> specifying the header name
* @return a <code>String</code> containing the value of the requested
* header, or <code>null</code> if the item does not have a header
* of that name
*/
String getHeader(String name);
/**
* <p>
* Returns all the values of the specified item header as an
* <code>Iterator</code> of <code>String</code> objects.
* </p>
* <p>
* If the item did not include any headers of the specified name, this
* method returns an empty <code>Iterator</code>. The header name is
* case insensitive.
* </p>
*
* @param name a <code>String</code> specifying the header name
* @return an <code>Iterator</code> containing the values of the
* requested header. If the item does not have any headers of
* that name, return an empty <code>Iterator</code>
*/
Iterator<String> getHeaders(String name);
/**
* <p>
* Returns an <code>Iterator</code> of all the header names.
* </p>
*
* @return an <code>Iterator</code> containing all of the names of
* headers provided with this file item. If the item does not have
* any headers return an empty <code>Iterator</code>
*/
Iterator<String> getHeaderNames();
}

View File

@@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
/**
* Interface that will indicate that {@link FileItem} or {@link FileItemStream}
* implementations will accept the headers read for the item.
*
* @since 1.2.1
*
* @see FileItem
* @see FileItemStream
*/
public interface FileItemHeadersSupport {
/**
* Returns the collection of headers defined locally within this item.
*
* @return the {@link FileItemHeaders} present for this item.
*/
FileItemHeaders getHeaders();
/**
* Sets the headers read from within an item. Implementations of
* {@link FileItem} or {@link FileItemStream} should implement this
* interface to be able to get the raw headers found within the item
* header block.
*
* @param headers the instance that holds onto the headers
* for this instance.
*/
void setHeaders(FileItemHeaders headers);
}

View File

@@ -0,0 +1,97 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
import java.io.IOException;
import java.util.List;
import org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException;
import org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException;
/**
* An iterator, as returned by
* {@link FileUploadBase#getItemIterator(RequestContext)}.
*/
public interface FileItemIterator {
/** Returns the maximum size of a single file. An {@link FileSizeLimitExceededException}
* will be thrown, if there is an uploaded file, which is exceeding this value.
* By default, this value will be copied from the {@link FileUploadBase#getFileSizeMax()
* FileUploadBase} object, however, the user may replace the default value with a
* request specific value by invoking {@link #setFileSizeMax(long)} on this object.
* @return The maximum size of a single, uploaded file. The value -1 indicates "unlimited".
*/
public long getFileSizeMax();
/** Sets the maximum size of a single file. An {@link FileSizeLimitExceededException}
* will be thrown, if there is an uploaded file, which is exceeding this value.
* By default, this value will be copied from the {@link FileUploadBase#getFileSizeMax()
* FileUploadBase} object, however, the user may replace the default value with a
* request specific value by invoking {@link #setFileSizeMax(long)} on this object, so
* there is no need to configure it here.
* <em>Note:</em>Changing this value doesn't affect files, that have already been uploaded.
* @param pFileSizeMax The maximum size of a single, uploaded file. The value -1 indicates "unlimited".
*/
public void setFileSizeMax(long pFileSizeMax);
/** Returns the maximum size of the complete HTTP request. A {@link SizeLimitExceededException}
* will be thrown, if the HTTP request will exceed this value.
* By default, this value will be copied from the {@link FileUploadBase#getSizeMax()
* FileUploadBase} object, however, the user may replace the default value with a
* request specific value by invoking {@link #setSizeMax(long)} on this object.
* @return The maximum size of the complete HTTP requqest. The value -1 indicates "unlimited".
*/
public long getSizeMax();
/** Returns the maximum size of the complete HTTP request. A {@link SizeLimitExceededException}
* will be thrown, if the HTTP request will exceed this value.
* By default, this value will be copied from the {@link FileUploadBase#getSizeMax()
* FileUploadBase} object, however, the user may replace the default value with a
* request specific value by invoking {@link #setSizeMax(long)} on this object.
* <em>Note:</em> Setting the maximum size on this object will work only, if the iterator is not
* yet initialized. In other words: If the methods {@link #hasNext()}, {@link #next()} have not
* yet been invoked.
* @param pSizeMax The maximum size of the complete HTTP request. The value -1 indicates "unlimited".
*/
public void setSizeMax(long pSizeMax);
/**
* Returns, whether another instance of {@link FileItemStream}
* is available.
*
* @throws FileUploadException Parsing or processing the
* file item failed.
* @throws IOException Reading the file item failed.
* @return True, if one or more additional file items
* are available, otherwise false.
*/
public boolean hasNext() throws FileUploadException, IOException;
/**
* Returns the next available {@link FileItemStream}.
*
* @throws java.util.NoSuchElementException No more items are available. Use
* {@link #hasNext()} to prevent this exception.
* @throws FileUploadException Parsing or processing the
* file item failed.
* @throws IOException Reading the file item failed.
* @return FileItemStream instance, which provides
* access to the next file item.
*/
public FileItemStream next() throws FileUploadException, IOException;
public List<FileItem> getFileItems() throws FileUploadException, IOException;
}

View File

@@ -0,0 +1,102 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
import java.io.IOException;
import java.io.InputStream;
/**
* <p> This interface provides access to a file or form item that was
* received within a <code>multipart/form-data</code> POST request.
* The items contents are retrieved by calling {@link #openStream()}.</p>
* <p>Instances of this class are created by accessing the
* iterator, returned by
* {@link FileUploadBase#getItemIterator(RequestContext)}.</p>
* <p><em>Note</em>: There is an interaction between the iterator and
* its associated instances of {@link FileItemStream}: By invoking
* {@link java.util.Iterator#hasNext()} on the iterator, you discard all data,
* which hasn't been read so far from the previous data.</p>
*/
public interface FileItemStream extends FileItemHeadersSupport {
/**
* This exception is thrown, if an attempt is made to read
* data from the {@link InputStream}, which has been returned
* by {@link FileItemStream#openStream()}, after
* {@link java.util.Iterator#hasNext()} has been invoked on the
* iterator, which created the {@link FileItemStream}.
*/
public static class ItemSkippedException extends IOException {
/**
* The exceptions serial version UID, which is being used
* when serializing an exception instance.
*/
private static final long serialVersionUID = -7280778431581963740L;
}
/**
* Creates an {@link InputStream}, which allows to read the
* items contents.
*
* @return The input stream, from which the items data may
* be read.
* @throws IllegalStateException The method was already invoked on
* this item. It is not possible to recreate the data stream.
* @throws IOException An I/O error occurred.
* @see ItemSkippedException
*/
InputStream openStream() throws IOException;
/**
* Returns the content type passed by the browser or <code>null</code> if
* not defined.
*
* @return The content type passed by the browser or <code>null</code> if
* not defined.
*/
String getContentType();
/**
* Returns the original file name in the client's file system, as provided by
* the browser (or other client software). In most cases, this will be the
* base file name, without path information. However, some clients, such as
* the Opera browser, do include path information.
*
* @return The original file name in the client's file system.
*/
String getName();
/**
* Returns the name of the field in the multipart form corresponding to
* this file item.
*
* @return The name of the form field.
*/
String getFieldName();
/**
* Determines whether or not a <code>FileItem</code> instance represents
* a simple form field.
*
* @return <code>true</code> if the instance represents a simple form
* field; <code>false</code> if it represents an uploaded file.
*/
boolean isFormField();
}

View File

@@ -0,0 +1,92 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
/**
* <p>High level API for processing file uploads.</p>
*
* <p>This class handles multiple files per single HTML widget, sent using
* <code>multipart/mixed</code> encoding type, as specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Use {@link
* #parseRequest(RequestContext)} to acquire a list
* of {@link org.apache.tomcat.util.http.fileupload.FileItem FileItems} associated
* with a given HTML widget.</p>
*
* <p>How the data for individual parts is stored is determined by the factory
* used to create them; a given part may be in memory, on disk, or somewhere
* else.</p>
*/
public class FileUpload
extends FileUploadBase {
// ----------------------------------------------------------- Data members
/**
* The factory to use to create new form items.
*/
private FileItemFactory fileItemFactory;
// ----------------------------------------------------------- Constructors
/**
* Constructs an uninitialised instance of this class.
*
* A factory must be
* configured, using <code>setFileItemFactory()</code>, before attempting
* to parse requests.
*
* @see #FileUpload(FileItemFactory)
*/
public FileUpload() {
super();
}
/**
* Constructs an instance of this class which uses the supplied factory to
* create <code>FileItem</code> instances.
*
* @see #FileUpload()
* @param fileItemFactory The factory to use for creating file items.
*/
public FileUpload(FileItemFactory fileItemFactory) {
super();
this.fileItemFactory = fileItemFactory;
}
// ----------------------------------------------------- Property accessors
/**
* Returns the factory class used when creating file items.
*
* @return The factory class for new file items.
*/
@Override
public FileItemFactory getFileItemFactory() {
return fileItemFactory;
}
/**
* Sets the factory class to use when creating file items.
*
* @param factory The factory class for new file items.
*/
@Override
public void setFileItemFactory(FileItemFactory factory) {
this.fileItemFactory = factory;
}
}

View File

@@ -0,0 +1,568 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl;
import org.apache.tomcat.util.http.fileupload.impl.FileItemStreamImpl;
import org.apache.tomcat.util.http.fileupload.impl.FileUploadIOException;
import org.apache.tomcat.util.http.fileupload.impl.IOFileUploadException;
import org.apache.tomcat.util.http.fileupload.util.FileItemHeadersImpl;
import org.apache.tomcat.util.http.fileupload.util.Streams;
/**
* <p>High level API for processing file uploads.</p>
*
* <p>This class handles multiple files per single HTML widget, sent using
* <code>multipart/mixed</code> encoding type, as specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Use {@link
* #parseRequest(RequestContext)} to acquire a list of {@link
* org.apache.tomcat.util.http.fileupload.FileItem}s associated with a given HTML
* widget.</p>
*
* <p>How the data for individual parts is stored is determined by the factory
* used to create them; a given part may be in memory, on disk, or somewhere
* else.</p>
*/
public abstract class FileUploadBase {
// ---------------------------------------------------------- Class methods
/**
* <p>Utility method that determines whether the request contains multipart
* content.</p>
*
* <p><strong>NOTE:</strong>This method will be moved to the
* <code>ServletFileUpload</code> class after the FileUpload 1.1 release.
* Unfortunately, since this method is static, it is not possible to
* provide its replacement until this method is removed.</p>
*
* @param ctx The request context to be evaluated. Must be non-null.
*
* @return <code>true</code> if the request is multipart;
* <code>false</code> otherwise.
*/
public static final boolean isMultipartContent(RequestContext ctx) {
String contentType = ctx.getContentType();
if (contentType == null) {
return false;
}
if (contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART)) {
return true;
}
return false;
}
// ----------------------------------------------------- Manifest constants
/**
* HTTP content type header name.
*/
public static final String CONTENT_TYPE = "Content-type";
/**
* HTTP content disposition header name.
*/
public static final String CONTENT_DISPOSITION = "Content-disposition";
/**
* HTTP content length header name.
*/
public static final String CONTENT_LENGTH = "Content-length";
/**
* Content-disposition value for form data.
*/
public static final String FORM_DATA = "form-data";
/**
* Content-disposition value for file attachment.
*/
public static final String ATTACHMENT = "attachment";
/**
* Part of HTTP content type header.
*/
public static final String MULTIPART = "multipart/";
/**
* HTTP content type header for multipart forms.
*/
public static final String MULTIPART_FORM_DATA = "multipart/form-data";
/**
* HTTP content type header for multiple uploads.
*/
public static final String MULTIPART_MIXED = "multipart/mixed";
// ----------------------------------------------------------- Data members
/**
* The maximum size permitted for the complete request, as opposed to
* {@link #fileSizeMax}. A value of -1 indicates no maximum.
*/
private long sizeMax = -1;
/**
* The maximum size permitted for a single uploaded file, as opposed
* to {@link #sizeMax}. A value of -1 indicates no maximum.
*/
private long fileSizeMax = -1;
/**
* The content encoding to use when reading part headers.
*/
private String headerEncoding;
/**
* The progress listener.
*/
private ProgressListener listener;
// ----------------------------------------------------- Property accessors
/**
* Returns the factory class used when creating file items.
*
* @return The factory class for new file items.
*/
public abstract FileItemFactory getFileItemFactory();
/**
* Sets the factory class to use when creating file items.
*
* @param factory The factory class for new file items.
*/
public abstract void setFileItemFactory(FileItemFactory factory);
/**
* Returns the maximum allowed size of a complete request, as opposed
* to {@link #getFileSizeMax()}.
*
* @return The maximum allowed size, in bytes. The default value of
* -1 indicates, that there is no limit.
*
* @see #setSizeMax(long)
*
*/
public long getSizeMax() {
return sizeMax;
}
/**
* Sets the maximum allowed size of a complete request, as opposed
* to {@link #setFileSizeMax(long)}.
*
* @param sizeMax The maximum allowed size, in bytes. The default value of
* -1 indicates, that there is no limit.
*
* @see #getSizeMax()
*
*/
public void setSizeMax(long sizeMax) {
this.sizeMax = sizeMax;
}
/**
* Returns the maximum allowed size of a single uploaded file,
* as opposed to {@link #getSizeMax()}.
*
* @see #setFileSizeMax(long)
* @return Maximum size of a single uploaded file.
*/
public long getFileSizeMax() {
return fileSizeMax;
}
/**
* Sets the maximum allowed size of a single uploaded file,
* as opposed to {@link #getSizeMax()}.
*
* @see #getFileSizeMax()
* @param fileSizeMax Maximum size of a single uploaded file.
*/
public void setFileSizeMax(long fileSizeMax) {
this.fileSizeMax = fileSizeMax;
}
/**
* Retrieves the character encoding used when reading the headers of an
* individual part. When not specified, or <code>null</code>, the request
* encoding is used. If that is also not specified, or <code>null</code>,
* the platform default encoding is used.
*
* @return The encoding used to read part headers.
*/
public String getHeaderEncoding() {
return headerEncoding;
}
/**
* Specifies the character encoding to be used when reading the headers of
* individual part. When not specified, or <code>null</code>, the request
* encoding is used. If that is also not specified, or <code>null</code>,
* the platform default encoding is used.
*
* @param encoding The encoding used to read part headers.
*/
public void setHeaderEncoding(String encoding) {
headerEncoding = encoding;
}
// --------------------------------------------------------- Public methods
/**
* Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant <code>multipart/form-data</code> stream.
*
* @param ctx The context for the request to be parsed.
*
* @return An iterator to instances of <code>FileItemStream</code>
* parsed from the request, in the order that they were
* transmitted.
*
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
* @throws IOException An I/O error occurred. This may be a network
* error while communicating with the client or a problem while
* storing the uploaded content.
*/
public FileItemIterator getItemIterator(RequestContext ctx)
throws FileUploadException, IOException {
try {
return new FileItemIteratorImpl(this, ctx);
} catch (FileUploadIOException e) {
// unwrap encapsulated SizeException
throw (FileUploadException) e.getCause();
}
}
/**
* Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant <code>multipart/form-data</code> stream.
*
* @param ctx The context for the request to be parsed.
*
* @return A list of <code>FileItem</code> instances parsed from the
* request, in the order that they were transmitted.
*
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
*/
public List<FileItem> parseRequest(RequestContext ctx)
throws FileUploadException {
List<FileItem> items = new ArrayList<>();
boolean successful = false;
try {
FileItemIterator iter = getItemIterator(ctx);
FileItemFactory fac = getFileItemFactory();
final byte[] buffer = new byte[Streams.DEFAULT_BUFFER_SIZE];
if (fac == null) {
throw new NullPointerException("No FileItemFactory has been set.");
}
while (iter.hasNext()) {
final FileItemStream item = iter.next();
// Don't use getName() here to prevent an InvalidFileNameException.
final String fileName = ((FileItemStreamImpl) item).getName();
FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(),
item.isFormField(), fileName);
items.add(fileItem);
try {
Streams.copy(item.openStream(), fileItem.getOutputStream(), true, buffer);
} catch (FileUploadIOException e) {
throw (FileUploadException) e.getCause();
} catch (IOException e) {
throw new IOFileUploadException(String.format("Processing of %s request failed. %s",
MULTIPART_FORM_DATA, e.getMessage()), e);
}
final FileItemHeaders fih = item.getHeaders();
fileItem.setHeaders(fih);
}
successful = true;
return items;
} catch (FileUploadIOException e) {
throw (FileUploadException) e.getCause();
} catch (IOException e) {
throw new FileUploadException(e.getMessage(), e);
} finally {
if (!successful) {
for (FileItem fileItem : items) {
try {
fileItem.delete();
} catch (Exception ignored) {
// ignored TODO perhaps add to tracker delete failure list somehow?
}
}
}
}
}
/**
* Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant <code>multipart/form-data</code> stream.
*
* @param ctx The context for the request to be parsed.
*
* @return A map of <code>FileItem</code> instances parsed from the request.
*
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
*
* @since 1.3
*/
public Map<String, List<FileItem>> parseParameterMap(RequestContext ctx)
throws FileUploadException {
final List<FileItem> items = parseRequest(ctx);
final Map<String, List<FileItem>> itemsMap = new HashMap<>(items.size());
for (FileItem fileItem : items) {
String fieldName = fileItem.getFieldName();
List<FileItem> mappedItems = itemsMap.get(fieldName);
if (mappedItems == null) {
mappedItems = new ArrayList<>();
itemsMap.put(fieldName, mappedItems);
}
mappedItems.add(fileItem);
}
return itemsMap;
}
// ------------------------------------------------------ Protected methods
/**
* Retrieves the boundary from the <code>Content-type</code> header.
*
* @param contentType The value of the content type header from which to
* extract the boundary value.
*
* @return The boundary, as a byte array.
*/
public byte[] getBoundary(String contentType) {
ParameterParser parser = new ParameterParser();
parser.setLowerCaseNames(true);
// Parameter parser can handle null input
Map<String,String> params =
parser.parse(contentType, new char[] {';', ','});
String boundaryStr = params.get("boundary");
if (boundaryStr == null) {
return null;
}
byte[] boundary;
boundary = boundaryStr.getBytes(StandardCharsets.ISO_8859_1);
return boundary;
}
/**
* Retrieves the file name from the <code>Content-disposition</code>
* header.
*
* @param headers The HTTP headers object.
*
* @return The file name for the current <code>encapsulation</code>.
*/
public String getFileName(FileItemHeaders headers) {
return getFileName(headers.getHeader(CONTENT_DISPOSITION));
}
/**
* Returns the given content-disposition headers file name.
* @param pContentDisposition The content-disposition headers value.
* @return The file name
*/
private String getFileName(String pContentDisposition) {
String fileName = null;
if (pContentDisposition != null) {
String cdl = pContentDisposition.toLowerCase(Locale.ENGLISH);
if (cdl.startsWith(FORM_DATA) || cdl.startsWith(ATTACHMENT)) {
ParameterParser parser = new ParameterParser();
parser.setLowerCaseNames(true);
// Parameter parser can handle null input
Map<String, String> params = parser.parse(pContentDisposition, ';');
if (params.containsKey("filename")) {
fileName = params.get("filename");
if (fileName != null) {
fileName = fileName.trim();
} else {
// Even if there is no value, the parameter is present,
// so we return an empty file name rather than no file
// name.
fileName = "";
}
}
}
}
return fileName;
}
/**
* Retrieves the field name from the <code>Content-disposition</code>
* header.
*
* @param headers A <code>Map</code> containing the HTTP request headers.
*
* @return The field name for the current <code>encapsulation</code>.
*/
public String getFieldName(FileItemHeaders headers) {
return getFieldName(headers.getHeader(CONTENT_DISPOSITION));
}
/**
* Returns the field name, which is given by the content-disposition
* header.
* @param pContentDisposition The content-dispositions header value.
* @return The field jake
*/
private String getFieldName(String pContentDisposition) {
String fieldName = null;
if (pContentDisposition != null
&& pContentDisposition.toLowerCase(Locale.ENGLISH).startsWith(FORM_DATA)) {
ParameterParser parser = new ParameterParser();
parser.setLowerCaseNames(true);
// Parameter parser can handle null input
Map<String, String> params = parser.parse(pContentDisposition, ';');
fieldName = params.get("name");
if (fieldName != null) {
fieldName = fieldName.trim();
}
}
return fieldName;
}
/**
* <p> Parses the <code>header-part</code> and returns as key/value
* pairs.
*
* <p> If there are multiple headers of the same names, the name
* will map to a comma-separated list containing the values.
*
* @param headerPart The <code>header-part</code> of the current
* <code>encapsulation</code>.
*
* @return A <code>Map</code> containing the parsed HTTP request headers.
*/
public FileItemHeaders getParsedHeaders(String headerPart) {
final int len = headerPart.length();
FileItemHeadersImpl headers = newFileItemHeaders();
int start = 0;
for (;;) {
int end = parseEndOfLine(headerPart, start);
if (start == end) {
break;
}
StringBuilder header = new StringBuilder(headerPart.substring(start, end));
start = end + 2;
while (start < len) {
int nonWs = start;
while (nonWs < len) {
char c = headerPart.charAt(nonWs);
if (c != ' ' && c != '\t') {
break;
}
++nonWs;
}
if (nonWs == start) {
break;
}
// Continuation line found
end = parseEndOfLine(headerPart, nonWs);
header.append(" ").append(headerPart.substring(nonWs, end));
start = end + 2;
}
parseHeaderLine(headers, header.toString());
}
return headers;
}
/**
* Creates a new instance of {@link FileItemHeaders}.
* @return The new instance.
*/
protected FileItemHeadersImpl newFileItemHeaders() {
return new FileItemHeadersImpl();
}
/**
* Skips bytes until the end of the current line.
* @param headerPart The headers, which are being parsed.
* @param end Index of the last byte, which has yet been
* processed.
* @return Index of the \r\n sequence, which indicates
* end of line.
*/
private int parseEndOfLine(String headerPart, int end) {
int index = end;
for (;;) {
int offset = headerPart.indexOf('\r', index);
if (offset == -1 || offset + 1 >= headerPart.length()) {
throw new IllegalStateException(
"Expected headers to be terminated by an empty line.");
}
if (headerPart.charAt(offset + 1) == '\n') {
return offset;
}
index = offset + 1;
}
}
/**
* Reads the next header line.
* @param headers String with all headers.
* @param header Map where to store the current header.
*/
private void parseHeaderLine(FileItemHeadersImpl headers, String header) {
final int colonOffset = header.indexOf(':');
if (colonOffset == -1) {
// This header line is malformed, skip it.
return;
}
String headerName = header.substring(0, colonOffset).trim();
String headerValue =
header.substring(header.indexOf(':') + 1).trim();
headers.addHeader(headerName, headerValue);
}
/**
* Returns the progress listener.
*
* @return The progress listener, if any, or null.
*/
public ProgressListener getProgressListener() {
return listener;
}
/**
* Sets the progress listener.
*
* @param pListener The progress listener, if any. Defaults to null.
*/
public void setProgressListener(ProgressListener pListener) {
listener = pListener;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
/**
* Exception for errors encountered while processing the request.
*/
public class FileUploadException extends Exception {
private static final long serialVersionUID = -4222909057964038517L;
public FileUploadException() {
super();
}
public FileUploadException(String message, Throwable cause) {
super(message, cause);
}
public FileUploadException(String message) {
super(message);
}
public FileUploadException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,309 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* General file manipulation utilities.
* <p>
* Facilities are provided in the following areas:
* <ul>
* <li>writing to a file
* <li>reading from a file
* <li>make a directory including parent directories
* <li>copying files and directories
* <li>deleting files and directories
* <li>converting to and from a URL
* <li>listing files and directories by filter and extension
* <li>comparing file content
* <li>file last changed date
* <li>calculating a checksum
* </ul>
* <p>
* Note that a specific charset should be specified whenever possible.
* Relying on the platform default means that the code is Locale-dependent.
* Only use the default if the files are known to always use the platform default.
* <p>
* Origin of code: Excalibur, Alexandria, Commons-Utils
*/
public class FileUtils {
/**
* Instances should NOT be constructed in standard programming.
*/
public FileUtils() {
super();
}
//-----------------------------------------------------------------------
/**
* Deletes a directory recursively.
*
* @param directory directory to delete
* @throws IOException in case deletion is unsuccessful
* @throws IllegalArgumentException if {@code directory} does not exist or is not a directory
*/
public static void deleteDirectory(final File directory) throws IOException {
if (!directory.exists()) {
return;
}
if (!isSymlink(directory)) {
cleanDirectory(directory);
}
if (!directory.delete()) {
final String message =
"Unable to delete directory " + directory + ".";
throw new IOException(message);
}
}
/**
* Cleans a directory without deleting it.
*
* @param directory directory to clean
* @throws IOException in case cleaning is unsuccessful
* @throws IllegalArgumentException if {@code directory} does not exist or is not a directory
*/
public static void cleanDirectory(final File directory) throws IOException {
if (!directory.exists()) {
final String message = directory + " does not exist";
throw new IllegalArgumentException(message);
}
if (!directory.isDirectory()) {
final String message = directory + " is not a directory";
throw new IllegalArgumentException(message);
}
final File[] files = directory.listFiles();
if (files == null) { // null if security restricted
throw new IOException("Failed to list contents of " + directory);
}
IOException exception = null;
for (File file : files) {
try {
forceDelete(file);
} catch (IOException ioe) {
exception = ioe;
}
}
if (null != exception) {
throw exception;
}
}
//-----------------------------------------------------------------------
/**
* Deletes a file. If file is a directory, delete it and all sub-directories.
* <p>
* The difference between File.delete() and this method are:
* <ul>
* <li>A directory to be deleted does not have to be empty.</li>
* <li>You get exceptions when a file or directory cannot be deleted.
* (java.io.File methods returns a boolean)</li>
* </ul>
*
* @param file file or directory to delete, must not be {@code null}
* @throws NullPointerException if the directory is {@code null}
* @throws FileNotFoundException if the file was not found
* @throws IOException in case deletion is unsuccessful
*/
public static void forceDelete(final File file) throws IOException {
if (file.isDirectory()) {
deleteDirectory(file);
} else {
final boolean filePresent = file.exists();
if (!file.delete()) {
if (!filePresent) {
throw new FileNotFoundException("File does not exist: " + file);
}
final String message =
"Unable to delete file: " + file;
throw new IOException(message);
}
}
}
/**
* Schedules a file to be deleted when JVM exits.
* If file is directory delete it and all sub-directories.
*
* @param file file or directory to delete, must not be {@code null}
* @throws NullPointerException if the file is {@code null}
* @throws IOException in case deletion is unsuccessful
*/
public static void forceDeleteOnExit(final File file) throws IOException {
if (file.isDirectory()) {
deleteDirectoryOnExit(file);
} else {
file.deleteOnExit();
}
}
/**
* Schedules a directory recursively for deletion on JVM exit.
*
* @param directory directory to delete, must not be {@code null}
* @throws NullPointerException if the directory is {@code null}
* @throws IOException in case deletion is unsuccessful
*/
private static void deleteDirectoryOnExit(final File directory) throws IOException {
if (!directory.exists()) {
return;
}
directory.deleteOnExit();
if (!isSymlink(directory)) {
cleanDirectoryOnExit(directory);
}
}
/**
* Cleans a directory without deleting it.
*
* @param directory directory to clean, must not be {@code null}
* @throws NullPointerException if the directory is {@code null}
* @throws IOException in case cleaning is unsuccessful
*/
private static void cleanDirectoryOnExit(final File directory) throws IOException {
if (!directory.exists()) {
String message = directory + " does not exist";
throw new IllegalArgumentException(message);
}
if (!directory.isDirectory()) {
String message = directory + " is not a directory";
throw new IllegalArgumentException(message);
}
File[] files = directory.listFiles();
if (files == null) { // null if security restricted
throw new IOException("Failed to list contents of " + directory);
}
IOException exception = null;
for (File file : files) {
try {
forceDeleteOnExit(file);
} catch (IOException ioe) {
exception = ioe;
}
}
if (null != exception) {
throw exception;
}
}
/**
* Makes a directory, including any necessary but nonexistent parent
* directories. If a file already exists with specified name but it is
* not a directory then an IOException is thrown.
* If the directory cannot be created (or does not already exist)
* then an IOException is thrown.
*
* @param directory directory to create, must not be {@code null}
* @throws NullPointerException if the directory is {@code null}
* @throws IOException if the directory cannot be created or the file already exists but is not a directory
*/
public static void forceMkdir(final File directory) throws IOException {
if (directory.exists()) {
if (!directory.isDirectory()) {
final String message =
"File "
+ directory
+ " exists and is "
+ "not a directory. Unable to create directory.";
throw new IOException(message);
}
} else {
if (!directory.mkdirs()) {
// Double-check that some other thread or process hasn't made
// the directory in the background
if (!directory.isDirectory()) {
final String message =
"Unable to create directory " + directory;
throw new IOException(message);
}
}
}
}
/**
* Makes any necessary but nonexistent parent directories for a given File. If the parent directory cannot be
* created then an IOException is thrown.
*
* @param file file with parent to create, must not be {@code null}
* @throws NullPointerException if the file is {@code null}
* @throws IOException if the parent directory cannot be created
* @since 2.5
*/
public static void forceMkdirParent(final File file) throws IOException {
final File parent = file.getParentFile();
if (parent == null) {
return;
}
forceMkdir(parent);
}
/**
* Determines whether the specified file is a Symbolic Link rather than an actual file.
* <p>
* Will not return true if there is a Symbolic Link anywhere in the path,
* only if the specific file is.
* <p>
* <b>Note:</b> the current implementation always returns {@code false} if
* the system is detected as Windows using
* {@link File#separatorChar} == '\\'
*
* @param file the file to check
* @return true if the file is a Symbolic Link
* @throws IOException if an IO error occurs while checking the file
* @since 2.0
*/
public static boolean isSymlink(File file) throws IOException {
if (file == null) {
throw new NullPointerException("File must not be null");
}
//FilenameUtils.isSystemWindows()
if (File.separatorChar == '\\') {
return false;
}
File fileInCanonicalDir = null;
if (file.getParent() == null) {
fileInCanonicalDir = file;
} else {
File canonicalDir = file.getParentFile().getCanonicalFile();
fileInCanonicalDir = new File(canonicalDir, file.getName());
}
if (fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile())) {
return false;
} else {
return true;
}
}
}

View File

@@ -0,0 +1,249 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
import java.io.Closeable;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* General IO stream manipulation utilities.
* <p>
* This class provides static utility methods for input/output operations.
* <ul>
* <li>closeQuietly - these methods close a stream ignoring nulls and exceptions
* <li>toXxx/read - these methods read data from a stream
* <li>write - these methods write data to a stream
* <li>copy - these methods copy all the data from one stream to another
* <li>contentEquals - these methods compare the content of two streams
* </ul>
* <p>
* The byte-to-char methods and char-to-byte methods involve a conversion step.
* Two methods are provided in each case, one that uses the platform default
* encoding and the other which allows you to specify an encoding. You are
* encouraged to always specify an encoding because relying on the platform
* default can lead to unexpected results, for example when moving from
* development to production.
* <p>
* All the methods in this class that read a stream are buffered internally.
* This means that there is no cause to use a <code>BufferedInputStream</code>
* or <code>BufferedReader</code>. The default buffer size of 4K has been shown
* to be efficient in tests.
* <p>
* Wherever possible, the methods in this class do <em>not</em> flush or close
* the stream. This is to avoid making non-portable assumptions about the
* streams' origin and further use. Thus the caller is still responsible for
* closing streams after use.
* <p>
* Origin of code: Excalibur.
*/
public class IOUtils {
// NOTE: This class is focused on InputStream, OutputStream, Reader and
// Writer. Each method should take at least one of these as a parameter,
// or return one of them.
/**
* Represents the end-of-file (or stream).
* @since 2.5 (made public)
*/
public static final int EOF = -1;
/**
* The default buffer size ({@value}) to use for
* {@link #copyLarge(InputStream, OutputStream)}.
*/
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
/**
* Closes a <code>Closeable</code> unconditionally.
* <p>
* Equivalent to {@link Closeable#close()}, except any exceptions will be ignored. This is typically used in
* finally blocks.
* <p>
* Example code:
* </p>
* <pre>
* Closeable closeable = null;
* try {
* closeable = new FileReader(&quot;foo.txt&quot;);
* // process closeable
* closeable.close();
* } catch (Exception e) {
* // error handling
* } finally {
* IOUtils.closeQuietly(closeable);
* }
* </pre>
* <p>
* Closing all streams:
* </p>
* <pre>
* try {
* return IOUtils.copy(inputStream, outputStream);
* } finally {
* IOUtils.closeQuietly(inputStream);
* IOUtils.closeQuietly(outputStream);
* }
* </pre>
*
* @param closeable the objects to close, may be null or already closed
* @since 2.0
*/
public static void closeQuietly(final Closeable closeable) {
try {
if (closeable != null) {
closeable.close();
}
} catch (final IOException ioe) {
// ignore
}
}
// copy from InputStream
//-----------------------------------------------------------------------
/**
* Copies bytes from an <code>InputStream</code> to an
* <code>OutputStream</code>.
* <p>
* This method buffers the input internally, so there is no need to use a
* <code>BufferedInputStream</code>.
* <p>
* Large streams (over 2GB) will return a bytes copied value of
* <code>-1</code> after the copy has completed since the correct
* number of bytes cannot be returned as an int. For large streams
* use the <code>copyLarge(InputStream, OutputStream)</code> method.
*
* @param input the <code>InputStream</code> to read from
* @param output the <code>OutputStream</code> to write to
* @return the number of bytes copied, or -1 if &gt; Integer.MAX_VALUE
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
* @since 1.1
*/
public static int copy(final InputStream input, final OutputStream output) throws IOException {
final long count = copyLarge(input, output);
if (count > Integer.MAX_VALUE) {
return -1;
}
return (int) count;
}
/**
* Copies bytes from a large (over 2GB) <code>InputStream</code> to an
* <code>OutputStream</code>.
* <p>
* This method buffers the input internally, so there is no need to use a
* <code>BufferedInputStream</code>.
* <p>
* The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}.
*
* @param input the <code>InputStream</code> to read from
* @param output the <code>OutputStream</code> to write to
* @return the number of bytes copied
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
* @since 1.3
*/
public static long copyLarge(final InputStream input, final OutputStream output)
throws IOException {
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
long count = 0;
int n = 0;
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
/**
* Reads bytes from an input stream.
* This implementation guarantees that it will read as many bytes
* as possible before giving up; this may not always be the case for
* subclasses of {@link InputStream}.
*
* @param input where to read input from
* @param buffer destination
* @param offset initial offset into buffer
* @param length length to read, must be &gt;= 0
* @return actual length read; may be less than requested if EOF was reached
* @throws IOException if a read error occurs
* @since 2.2
*/
public static int read(final InputStream input, final byte[] buffer, final int offset, final int length)
throws IOException {
if (length < 0) {
throw new IllegalArgumentException("Length must not be negative: " + length);
}
int remaining = length;
while (remaining > 0) {
final int location = length - remaining;
final int count = input.read(buffer, offset + location, remaining);
if (EOF == count) { // EOF
break;
}
remaining -= count;
}
return length - remaining;
}
/**
* Reads the requested number of bytes or fail if there are not enough left.
* <p>
* This allows for the possibility that {@link InputStream#read(byte[], int, int)} may
* not read as many bytes as requested (most likely because of reaching EOF).
*
* @param input where to read input from
* @param buffer destination
* @param offset initial offset into buffer
* @param length length to read, must be &gt;= 0
*
* @throws IOException if there is a problem reading the file
* @throws IllegalArgumentException if length is negative
* @throws EOFException if the number of bytes read was incorrect
* @since 2.2
*/
public static void readFully(final InputStream input, final byte[] buffer, final int offset, final int length)
throws IOException {
final int actual = read(input, buffer, offset, length);
if (actual != length) {
throw new EOFException("Length to read: " + length + " actual: " + actual);
}
}
/**
* Reads the requested number of bytes or fail if there are not enough left.
* <p>
* This allows for the possibility that {@link InputStream#read(byte[], int, int)} may
* not read as many bytes as requested (most likely because of reaching EOF).
*
* @param input where to read input from
* @param buffer destination
*
* @throws IOException if there is a problem reading the file
* @throws IllegalArgumentException if length is negative
* @throws EOFException if the number of bytes read was incorrect
* @since 2.2
*/
public static void readFully(final InputStream input, final byte[] buffer) throws IOException {
readFully(input, buffer, 0, buffer.length);
}
}

View File

@@ -0,0 +1,62 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
/**
* This exception is thrown in case of an invalid file name.
* A file name is invalid, if it contains a NUL character.
* Attackers might use this to circumvent security checks:
* For example, a malicious user might upload a file with the name
* "foo.exe\0.png". This file name might pass security checks (i.e.
* checks for the extension ".png"), while, depending on the underlying
* C library, it might create a file named "foo.exe", as the NUL
* character is the string terminator in C.
*/
public class InvalidFileNameException extends RuntimeException {
/**
* Serial version UID, being used, if the exception
* is serialized.
*/
private static final long serialVersionUID = 7922042602454350470L;
/**
* The file name causing the exception.
*/
private final String name;
/**
* Creates a new instance.
*
* @param pName The file name causing the exception.
* @param pMessage A human readable error message.
*/
public InvalidFileNameException(String pName, String pMessage) {
super(pMessage);
name = pName;
}
/**
* Returns the invalid file name.
*
* @return the invalid file name.
*/
public String getName() {
return name;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,339 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.apache.tomcat.util.http.fileupload.util.mime.MimeUtility;
/**
* A simple parser intended to parse sequences of name/value pairs.
*
* Parameter values are expected to be enclosed in quotes if they
* contain unsafe characters, such as '=' characters or separators.
* Parameter values are optional and can be omitted.
*
* <p>
* <code>param1 = value; param2 = "anything goes; really"; param3</code>
* </p>
*/
public class ParameterParser {
/**
* String to be parsed.
*/
private char[] chars = null;
/**
* Current position in the string.
*/
private int pos = 0;
/**
* Maximum position in the string.
*/
private int len = 0;
/**
* Start of a token.
*/
private int i1 = 0;
/**
* End of a token.
*/
private int i2 = 0;
/**
* Whether names stored in the map should be converted to lower case.
*/
private boolean lowerCaseNames = false;
/**
* Default ParameterParser constructor.
*/
public ParameterParser() {
super();
}
/**
* Are there any characters left to parse?
*
* @return {@code true} if there are unparsed characters,
* {@code false} otherwise.
*/
private boolean hasChar() {
return this.pos < this.len;
}
/**
* A helper method to process the parsed token. This method removes
* leading and trailing blanks as well as enclosing quotation marks,
* when necessary.
*
* @param quoted {@code true} if quotation marks are expected,
* {@code false} otherwise.
* @return the token
*/
private String getToken(boolean quoted) {
// Trim leading white spaces
while ((i1 < i2) && (Character.isWhitespace(chars[i1]))) {
i1++;
}
// Trim trailing white spaces
while ((i2 > i1) && (Character.isWhitespace(chars[i2 - 1]))) {
i2--;
}
// Strip away quotation marks if necessary
if (quoted
&& ((i2 - i1) >= 2)
&& (chars[i1] == '"')
&& (chars[i2 - 1] == '"')) {
i1++;
i2--;
}
String result = null;
if (i2 > i1) {
result = new String(chars, i1, i2 - i1);
}
return result;
}
/**
* Tests if the given character is present in the array of characters.
*
* @param ch the character to test for presence in the array of characters
* @param charray the array of characters to test against
*
* @return {@code true} if the character is present in the array of
* characters, {@code false} otherwise.
*/
private boolean isOneOf(char ch, final char[] charray) {
boolean result = false;
for (char element : charray) {
if (ch == element) {
result = true;
break;
}
}
return result;
}
/**
* Parses out a token until any of the given terminators
* is encountered.
*
* @param terminators the array of terminating characters. Any of these
* characters when encountered signify the end of the token
*
* @return the token
*/
private String parseToken(final char[] terminators) {
char ch;
i1 = pos;
i2 = pos;
while (hasChar()) {
ch = chars[pos];
if (isOneOf(ch, terminators)) {
break;
}
i2++;
pos++;
}
return getToken(false);
}
/**
* Parses out a token until any of the given terminators
* is encountered outside the quotation marks.
*
* @param terminators the array of terminating characters. Any of these
* characters when encountered outside the quotation marks signify the end
* of the token
*
* @return the token
*/
private String parseQuotedToken(final char[] terminators) {
char ch;
i1 = pos;
i2 = pos;
boolean quoted = false;
boolean charEscaped = false;
while (hasChar()) {
ch = chars[pos];
if (!quoted && isOneOf(ch, terminators)) {
break;
}
if (!charEscaped && ch == '"') {
quoted = !quoted;
}
charEscaped = (!charEscaped && ch == '\\');
i2++;
pos++;
}
return getToken(true);
}
/**
* Returns {@code true} if parameter names are to be converted to lower
* case when name/value pairs are parsed.
*
* @return {@code true} if parameter names are to be
* converted to lower case when name/value pairs are parsed.
* Otherwise returns {@code false}
*/
public boolean isLowerCaseNames() {
return this.lowerCaseNames;
}
/**
* Sets the flag if parameter names are to be converted to lower case when
* name/value pairs are parsed.
*
* @param b {@code true} if parameter names are to be
* converted to lower case when name/value pairs are parsed.
* {@code false} otherwise.
*/
public void setLowerCaseNames(boolean b) {
this.lowerCaseNames = b;
}
/**
* Extracts a map of name/value pairs from the given string. Names are
* expected to be unique. Multiple separators may be specified and
* the earliest found in the input string is used.
*
* @param str the string that contains a sequence of name/value pairs
* @param separators the name/value pairs separators
*
* @return a map of name/value pairs
*/
public Map<String, String> parse(final String str, char[] separators) {
if (separators == null || separators.length == 0) {
return new HashMap<>();
}
char separator = separators[0];
if (str != null) {
int idx = str.length();
for (char separator2 : separators) {
int tmp = str.indexOf(separator2);
if (tmp != -1 && tmp < idx) {
idx = tmp;
separator = separator2;
}
}
}
return parse(str, separator);
}
/**
* Extracts a map of name/value pairs from the given string. Names are
* expected to be unique.
*
* @param str the string that contains a sequence of name/value pairs
* @param separator the name/value pairs separator
*
* @return a map of name/value pairs
*/
public Map<String, String> parse(final String str, char separator) {
if (str == null) {
return new HashMap<>();
}
return parse(str.toCharArray(), separator);
}
/**
* Extracts a map of name/value pairs from the given array of
* characters. Names are expected to be unique.
*
* @param charArray the array of characters that contains a sequence of
* name/value pairs
* @param separator the name/value pairs separator
*
* @return a map of name/value pairs
*/
public Map<String, String> parse(final char[] charArray, char separator) {
if (charArray == null) {
return new HashMap<>();
}
return parse(charArray, 0, charArray.length, separator);
}
/**
* Extracts a map of name/value pairs from the given array of
* characters. Names are expected to be unique.
*
* @param charArray the array of characters that contains a sequence of
* name/value pairs
* @param offset - the initial offset.
* @param length - the length.
* @param separator the name/value pairs separator
*
* @return a map of name/value pairs
*/
public Map<String, String> parse(
final char[] charArray,
int offset,
int length,
char separator) {
if (charArray == null) {
return new HashMap<>();
}
HashMap<String, String> params = new HashMap<>();
this.chars = charArray;
this.pos = offset;
this.len = length;
String paramName = null;
String paramValue = null;
while (hasChar()) {
paramName = parseToken(new char[] {
'=', separator });
paramValue = null;
if (hasChar() && (charArray[pos] == '=')) {
pos++; // skip '='
paramValue = parseQuotedToken(new char[] {
separator });
if (paramValue != null) {
try {
paramValue = MimeUtility.decodeText(paramValue);
} catch (UnsupportedEncodingException e) {
// let's keep the original value in this case
}
}
}
if (hasChar() && (charArray[pos] == separator)) {
pos++; // skip separator
}
if ((paramName != null) && (paramName.length() > 0)) {
if (this.lowerCaseNames) {
paramName = paramName.toLowerCase(Locale.ENGLISH);
}
params.put(paramName, paramValue);
}
}
return params;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
/**
* The {@link ProgressListener} may be used to display a progress bar
* or do stuff like that.
*/
public interface ProgressListener {
/**
* Updates the listeners status information.
*
* @param pBytesRead The total number of bytes, which have been read
* so far.
* @param pContentLength The total number of bytes, which are being
* read. May be -1, if this number is unknown.
* @param pItems The number of the field, which is currently being
* read. (0 = no item so far, 1 = first item is being read, ...)
*/
void update(long pBytesRead, long pContentLength, int pItems);
}

View File

@@ -0,0 +1,54 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
import java.io.IOException;
import java.io.InputStream;
/**
* <p>Abstracts access to the request information needed for file uploads. This
* interface should be implemented for each type of request that may be
* handled by FileUpload, such as servlets and portlets.</p>
*
* @since FileUpload 1.1
*/
public interface RequestContext {
/**
* Retrieve the character encoding for the request.
*
* @return The character encoding for the request.
*/
String getCharacterEncoding();
/**
* Retrieve the content type of the request.
*
* @return The content type of the request.
*/
String getContentType();
/**
* Retrieve the input stream for the request.
*
* @return The input stream for the request.
*
* @throws IOException if a problem occurs.
*/
InputStream getInputStream() throws IOException;
}

View File

@@ -0,0 +1,226 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
import java.io.IOException;
import java.io.OutputStream;
/**
* An output stream which triggers an event when a specified number of bytes of
* data have been written to it. The event can be used, for example, to throw
* an exception if a maximum has been reached, or to switch the underlying
* stream type when the threshold is exceeded.
* <p>
* This class overrides all <code>OutputStream</code> methods. However, these
* overrides ultimately call the corresponding methods in the underlying output
* stream implementation.
* <p>
* NOTE: This implementation may trigger the event <em>before</em> the threshold
* is actually reached, since it triggers when a pending write operation would
* cause the threshold to be exceeded.
*/
public abstract class ThresholdingOutputStream
extends OutputStream
{
// ----------------------------------------------------------- Data members
/**
* The threshold at which the event will be triggered.
*/
private final int threshold;
/**
* The number of bytes written to the output stream.
*/
private long written;
/**
* Whether or not the configured threshold has been exceeded.
*/
private boolean thresholdExceeded;
// ----------------------------------------------------------- Constructors
/**
* Constructs an instance of this class which will trigger an event at the
* specified threshold.
*
* @param threshold The number of bytes at which to trigger an event.
*/
public ThresholdingOutputStream(final int threshold)
{
this.threshold = threshold;
}
// --------------------------------------------------- OutputStream methods
/**
* Writes the specified byte to this output stream.
*
* @param b The byte to be written.
*
* @throws IOException if an error occurs.
*/
@Override
public void write(final int b) throws IOException
{
checkThreshold(1);
getStream().write(b);
written++;
}
/**
* Writes <code>b.length</code> bytes from the specified byte array to this
* output stream.
*
* @param b The array of bytes to be written.
*
* @throws IOException if an error occurs.
*/
@Override
public void write(final byte b[]) throws IOException
{
checkThreshold(b.length);
getStream().write(b);
written += b.length;
}
/**
* Writes <code>len</code> bytes from the specified byte array starting at
* offset <code>off</code> to this output stream.
*
* @param b The byte array from which the data will be written.
* @param off The start offset in the byte array.
* @param len The number of bytes to write.
*
* @throws IOException if an error occurs.
*/
@Override
public void write(final byte b[], final int off, final int len) throws IOException
{
checkThreshold(len);
getStream().write(b, off, len);
written += len;
}
/**
* Flushes this output stream and forces any buffered output bytes to be
* written out.
*
* @throws IOException if an error occurs.
*/
@Override
public void flush() throws IOException
{
getStream().flush();
}
/**
* Closes this output stream and releases any system resources associated
* with this stream.
*
* @throws IOException if an error occurs.
*/
@Override
public void close() throws IOException
{
try
{
flush();
}
catch (final IOException ignored)
{
// ignore
}
getStream().close();
}
// --------------------------------------------------------- Public methods
/**
* Determines whether or not the configured threshold has been exceeded for
* this output stream.
*
* @return {@code true} if the threshold has been reached;
* {@code false} otherwise.
*/
public boolean isThresholdExceeded()
{
return written > threshold;
}
// ------------------------------------------------------ Protected methods
/**
* Checks to see if writing the specified number of bytes would cause the
* configured threshold to be exceeded. If so, triggers an event to allow
* a concrete implementation to take action on this.
*
* @param count The number of bytes about to be written to the underlying
* output stream.
*
* @throws IOException if an error occurs.
*/
protected void checkThreshold(final int count) throws IOException
{
if (!thresholdExceeded && written + count > threshold)
{
thresholdExceeded = true;
thresholdReached();
}
}
// ------------------------------------------------------- Abstract methods
/**
* Returns the underlying output stream, to which the corresponding
* <code>OutputStream</code> methods in this class will ultimately delegate.
*
* @return The underlying output stream.
*
* @throws IOException if an error occurs.
*/
protected abstract OutputStream getStream() throws IOException;
/**
* Indicates that the configured threshold has been reached, and that a
* subclass should take whatever action necessary on this event. This may
* include changing the underlying output stream.
*
* @throws IOException if an error occurs.
*/
protected abstract void thresholdReached() throws IOException;
}

View File

@@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload;
/**
* Enhanced access to the request information needed for file uploads,
* which fixes the Content Length data access in {@link RequestContext}.
*
* The reason of introducing this new interface is just for backward compatibility
* and it might vanish for a refactored 2.x version moving the new method into
* RequestContext again.
*
* @since 1.3
*/
public interface UploadContext extends RequestContext {
/**
* Retrieve the content length of the request.
*
* @return The content length of the request.
* @since 1.3
*/
long contentLength();
}

View File

@@ -0,0 +1,654 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.disk;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.tomcat.util.http.fileupload.DeferredFileOutputStream;
import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.FileItemHeaders;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.apache.tomcat.util.http.fileupload.ParameterParser;
import org.apache.tomcat.util.http.fileupload.util.Streams;
/**
* <p> The default implementation of the
* {@link org.apache.tomcat.util.http.fileupload.FileItem FileItem} interface.
*
* <p> After retrieving an instance of this class from a {@link
* org.apache.tomcat.util.http.fileupload.FileUpload FileUpload} instance (see
* {@link org.apache.tomcat.util.http.fileupload.FileUpload
* #parseRequest(org.apache.tomcat.util.http.fileupload.RequestContext)}), you
* may either request all contents of file at once using {@link #get()} or
* request an {@link java.io.InputStream InputStream} with
* {@link #getInputStream()} and process the file without attempting to load
* it into memory, which may come handy with large files.
*
* <p>Temporary files, which are created for file items, should be
* deleted later on.</p>
*
* @since FileUpload 1.1
*/
public class DiskFileItem
implements FileItem {
// ----------------------------------------------------- Manifest constants
/**
* Default content charset to be used when no explicit charset
* parameter is provided by the sender. Media subtypes of the
* "text" type are defined to have a default charset value of
* "ISO-8859-1" when received via HTTP.
*/
public static final String DEFAULT_CHARSET = "ISO-8859-1";
// ----------------------------------------------------------- Data members
/**
* UID used in unique file name generation.
*/
private static final String UID =
UUID.randomUUID().toString().replace('-', '_');
/**
* Counter used in unique identifier generation.
*/
private static final AtomicInteger COUNTER = new AtomicInteger(0);
/**
* The name of the form field as provided by the browser.
*/
private String fieldName;
/**
* The content type passed by the browser, or <code>null</code> if
* not defined.
*/
private final String contentType;
/**
* Whether or not this item is a simple form field.
*/
private boolean isFormField;
/**
* The original file name in the user's file system.
*/
private final String fileName;
/**
* The size of the item, in bytes. This is used to cache the size when a
* file item is moved from its original location.
*/
private long size = -1;
/**
* The threshold above which uploads will be stored on disk.
*/
private final int sizeThreshold;
/**
* The directory in which uploaded files will be stored, if stored on disk.
*/
private final File repository;
/**
* Cached contents of the file.
*/
private byte[] cachedContent;
/**
* Output stream for this item.
*/
private transient DeferredFileOutputStream dfos;
/**
* The temporary file to use.
*/
private transient File tempFile;
/**
* The file items headers.
*/
private FileItemHeaders headers;
/**
* Default content charset to be used when no explicit charset
* parameter is provided by the sender.
*/
private String defaultCharset = DEFAULT_CHARSET;
// ----------------------------------------------------------- Constructors
/**
* Constructs a new <code>DiskFileItem</code> instance.
*
* @param fieldName The name of the form field.
* @param contentType The content type passed by the browser or
* <code>null</code> if not specified.
* @param isFormField Whether or not this item is a plain form field, as
* opposed to a file upload.
* @param fileName The original file name in the user's file system, or
* <code>null</code> if not specified.
* @param sizeThreshold The threshold, in bytes, below which items will be
* retained in memory and above which they will be
* stored as a file.
* @param repository The data repository, which is the directory in
* which files will be created, should the item size
* exceed the threshold.
*/
public DiskFileItem(String fieldName,
String contentType, boolean isFormField, String fileName,
int sizeThreshold, File repository) {
this.fieldName = fieldName;
this.contentType = contentType;
this.isFormField = isFormField;
this.fileName = fileName;
this.sizeThreshold = sizeThreshold;
this.repository = repository;
}
// ------------------------------- Methods from javax.activation.DataSource
/**
* Returns an {@link java.io.InputStream InputStream} that can be
* used to retrieve the contents of the file.
*
* @return An {@link java.io.InputStream InputStream} that can be
* used to retrieve the contents of the file.
*
* @throws IOException if an error occurs.
*/
@Override
public InputStream getInputStream()
throws IOException {
if (!isInMemory()) {
return new FileInputStream(dfos.getFile());
}
if (cachedContent == null) {
cachedContent = dfos.getData();
}
return new ByteArrayInputStream(cachedContent);
}
/**
* Returns the content type passed by the agent or <code>null</code> if
* not defined.
*
* @return The content type passed by the agent or <code>null</code> if
* not defined.
*/
@Override
public String getContentType() {
return contentType;
}
/**
* Returns the content charset passed by the agent or <code>null</code> if
* not defined.
*
* @return The content charset passed by the agent or <code>null</code> if
* not defined.
*/
public String getCharSet() {
ParameterParser parser = new ParameterParser();
parser.setLowerCaseNames(true);
// Parameter parser can handle null input
Map<String, String> params = parser.parse(getContentType(), ';');
return params.get("charset");
}
/**
* Returns the original file name in the client's file system.
*
* @return The original file name in the client's file system.
* @throws org.apache.tomcat.util.http.fileupload.InvalidFileNameException
* The file name contains a NUL character, which might be an indicator of
* a security attack. If you intend to use the file name anyways, catch
* the exception and use {@link
* org.apache.tomcat.util.http.fileupload.InvalidFileNameException#getName()}.
*/
@Override
public String getName() {
return Streams.checkFileName(fileName);
}
// ------------------------------------------------------- FileItem methods
/**
* Provides a hint as to whether or not the file contents will be read
* from memory.
*
* @return <code>true</code> if the file contents will be read
* from memory; <code>false</code> otherwise.
*/
@Override
public boolean isInMemory() {
if (cachedContent != null) {
return true;
}
return dfos.isInMemory();
}
/**
* Returns the size of the file.
*
* @return The size of the file, in bytes.
*/
@Override
public long getSize() {
if (size >= 0) {
return size;
} else if (cachedContent != null) {
return cachedContent.length;
} else if (dfos.isInMemory()) {
return dfos.getData().length;
} else {
return dfos.getFile().length();
}
}
/**
* Returns the contents of the file as an array of bytes. If the
* contents of the file were not yet cached in memory, they will be
* loaded from the disk storage and cached.
*
* @return The contents of the file as an array of bytes
* or {@code null} if the data cannot be read
*/
@Override
public byte[] get() {
if (isInMemory()) {
if (cachedContent == null && dfos != null) {
cachedContent = dfos.getData();
}
return cachedContent;
}
byte[] fileData = new byte[(int) getSize()];
InputStream fis = null;
try {
fis = new FileInputStream(dfos.getFile());
IOUtils.readFully(fis, fileData);
} catch (IOException e) {
fileData = null;
} finally {
IOUtils.closeQuietly(fis);
}
return fileData;
}
/**
* Returns the contents of the file as a String, using the specified
* encoding. This method uses {@link #get()} to retrieve the
* contents of the file.
*
* @param charset The charset to use.
*
* @return The contents of the file, as a string.
*
* @throws UnsupportedEncodingException if the requested character
* encoding is not available.
*/
@Override
public String getString(final String charset)
throws UnsupportedEncodingException {
return new String(get(), charset);
}
/**
* Returns the contents of the file as a String, using the default
* character encoding. This method uses {@link #get()} to retrieve the
* contents of the file.
*
* <b>TODO</b> Consider making this method throw UnsupportedEncodingException.
*
* @return The contents of the file, as a string.
*/
@Override
public String getString() {
byte[] rawdata = get();
String charset = getCharSet();
if (charset == null) {
charset = defaultCharset;
}
try {
return new String(rawdata, charset);
} catch (UnsupportedEncodingException e) {
return new String(rawdata);
}
}
/**
* A convenience method to write an uploaded item to disk. The client code
* is not concerned with whether or not the item is stored in memory, or on
* disk in a temporary location. They just want to write the uploaded item
* to a file.
* <p>
* This implementation first attempts to rename the uploaded item to the
* specified destination file, if the item was originally written to disk.
* Otherwise, the data will be copied to the specified file.
* <p>
* This method is only guaranteed to work <em>once</em>, the first time it
* is invoked for a particular item. This is because, in the event that the
* method renames a temporary file, that file will no longer be available
* to copy or rename again at a later time.
*
* @param file The <code>File</code> into which the uploaded item should
* be stored.
*
* @throws Exception if an error occurs.
*/
@Override
public void write(File file) throws Exception {
if (isInMemory()) {
FileOutputStream fout = null;
try {
fout = new FileOutputStream(file);
fout.write(get());
fout.close();
} finally {
IOUtils.closeQuietly(fout);
}
} else {
File outputFile = getStoreLocation();
if (outputFile != null) {
// Save the length of the file
size = outputFile.length();
/*
* The uploaded file is being stored on disk
* in a temporary location so move it to the
* desired file.
*/
if (file.exists()) {
if (!file.delete()) {
throw new FileUploadException(
"Cannot write uploaded file to disk!");
}
}
if (!outputFile.renameTo(file)) {
BufferedInputStream in = null;
BufferedOutputStream out = null;
try {
in = new BufferedInputStream(
new FileInputStream(outputFile));
out = new BufferedOutputStream(
new FileOutputStream(file));
IOUtils.copy(in, out);
out.close();
} finally {
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(out);
}
}
} else {
/*
* For whatever reason we cannot write the
* file to disk.
*/
throw new FileUploadException(
"Cannot write uploaded file to disk!");
}
}
}
/**
* Deletes the underlying storage for a file item, including deleting any
* associated temporary disk file. Although this storage will be deleted
* automatically when the <code>FileItem</code> instance is garbage
* collected, this method can be used to ensure that this is done at an
* earlier time, thus preserving system resources.
*/
@Override
public void delete() {
cachedContent = null;
File outputFile = getStoreLocation();
if (outputFile != null && !isInMemory() && outputFile.exists()) {
outputFile.delete();
}
}
/**
* Returns the name of the field in the multipart form corresponding to
* this file item.
*
* @return The name of the form field.
*
* @see #setFieldName(java.lang.String)
*
*/
@Override
public String getFieldName() {
return fieldName;
}
/**
* Sets the field name used to reference this file item.
*
* @param fieldName The name of the form field.
*
* @see #getFieldName()
*
*/
@Override
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
/**
* Determines whether or not a <code>FileItem</code> instance represents
* a simple form field.
*
* @return <code>true</code> if the instance represents a simple form
* field; <code>false</code> if it represents an uploaded file.
*
* @see #setFormField(boolean)
*
*/
@Override
public boolean isFormField() {
return isFormField;
}
/**
* Specifies whether or not a <code>FileItem</code> instance represents
* a simple form field.
*
* @param state <code>true</code> if the instance represents a simple form
* field; <code>false</code> if it represents an uploaded file.
*
* @see #isFormField()
*
*/
@Override
public void setFormField(boolean state) {
isFormField = state;
}
/**
* Returns an {@link java.io.OutputStream OutputStream} that can
* be used for storing the contents of the file.
*
* @return An {@link java.io.OutputStream OutputStream} that can be used
* for storing the contents of the file.
*
* @throws IOException if an error occurs.
*/
@Override
public OutputStream getOutputStream()
throws IOException {
if (dfos == null) {
File outputFile = getTempFile();
dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
}
return dfos;
}
// --------------------------------------------------------- Public methods
/**
* Returns the {@link java.io.File} object for the <code>FileItem</code>'s
* data's temporary location on the disk. Note that for
* <code>FileItem</code>s that have their data stored in memory,
* this method will return <code>null</code>. When handling large
* files, you can use {@link java.io.File#renameTo(java.io.File)} to
* move the file to new location without copying the data, if the
* source and destination locations reside within the same logical
* volume.
*
* @return The data file, or <code>null</code> if the data is stored in
* memory.
*/
public File getStoreLocation() {
if (dfos == null) {
return null;
}
if (isInMemory()) {
return null;
}
return dfos.getFile();
}
// ------------------------------------------------------ Protected methods
/**
* Removes the file contents from the temporary storage.
*/
@Override
protected void finalize() throws Throwable {
if (dfos == null || dfos.isInMemory()) {
return;
}
File outputFile = dfos.getFile();
if (outputFile != null && outputFile.exists()) {
outputFile.delete();
}
super.finalize();
}
/**
* Creates and returns a {@link java.io.File File} representing a uniquely
* named temporary file in the configured repository path. The lifetime of
* the file is tied to the lifetime of the <code>FileItem</code> instance;
* the file will be deleted when the instance is garbage collected.
* <p>
* <b>Note: Subclasses that override this method must ensure that they return the
* same File each time.</b>
*
* @return The {@link java.io.File File} to be used for temporary storage.
*/
protected File getTempFile() {
if (tempFile == null) {
File tempDir = repository;
if (tempDir == null) {
tempDir = new File(System.getProperty("java.io.tmpdir"));
}
String tempFileName = String.format("upload_%s_%s.tmp", UID, getUniqueId());
tempFile = new File(tempDir, tempFileName);
}
return tempFile;
}
// -------------------------------------------------------- Private methods
/**
* Returns an identifier that is unique within the class loader used to
* load this class, but does not have random-like appearance.
*
* @return A String with the non-random looking instance identifier.
*/
private static String getUniqueId() {
final int limit = 100000000;
int current = COUNTER.getAndIncrement();
String id = Integer.toString(current);
// If you manage to get more than 100 million of ids, you'll
// start getting ids longer than 8 characters.
if (current < limit) {
id = ("00000000" + id).substring(id.length());
}
return id;
}
/**
* Returns a string representation of this object.
*
* @return a string representation of this object.
*/
@Override
public String toString() {
return String.format("name=%s, StoreLocation=%s, size=%s bytes, isFormField=%s, FieldName=%s",
getName(), getStoreLocation(), Long.valueOf(getSize()),
Boolean.valueOf(isFormField()), getFieldName());
}
/**
* Returns the file item headers.
* @return The file items headers.
*/
@Override
public FileItemHeaders getHeaders() {
return headers;
}
/**
* Sets the file item headers.
* @param pHeaders The file items headers.
*/
@Override
public void setHeaders(FileItemHeaders pHeaders) {
headers = pHeaders;
}
/**
* Returns the default charset for use when no explicit charset
* parameter is provided by the sender.
* @return the default charset
*/
public String getDefaultCharset() {
return defaultCharset;
}
/**
* Sets the default charset for use when no explicit charset
* parameter is provided by the sender.
* @param charset the default charset
*/
public void setDefaultCharset(String charset) {
defaultCharset = charset;
}
}

View File

@@ -0,0 +1,205 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.disk;
import java.io.File;
import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.FileItemFactory;
/**
* <p>The default {@link org.apache.tomcat.util.http.fileupload.FileItemFactory}
* implementation. This implementation creates
* {@link org.apache.tomcat.util.http.fileupload.FileItem} instances which keep
* their
* content either in memory, for smaller items, or in a temporary file on disk,
* for larger items. The size threshold, above which content will be stored on
* disk, is configurable, as is the directory in which temporary files will be
* created.</p>
*
* <p>If not otherwise configured, the default configuration values are as
* follows:</p>
* <ul>
* <li>Size threshold is 10KB.</li>
* <li>Repository is the system default temp directory, as returned by
* <code>System.getProperty("java.io.tmpdir")</code>.</li>
* </ul>
* <p>
* <b>NOTE</b>: Files are created in the system default temp directory with
* predictable names. This means that a local attacker with write access to that
* directory can perform a TOUTOC attack to replace any uploaded file with a
* file of the attackers choice. The implications of this will depend on how the
* uploaded file is used but could be significant. When using this
* implementation in an environment with local, untrusted users,
* {@link #setRepository(File)} MUST be used to configure a repository location
* that is not publicly writable. In a Servlet container the location identified
* by the ServletContext attribute <code>javax.servlet.context.tempdir</code>
* may be used.
* </p>
*
* <p>Temporary files, which are created for file items, should be
* deleted later on.</p>
*
* @since FileUpload 1.1
*/
public class DiskFileItemFactory implements FileItemFactory {
// ----------------------------------------------------- Manifest constants
/**
* The default threshold above which uploads will be stored on disk.
*/
public static final int DEFAULT_SIZE_THRESHOLD = 10240;
// ----------------------------------------------------- Instance Variables
/**
* The directory in which uploaded files will be stored, if stored on disk.
*/
private File repository;
/**
* The threshold above which uploads will be stored on disk.
*/
private int sizeThreshold = DEFAULT_SIZE_THRESHOLD;
/**
* Default content charset to be used when no explicit charset
* parameter is provided by the sender.
*/
private String defaultCharset = DiskFileItem.DEFAULT_CHARSET;
// ----------------------------------------------------------- Constructors
/**
* Constructs an unconfigured instance of this class. The resulting factory
* may be configured by calling the appropriate setter methods.
*/
public DiskFileItemFactory() {
this(DEFAULT_SIZE_THRESHOLD, null);
}
/**
* Constructs a preconfigured instance of this class.
*
* @param sizeThreshold The threshold, in bytes, below which items will be
* retained in memory and above which they will be
* stored as a file.
* @param repository The data repository, which is the directory in
* which files will be created, should the item size
* exceed the threshold.
*/
public DiskFileItemFactory(int sizeThreshold, File repository) {
this.sizeThreshold = sizeThreshold;
this.repository = repository;
}
// ------------------------------------------------------------- Properties
/**
* Returns the directory used to temporarily store files that are larger
* than the configured size threshold.
*
* @return The directory in which temporary files will be located.
*
* @see #setRepository(java.io.File)
*
*/
public File getRepository() {
return repository;
}
/**
* Sets the directory used to temporarily store files that are larger
* than the configured size threshold.
*
* @param repository The directory in which temporary files will be located.
*
* @see #getRepository()
*
*/
public void setRepository(File repository) {
this.repository = repository;
}
/**
* Returns the size threshold beyond which files are written directly to
* disk. The default value is 10240 bytes.
*
* @return The size threshold, in bytes.
*
* @see #setSizeThreshold(int)
*/
public int getSizeThreshold() {
return sizeThreshold;
}
/**
* Sets the size threshold beyond which files are written directly to disk.
*
* @param sizeThreshold The size threshold, in bytes.
*
* @see #getSizeThreshold()
*
*/
public void setSizeThreshold(int sizeThreshold) {
this.sizeThreshold = sizeThreshold;
}
// --------------------------------------------------------- Public Methods
/**
* Create a new {@link DiskFileItem}
* instance from the supplied parameters and the local factory
* configuration.
*
* @param fieldName The name of the form field.
* @param contentType The content type of the form field.
* @param isFormField <code>true</code> if this is a plain form field;
* <code>false</code> otherwise.
* @param fileName The name of the uploaded file, if any, as supplied
* by the browser or other client.
*
* @return The newly created file item.
*/
@Override
public FileItem createItem(String fieldName, String contentType,
boolean isFormField, String fileName) {
DiskFileItem result = new DiskFileItem(fieldName, contentType,
isFormField, fileName, sizeThreshold, repository);
result.setDefaultCharset(defaultCharset);
return result;
}
/**
* Returns the default charset for use when no explicit charset
* parameter is provided by the sender.
* @return the default charset
*/
public String getDefaultCharset() {
return defaultCharset;
}
/**
* Sets the default charset for use when no explicit charset
* parameter is provided by the sender.
* @param pCharset the default charset
*/
public void setDefaultCharset(String pCharset) {
defaultCharset = pCharset;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <p>
* A disk-based implementation of the
* {@link org.apache.tomcat.util.http.fileupload.FileItem FileItem}
* interface. This implementation retains smaller items in memory, while
* writing larger ones to disk. The threshold between these two is
* configurable, as is the location of files that are written to disk.
* </p>
* <p>
* In typical usage, an instance of
* {@link org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory DiskFileItemFactory}
* would be created, configured, and then passed to a
* {@link org.apache.tomcat.util.http.fileupload.FileUpload FileUpload}
* implementation such as
* {@link org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload ServletFileUpload}.
* </p>
* <p>
* The following code fragment demonstrates this usage.
* </p>
* <pre>
* DiskFileItemFactory factory = new DiskFileItemFactory();
* // maximum size that will be stored in memory
* factory.setSizeThreshold(4096);
* // the location for saving data that is larger than getSizeThreshold()
* factory.setRepository(new File("/tmp"));
*
* ServletFileUpload upload = new ServletFileUpload(factory);
* </pre>
* <p>
* Please see the FileUpload
* <a href="https://commons.apache.org/fileupload/using.html" target="_top">User Guide</a>
* for further details and examples of how to use this package.
* </p>
*/
package org.apache.tomcat.util.http.fileupload.disk;

View File

@@ -0,0 +1,339 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.impl;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.NoSuchElementException;
import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.FileItemHeaders;
import org.apache.tomcat.util.http.fileupload.FileItemIterator;
import org.apache.tomcat.util.http.fileupload.FileItemStream;
import org.apache.tomcat.util.http.fileupload.FileUploadBase;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.apache.tomcat.util.http.fileupload.MultipartStream;
import org.apache.tomcat.util.http.fileupload.ProgressListener;
import org.apache.tomcat.util.http.fileupload.RequestContext;
import org.apache.tomcat.util.http.fileupload.UploadContext;
import org.apache.tomcat.util.http.fileupload.util.LimitedInputStream;
/**
* The iterator, which is returned by
* {@link FileUploadBase#getItemIterator(RequestContext)}.
*/
public class FileItemIteratorImpl implements FileItemIterator {
private final FileUploadBase fileUploadBase;
private final RequestContext ctx;
private long sizeMax, fileSizeMax;
@Override
public long getSizeMax() {
return sizeMax;
}
@Override
public void setSizeMax(long sizeMax) {
this.sizeMax = sizeMax;
}
@Override
public long getFileSizeMax() {
return fileSizeMax;
}
@Override
public void setFileSizeMax(long fileSizeMax) {
this.fileSizeMax = fileSizeMax;
}
/**
* The multi part stream to process.
*/
private MultipartStream multiPartStream;
/**
* The notifier, which used for triggering the
* {@link ProgressListener}.
*/
private MultipartStream.ProgressNotifier progressNotifier;
/**
* The boundary, which separates the various parts.
*/
private byte[] multiPartBoundary;
/**
* The item, which we currently process.
*/
private FileItemStreamImpl currentItem;
/**
* The current items field name.
*/
private String currentFieldName;
/**
* Whether we are currently skipping the preamble.
*/
private boolean skipPreamble;
/**
* Whether the current item may still be read.
*/
private boolean itemValid;
/**
* Whether we have seen the end of the file.
*/
private boolean eof;
/**
* Creates a new instance.
*
* @param pFileUploadBase Upload instance
* @param pRequestContext The request context.
* @throws FileUploadException An error occurred while
* parsing the request.
* @throws IOException An I/O error occurred.
*/
public FileItemIteratorImpl(FileUploadBase pFileUploadBase, RequestContext pRequestContext)
throws FileUploadException, IOException {
fileUploadBase = pFileUploadBase;
sizeMax = fileUploadBase.getSizeMax();
fileSizeMax = fileUploadBase.getFileSizeMax();
ctx = pRequestContext;
if (ctx == null) {
throw new NullPointerException("ctx parameter");
}
skipPreamble = true;
findNextItem();
}
protected void init(FileUploadBase fileUploadBase, @SuppressWarnings("unused") RequestContext pRequestContext)
throws FileUploadException, IOException {
String contentType = ctx.getContentType();
if ((null == contentType)
|| (!contentType.toLowerCase(Locale.ENGLISH).startsWith(FileUploadBase.MULTIPART))) {
throw new InvalidContentTypeException(
String.format("the request doesn't contain a %s or %s stream, content type header is %s",
FileUploadBase.MULTIPART_FORM_DATA, FileUploadBase.MULTIPART_MIXED, contentType));
}
final long requestSize = ((UploadContext) ctx).contentLength();
InputStream input; // N.B. this is eventually closed in MultipartStream processing
if (sizeMax >= 0) {
if (requestSize != -1 && requestSize > sizeMax) {
throw new SizeLimitExceededException(
String.format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",
Long.valueOf(requestSize), Long.valueOf(sizeMax)),
requestSize, sizeMax);
}
// N.B. this is eventually closed in MultipartStream processing
input = new LimitedInputStream(ctx.getInputStream(), sizeMax) {
@Override
protected void raiseError(long pSizeMax, long pCount)
throws IOException {
FileUploadException ex = new SizeLimitExceededException(
String.format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",
Long.valueOf(pCount), Long.valueOf(pSizeMax)),
pCount, pSizeMax);
throw new FileUploadIOException(ex);
}
};
} else {
input = ctx.getInputStream();
}
String charEncoding = fileUploadBase.getHeaderEncoding();
if (charEncoding == null) {
charEncoding = ctx.getCharacterEncoding();
}
multiPartBoundary = fileUploadBase.getBoundary(contentType);
if (multiPartBoundary == null) {
IOUtils.closeQuietly(input); // avoid possible resource leak
throw new FileUploadException("the request was rejected because no multipart boundary was found");
}
progressNotifier = new MultipartStream.ProgressNotifier(fileUploadBase.getProgressListener(), requestSize);
try {
multiPartStream = new MultipartStream(input, multiPartBoundary, progressNotifier);
} catch (IllegalArgumentException iae) {
IOUtils.closeQuietly(input); // avoid possible resource leak
throw new InvalidContentTypeException(
String.format("The boundary specified in the %s header is too long", FileUploadBase.CONTENT_TYPE), iae);
}
multiPartStream.setHeaderEncoding(charEncoding);
}
public MultipartStream getMultiPartStream() throws FileUploadException, IOException {
if (multiPartStream == null) {
init(fileUploadBase, ctx);
}
return multiPartStream;
}
/**
* Called for finding the next item, if any.
*
* @return True, if an next item was found, otherwise false.
* @throws IOException An I/O error occurred.
*/
private boolean findNextItem() throws FileUploadException, IOException {
if (eof) {
return false;
}
if (currentItem != null) {
currentItem.close();
currentItem = null;
}
final MultipartStream multi = getMultiPartStream();
for (;;) {
boolean nextPart;
if (skipPreamble) {
nextPart = multi.skipPreamble();
} else {
nextPart = multi.readBoundary();
}
if (!nextPart) {
if (currentFieldName == null) {
// Outer multipart terminated -> No more data
eof = true;
return false;
}
// Inner multipart terminated -> Return to parsing the outer
multi.setBoundary(multiPartBoundary);
currentFieldName = null;
continue;
}
FileItemHeaders headers = fileUploadBase.getParsedHeaders(multi.readHeaders());
if (currentFieldName == null) {
// We're parsing the outer multipart
String fieldName = fileUploadBase.getFieldName(headers);
if (fieldName != null) {
String subContentType = headers.getHeader(FileUploadBase.CONTENT_TYPE);
if (subContentType != null
&& subContentType.toLowerCase(Locale.ENGLISH)
.startsWith(FileUploadBase.MULTIPART_MIXED)) {
currentFieldName = fieldName;
// Multiple files associated with this field name
byte[] subBoundary = fileUploadBase.getBoundary(subContentType);
multi.setBoundary(subBoundary);
skipPreamble = true;
continue;
}
String fileName = fileUploadBase.getFileName(headers);
currentItem = new FileItemStreamImpl(this, fileName,
fieldName, headers.getHeader(FileUploadBase.CONTENT_TYPE),
fileName == null, getContentLength(headers));
currentItem.setHeaders(headers);
progressNotifier.noteItem();
itemValid = true;
return true;
}
} else {
String fileName = fileUploadBase.getFileName(headers);
if (fileName != null) {
currentItem = new FileItemStreamImpl(this, fileName,
currentFieldName,
headers.getHeader(FileUploadBase.CONTENT_TYPE),
false, getContentLength(headers));
currentItem.setHeaders(headers);
progressNotifier.noteItem();
itemValid = true;
return true;
}
}
multi.discardBodyData();
}
}
private long getContentLength(FileItemHeaders pHeaders) {
try {
return Long.parseLong(pHeaders.getHeader(FileUploadBase.CONTENT_LENGTH));
} catch (Exception e) {
return -1;
}
}
/**
* Returns, whether another instance of {@link FileItemStream}
* is available.
*
* @throws FileUploadException Parsing or processing the
* file item failed.
* @throws IOException Reading the file item failed.
* @return True, if one or more additional file items
* are available, otherwise false.
*/
@Override
public boolean hasNext() throws FileUploadException, IOException {
if (eof) {
return false;
}
if (itemValid) {
return true;
}
try {
return findNextItem();
} catch (FileUploadIOException e) {
// unwrap encapsulated SizeException
throw (FileUploadException) e.getCause();
}
}
/**
* Returns the next available {@link FileItemStream}.
*
* @throws java.util.NoSuchElementException No more items are
* available. Use {@link #hasNext()} to prevent this exception.
* @throws FileUploadException Parsing or processing the
* file item failed.
* @throws IOException Reading the file item failed.
* @return FileItemStream instance, which provides
* access to the next file item.
*/
@Override
public FileItemStream next() throws FileUploadException, IOException {
if (eof || (!itemValid && !hasNext())) {
throw new NoSuchElementException();
}
itemValid = false;
return currentItem;
}
@Override
public List<FileItem> getFileItems() throws FileUploadException, IOException {
final List<FileItem> items = new ArrayList<>();
while (hasNext()) {
final FileItemStream fis = next();
final FileItem fi = fileUploadBase.getFileItemFactory().createItem(fis.getFieldName(), fis.getContentType(), fis.isFormField(), fis.getName());
items.add(fi);
}
return items;
}
}

View File

@@ -0,0 +1,213 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.impl;
import java.io.IOException;
import java.io.InputStream;
import org.apache.tomcat.util.http.fileupload.FileItemHeaders;
import org.apache.tomcat.util.http.fileupload.FileItemStream;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.apache.tomcat.util.http.fileupload.InvalidFileNameException;
import org.apache.tomcat.util.http.fileupload.MultipartStream.ItemInputStream;
import org.apache.tomcat.util.http.fileupload.util.Closeable;
import org.apache.tomcat.util.http.fileupload.util.LimitedInputStream;
import org.apache.tomcat.util.http.fileupload.util.Streams;
/**
* Default implementation of {@link FileItemStream}.
*/
public class FileItemStreamImpl implements FileItemStream {
private final FileItemIteratorImpl fileItemIteratorImpl;
/**
* The file items content type.
*/
private final String contentType;
/**
* The file items field name.
*/
private final String fieldName;
/**
* The file items file name.
*/
final String name;
/**
* Whether the file item is a form field.
*/
private final boolean formField;
/**
* The file items input stream.
*/
private final InputStream stream;
/**
* The headers, if any.
*/
private FileItemHeaders headers;
/**
* Creates a new instance.
* @param pFileItemIterator Iterator for all files in this upload
* @param pName The items file name, or null.
* @param pFieldName The items field name.
* @param pContentType The items content type, or null.
* @param pFormField Whether the item is a form field.
* @param pContentLength The items content length, if known, or -1
* @throws FileUploadException If an error is encountered processing the request
* @throws IOException Creating the file item failed.
*/
public FileItemStreamImpl(FileItemIteratorImpl pFileItemIterator, String pName, String pFieldName,
String pContentType, boolean pFormField,
long pContentLength) throws FileUploadException, IOException {
fileItemIteratorImpl = pFileItemIterator;
name = pName;
fieldName = pFieldName;
contentType = pContentType;
formField = pFormField;
final long fileSizeMax = fileItemIteratorImpl.getFileSizeMax();
if (fileSizeMax != -1) { // Check if limit is already exceeded
if (pContentLength != -1
&& pContentLength > fileSizeMax) {
FileSizeLimitExceededException e =
new FileSizeLimitExceededException(
String.format("The field %s exceeds its maximum permitted size of %s bytes.",
fieldName, Long.valueOf(fileSizeMax)),
pContentLength, fileSizeMax);
e.setFileName(pName);
e.setFieldName(pFieldName);
throw new FileUploadIOException(e);
}
}
// OK to construct stream now
final ItemInputStream itemStream = fileItemIteratorImpl.getMultiPartStream().newInputStream();
InputStream istream = itemStream;
if (fileSizeMax != -1) {
istream = new LimitedInputStream(istream, fileSizeMax) {
@Override
protected void raiseError(long pSizeMax, long pCount)
throws IOException {
itemStream.close(true);
FileSizeLimitExceededException e =
new FileSizeLimitExceededException(
String.format("The field %s exceeds its maximum permitted size of %s bytes.",
fieldName, Long.valueOf(pSizeMax)),
pCount, pSizeMax);
e.setFieldName(fieldName);
e.setFileName(name);
throw new FileUploadIOException(e);
}
};
}
stream = istream;
}
/**
* Returns the items content type, or null.
*
* @return Content type, if known, or null.
*/
@Override
public String getContentType() {
return contentType;
}
/**
* Returns the items field name.
*
* @return Field name.
*/
@Override
public String getFieldName() {
return fieldName;
}
/**
* Returns the items file name.
*
* @return File name, if known, or null.
* @throws InvalidFileNameException The file name contains a NUL character,
* which might be an indicator of a security attack. If you intend to
* use the file name anyways, catch the exception and use
* InvalidFileNameException#getName().
*/
@Override
public String getName() {
return Streams.checkFileName(name);
}
/**
* Returns, whether this is a form field.
*
* @return True, if the item is a form field,
* otherwise false.
*/
@Override
public boolean isFormField() {
return formField;
}
/**
* Returns an input stream, which may be used to
* read the items contents.
*
* @return Opened input stream.
* @throws IOException An I/O error occurred.
*/
@Override
public InputStream openStream() throws IOException {
if (((Closeable) stream).isClosed()) {
throw new FileItemStream.ItemSkippedException();
}
return stream;
}
/**
* Closes the file item.
*
* @throws IOException An I/O error occurred.
*/
public void close() throws IOException {
stream.close();
}
/**
* Returns the file item headers.
*
* @return The items header object
*/
@Override
public FileItemHeaders getHeaders() {
return headers;
}
/**
* Sets the file item headers.
*
* @param pHeaders The items header object
*/
@Override
public void setHeaders(FileItemHeaders pHeaders) {
headers = pHeaders;
}
}

View File

@@ -0,0 +1,94 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.impl;
/**
* Thrown to indicate that A files size exceeds the configured maximum.
*/
public class FileSizeLimitExceededException
extends SizeException {
/**
* The exceptions UID, for serializing an instance.
*/
private static final long serialVersionUID = 8150776562029630058L;
/**
* File name of the item, which caused the exception.
*/
private String fileName;
/**
* Field name of the item, which caused the exception.
*/
private String fieldName;
/**
* Constructs a <code>SizeExceededException</code> with
* the specified detail message, and actual and permitted sizes.
*
* @param message The detail message.
* @param actual The actual request size.
* @param permitted The maximum permitted request size.
*/
public FileSizeLimitExceededException(String message, long actual,
long permitted) {
super(message, actual, permitted);
}
/**
* Returns the file name of the item, which caused the
* exception.
*
* @return File name, if known, or null.
*/
public String getFileName() {
return fileName;
}
/**
* Sets the file name of the item, which caused the
* exception.
*
* @param pFileName the file name of the item, which caused the exception.
*/
public void setFileName(String pFileName) {
fileName = pFileName;
}
/**
* Returns the field name of the item, which caused the
* exception.
*
* @return Field name, if known, or null.
*/
public String getFieldName() {
return fieldName;
}
/**
* Sets the field name of the item, which caused the
* exception.
*
* @param pFieldName the field name of the item,
* which caused the exception.
*/
public void setFieldName(String pFieldName) {
fieldName = pFieldName;
}
}

View File

@@ -0,0 +1,63 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.impl;
import java.io.IOException;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
/**
* This exception is thrown for hiding an inner
* {@link FileUploadException} in an {@link IOException}.
*/
public class FileUploadIOException extends IOException {
/**
* The exceptions UID, for serializing an instance.
*/
private static final long serialVersionUID = -7047616958165584154L;
/**
* The exceptions cause; we overwrite the parent
* classes field, which is available since Java
* 1.4 only.
*/
private final FileUploadException cause;
/**
* Creates a <code>FileUploadIOException</code> with the
* given cause.
*
* @param pCause The exceptions cause, if any, or null.
*/
public FileUploadIOException(FileUploadException pCause) {
// We're not doing super(pCause) cause of 1.3 compatibility.
cause = pCause;
}
/**
* Returns the exceptions cause.
*
* @return The exceptions cause, if any, or null.
*/
@SuppressWarnings("sync-override") // Field is final
@Override
public Throwable getCause() {
return cause;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.impl;
import java.io.IOException;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
/**
* Thrown to indicate an IOException.
*/
public class IOFileUploadException extends FileUploadException {
/**
* The exceptions UID, for serializing an instance.
*/
private static final long serialVersionUID = 1749796615868477269L;
/**
* The exceptions cause; we overwrite the parent
* classes field, which is available since Java
* 1.4 only.
*/
private final IOException cause;
/**
* Creates a new instance with the given cause.
*
* @param pMsg The detail message.
* @param pException The exceptions cause.
*/
public IOFileUploadException(String pMsg, IOException pException) {
super(pMsg);
cause = pException;
}
/**
* Returns the exceptions cause.
*
* @return The exceptions cause, if any, or null.
*/
@SuppressWarnings("sync-override") // Field is final
@Override
public Throwable getCause() {
return cause;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.impl;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
/**
* Thrown to indicate that the request is not a multipart request.
*/
public class InvalidContentTypeException
extends FileUploadException {
/**
* The exceptions UID, for serializing an instance.
*/
private static final long serialVersionUID = -9073026332015646668L;
/**
* Constructs a <code>InvalidContentTypeException</code> with no
* detail message.
*/
public InvalidContentTypeException() {
super();
}
/**
* Constructs an <code>InvalidContentTypeException</code> with
* the specified detail message.
*
* @param message The detail message.
*/
public InvalidContentTypeException(String message) {
super(message);
}
/**
* Constructs an <code>InvalidContentTypeException</code> with
* the specified detail message and cause.
*
* @param msg The detail message.
* @param cause the original cause
*
* @since 1.3.1
*/
public InvalidContentTypeException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,75 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.impl;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
/**
* This exception is thrown, if a requests permitted size
* is exceeded.
*/
public abstract class SizeException extends FileUploadException {
/**
* Serial version UID, being used, if serialized.
*/
private static final long serialVersionUID = -8776225574705254126L;
/**
* The actual size of the request.
*/
private final long actual;
/**
* The maximum permitted size of the request.
*/
private final long permitted;
/**
* Creates a new instance.
*
* @param message The detail message.
* @param actual The actual number of bytes in the request.
* @param permitted The requests size limit, in bytes.
*/
protected SizeException(String message, long actual, long permitted) {
super(message);
this.actual = actual;
this.permitted = permitted;
}
/**
* Retrieves the actual size of the request.
*
* @return The actual size of the request.
* @since 1.3
*/
public long getActualSize() {
return actual;
}
/**
* Retrieves the permitted size of the request.
*
* @return The permitted size of the request.
* @since 1.3
*/
public long getPermittedSize() {
return permitted;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.impl;
/**
* Thrown to indicate that the request size exceeds the configured maximum.
*/
public class SizeLimitExceededException
extends SizeException {
/**
* The exceptions UID, for serializing an instance.
*/
private static final long serialVersionUID = -2474893167098052828L;
/**
* Constructs a <code>SizeExceededException</code> with
* the specified detail message, and actual and permitted sizes.
*
* @param message The detail message.
* @param actual The actual request size.
* @param permitted The maximum permitted request size.
*/
public SizeLimitExceededException(String message, long actual,
long permitted) {
super(message, actual, permitted);
}
}

View File

@@ -0,0 +1,88 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <p><b>NOTE:</b> This code has been copied from commons-fileupload trunk
* 1.3 and commons-io 1.4 and package renamed to avoid clashes with
* any web apps that may wish to use these libraries.
* </p>
* <p>
* A component for handling HTML file uploads as specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt" target="_top">RFC&nbsp;1867</a>.
* This component provides support for uploads within both servlets (JSR 53)
* and portlets (JSR 168).
* </p>
* <p>
* While this package provides the generic functionality for file uploads,
* these classes are not typically used directly. Instead, normal usage
* involves one of the provided extensions of
* {@link org.apache.tomcat.util.http.fileupload.FileUpload FileUpload} such as
* {@link org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload ServletFileUpload}
* together with a factory for
* {@link org.apache.tomcat.util.http.fileupload.FileItem FileItem} instances,
* such as
* {@link org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory DiskFileItemFactory}.
* </p>
* <p>
* The following is a brief example of typical usage in a servlet, storing
* the uploaded files on disk.
* </p>
* <pre>public void doPost(HttpServletRequest req, HttpServletResponse res) {
* DiskFileItemFactory factory = new DiskFileItemFactory();
* // maximum size that will be stored in memory
* factory.setSizeThreshold(4096);
* // the location for saving data that is larger than getSizeThreshold()
* factory.setRepository(new File("/tmp"));
*
* ServletFileUpload upload = new ServletFileUpload(factory);
* // maximum size before a FileUploadException will be thrown
* upload.setSizeMax(1000000);
*
* List fileItems = upload.parseRequest(req);
* // assume we know there are two files. The first file is a small
* // text file, the second is unknown and is written to a file on
* // the server
* Iterator i = fileItems.iterator();
* String comment = ((FileItem)i.next()).getString();
* FileItem fi = (FileItem)i.next();
* // file name on the client
* String fileName = fi.getName();
* // save comment and file name to database
* ...
* // write the file
* fi.write(new File("/www/uploads/", fileName));
* }
* </pre>
* <p>
* In the example above, the first file is loaded into memory as a
* <code>String</code>. Before calling the <code>getString</code> method,
* the data may have been in memory or on disk depending on its size. The
* second file we assume it will be large and therefore never explicitly
* load it into memory, though if it is less than 4096 bytes it will be
* in memory before it is written to its final location. When writing to
* the final location, if the data is larger than the threshold, an attempt
* is made to rename the temporary file to the given location. If it cannot
* be renamed, it is streamed to the new location.
* </p>
* <p>
* Please see the FileUpload
* <a href="https://commons.apache.org/fileupload/using.html" target="_top">User Guide</a>
* for further details and examples of how to use this package.
* </p>
*/
package org.apache.tomcat.util.http.fileupload;

View File

@@ -0,0 +1,138 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.servlet;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.FileItemFactory;
import org.apache.tomcat.util.http.fileupload.FileItemIterator;
import org.apache.tomcat.util.http.fileupload.FileUpload;
import org.apache.tomcat.util.http.fileupload.FileUploadBase;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
/**
* <p>High level API for processing file uploads.</p>
*
* <p>This class handles multiple files per single HTML widget, sent using
* <code>multipart/mixed</code> encoding type, as specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Use {@link
* #parseRequest(org.apache.tomcat.util.http.fileupload.RequestContext)} to
* acquire a list of {@link org.apache.tomcat.util.http.fileupload.FileItem}s
* associated with a given HTML widget.</p>
*
* <p>How the data for individual parts is stored is determined by the factory
* used to create them; a given part may be in memory, on disk, or somewhere
* else.</p>
*/
public class ServletFileUpload extends FileUpload {
/**
* Constant for HTTP POST method.
*/
private static final String POST_METHOD = "POST";
// ---------------------------------------------------------- Class methods
/**
* Utility method that determines whether the request contains multipart
* content.
*
* @param request The servlet request to be evaluated. Must be non-null.
*
* @return <code>true</code> if the request is multipart;
* <code>false</code> otherwise.
*/
public static final boolean isMultipartContent(
HttpServletRequest request) {
if (!POST_METHOD.equalsIgnoreCase(request.getMethod())) {
return false;
}
return FileUploadBase.isMultipartContent(new ServletRequestContext(request));
}
// ----------------------------------------------------------- Constructors
/**
* Constructs an uninitialised instance of this class. A factory must be
* configured, using <code>setFileItemFactory()</code>, before attempting
* to parse requests.
*
* @see FileUpload#FileUpload(FileItemFactory)
*/
public ServletFileUpload() {
super();
}
/**
* Constructs an instance of this class which uses the supplied factory to
* create <code>FileItem</code> instances.
*
* @see FileUpload#FileUpload()
* @param fileItemFactory The factory to use for creating file items.
*/
public ServletFileUpload(FileItemFactory fileItemFactory) {
super(fileItemFactory);
}
// --------------------------------------------------------- Public methods
/**
* Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant <code>multipart/form-data</code> stream.
*
* @param request The servlet request to be parsed.
*
* @return A map of <code>FileItem</code> instances parsed from the request.
*
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
*
* @since 1.3
*/
public Map<String, List<FileItem>> parseParameterMap(HttpServletRequest request)
throws FileUploadException {
return parseParameterMap(new ServletRequestContext(request));
}
/**
* Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>
* compliant <code>multipart/form-data</code> stream.
*
* @param request The servlet request to be parsed.
*
* @return An iterator to instances of <code>FileItemStream</code>
* parsed from the request, in the order that they were
* transmitted.
*
* @throws FileUploadException if there are problems reading/parsing
* the request or storing files.
* @throws IOException An I/O error occurred. This may be a network
* error while communicating with the client or a problem while
* storing the uploaded content.
*/
public FileItemIterator getItemIterator(HttpServletRequest request)
throws FileUploadException, IOException {
return super.getItemIterator(new ServletRequestContext(request));
}
}

View File

@@ -0,0 +1,117 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.servlet;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.http.HttpServletRequest;
import org.apache.tomcat.util.http.fileupload.FileUploadBase;
import org.apache.tomcat.util.http.fileupload.UploadContext;
/**
* <p>Provides access to the request information needed for a request made to
* an HTTP servlet.</p>
*
* @since FileUpload 1.1
*/
public class ServletRequestContext implements UploadContext {
// ----------------------------------------------------- Instance Variables
/**
* The request for which the context is being provided.
*/
private final HttpServletRequest request;
// ----------------------------------------------------------- Constructors
/**
* Construct a context for this request.
*
* @param request The request to which this context applies.
*/
public ServletRequestContext(HttpServletRequest request) {
this.request = request;
}
// --------------------------------------------------------- Public Methods
/**
* Retrieve the character encoding for the request.
*
* @return The character encoding for the request.
*/
@Override
public String getCharacterEncoding() {
return request.getCharacterEncoding();
}
/**
* Retrieve the content type of the request.
*
* @return The content type of the request.
*/
@Override
public String getContentType() {
return request.getContentType();
}
/**
* Retrieve the content length of the request.
*
* @return The content length of the request.
* @since 1.3
*/
@Override
public long contentLength() {
long size;
try {
size = Long.parseLong(request.getHeader(FileUploadBase.CONTENT_LENGTH));
} catch (NumberFormatException e) {
size = request.getContentLength();
}
return size;
}
/**
* Retrieve the input stream for the request.
*
* @return The input stream for the request.
*
* @throws IOException if a problem occurs.
*/
@Override
public InputStream getInputStream() throws IOException {
return request.getInputStream();
}
/**
* Returns a string representation of this object.
*
* @return a string representation of this object.
*/
@Override
public String toString() {
return String.format("ContentLength=%s, ContentType=%s",
Long.valueOf(this.contentLength()),
this.getContentType());
}
}

View File

@@ -0,0 +1,45 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <p>
* An implementation of
* {@link org.apache.tomcat.util.http.fileupload.FileUpload FileUpload}
* for use in servlets conforming to JSR 53. This implementation requires
* only access to the servlet's current <code>HttpServletRequest</code>
* instance, and a suitable
* {@link org.apache.tomcat.util.http.fileupload.FileItemFactory FileItemFactory}
* implementation, such as
* {@link org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory DiskFileItemFactory}.
* </p>
* <p>
* The following code fragment demonstrates typical usage.
* </p>
* <pre>
* DiskFileItemFactory factory = new DiskFileItemFactory();
* // Configure the factory here, if desired.
* ServletFileUpload upload = new ServletFileUpload(factory);
* // Configure the uploader here, if desired.
* List fileItems = upload.parseRequest(request);
* </pre>
* <p>
* Please see the FileUpload
* <a href="https://commons.apache.org/fileupload/using.html" target="_top">User Guide</a>
* for further details and examples of how to use this package.
* </p>
*/
package org.apache.tomcat.util.http.fileupload.servlet;

View File

@@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.util;
import java.io.IOException;
/**
* Interface of an object, which may be closed.
*/
public interface Closeable {
/**
* Closes the object.
*
* @throws IOException An I/O error occurred.
*/
void close() throws IOException;
/**
* Returns, whether the object is already closed.
*
* @return True, if the object is closed, otherwise false.
* @throws IOException An I/O error occurred.
*/
boolean isClosed() throws IOException;
}

View File

@@ -0,0 +1,100 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.util;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.tomcat.util.http.fileupload.FileItemHeaders;
/**
* Default implementation of the {@link FileItemHeaders} interface.
*
* @since 1.2.1
*/
public class FileItemHeadersImpl implements FileItemHeaders, Serializable {
/**
* Serial version UID, being used, if serialized.
*/
private static final long serialVersionUID = -4455695752627032559L;
/**
* Map of <code>String</code> keys to a <code>List</code> of
* <code>String</code> instances.
*/
private final Map<String,List<String>> headerNameToValueListMap =
new LinkedHashMap<>();
/**
* {@inheritDoc}
*/
@Override
public String getHeader(String name) {
String nameLower = name.toLowerCase(Locale.ENGLISH);
List<String> headerValueList = headerNameToValueListMap.get(nameLower);
if (null == headerValueList) {
return null;
}
return headerValueList.get(0);
}
/**
* {@inheritDoc}
*/
@Override
public Iterator<String> getHeaderNames() {
return headerNameToValueListMap.keySet().iterator();
}
/**
* {@inheritDoc}
*/
@Override
public Iterator<String> getHeaders(String name) {
String nameLower = name.toLowerCase(Locale.ENGLISH);
List<String> headerValueList = headerNameToValueListMap.get(nameLower);
if (null == headerValueList) {
headerValueList = Collections.emptyList();
}
return headerValueList.iterator();
}
/**
* Method to add header values to this instance.
*
* @param name name of this header
* @param value value of this header
*/
public synchronized void addHeader(String name, String value) {
String nameLower = name.toLowerCase(Locale.ENGLISH);
List<String> headerValueList = headerNameToValueListMap.get(nameLower);
if (null == headerValueList) {
headerValueList = new ArrayList<>();
headerNameToValueListMap.put(nameLower, headerValueList);
}
headerValueList.add(value);
}
}

View File

@@ -0,0 +1,166 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.util;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* An input stream, which limits its data size. This stream is
* used, if the content length is unknown.
*/
public abstract class LimitedInputStream extends FilterInputStream implements Closeable {
/**
* The maximum size of an item, in bytes.
*/
private final long sizeMax;
/**
* The current number of bytes.
*/
private long count;
/**
* Whether this stream is already closed.
*/
private boolean closed;
/**
* Creates a new instance.
*
* @param inputStream The input stream, which shall be limited.
* @param pSizeMax The limit; no more than this number of bytes
* shall be returned by the source stream.
*/
public LimitedInputStream(InputStream inputStream, long pSizeMax) {
super(inputStream);
sizeMax = pSizeMax;
}
/**
* Called to indicate, that the input streams limit has
* been exceeded.
*
* @param pSizeMax The input streams limit, in bytes.
* @param pCount The actual number of bytes.
* @throws IOException The called method is expected
* to raise an IOException.
*/
protected abstract void raiseError(long pSizeMax, long pCount)
throws IOException;
/**
* Called to check, whether the input streams
* limit is reached.
*
* @throws IOException The given limit is exceeded.
*/
private void checkLimit() throws IOException {
if (count > sizeMax) {
raiseError(sizeMax, count);
}
}
/**
* Reads the next byte of data from this input stream. The value
* byte is returned as an <code>int</code> in the range
* <code>0</code> to <code>255</code>. If no byte is available
* because the end of the stream has been reached, the value
* <code>-1</code> is returned. This method blocks until input data
* is available, the end of the stream is detected, or an exception
* is thrown.
* <p>
* This method
* simply performs <code>in.read()</code> and returns the result.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* stream is reached.
* @throws IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
@Override
public int read() throws IOException {
int res = super.read();
if (res != -1) {
count++;
checkLimit();
}
return res;
}
/**
* Reads up to <code>len</code> bytes of data from this input stream
* into an array of bytes. If <code>len</code> is not zero, the method
* blocks until some input is available; otherwise, no
* bytes are read and <code>0</code> is returned.
* <p>
* This method simply performs <code>in.read(b, off, len)</code>
* and returns the result.
*
* @param b the buffer into which the data is read.
* @param off The start offset in the destination array
* <code>b</code>.
* @param len the maximum number of bytes read.
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the stream has been reached.
* @throws NullPointerException If <code>b</code> is <code>null</code>.
* @throws IndexOutOfBoundsException If <code>off</code> is negative,
* <code>len</code> is negative, or <code>len</code> is greater than
* <code>b.length - off</code>
* @throws IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
@Override
public int read(byte[] b, int off, int len) throws IOException {
int res = super.read(b, off, len);
if (res > 0) {
count += res;
checkLimit();
}
return res;
}
/**
* Returns, whether this stream is already closed.
*
* @return True, if the stream is closed, otherwise false.
* @throws IOException An I/O error occurred.
*/
@Override
public boolean isClosed() throws IOException {
return closed;
}
/**
* Closes this input stream and releases any system resources
* associated with the stream.
* This
* method simply performs <code>in.close()</code>.
*
* @throws IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
@Override
public void close() throws IOException {
closed = true;
super.close();
}
}

View File

@@ -0,0 +1,193 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.apache.tomcat.util.http.fileupload.InvalidFileNameException;
/**
* Utility class for working with streams.
*/
public final class Streams {
/**
* Private constructor, to prevent instantiation.
* This class has only static methods.
*/
private Streams() {
// Does nothing
}
/**
* Default buffer size for use in
* {@link #copy(InputStream, OutputStream, boolean)}.
*/
public static final int DEFAULT_BUFFER_SIZE = 8192;
/**
* Copies the contents of the given {@link InputStream}
* to the given {@link OutputStream}. Shortcut for
* <pre>
* copy(pInputStream, pOutputStream, new byte[8192]);
* </pre>
*
* @param inputStream The input stream, which is being read.
* It is guaranteed, that {@link InputStream#close()} is called
* on the stream.
* @param outputStream The output stream, to which data should
* be written. May be null, in which case the input streams
* contents are simply discarded.
* @param closeOutputStream True guarantees, that {@link OutputStream#close()}
* is called on the stream. False indicates, that only
* {@link OutputStream#flush()} should be called finally.
*
* @return Number of bytes, which have been copied.
* @throws IOException An I/O error occurred.
*/
public static long copy(InputStream inputStream, OutputStream outputStream, boolean closeOutputStream)
throws IOException {
return copy(inputStream, outputStream, closeOutputStream, new byte[DEFAULT_BUFFER_SIZE]);
}
/**
* Copies the contents of the given {@link InputStream}
* to the given {@link OutputStream}.
*
* @param inputStream The input stream, which is being read.
* It is guaranteed, that {@link InputStream#close()} is called
* on the stream.
* @param outputStream The output stream, to which data should
* be written. May be null, in which case the input streams
* contents are simply discarded.
* @param closeOutputStream True guarantees, that {@link OutputStream#close()}
* is called on the stream. False indicates, that only
* {@link OutputStream#flush()} should be called finally.
* @param buffer Temporary buffer, which is to be used for
* copying data.
* @return Number of bytes, which have been copied.
* @throws IOException An I/O error occurred.
*/
public static long copy(InputStream inputStream,
OutputStream outputStream, boolean closeOutputStream,
byte[] buffer)
throws IOException {
OutputStream out = outputStream;
InputStream in = inputStream;
try {
long total = 0;
for (;;) {
int res = in.read(buffer);
if (res == -1) {
break;
}
if (res > 0) {
total += res;
if (out != null) {
out.write(buffer, 0, res);
}
}
}
if (out != null) {
if (closeOutputStream) {
out.close();
} else {
out.flush();
}
out = null;
}
in.close();
in = null;
return total;
} finally {
IOUtils.closeQuietly(in);
if (closeOutputStream) {
IOUtils.closeQuietly(out);
}
}
}
/**
* This convenience method allows to read a
* {@link org.apache.tomcat.util.http.fileupload.FileItemStream}'s
* content into a string. The platform's default character encoding
* is used for converting bytes into characters.
*
* @param inputStream The input stream to read.
* @see #asString(InputStream, String)
* @return The streams contents, as a string.
* @throws IOException An I/O error occurred.
*/
public static String asString(InputStream inputStream) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
copy(inputStream, baos, true);
return baos.toString();
}
/**
* This convenience method allows to read a
* {@link org.apache.tomcat.util.http.fileupload.FileItemStream}'s
* content into a string, using the given character encoding.
*
* @param inputStream The input stream to read.
* @param encoding The character encoding, typically "UTF-8".
* @see #asString(InputStream)
* @return The streams contents, as a string.
* @throws IOException An I/O error occurred.
*/
public static String asString(InputStream inputStream, String encoding) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
copy(inputStream, baos, true);
return baos.toString(encoding);
}
/**
* Checks, whether the given file name is valid in the sense,
* that it doesn't contain any NUL characters. If the file name
* is valid, it will be returned without any modifications. Otherwise,
* an {@link InvalidFileNameException} is raised.
*
* @param fileName The file name to check
* @return Unmodified file name, if valid.
* @throws InvalidFileNameException The file name was found to be invalid.
*/
public static String checkFileName(String fileName) {
if (fileName != null && fileName.indexOf('\u0000') != -1) {
// pFileName.replace("\u0000", "\\0")
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < fileName.length(); i++) {
char c = fileName.charAt(i);
switch (c) {
case 0:
sb.append("\\0");
break;
default:
sb.append(c);
break;
}
}
throw new InvalidFileNameException(fileName,
"Invalid file name: " + sb);
}
return fileName;
}
}

View File

@@ -0,0 +1,278 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.util.mime;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.apache.tomcat.util.codec.binary.Base64;
/**
* Utility class to decode MIME texts.
*
* @since 1.3
*/
public final class MimeUtility {
/**
* The {@code US-ASCII} charset identifier constant.
*/
private static final String US_ASCII_CHARSET = "US-ASCII";
/**
* The marker to indicate text is encoded with BASE64 algorithm.
*/
private static final String BASE64_ENCODING_MARKER = "B";
/**
* The marker to indicate text is encoded with QuotedPrintable algorithm.
*/
private static final String QUOTEDPRINTABLE_ENCODING_MARKER = "Q";
/**
* If the text contains any encoded tokens, those tokens will be marked with "=?".
*/
private static final String ENCODED_TOKEN_MARKER = "=?";
/**
* If the text contains any encoded tokens, those tokens will terminate with "=?".
*/
private static final String ENCODED_TOKEN_FINISHER = "?=";
/**
* The linear whitespace chars sequence.
*/
private static final String LINEAR_WHITESPACE = " \t\r\n";
/**
* Mappings between MIME and Java charset.
*/
private static final Map<String, String> MIME2JAVA = new HashMap<>();
static {
MIME2JAVA.put("ja_jp.iso2022-7", "ISO2022JP");
MIME2JAVA.put("ja_jp.eucjp", "EUCJIS");
MIME2JAVA.put("x-us-ascii", "ISO-8859-1");
}
/**
* Hidden constructor, this class must not be instantiated.
*/
private MimeUtility() {
// do nothing
}
/**
* Decode a string of text obtained from a mail header into
* its proper form. The text generally will consist of a
* string of tokens, some of which may be encoded using
* base64 encoding.
*
* @param text The text to decode.
*
* @return The decoded text string.
* @throws UnsupportedEncodingException if the detected encoding in the input text is not supported.
*/
public static String decodeText(String text) throws UnsupportedEncodingException {
// if the text contains any encoded tokens, those tokens will be marked with "=?". If the
// source string doesn't contain that sequent, no decoding is required.
if (!text.contains(ENCODED_TOKEN_MARKER)) {
return text;
}
int offset = 0;
int endOffset = text.length();
int startWhiteSpace = -1;
int endWhiteSpace = -1;
StringBuilder decodedText = new StringBuilder(text.length());
boolean previousTokenEncoded = false;
while (offset < endOffset) {
char ch = text.charAt(offset);
// is this a whitespace character?
if (LINEAR_WHITESPACE.indexOf(ch) != -1) { // whitespace found
startWhiteSpace = offset;
while (offset < endOffset) {
// step over the white space characters.
ch = text.charAt(offset);
if (LINEAR_WHITESPACE.indexOf(ch) != -1) { // whitespace found
offset++;
} else {
// record the location of the first non lwsp and drop down to process the
// token characters.
endWhiteSpace = offset;
break;
}
}
} else {
// we have a word token. We need to scan over the word and then try to parse it.
int wordStart = offset;
while (offset < endOffset) {
// step over the non white space characters.
ch = text.charAt(offset);
if (LINEAR_WHITESPACE.indexOf(ch) == -1) { // not white space
offset++;
} else {
break;
}
//NB: Trailing whitespace on these header strings will just be discarded.
}
// pull out the word token.
String word = text.substring(wordStart, offset);
// is the token encoded? decode the word
if (word.startsWith(ENCODED_TOKEN_MARKER)) {
try {
// if this gives a parsing failure, treat it like a non-encoded word.
String decodedWord = decodeWord(word);
// are any whitespace characters significant? Append 'em if we've got 'em.
if (!previousTokenEncoded && startWhiteSpace != -1) {
decodedText.append(text.substring(startWhiteSpace, endWhiteSpace));
startWhiteSpace = -1;
}
// this is definitely a decoded token.
previousTokenEncoded = true;
// and add this to the text.
decodedText.append(decodedWord);
// we continue parsing from here...we allow parsing errors to fall through
// and get handled as normal text.
continue;
} catch (ParseException e) {
// just ignore it, skip to next word
}
}
// this is a normal token, so it doesn't matter what the previous token was. Add the white space
// if we have it.
if (startWhiteSpace != -1) {
decodedText.append(text.substring(startWhiteSpace, endWhiteSpace));
startWhiteSpace = -1;
}
// this is not a decoded token.
previousTokenEncoded = false;
decodedText.append(word);
}
}
return decodedText.toString();
}
/**
* Parse a string using the RFC 2047 rules for an "encoded-word"
* type. This encoding has the syntax:
*
* encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
*
* @param word The possibly encoded word value.
*
* @return The decoded word.
* @throws ParseException
* @throws UnsupportedEncodingException
*/
private static String decodeWord(String word) throws ParseException, UnsupportedEncodingException {
// encoded words start with the characters "=?". If this not an encoded word, we throw a
// ParseException for the caller.
if (!word.startsWith(ENCODED_TOKEN_MARKER)) {
throw new ParseException("Invalid RFC 2047 encoded-word: " + word);
}
int charsetPos = word.indexOf('?', 2);
if (charsetPos == -1) {
throw new ParseException("Missing charset in RFC 2047 encoded-word: " + word);
}
// pull out the character set information (this is the MIME name at this point).
String charset = word.substring(2, charsetPos).toLowerCase(Locale.ENGLISH);
// now pull out the encoding token the same way.
int encodingPos = word.indexOf('?', charsetPos + 1);
if (encodingPos == -1) {
throw new ParseException("Missing encoding in RFC 2047 encoded-word: " + word);
}
String encoding = word.substring(charsetPos + 1, encodingPos);
// and finally the encoded text.
int encodedTextPos = word.indexOf(ENCODED_TOKEN_FINISHER, encodingPos + 1);
if (encodedTextPos == -1) {
throw new ParseException("Missing encoded text in RFC 2047 encoded-word: " + word);
}
String encodedText = word.substring(encodingPos + 1, encodedTextPos);
// seems a bit silly to encode a null string, but easy to deal with.
if (encodedText.length() == 0) {
return "";
}
try {
// the decoder writes directly to an output stream.
ByteArrayOutputStream out = new ByteArrayOutputStream(encodedText.length());
byte[] decodedData;
// Base64 encoded?
if (encoding.equals(BASE64_ENCODING_MARKER)) {
decodedData = Base64.decodeBase64(encodedText);
} else if (encoding.equals(QUOTEDPRINTABLE_ENCODING_MARKER)) { // maybe quoted printable.
byte[] encodedData = encodedText.getBytes(US_ASCII_CHARSET);
QuotedPrintableDecoder.decode(encodedData, out);
decodedData = out.toByteArray();
} else {
throw new UnsupportedEncodingException("Unknown RFC 2047 encoding: " + encoding);
}
// Convert decoded byte data into a string.
return new String(decodedData, javaCharset(charset));
} catch (IOException e) {
throw new UnsupportedEncodingException("Invalid RFC 2047 encoding");
}
}
/**
* Translate a MIME standard character set name into the Java
* equivalent.
*
* @param charset The MIME standard name.
*
* @return The Java equivalent for this name.
*/
private static String javaCharset(String charset) {
// nothing in, nothing out.
if (charset == null) {
return null;
}
String mappedCharset = MIME2JAVA.get(charset.toLowerCase(Locale.ENGLISH));
// if there is no mapping, then the original name is used. Many of the MIME character set
// names map directly back into Java. The reverse isn't necessarily true.
if (mappedCharset == null) {
return charset;
}
return mappedCharset;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.util.mime;
/**
* @since 1.3
*/
final class ParseException extends Exception {
/**
* The UID to use when serializing this instance.
*/
private static final long serialVersionUID = 5355281266579392077L;
/**
* Constructs a new exception with the specified detail message.
*
* @param message the detail message.
*/
public ParseException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,113 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.util.http.fileupload.util.mime;
import java.io.IOException;
import java.io.OutputStream;
/**
* @since 1.3
*/
final class QuotedPrintableDecoder {
/**
* The shift value required to create the upper nibble
* from the first of 2 byte values converted from ascii hex.
*/
private static final int UPPER_NIBBLE_SHIFT = Byte.SIZE / 2;
/**
* Hidden constructor, this class must not be instantiated.
*/
private QuotedPrintableDecoder() {
// do nothing
}
/**
* Decode the encoded byte data writing it to the given output stream.
*
* @param data The array of byte data to decode.
* @param out The output stream used to return the decoded data.
*
* @return the number of bytes produced.
* @throws IOException if a problem occurs during either decoding or
* writing to the stream
*/
public static int decode(byte[] data, OutputStream out) throws IOException {
int off = 0;
int length = data.length;
int endOffset = off + length;
int bytesWritten = 0;
while (off < endOffset) {
byte ch = data[off++];
// space characters were translated to '_' on encode, so we need to translate them back.
if (ch == '_') {
out.write(' ');
} else if (ch == '=') {
// we found an encoded character. Reduce the 3 char sequence to one.
// but first, make sure we have two characters to work with.
if (off + 1 >= endOffset) {
throw new IOException("Invalid quoted printable encoding; truncated escape sequence");
}
byte b1 = data[off++];
byte b2 = data[off++];
// we've found an encoded carriage return. The next char needs to be a newline
if (b1 == '\r') {
if (b2 != '\n') {
throw new IOException("Invalid quoted printable encoding; CR must be followed by LF");
}
// this was a soft linebreak inserted by the encoding. We just toss this away
// on decode.
} else {
// this is a hex pair we need to convert back to a single byte.
int c1 = hexToBinary(b1);
int c2 = hexToBinary(b2);
out.write((c1 << UPPER_NIBBLE_SHIFT) | c2);
// 3 bytes in, one byte out
bytesWritten++;
}
} else {
// simple character, just write it out.
out.write(ch);
bytesWritten++;
}
}
return bytesWritten;
}
/**
* Convert a hex digit to the binary value it represents.
*
* @param b the ascii hex byte to convert (0-0, A-F, a-f)
* @return the int value of the hex byte, 0-15
* @throws IOException if the byte is not a valid hex digit.
*/
private static int hexToBinary(final byte b) throws IOException {
// CHECKSTYLE IGNORE MagicNumber FOR NEXT 1 LINE
final int i = Character.digit((char) b, 16);
if (i == -1) {
throw new IOException("Invalid quoted printable encoding: not a valid hex digit: " + b);
}
return i;
}
}

View File

@@ -0,0 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* MIME decoder implementation, imported and retailed from
* <a href="http://svn.apache.org/repos/asf/geronimo/specs/tags/geronimo-javamail_1.4_spec-1.4/">Apache Geronimo</a>.
*/
package org.apache.tomcat.util.http.fileupload.util.mime;

View File

@@ -0,0 +1,23 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This package contains various IO related utility classes
* or methods, which are basically reusable and not necessarily
* restricted to the scope of a file upload.
*/
package org.apache.tomcat.util.http.fileupload.util;