init
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
204
java/org/apache/tomcat/util/http/fileupload/FileItem.java
Normal file
204
java/org/apache/tomcat/util/http/fileupload/FileItem.java
Normal 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;
|
||||
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
102
java/org/apache/tomcat/util/http/fileupload/FileItemStream.java
Normal file
102
java/org/apache/tomcat/util/http/fileupload/FileItemStream.java
Normal 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();
|
||||
|
||||
}
|
||||
92
java/org/apache/tomcat/util/http/fileupload/FileUpload.java
Normal file
92
java/org/apache/tomcat/util/http/fileupload/FileUpload.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
568
java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
Normal file
568
java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
309
java/org/apache/tomcat/util/http/fileupload/FileUtils.java
Normal file
309
java/org/apache/tomcat/util/http/fileupload/FileUtils.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
249
java/org/apache/tomcat/util/http/fileupload/IOUtils.java
Normal file
249
java/org/apache/tomcat/util/http/fileupload/IOUtils.java
Normal 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("foo.txt");
|
||||
* // 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 > 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 >= 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 >= 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
1011
java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
Normal file
1011
java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
Normal file
File diff suppressed because it is too large
Load Diff
339
java/org/apache/tomcat/util/http/fileupload/ParameterParser.java
Normal file
339
java/org/apache/tomcat/util/http/fileupload/ParameterParser.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 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;
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
193
java/org/apache/tomcat/util/http/fileupload/util/Streams.java
Normal file
193
java/org/apache/tomcat/util/http/fileupload/util/Streams.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user