* This class can be used to process data streams conforming to MIME 'multipart' format as defined in RFC
* 1867. Arbitrarily large amounts of data in the stream can be processed under constant memory usage.
*
*
* Note that body-data can contain another mulipart entity. There is limited support for single pass processing of such nested streams. The nested stream is
* required to have a boundary token of the same length as the parent stream (see {@link #setBoundary(byte[])}).
*
* {
/**
* Boundary.
*/
private byte[] boundary;
/**
* Progress notifier.
*/
private ProgressNotifier progressNotifier;
/**
* The per part size limit for headers.
*/
private int maxPartHeaderSize = DEFAULT_PART_HEADER_SIZE_MAX;
/**
* Constructs a new instance.
*/
public Builder() {
setBufferSizeDefault(DEFAULT_BUFSIZE);
}
/**
* Constructs a new instance.
*
* This builder uses the InputStream, buffer size, boundary and progress notifier aspects.
*
*
* You must provide an origin that can be converted to a Reader by this builder, otherwise, this call will throw an
* {@link UnsupportedOperationException}.
*
*
* @return a new instance.
* @throws IOException if an I/O error occurs.
* @throws UnsupportedOperationException if the origin cannot provide a Path.
* @see AbstractOrigin#getReader(Charset)
*/
@Override
public MultipartInput get() throws IOException {
return new MultipartInput(this);
}
/**
* Gets the per part size limit for headers.
*
* @return The maximum size of the headers in bytes.
* @since 2.0.0-M5
*/
public int getMaxPartHeaderSize() {
return maxPartHeaderSize;
}
/**
* Sets the boundary.
*
* @param boundary the boundary.
* @return {@code this} instance.
*/
public Builder setBoundary(final byte[] boundary) {
this.boundary = boundary;
return this;
}
/**
* Sets the per part size limit for headers.
* @param partHeaderSizeMax The maximum size of the headers in bytes.
* @return This builder.
* @since 2.0.0-M5
*/
public Builder setMaxPartHeaderSize(final int partHeaderSizeMax) {
this.maxPartHeaderSize = partHeaderSizeMax;
return this;
}
/**
* Sets the progress notifier.
*
* @param progressNotifier progress notifier.
* @return {@code this} instance.
*/
public Builder setProgressNotifier(final ProgressNotifier progressNotifier) {
this.progressNotifier = progressNotifier;
return this;
}
}
/**
* Signals an attempt to set an invalid boundary token.
*/
public static class FileUploadBoundaryException extends FileUploadException {
/**
* The UID to use when serializing this instance.
*/
private static final long serialVersionUID = 2;
/**
* Constructs an instance with the specified detail message.
*
* @param message The detail message (which is saved for later retrieval by the {@link #getMessage()} method)
*/
public FileUploadBoundaryException(final String message) {
super(message);
}
}
/**
* An {@link InputStream} for reading an items contents.
*/
public class ItemInputStream extends InputStream {
/**
* Offset when converting negative bytes to integers.
*/
private static final int BYTE_POSITIVE_OFFSET = 256;
/**
* The number of bytes, which have been read so far.
*/
private long total;
/**
* The number of bytes, which must be hold, because they might be a part of the boundary.
*/
private int pad;
/**
* The current offset in the buffer.
*/
private int pos;
/**
* Whether the stream is already closed.
*/
private boolean closed;
/**
* Creates a new instance.
*/
ItemInputStream() {
findSeparator();
}
/**
* Returns the number of bytes, which are currently available, without blocking.
*
* @throws IOException An I/O error occurs.
* @return Number of bytes in the buffer.
*/
@Override
public int available() throws IOException {
if (pos == -1) {
return tail - head - pad;
}
return pos - head;
}
private void checkOpen() throws ItemSkippedException {
if (closed) {
throw new FileItemInput.ItemSkippedException("checkOpen()");
}
}
/**
* Closes the input stream.
*
* @throws IOException An I/O error occurred.
*/
@Override
public void close() throws IOException {
close(false);
}
/**
* Closes the input stream.
*
* @param closeUnderlying Whether to close the underlying stream (hard close)
* @throws IOException An I/O error occurred.
*/
public void close(final boolean closeUnderlying) throws IOException {
if (closed) {
return;
}
if (closeUnderlying) {
closed = true;
input.close();
} else {
for (;;) {
var avail = available();
if (avail == 0) {
avail = makeAvailable();
if (avail == 0) {
break;
}
}
if (skip(avail) != avail) {
// TODO What to do?
}
}
}
closed = true;
}
/**
* Called for finding the separator.
*/
private void findSeparator() {
pos = MultipartInput.this.findSeparator();
if (pos == -1) {
if (tail - head > keepRegion) {
pad = keepRegion;
} else {
pad = tail - head;
}
}
}
/**
* Gets the number of bytes, which have been read by the stream.
*
* @return Number of bytes, which have been read so far.
*/
public long getBytesRead() {
return total;
}
/**
* Tests whether this instance is closed.
*
* @return whether this instance is closed.
*/
public boolean isClosed() {
return closed;
}
/**
* Attempts to read more data.
*
* @return Number of available bytes
* @throws IOException An I/O error occurred.
*/
private int makeAvailable() throws IOException {
if (pos != -1) {
return 0;
}
// Move the data to the beginning of the buffer.
total += tail - head - pad;
System.arraycopy(buffer, tail - pad, buffer, 0, pad);
// Refill buffer with new data.
head = 0;
tail = pad;
for (;;) {
final var bytesRead = input.read(buffer, tail, bufSize - tail);
if (bytesRead == -1) {
// The last pad amount is left in the buffer.
// Boundary can't be in there so signal an error
// condition.
final var msg = "Stream ended unexpectedly";
throw new MalformedStreamException(msg);
}
if (notifier != null) {
notifier.noteBytesRead(bytesRead);
}
tail += bytesRead;
findSeparator();
final var av = available();
if (av > 0 || pos != -1) {
return av;
}
}
}
/**
* Reads the next byte in the stream.
*
* @return The next byte in the stream, as a non-negative integer, or -1 for EOF.
* @throws IOException An I/O error occurred.
*/
@Override
public int read() throws IOException {
checkOpen();
if (available() == 0 && makeAvailable() == 0) {
return -1;
}
++total;
final int b = buffer[head++];
if (b >= 0) {
return b;
}
return b + BYTE_POSITIVE_OFFSET;
}
/**
* Reads bytes into the given buffer.
*
* @param b The destination buffer, where to write to.
* @param off Offset of the first byte in the buffer.
* @param len Maximum number of bytes to read.
* @return Number of bytes, which have been actually read, or -1 for EOF.
* @throws IOException An I/O error occurred.
*/
@Override
public int read(final byte[] b, final int off, final int len) throws IOException {
checkOpen();
if (len == 0) {
return 0;
}
var res = available();
if (res == 0) {
res = makeAvailable();
if (res == 0) {
return -1;
}
}
res = Math.min(res, len);
System.arraycopy(buffer, head, b, off, res);
head += res;
total += res;
return res;
}
/**
* Skips the given number of bytes.
*
* @param bytes Number of bytes to skip.
* @return The number of bytes, which have actually been skipped.
* @throws IOException An I/O error occurred.
*/
@Override
public long skip(final long bytes) throws IOException {
checkOpen();
var available = available();
if (available == 0) {
available = makeAvailable();
if (available == 0) {
return 0;
}
}
// Fix "Implicit narrowing conversion in compound assignment"
// https://github.com/apache/commons-fileupload/security/code-scanning/118
// Math.min always returns an int because available is an int.
final var res = Math.toIntExact(Math.min(available, bytes));
head += res;
return res;
}
}
/**
* Signals that the input stream fails to follow the required syntax.
*/
public static class MalformedStreamException extends FileUploadException {
/**
* The UID to use when serializing this instance.
*/
private static final long serialVersionUID = 2;
/**
* Constructs an {@code MalformedStreamException} with the specified detail message.
*
* @param message The detail message.
*/
public MalformedStreamException(final String message) {
super(message);
}
/**
* Constructs an {@code MalformedStreamException} with the specified detail message.
*
* @param message The detail message.
* @param cause The cause (which is saved for later retrieval by the {@link #getCause()} method). (A null value is permitted, and indicates that the
* cause is nonexistent or unknown.)
*/
public MalformedStreamException(final String message, final Throwable cause) {
super(message, cause);
}
}
/**
* Internal class, which is used to invoke the {@link ProgressListener}.
*/
public static class ProgressNotifier {
/**
* The listener to invoke.
*/
private final ProgressListener progressListener;
/**
* Number of expected bytes, if known, or -1.
*/
private final long contentLength;
/**
* Number of bytes, which have been read so far.
*/
private long bytesRead;
/**
* Number of items, which have been read so far.
*/
private int items;
/**
* Creates a new instance with the given listener and content length.
*
* @param progressListener The listener to invoke.
* @param contentLength The expected content length.
*/
public ProgressNotifier(final ProgressListener progressListener, final long contentLength) {
this.progressListener = progressListener != null ? progressListener : ProgressListener.NOP;
this.contentLength = contentLength;
}
/**
* Called to indicate that bytes have been read.
*
* @param byteCount Number of bytes, which have been read.
*/
void noteBytesRead(final int byteCount) {
//
// Indicates, that the given number of bytes have been read from the input stream.
//
bytesRead += byteCount;
notifyListener();
}
/**
* Called to indicate, that a new file item has been detected.
*/
public void noteItem() {
++items;
notifyListener();
}
/**
* Called for notifying the listener.
*/
private void notifyListener() {
progressListener.update(bytesRead, contentLength, items);
}
}
/**
* The Carriage Return ASCII character value.
*/
public static final byte CR = 0x0D;
/**
* The Line Feed ASCII character value.
*/
public static final byte LF = 0x0A;
/**
* The dash (-) ASCII character value.
*/
public static final byte DASH = 0x2D;
/**
* The default length of the buffer used for processing a request.
*/
static final int DEFAULT_BUFSIZE = 4096;
/**
* Default per part header size limit in bytes.
* @since 2.0.0-M4
*/
public static final int DEFAULT_PART_HEADER_SIZE_MAX = 512;
/**
* A byte sequence that marks the end of {@code header-part} ({@code CRLFCRLF}).
*/
static final byte[] HEADER_SEPARATOR = { CR, LF, CR, LF };
/**
* A byte sequence that that follows a delimiter that will be followed by an encapsulation ({@code CRLF}).
*/
static final byte[] FIELD_SEPARATOR = { CR, LF };
/**
* A byte sequence that that follows a delimiter of the last encapsulation in the stream ({@code --}).
*/
static final byte[] STREAM_TERMINATOR = { DASH, DASH };
/**
* A byte sequence that precedes a boundary ({@code CRLF--}).
*/
static final byte[] BOUNDARY_PREFIX = { CR, LF, DASH, DASH };
/**
* Compares {@code count} first bytes in the arrays {@code a} and {@code b}.
*
* @param a The first array to compare.
* @param b The second array to compare.
* @param count How many bytes should be compared.
* @return {@code true} if {@code count} first bytes in arrays {@code a} and {@code b} are equal.
*/
static boolean arrayEquals(final byte[] a, final byte[] b, final int count) {
for (var i = 0; i < count; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
/**
* Constructs a new {@link Builder}.
*
* @return a new {@link Builder}.
*/
public static Builder builder() {
return new Builder();
}
/**
* The input stream from which data is read.
*/
private final InputStream input;
/**
* The length of the boundary token plus the leading {@code CRLF--}.
*/
private int boundaryLength;
/**
* The amount of data, in bytes, that must be kept in the buffer in order to detect delimiters reliably.
*/
private final int keepRegion;
/**
* The byte sequence that partitions the stream.
*/
private final byte[] boundary;
/**
* The table for Knuth-Morris-Pratt search algorithm.
*/
private final int[] boundaryTable;
/**
* The length of the buffer used for processing the request.
*/
private final int bufSize;
/**
* The buffer used for processing the request.
*/
private final byte[] buffer;
/**
* The index of first valid character in the buffer.
* 0 <= head < bufSize
*/
private int head;
/**
* The index of last valid character in the buffer + 1.
* 0 <= tail <= bufSize
*/
private int tail;
/**
* The content encoding to use when reading headers.
*/
private Charset headerCharset;
/**
* The progress notifier, if any, or null.
*/
private final ProgressNotifier notifier;
/**
* The maximum size of the headers in bytes.
*/
private final int maxPartHeaderSize;
/**
* Constructs a {@code MultipartInput} with a custom size buffer.
*
* Note that the buffer must be at least big enough to contain the boundary string, plus 4 characters for CR/LF and double dash, plus at least one byte of
* data. Too small a buffer size setting will degrade performance.
*
*
* @param input The {@code InputStream} to serve as a data source.
* @param boundary The token used for dividing the stream into {@code encapsulations}.
* @param bufferSize The size of the buffer to be used, in bytes.
* @param notifier The notifier, which is used for calling the progress listener, if any.
* @throws IOException Thrown if an I/O error occurs.
* @throws IllegalArgumentException If the buffer size is too small.
*/
private MultipartInput(final Builder builder) throws IOException {
if (builder.boundary == null) {
throw new IllegalArgumentException("boundary may not be null");
}
// We prepend CR/LF to the boundary to chop trailing CR/LF from
// body-data tokens.
this.boundaryLength = builder.boundary.length + BOUNDARY_PREFIX.length;
if (builder.getBufferSize() < this.boundaryLength + 1) {
throw new IllegalArgumentException("The buffer size specified for the MultipartInput is too small");
}
this.input = builder.getInputStream();
this.bufSize = Math.max(builder.getBufferSize(), boundaryLength * 2);
this.buffer = new byte[this.bufSize];
this.notifier = builder.progressNotifier;
this.maxPartHeaderSize = builder.getMaxPartHeaderSize();
this.boundary = new byte[this.boundaryLength];
this.boundaryTable = new int[this.boundaryLength + 1];
this.keepRegion = this.boundary.length;
System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0, BOUNDARY_PREFIX.length);
System.arraycopy(builder.boundary, 0, this.boundary, BOUNDARY_PREFIX.length, builder.boundary.length);
computeBoundaryTable();
head = 0;
tail = 0;
}
/**
* Computes the table used for Knuth-Morris-Pratt search algorithm.
*/
private void computeBoundaryTable() {
var position = 2;
var candidate = 0;
boundaryTable[0] = -1;
boundaryTable[1] = 0;
while (position <= boundaryLength) {
if (boundary[position - 1] == boundary[candidate]) {
boundaryTable[position] = candidate + 1;
candidate++;
position++;
} else if (candidate > 0) {
candidate = boundaryTable[candidate];
} else {
boundaryTable[position] = 0;
position++;
}
}
}
/**
* Reads {@code body-data} from the current {@code encapsulation} and discards it.
*
* Use this method to skip encapsulations you don't need or don't understand.
*
*
* @return The amount of data discarded.
* @throws MalformedStreamException if the stream ends unexpectedly.
* @throws IOException if an i/o error occurs.
*/
public long discardBodyData() throws MalformedStreamException, IOException {
return readBodyData(NullOutputStream.INSTANCE);
}
/**
* Searches for a byte of specified value in the {@code buffer}, starting at the specified {@code position}.
*
* @param value The value to find.
* @param pos The starting position for searching.
* @return The position of byte found, counting from beginning of the {@code buffer}, or {@code -1} if not found.
*/
protected int findByte(final byte value, final int pos) {
for (var i = pos; i < tail; i++) {
if (buffer[i] == value) {
return i;
}
}
return -1;
}
/**
* Searches for the {@code boundary} in the {@code buffer} region delimited by {@code head} and {@code tail}.
*
* @return The position of the boundary found, counting from the beginning of the {@code buffer}, or {@code -1} if not found.
*/
protected int findSeparator() {
var bufferPos = this.head;
var tablePos = 0;
while (bufferPos < this.tail) {
while (tablePos >= 0 && buffer[bufferPos] != boundary[tablePos]) {
tablePos = boundaryTable[tablePos];
}
bufferPos++;
tablePos++;
if (tablePos == boundaryLength) {
return bufferPos - boundaryLength;
}
}
return -1;
}
/**
* Gets the character encoding used when reading the headers of an individual part. When not specified, or {@code null}, the platform default encoding is
* used.
*
* @return The encoding used to read part headers.
*/
public Charset getHeaderCharset() {
return headerCharset;
}
/**
* Returns the per part size limit for headers.
*
* @return The maximum size of the headers in bytes.
* @since 2.0.0-M5
*/
public int getMaxPartHeaderSize() {
return maxPartHeaderSize;
}
/**
* Creates a new {@link ItemInputStream}.
*
* @return A new instance of {@link ItemInputStream}.
*/
public ItemInputStream newInputStream() {
return new ItemInputStream();
}
/**
* Reads {@code body-data} from the current {@code encapsulation} and writes its contents into the output {@code Stream}.
*
* Arbitrary large amounts of data can be processed by this method using a constant size buffer. (see {@link MultipartInput#builder()}).
*
*
* @param output The {@code Stream} to write data into. May be null, in which case this method is equivalent to {@link #discardBodyData()}.
* @return the amount of data written.
* @throws MalformedStreamException if the stream ends unexpectedly.
* @throws IOException if an i/o error occurs.
*/
public long readBodyData(final OutputStream output) throws MalformedStreamException, IOException {
try (var inputStream = newInputStream()) {
return IOUtils.copyLarge(inputStream, output);
}
}
/**
* Skips a {@code boundary} token, and checks whether more {@code encapsulations} are contained in the stream.
*
* @return {@code true} if there are more encapsulations in this stream; {@code false} otherwise.
* @throws FileUploadSizeException if the bytes read from the stream exceeded the size limits
* @throws MalformedStreamException if the stream ends unexpectedly or fails to follow required syntax.
*/
public boolean readBoundary() throws FileUploadSizeException, MalformedStreamException {
final var marker = new byte[2];
final boolean nextChunk;
head += boundaryLength;
try {
marker[0] = readByte();
if (marker[0] == LF) {
// Work around IE5 Mac bug with input type=image.
// Because the boundary delimiter, not including the trailing
// CRLF, must not appear within any file (RFC 2046, section
// 5.1.1), we know the missing CR is due to a buggy browser
// rather than a file containing something similar to a
// boundary.
return true;
}
marker[1] = readByte();
if (arrayEquals(marker, STREAM_TERMINATOR, 2)) {
nextChunk = false;
} else if (arrayEquals(marker, FIELD_SEPARATOR, 2)) {
nextChunk = true;
} else {
throw new MalformedStreamException("Unexpected characters follow a boundary");
}
} catch (final FileUploadSizeException e) {
throw e;
} catch (final IOException e) {
throw new MalformedStreamException("Stream ended unexpectedly", e);
}
return nextChunk;
}
/**
* Reads a byte from the {@code buffer}, and refills it as necessary.
*
* @return The next byte from the input stream.
* @throws IOException if there is no more data available.
*/
public byte readByte() throws IOException {
// Buffer depleted ?
if (head == tail) {
head = 0;
// Refill.
tail = input.read(buffer, head, bufSize);
if (tail == -1) {
// No more data available.
throw new IOException("No more data is available");
}
if (notifier != null) {
notifier.noteBytesRead(tail);
}
}
return buffer[head++];
}
/**
* Reads the {@code header-part} of the current {@code encapsulation}.
*
* Headers are returned verbatim to the input stream, including the trailing {@code CRLF} marker. Parsing is left to the application.
*
*
* TODO allow limiting maximum header size to protect against abuse.
*
*
* @return The {@code header-part} of the current encapsulation.
* @throws FileUploadSizeException if the bytes read from the stream exceeded the size limits.
* @throws MalformedStreamException if the stream ends unexpectedly.
*/
public String readHeaders() throws FileUploadSizeException, MalformedStreamException {
var i = 0;
byte b;
// to support multi-byte characters
final var baos = new ByteArrayOutputStream();
var size = 0;
while (i < HEADER_SEPARATOR.length) {
try {
b = readByte();
} catch (final FileUploadSizeException e) {
// wraps a FileUploadSizeException, re-throw as it will be unwrapped later
throw e;
} catch (final IOException e) {
throw new MalformedStreamException("Stream ended unexpectedly", e);
}
final int phsm = getMaxPartHeaderSize();
if (phsm != -1 && ++size > phsm) {
throw new FileUploadSizeException(
String.format("Header section has more than %s bytes (maybe it is not properly terminated)", Integer.valueOf(phsm)), phsm, size);
}
if (b == HEADER_SEPARATOR[i]) {
i++;
} else {
i = 0;
}
baos.write(b);
}
try {
return baos.toString(Charsets.toCharset(headerCharset, Charset.defaultCharset()).name());
} catch (final UnsupportedEncodingException e) {
// not possible
throw new IllegalStateException(e);
}
}
/**
* Changes the boundary token used for partitioning the stream.
*
* This method allows single pass processing of nested multipart streams.
*
*
* The boundary token of the nested stream is {@code required} to be of the same length as the boundary token in parent stream.
*
*
* Restoring the parent stream boundary token after processing of a nested stream is left to the application.
*
*
* @param boundary The boundary to be used for parsing of the nested stream.
* @throws FileUploadBoundaryException if the {@code boundary} has a different length than the one being currently parsed.
*/
public void setBoundary(final byte[] boundary) throws FileUploadBoundaryException {
if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) {
throw new FileUploadBoundaryException("The length of a boundary token cannot be changed");
}
System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, boundary.length);
computeBoundaryTable();
}
/**
* Sets the character encoding to be used when reading the headers of individual parts. When not specified, or {@code null}, the platform default encoding
* is used.
*
* @param headerCharset The encoding used to read part headers.
*/
public void setHeaderCharset(final Charset headerCharset) {
this.headerCharset = headerCharset;
}
/**
* Finds the beginning of the first {@code encapsulation}.
*
* @return {@code true} if an {@code encapsulation} was found in the stream.
* @throws IOException if an i/o error occurs.
*/
public boolean skipPreamble() throws IOException {
// First delimiter may be not preceded with a CRLF.
System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2);
boundaryLength = boundary.length - 2;
computeBoundaryTable();
try {
// Discard all data up to the delimiter.
discardBodyData();
// Read boundary - if succeeded, the stream contains an
// encapsulation.
return readBoundary();
} catch (final MalformedStreamException e) {
return false;
} finally {
// Restore delimiter.
System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2);
boundaryLength = boundary.length;
boundary[0] = CR;
boundary[1] = LF;
computeBoundaryTable();
}
}
}
././@LongLink 0000644 0000000 0000000 00000000202 00000000000 011575 L ustar root root libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/DiskFileItem.java libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileu0000644 0001750 0001750 00000064367 15142145735 032102 0 ustar zigo zigo /*
* 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
*
* https://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.commons.fileupload2.core;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import org.apache.commons.fileupload2.core.DeferrableOutputStream.Listener;
import org.apache.commons.fileupload2.core.DeferrableOutputStream.State;
import org.apache.commons.fileupload2.core.FileItemFactory.AbstractFileItemBuilder;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.FileCleaningTracker;
import org.apache.commons.io.build.AbstractOrigin;
import org.apache.commons.io.file.PathUtils;
/**
* The default implementation of the {@link FileItem FileItem} interface.
*
* After retrieving an instance of this class from a {@link DiskFileItemFactory} instance (see
* {@code org.apache.commons.fileupload2.core.servlet.ServletFileUpload
* #parseRequest(javax.servlet.http.HttpServletRequest)}), 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.
*
* State model: Instances of {@link DiskFileItem} are subject to a carefully designed state model.
* Depending on the so-called {@link #getThreshold() threshold}, either of the three models are possible:
*
* - threshold = -1
* Uploaded data is never kept in memory. Instead, a temporary file is being created immediately.
*
* {@link #isInMemory()} will always return false, {@link #getPath()} will always return the path
* of an existing file. The temporary file may be empty.
* - threshold = 0
* Uploaded data is never kept in memory. (Same as threshold=-1.) However, the temporary file is
* only created, if data was uploaded. Or, in other words: The uploaded file will never be
* empty.
*
* {@link #isInMemory()} will return true, if no data was uploaded, otherwise it will be false.
* In the former case {@link #getPath()} will return null, but in the latter case it returns
* the path of an existing, non-empty file.
* - threshold > 0
* Uploaded data will be kept in memory, if the size is below the threshold. If the size
* is equal to, or above the threshold, then a temporary file has been created, and all
* uploaded data has been transferred to that file.
*
* {@link #isInMemory()} returns true, if the size of the uploaded data is below the threshold.
* If so, {@link #getPath()} returns null. Otherwise, {@link #isInMemory()} returns false,
* and {@link #getPath()} returns the path of an existing, temporary file. The size
* of the temporary file is equal to, or above the threshold.
*
*
* Temporary files, which are created for file items, should be deleted later on. The best way to do this is using a
* {@link org.apache.commons.io.FileCleaningTracker}, which you can set on the {@link DiskFileItemFactory}. However, if you do use such a tracker, then you must
* consider the following: Temporary files are automatically deleted as soon as they are no longer needed. (More precisely, when the corresponding instance of
* {@link java.io.File} is garbage collected.) This is done by the so-called reaper thread, which is started and stopped automatically by the
* {@link org.apache.commons.io.FileCleaningTracker} when there are files to be tracked. It might make sense to terminate that thread, for example, if your web
* application ends. See the section on "Resource cleanup" in the users guide of Commons FileUpload.
*/
public final class DiskFileItem implements FileItem {
/**
* Builds a new {@link DiskFileItem} instance.
*
* For example:
*
*
* {@code
* final FileItem fileItem = fileItemFactory.fileItemBuilder()
* .setFieldName("FieldName")
* .setContentType("ContentType")
* .setFormField(true)
* .setFileName("FileName")
* .setFileItemHeaders(...)
* .get();
* }
*
*/
public static class Builder extends AbstractFileItemBuilder {
/**
* The threshold. We do maintain this separate from the {@link #getBufferSize()},
* because the parent class might change the value in {@link #setBufferSize(int)}.
*/
private int threshold;
/**
* Constructs a new instance.
*/
public Builder() {
setBufferSize(DiskFileItemFactory.DEFAULT_THRESHOLD);
setPath(PathUtils.getTempDirectory());
setCharset(DEFAULT_CHARSET);
setCharsetDefault(DEFAULT_CHARSET);
}
/**
* Constructs a new instance.
*
* You must provide an origin that can be converted to a Reader by this builder, otherwise, this call will throw an
* {@link UnsupportedOperationException}.
*
*
* @return a new instance.
* @throws UnsupportedOperationException if the origin cannot provide a Path.
* @see AbstractOrigin#getReader(Charset)
*/
@Override
public DiskFileItem get() {
final var diskFileItem = new DiskFileItem(this);
final var tracker = getFileCleaningTracker();
if (tracker != null) {
diskFileItem.setFileCleaningTracker(tracker);
}
return diskFileItem;
}
/**
* Equivalent to {@link #getThreshold()}.
* @return The threshold, which is being used.
* @see #getThreshold()
* @deprecated Since 2.0.0, use {@link #getThreshold()} instead.
*/
public int getBufferSize() {
return getThreshold();
}
/**
* Returns the threshold.
* @return The threshold.
*/
public int getThreshold() {
return threshold;
}
/**
* Equivalent to {@link #setThreshold(int)}.
* @param bufferSize The threshold, which is being used.
* @see #setThreshold(int)
* @return This builder.
* @deprecated Since 2.0.0, use {@link #setThreshold(int)} instead.
*/
@Override
public Builder setBufferSize(final int bufferSize) {
return setThreshold(bufferSize);
}
/**
* Sets the threshold. The uploaded data is typically kept in memory, until
* a certain number of bytes (the threshold) is reached. At this point, the
* incoming data is transferred to a temporary file, and the in-memory data
* is removed.
* @param threshold The threshold, which is being used.
* @return This builder.
*/
public Builder setThreshold(final int threshold) {
this.threshold = threshold;
return this;
}
}
/**
* 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 Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;
/**
* 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();
/**
* Constructs a new {@link Builder}.
*
* @return a new {@link Builder}.
*/
public static Builder builder() {
return new Builder();
}
/**
* Tests if the file name is valid. For example, if it contains a NUL characters, it's invalid. If the file name is valid, it will be returned without any
* modifications. Otherwise, throw an {@link InvalidPathException}.
*
* @param fileName The file name to check
* @return Unmodified file name, if valid.
* @throws InvalidPathException The file name is invalid.
*/
public static String checkFileName(final String fileName) {
if (fileName != null) {
// Specific NUL check to build a better exception message.
final var indexOf0 = fileName.indexOf(0);
if (indexOf0 != -1) {
final var sb = new StringBuilder();
for (var i = 0; i < fileName.length(); i++) {
final var c = fileName.charAt(i);
if (c == 0) {
sb.append("\\0");
} else {
sb.append(c);
}
}
throw new InvalidPathException(fileName, sb.toString(), indexOf0);
}
// Throws InvalidPathException on invalid file names
Paths.get(fileName);
}
return fileName;
}
/**
* Gets 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 var limit = 100_000_000;
final var current = COUNTER.getAndIncrement();
var 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;
}
/**
* The name of the form field as provided by the browser.
*/
private String fieldName;
/**
* The content type passed by the browser, or {@code null} if not defined.
*/
private final String contentType;
/**
* Whether or not this item is a simple form field.
*/
private volatile boolean isFormField;
/**
* The original file name in the user's file system.
*/
private final String fileName;
/**
* The threshold above which uploads will be stored on disk.
*/
private final int threshold;
/**
* The directory in which uploaded files will be stored, if stored on disk, never null.
*/
private final Path repository;
/**
* Output stream for this item.
*/
private DeferrableOutputStream dos;
/**
* The file items headers.
*/
private FileItemHeaders fileItemHeaders;
/**
* Default content Charset to be used when no explicit Charset parameter is provided by the sender.
*/
private Charset charsetDefault = DEFAULT_CHARSET;
/**
* The {@link FileCleaningTracker}, which is being used to remove
* temporary files.
*/
private FileCleaningTracker fileCleaningTracker;
/**
* Constructs a new {@code DiskFileItem} instance.
*
* @param builder The DiskFileItem builder.
*/
private DiskFileItem(final Builder builder) {
this.fieldName = builder.getFieldName();
this.contentType = builder.getContentType();
this.charsetDefault = builder.getCharset();
this.isFormField = builder.isFormField();
this.fileName = builder.getFileName();
this.fileItemHeaders = builder.getFileItemHeaders();
this.threshold = builder.getThreshold();
this.repository = builder.getPath() != null ? builder.getPath() : PathUtils.getTempDirectory();
}
/**
* Deletes the underlying storage for a file item, including deleting any associated temporary disk file. This method can be used to ensure that this is
* done at an earlier time, thus preserving system resources.
*
* @throws IOException if an error occurs.
*/
@Override
public DiskFileItem delete() throws IOException {
if (dos != null) {
final Path path = dos.getPath();
if (path != null) {
Files.deleteIfExists(path);
}
}
return this;
}
/**
* Gets 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.
* @throws IOException if an I/O error occurs.
* @throws OutOfMemoryError See {@link Files#readAllBytes(Path)}: If an array of the required size cannot be allocated, for example the file is larger
* than {@code 2GB}. If so, you should use {@link #getInputStream()}.
* @see #getInputStream()
* @deprecated Since 2.0.0, use {@link #getInputStream()}, or {@link #getReader()}, instead.
*/
@Override
public byte[] get() throws IOException {
if (dos != null) {
final byte[] bytes = dos.getBytes();
if (bytes != null) {
return bytes;
}
final Path path = dos.getPath();
if (path != null && dos.getState() == State.closed) {
return Files.readAllBytes(path);
}
}
return null;
}
/**
* Gets the content charset passed by the agent or {@code null} if not defined.
*
* @return The content charset passed by the agent or {@code null} if not defined.
*/
public Charset getCharset() {
final var parser = new ParameterParser();
parser.setLowerCaseNames(true);
// Parameter parser can handle null input
final var params = parser.parse(getContentType(), ';');
return Charsets.toCharset(params.get("charset"), charsetDefault);
}
/**
* Gets the default charset for use when no explicit charset parameter is provided by the sender.
*
* @return the default charset
*/
public Charset getCharsetDefault() {
return charsetDefault;
}
/**
* Gets the content type passed by the agent or {@code null} if not defined.
*
* @return The content type passed by the agent or {@code null} if not defined.
*/
@Override
public String getContentType() {
return contentType;
}
/**
* Gets the name of the field in the multipart form corresponding to this file item.
*
* @return The name of the form field.
* @see #setFieldName(String)
*/
@Override
public String getFieldName() {
return fieldName;
}
/**
* Returns the {@link FileCleaningTracker}, which is being used to remove
* temporary files.
* @return The {@link FileCleaningTracker}, which is being used to remove
* temporary files.
*/
public FileCleaningTracker getFileCleaningTracker() {
return fileCleaningTracker;
}
/**
* Gets the file item headers.
*
* @return The file items headers.
*/
@Override
public FileItemHeaders getHeaders() {
return fileItemHeaders;
}
/**
* Gets 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 (dos != null && dos.getState() == State.closed) {
return dos.getInputStream();
}
throw new IllegalStateException("The file item has not been fully read.");
}
/**
* Gets the original file name in the client's file system.
*
* @return The original file name in the client's file system.
* @throws InvalidPathException 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 InvalidPathException#getInput()}.
*/
@Override
public String getName() {
return checkFileName(fileName);
}
/**
* Gets 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.
*/
@Override
public OutputStream getOutputStream() {
if (dos == null) {
final Supplier pathSupplier =
() -> this.repository.resolve(String.format("upload_%s_%s.tmp", UID, getUniqueId()));
try {
final Listener persistenceListener = new Listener() {
@Override
public void persisted(final Path pPath) {
Listener.super.persisted(pPath);
final FileCleaningTracker fct = getFileCleaningTracker();
if (fct != null) {
fct.track(getPath(), this);
}
}
};
dos = new DeferrableOutputStream(threshold, pathSupplier, persistenceListener);
} catch (final IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
return dos;
}
/**
* Gets the {@link Path} for the {@code FileItem}'s data's temporary location on the disk. Note that for {@code FileItem}s that have their data stored in
* memory, this method will return {@code null}. When handling large files, you can use {@link Files#move(Path,Path,CopyOption...)} to move the file to a
* 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} if the data is stored in memory.
*/
public Path getPath() {
return dos == null ? null : dos.getPath();
}
/**
* Returns the contents of the file as a {@link Reader}, using the specified
* {@link #getCharset()}. If the contents are not yet available, returns null.
* This is the case, for example, if the underlying output stream has not yet
* been closed.
* @return The contents of the file as a {@link Reader}
* @throws UnsupportedEncodingException The character set, which is
* specified in the files "content-type" header, is invalid.
* @throws IOException An I/O error occurred, while the
* underlying {@link #getInputStream() input stream} was created.
*/
public Reader getReader() throws IOException, UnsupportedEncodingException {
final InputStream is = getInputStream();
final var parser = new ParameterParser();
parser.setLowerCaseNames(true);
// Parameter parser can handle null input
final var params = parser.parse(getContentType(), ';');
final Charset cs = Charsets.toCharset(params.get("charset"), charsetDefault);
return new InputStreamReader(is, cs);
}
/**
* Gets the size of the file.
*
* @return The size of the file, in bytes.
*/
@Override
public long getSize() {
return dos == null ? 0L : dos.getSize();
}
/**
* Gets 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.
*
* @return The contents of the file, as a string, if available, or null.
* @throws IOException if an I/O error occurs
* @throws OutOfMemoryError See {@link Files#readAllBytes(Path)}: If a string of the required size cannot be allocated,
* for example the file is larger than {@code 2GB}. If so, you should use {@link #getReader()}.
* @throws UnsupportedEncodingException The character set, which is
* specified in the files "content-type" header, is invalid.
* @deprecated Since 2.0.0, use {@link #getReader()} instead.
*/
@Override
public String getString() throws IOException, UnsupportedEncodingException, OutOfMemoryError {
final byte[] bytes = get();
return bytes == null ? null : new String(bytes, getCharset());
}
/**
* Gets 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 IOException if an I/O error occurs
*/
@Override
public String getString(final Charset charset) throws IOException {
return new String(get(), Charsets.toCharset(charset, charsetDefault));
}
/**
* Returns the file items threshold.
* @return The threshold.
*/
public int getThreshold() {
return threshold;
}
/**
* Tests whether or not a {@code FileItem} instance represents a simple form field.
*
* @return {@code true} if the instance represents a simple form field; {@code false} if it represents an uploaded file.
* @see #setFormField(boolean)
*/
@Override
public boolean isFormField() {
return isFormField;
}
/**
* Provides a hint as to whether or not the file contents will be read from memory.
*
* @return {@code true} if the file contents will be read from memory; {@code false} otherwise.
*/
@Override
public boolean isInMemory() {
return dos == null || dos.isInMemory();
}
/**
* Sets the default charset for use when no explicit charset parameter is provided by the sender.
*
* @param charset the default charset
* @return {@code this} instance.
*/
public DiskFileItem setCharsetDefault(final Charset charset) {
charsetDefault = charset;
return this;
}
/**
* Sets the field name used to reference this file item.
*
* @param fieldName The name of the form field.
* @see #getFieldName()
*/
@Override
public DiskFileItem setFieldName(final String fieldName) {
this.fieldName = fieldName;
return this;
}
/**
* Sets the {@link FileCleaningTracker}, which is being used to remove
* temporary files.
* @param fileCleaningTracker The {@link FileCleaningTracker}, which is being used to
* remove temporary files.
*/
public void setFileCleaningTracker(final FileCleaningTracker fileCleaningTracker) {
this.fileCleaningTracker = fileCleaningTracker;
}
/**
* Specifies whether or not a {@code FileItem} instance represents a simple form field.
*
* @param state {@code true} if the instance represents a simple form field; {@code false} if it represents an uploaded file.
* @see #isFormField()
*/
@Override
public DiskFileItem setFormField(final boolean state) {
isFormField = state;
return this;
}
/**
* Sets the file item headers.
*
* @param headers The file items headers.
*/
@Override
public DiskFileItem setHeaders(final FileItemHeaders headers) {
this.fileItemHeaders = headers;
return this;
}
/**
* 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(), getPath(), getSize(), isFormField(),
getFieldName());
}
/**
* Writes 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.
*
*
* 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.
*
*
* This method is only guaranteed to work once, 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} into which the uploaded item should be stored.
* @throws IOException if an error occurs.
*/
@Override
public DiskFileItem write(final Path file) throws IOException {
if (isInMemory()) {
try (var fout = Files.newOutputStream(file)) {
fout.write(get());
} catch (final IOException e) {
throw new IOException("Unexpected output data", e);
}
} else {
final var outputFile = getPath();
if (outputFile == null) {
/*
* For whatever reason we cannot write the file to disk.
*/
throw new FileUploadException("Cannot write uploaded file to disk.");
}
//
// The uploaded file is being stored on disk in a temporary location so move it to the desired file.
//
Files.move(outputFile, file, StandardCopyOption.REPLACE_EXISTING);
}
return this;
}
}
././@LongLink 0000644 0000000 0000000 00000000227 00000000000 011604 L ustar root root libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/FileUploadFileCountLimitException.java libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileu0000644 0001750 0001750 00000002706 15142145735 032067 0 ustar zigo zigo /*
* 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
*
* https://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.commons.fileupload2.core;
/**
* Signals that a request contains more files than the specified limit.
*/
public class FileUploadFileCountLimitException extends FileUploadSizeException {
private static final long serialVersionUID = 2;
/**
* Constructs an instance.
*
* @param message The detail message (which is saved for later retrieval by the {@link #getMessage()} method)
* @param limit The limit that was exceeded.
* @param actual The actual value.
*/
public FileUploadFileCountLimitException(final String message, final long limit, final long actual) {
super(message, limit, actual);
}
}
././@LongLink 0000644 0000000 0000000 00000000204 00000000000 011577 L ustar root root libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/RequestContext.java libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileu0000644 0001750 0001750 00000004777 15142145735 032101 0 ustar zigo zigo /*
* 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
*
* https://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.commons.fileupload2.core;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import org.apache.commons.io.Charsets;
/**
* 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.
*
*/
public interface RequestContext {
/**
* Gets the character encoding for the request.
*
* @return The character encoding for the request.
*/
String getCharacterEncoding();
/**
* Gets the character encoding for the request or null if unspecified.
*
* @return The character encoding for the request or null.
* @throws UnsupportedCharsetException If the named Charset is unavailable.
*/
default Charset getCharset() throws UnsupportedCharsetException {
return Charsets.toCharset(getCharacterEncoding(), null);
}
/**
* Gets the content length of the request.
*
* @return The content length of the request.
*/
long getContentLength();
/**
* Gets the content type of the request.
*
* @return The content type of the request.
*/
String getContentType();
/**
* Gets the input stream for the request.
*
* @return The input stream for the request.
* @throws IOException if a problem occurs.
*/
InputStream getInputStream() throws IOException;
/**
* Is the Request of type {@code multipart/related}?
*
* @return the Request is of type {@code multipart/related}
* @since 2.0.0
*/
boolean isMultipartRelated();
}
././@LongLink 0000644 0000000 0000000 00000000211 00000000000 011575 L ustar root root libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/FileItemHeadersImpl.java libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileu0000644 0001750 0001750 00000005221 15142145735 032062 0 ustar zigo zigo /*
* 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
*
* https://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.commons.fileupload2.core;
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;
/**
* Default implementation of the {@link FileItemHeaders} interface.
*/
class FileItemHeadersImpl implements FileItemHeaders {
/**
* Map of {@code String} keys to a {@code List} of {@code String} instances.
*/
private final Map> headerNameToValueListMap = new LinkedHashMap<>();
/**
* Method to add header values to this instance.
*
* @param name name of this header
* @param value value of this header
*/
@Override
public synchronized void addHeader(final String name, final String value) {
headerNameToValueListMap.computeIfAbsent(toLowerCase(name), k -> new ArrayList<>()).add(value);
}
/**
* {@inheritDoc}
*/
@Override
public String getHeader(final String name) {
final var headerValueList = getList(name);
if (null == headerValueList) {
return null;
}
return headerValueList.get(0);
}
/**
* {@inheritDoc}
*/
@Override
public Iterator getHeaderNames() {
return headerNameToValueListMap.keySet().iterator();
}
/**
* {@inheritDoc}
*/
@Override
public Iterator getHeaders(final String name) {
var headerValueList = getList(name);
if (null == headerValueList) {
headerValueList = Collections.emptyList();
}
return headerValueList.iterator();
}
private List getList(final String name) {
return headerNameToValueListMap.get(toLowerCase(name));
}
private String toLowerCase(final String value) {
return value.toLowerCase(Locale.ROOT);
}
}
././@LongLink 0000644 0000000 0000000 00000000224 00000000000 011601 L ustar root root libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/FileUploadContentTypeException.java libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileu0000644 0001750 0001750 00000004150 15142145735 032062 0 ustar zigo zigo /*
* 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
*
* https://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.commons.fileupload2.core;
/**
* Signals that a request is not a multipart request.
*/
public class FileUploadContentTypeException extends FileUploadException {
/**
* The exceptions UID, for serializing an instance.
*/
private static final long serialVersionUID = 2;
/**
* The guilty content type.
*/
private String contentType;
/**
* Constructs an instance with the specified detail message.
*
* @param message The detail message (which is saved for later retrieval by the {@link #getMessage()} method)
* @param contentType The guilty content type.
*/
public FileUploadContentTypeException(final String message, final String contentType) {
super(message);
this.contentType = contentType;
}
/**
* Constructs an instance with the specified detail message and cause.
*
* @param message The detail message (which is saved for later retrieval by the {@link #getMessage()} method)
* @param cause the original cause
*/
public FileUploadContentTypeException(final String message, final Throwable cause) {
super(message, cause);
}
/**
* Gets the content type.
*
* @return the content type.
*/
public String getContentType() {
return contentType;
}
}
././@LongLink 0000644 0000000 0000000 00000000176 00000000000 011607 L ustar root root libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/FileItem.java libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileu0000644 0001750 0001750 00000016057 15142145735 032073 0 ustar zigo zigo /*
* 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
*
* https://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.commons.fileupload2.core;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
/**
*
* This class represents a file or form item that was received within a {@code multipart/form-data} POST request.
*
*
* After retrieving an instance of this class from a {@link AbstractFileUpload FileUpload} instance (see
* {@code org.apache.commons.fileupload2.core.servlet.ServletFileUpload #parseRequest(javax.servlet.http.HttpServletRequest)}), you may either request all
* contents of the file at once using {@link #get()} or request an {@link InputStream} with {@link #getInputStream()} and process the file without attempting to
* load it into memory, which may come handy with large files.
*
*
* While this interface does not extend {@code javax.activation.DataSource} (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} with minimal additional work.
*
*
* @param The FileItem type.
*/
public interface FileItem> extends FileItemHeadersProvider {
/**
* Deletes the underlying storage for a file item, including deleting any associated temporary disk file. Use this method to ensure that this is done at an
* earlier time, to preserve resources.
*
* @return {@code this} instance.
* @throws IOException if an error occurs.
*/
F delete() throws IOException;
/**
* Gets the contents of the file item as a byte array.
*
* @return The contents of the file item as a byte array.
* @throws IOException if an I/O error occurs
*/
byte[] get() throws IOException;
/**
* Gets the content type passed by the browser or {@code null} if not defined.
*
* @return The content type passed by the browser or {@code null} if not defined.
*/
String getContentType();
/**
* Gets the name of the field in the multipart form corresponding to this file item.
*
* @return The name of the form field.
*/
String getFieldName();
/**
* Gets 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;
/**
* Gets 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 InvalidPathException 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();
/**
* Gets 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.
*/
OutputStream getOutputStream() throws IOException;
/**
* Gets the size of the file item.
*
* @return The size of the file item, in bytes.
*/
long getSize();
/**
* Gets 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.
*
* @throws IOException if an I/O error occurs
*/
String getString() throws IOException;
/**
* Gets 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 toCharset The character encoding to use.
* @return The contents of the item, as a string.
* @throws IOException if an I/O error occurs
*/
String getString(Charset toCharset) throws IOException;
/**
* Tests whether or not a {@code FileItem} instance represents a simple form field.
*
* @return {@code true} if the instance represents a simple form field; {@code false} if it represents an uploaded file.
*/
boolean isFormField();
/**
* Tests a hint as to whether or not the file contents will be read from memory.
*
* @return {@code true} if the file contents will be read from memory; {@code false} otherwise.
*/
boolean isInMemory();
/**
* Sets the field name used to reference this file item.
*
* @param name The name of the form field.
* @return {@code this} instance.
*/
F setFieldName(String name);
/**
* Sets whether or not a {@code FileItem} instance represents a simple form field.
*
* @param state {@code true} if the instance represents a simple form field; {@code false} if it represents an uploaded file.
* @return {@code this} instance.
*/
F setFormField(boolean state);
/**
* Writes 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.
*
*
* 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} into which the uploaded item should be stored.
* @throws IOException if an error occurs.
* @return {@code this} instance.
*/
F write(Path file) throws IOException;
}
././@LongLink 0000644 0000000 0000000 00000000205 00000000000 011600 L ustar root root libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/FileItemFactory.java libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileu0000644 0001750 0001750 00000014301 15142145735 032061 0 ustar zigo zigo /*
* 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
*
* https://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.commons.fileupload2.core;
import org.apache.commons.io.FileCleaningTracker;
import org.apache.commons.io.build.AbstractStreamBuilder;
import org.apache.commons.io.file.PathUtils;
/**
* Creates {@link FileItem} instances.
*
* Factories can provide their own custom configuration, over and above that provided by the default file upload implementation.
*
*
* @param The {@link FileItem} type this factory creates.
*/
public interface FileItemFactory> {
/**
* Abstracts building for subclasses.
*
* @param the type of {@link FileItem} to build.
* @param the type of builder subclass.
*/
abstract class AbstractFileItemBuilder, B extends AbstractFileItemBuilder> extends AbstractStreamBuilder {
/**
* Create a new FileItemHeaders implementation.
*
* @return a new FileItemHeaders implementation.
*/
public static FileItemHeaders newFileItemHeaders() {
return new FileItemHeadersImpl();
}
/**
* Field name.
*/
private String fieldName;
/**
* Content type.
*/
private String contentType;
/**
* Is this a form field.
*/
private boolean isFormField;
/**
* File name.
*/
private String fileName;
/**
* File item headers.
*/
private FileItemHeaders fileItemHeaders = newFileItemHeaders();
/**
* The instance of {@link FileCleaningTracker}, which is responsible for deleting temporary files.
*
* May be null, if tracking files is not required.
*
*/
private FileCleaningTracker fileCleaningTracker;
/**
* Constructs a new instance.
*/
public AbstractFileItemBuilder() {
setBufferSize(DiskFileItemFactory.DEFAULT_THRESHOLD);
setPath(PathUtils.getTempDirectory());
}
/**
* Gets the content type.
*
* @return the content type.
*/
public String getContentType() {
return contentType;
}
/**
* Gets the field name.
*
* @return the field name.
*/
public String getFieldName() {
return fieldName;
}
/**
* Gets the file cleaning tracker.
*
* @return the file cleaning tracker.
*/
public FileCleaningTracker getFileCleaningTracker() {
return fileCleaningTracker;
}
/**
* Gets the field item headers.
*
* @return the field item headers.
*/
public FileItemHeaders getFileItemHeaders() {
return fileItemHeaders;
}
/**
* Gets the file name.
*
* @return the file name.
*/
public String getFileName() {
return fileName;
}
/**
* Tests whether this is a form field.
*
* @return whether this is a form field.
*/
public boolean isFormField() {
return isFormField;
}
/**
* Sets the content type.
*
* @param contentType the content type.
* @return {@code this} instance.
*/
public B setContentType(final String contentType) {
this.contentType = contentType;
return asThis();
}
/**
* Sets the field name.
*
* @param fieldName the field name.
* @return {@code this} instance.
*/
public B setFieldName(final String fieldName) {
this.fieldName = fieldName;
return asThis();
}
/**
* Sets the file cleaning tracker.
*
* @param fileCleaningTracker the file cleaning tracker.
* @return {@code this} instance.
*/
public B setFileCleaningTracker(final FileCleaningTracker fileCleaningTracker) {
this.fileCleaningTracker = fileCleaningTracker;
return asThis();
}
/**
* Sets the file item headers.
*
* @param fileItemHeaders the item headers.
* @return {@code this} instance.
*/
public B setFileItemHeaders(final FileItemHeaders fileItemHeaders) {
this.fileItemHeaders = fileItemHeaders != null ? fileItemHeaders : newFileItemHeaders();
return asThis();
}
/**
* Sets the file name.
*
* @param fileName the file name.
* @return {@code this} instance.
*/
public B setFileName(final String fileName) {
this.fileName = fileName;
return asThis();
}
/**
* Sets whether this is a form field.
*
* @param isFormField whether this is a form field.
* @return {@code this} instance.
*/
public B setFormField(final boolean isFormField) {
this.isFormField = isFormField;
return asThis();
}
}
/**
* Creates a new AbstractFileItemBuilder.
*
* @param The type of AbstractFileItemBuilder.
* @return a new AbstractFileItemBuilder.
*/
> AbstractFileItemBuilder fileItemBuilder();
}
././@LongLink 0000644 0000000 0000000 00000000227 00000000000 011604 L ustar root root libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/FileUploadByteCountLimitException.java libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileu0000644 0001750 0001750 00000004740 15142145735 032067 0 ustar zigo zigo /*
* 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
*
* https://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.commons.fileupload2.core;
/**
* Signals that a file size exceeds the configured maximum.
*/
public class FileUploadByteCountLimitException extends FileUploadSizeException {
/**
* The exceptions UID, for serializing an instance.
*/
private static final long serialVersionUID = 2;
/**
* File name of the item, which caused the exception.
*/
private final String fileName;
/**
* Field name of the item, which caused the exception.
*/
private final String fieldName;
/**
* Constructs an instance with the specified detail message, and actual and permitted sizes.
*
* @param message The detail message (which is saved for later retrieval by the {@link #getMessage()} method)
* @param actual The actual request size.
* @param permitted The maximum permitted request size.
* @param fileName File name of the item, which caused the exception.
* @param fieldName Field name of the item, which caused the exception.
*/
public FileUploadByteCountLimitException(final String message, final long actual, final long permitted, final String fileName, final String fieldName) {
super(message, permitted, actual);
this.fileName = fileName;
this.fieldName = fieldName;
}
/**
* Gets the field name of the item, which caused the exception.
*
* @return Field name, if known, or null.
*/
public String getFieldName() {
return fieldName;
}
/**
* Gets the file name of the item, which caused the exception.
*
* @return File name, if known, or null.
*/
public String getFileName() {
return fileName;
}
}
././@LongLink 0000644 0000000 0000000 00000000214 00000000000 011600 L ustar root root libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/AbstractRequestContext.java libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileu0000644 0001750 0001750 00000006722 15142145735 032071 0 ustar zigo zigo /*
* 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
*
* https://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.commons.fileupload2.core;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.LongSupplier;
import java.util.regex.Pattern;
/**
* Abstracts a RequestContext for implementations.
*
* @param The request type.
*/
public abstract class AbstractRequestContext implements RequestContext {
/**
* The Content-Type Pattern for multipart/related Requests.
*/
private static final Pattern MULTIPART_RELATED =
Pattern.compile("^\\s*multipart/related.*", Pattern.CASE_INSENSITIVE);
/**
* Supplies the content length default.
*/
private final LongSupplier contentLengthDefault;
/**
* Supplies the content length string.
*/
private final Function contentLengthString;
/**
* The request.
*/
private final T request;
/**
* Constructs a new instance.
*
* @param contentLengthString How to get the content length string.
* @param contentLengthDefault How to get the content length default.
* @param request The request.
*/
protected AbstractRequestContext(final Function contentLengthString, final LongSupplier contentLengthDefault, final T request) {
this.contentLengthString = Objects.requireNonNull(contentLengthString, "contentLengthString");
this.contentLengthDefault = Objects.requireNonNull(contentLengthDefault, "contentLengthDefault");
this.request = Objects.requireNonNull(request, "request");
}
/**
* Gets the content length of the request.
*
* @return The content length of the request.
*/
@Override
public long getContentLength() {
try {
return Long.parseLong(contentLengthString.apply(AbstractFileUpload.CONTENT_LENGTH));
} catch (final NumberFormatException e) {
return contentLengthDefault.getAsLong();
}
}
/**
* Gets the request.
*
* @return the request.
*/
public T getRequest() {
return request;
}
/**
* Tests whether the Request of type {@code multipart/related}?
*
* @return whether the Request is of type {@code multipart/related}
* @since 2.0.0
*/
@Override
public boolean isMultipartRelated() {
return MULTIPART_RELATED.matcher(getContentType()).matches();
}
/**
* Returns a string representation of this object.
*
* @return a string representation of this object.
*/
@Override
public String toString() {
return String.format("%s [ContentLength=%s, ContentType=%s]", getClass().getSimpleName(), getContentLength(), getContentType());
}
}
././@LongLink 0000644 0000000 0000000 00000000213 00000000000 011577 L ustar root root libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/FileItemInputIterator.java libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileu0000644 0001750 0001750 00000011276 15142145735 032071 0 ustar zigo zigo /*
* 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
*
* https://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.commons.fileupload2.core;
import java.io.IOException;
import javax.naming.SizeLimitExceededException;
import org.apache.commons.io.function.IOIterator;
/**
* An iterator, as returned by {@link AbstractFileUpload#getItemIterator(RequestContext)}.
*/
public interface FileItemInputIterator extends IOIterator {
/**
* Gets the maximum size of a single file. An {@link FileUploadByteCountLimitException} will be thrown, if there is an uploaded file, which is exceeding
* this value. By default, this value will be copied from the {@link AbstractFileUpload#getMaxFileSize() 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".
*/
long getFileSizeMax();
/**
* Gets 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 AbstractFileUpload#getMaxSize() 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 request. The value -1 indicates "unlimited".
*/
long getSizeMax();
/**
* Tests whether another instance of {@link FileItemInput} 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
boolean hasNext() throws IOException;
/**
* Returns the next available {@link FileItemInput}.
*
* @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 FileItemInput instance, which provides access to the next file item.
*/
@Override
FileItemInput next() throws IOException;
/**
* Sets the maximum size of a single file. An {@link FileUploadByteCountLimitException} will be thrown, if there is an uploaded file, which is exceeding
* this value. By default, this value will be copied from the {@link AbstractFileUpload#getMaxFileSize() 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.
*
* Note: Changing this value doesn't affect files, that have already been uploaded.
*
*
* @param fileSizeMax The maximum size of a single, uploaded file. The value -1 indicates "unlimited".
*/
void setFileSizeMax(long fileSizeMax);
/**
* Sets 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 AbstractFileUpload#getMaxSize() FileUploadBase} object, however, the user may replace the default
* value with a request specific value by invoking {@link #setSizeMax(long)} on this object.
*
* Note: 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 sizeMax The maximum size of the complete HTTP request. The value -1 indicates "unlimited".
*/
void setSizeMax(long sizeMax);
}
././@LongLink 0000644 0000000 0000000 00000000211 00000000000 011575 L ustar root root libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/FileUploadException.java libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileu0000644 0001750 0001750 00000003652 15142145735 032070 0 ustar zigo zigo /*
* 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
*
* https://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.commons.fileupload2.core;
import java.io.IOException;
/**
* Signals errors encountered while processing the request.
*/
public class FileUploadException extends IOException {
/**
* Serial version UID, being used, if the exception is serialized.
*/
private static final long serialVersionUID = 2;
/**
* Constructs an instance with a given detail message.
*
* @param message The detail message (which is saved for later retrieval by the {@link #getMessage()} method)
*/
public FileUploadException(final String message) {
super(message);
}
/**
* Constructs an instance with the given detail message and cause.
*
* @param message The detail message (which is saved for later retrieval by the {@link #getMessage()} method)
* @param cause The cause (which is saved for later retrieval by the {@link #getCause()} method). (A null value is permitted, and indicates that the cause
* is nonexistent or unknown.)
*/
public FileUploadException(final String message, final Throwable cause) {
super(message, cause);
}
}
././@LongLink 0000644 0000000 0000000 00000000211 00000000000 011575 L ustar root root libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/DiskFileItemFactory.java libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileu0000644 0001750 0001750 00000025613 15142145735 032071 0 ustar zigo zigo /*
* 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
*
* https://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.commons.fileupload2.core;
import java.nio.charset.Charset;
import java.nio.file.Path;
import org.apache.commons.io.FileCleaningTracker;
import org.apache.commons.io.build.AbstractOrigin;
import org.apache.commons.io.build.AbstractStreamBuilder;
import org.apache.commons.io.file.PathUtils;
/**
* The default {@link FileItemFactory} implementation.
*
* This implementation creates {@link 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.
*
*
* If not otherwise configured, the default configuration values are as follows:
*
*
* - Size threshold is 10 KB.
* - Repository is the system default temporary directory, as returned by {@code System.getProperty("java.io.tmpdir")}.
*
* State model: The created instances of {@link DiskFileItem} are subject to a carefully designed state model,
* which is also controlled by the threshold. Therefore, it is strongly recommended to set the threshold explicitly, using
* {@link Builder#setThreshold(int)}. Details
* on the state model can be found {@link DiskFileItem here}.
*
* NOTE: Files are created in the system default temporary 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 Builder#setPath(Path)} 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} may be used.
*
*
* Temporary files, which are created for file items, should be deleted later on. The best way to do this is using a {@link FileCleaningTracker}, which you can
* set on the {@link DiskFileItemFactory}. However, if you do use such a tracker, then you must consider the following: Temporary files are automatically
* deleted as soon as they are no longer needed. (More precisely, when the corresponding instance of {@link java.io.File} is garbage collected.) This is done by
* the so-called reaper thread, which is started and stopped automatically by the {@link FileCleaningTracker} when there are files to be tracked. It might make
* sense to terminate that thread, for example, if your web application ends. See the section on "Resource cleanup" in the users guide of Commons FileUpload.
*
*
* @see Builder
* @see Builder#get()
*/
public final class DiskFileItemFactory implements FileItemFactory {
/**
* Builds a new {@link DiskFileItemFactory} instance.
*
* For example:
*
*
* {@code
* DiskFileItemFactory factory = DiskFileItemFactory.builder().setPath(path).setBufferSize(DEFAULT_THRESHOLD).get();
* }
*
*/
public static class Builder extends AbstractStreamBuilder {
/**
* The instance of {@link FileCleaningTracker}, which is responsible for deleting temporary files.
*
* May be null, if tracking files is not required.
*
*/
private FileCleaningTracker fileCleaningTracker;
/**
* The threshold. We do maintain this separate from the {@link #getBufferSize()},
* because the parent class might change the value in {@link #setBufferSize(int)}.
*/
private int threshold;
/**
* Constructs a new instance.
*/
public Builder() {
setBufferSize(DEFAULT_THRESHOLD);
setPath(PathUtils.getTempDirectory());
setCharset(DiskFileItem.DEFAULT_CHARSET);
setCharsetDefault(DiskFileItem.DEFAULT_CHARSET);
}
/**
* Constructs a new instance.
*
* This builder use the aspects Path and buffer size.
*
*
* You must provide an origin that can be converted to a Reader by this builder, otherwise, this call will throw an
* {@link UnsupportedOperationException}.
*
*
* @return a new instance.
* @throws UnsupportedOperationException if the origin cannot provide a Path.
* @see AbstractOrigin#getReader(Charset)
*/
@Override
public DiskFileItemFactory get() {
return new DiskFileItemFactory(this);
}
/**
* Equivalent to {@link #getThreshold()}.
* @return The threshold, which is being used.
* @see #getThreshold()
* @deprecated Since 2.0.0, use {@link #getThreshold()} instead.
*/
public int getBufferSize() {
return getThreshold();
}
/**
* Returns the threshold.
* @return The threshold.
*/
public int getThreshold() {
return threshold;
}
/**
* Equivalent to {@link #setThreshold(int)}.
* @param bufferSize The threshold, which is being used.
* @see #setThreshold(int)
* @return This builder.
* @deprecated Since 2.0.0, use {@link #setThreshold(int)} instead.
*/
@Override
public Builder setBufferSize(final int bufferSize) {
return setThreshold(bufferSize);
}
/**
* Sets the tracker, which is responsible for deleting temporary files.
*
* @param fileCleaningTracker Callback to track files created, or null (default) to disable tracking.
* @return {@code this} instance.
*/
public Builder setFileCleaningTracker(final FileCleaningTracker fileCleaningTracker) {
this.fileCleaningTracker = fileCleaningTracker;
return this;
}
/**
* Sets the threshold. The uploaded data is typically kept in memory, until
* a certain number of bytes (the threshold) is reached. At this point, the
* incoming data is transferred to a temporary file, and the in-memory data
* is removed.
*
* The threshold will also control the state model of the created
* instances of {@link DiskFileItem}. Details on the state model can be
* found {@link DiskFileItem here}.
* @param threshold The threshold, which is being used.
* @return This builder.
*/
public Builder setThreshold(final int threshold) {
this.threshold = threshold;
return this;
}
}
/**
* The default threshold in bytes above which uploads will be stored on disk.
*/
public static final int DEFAULT_THRESHOLD = 10_240;
/**
* Constructs a new {@link Builder}.
*
* @return a new {@link Builder}.
*/
public static Builder builder() {
return new Builder();
}
/**
* The directory in which uploaded files will be stored, if stored on disk.
*/
private final Path repository;
/**
* The threshold above which uploads will be stored on disk.
*/
private final int threshold;
/**
* The instance of {@link FileCleaningTracker}, which is responsible for deleting temporary files.
*
* May be null, if tracking files is not required.
*
*/
private final FileCleaningTracker fileCleaningTracker;
/**
* Default content Charset to be used when no explicit Charset parameter is provided by the sender.
*/
private final Charset charsetDefault;
/**
* Constructs a preconfigured instance of this class.
*
* @param repository The data repository, which is the directory in which files will be created, should the item size exceed the threshold.
* @param threshold The threshold, in bytes, below which items will be retained in memory and above which they will be stored as a file.
* @param charsetDefault Sets the default charset for use when no explicit charset parameter is provided by the sender.
* @param fileCleaningTracker Callback to track files created, or null (default) to disable tracking.
*/
private DiskFileItemFactory(final Builder builder) {
this.threshold = builder.threshold;
this.repository = builder.getPath();
this.charsetDefault = builder.getCharset();
this.fileCleaningTracker = builder.fileCleaningTracker;
}
@SuppressWarnings("unchecked")
@Override
public DiskFileItem.Builder fileItemBuilder() {
// @formatter:off
return DiskFileItem.builder()
.setThreshold(threshold)
.setCharset(charsetDefault)
.setFileCleaningTracker(fileCleaningTracker)
.setPath(repository);
// @formatter:on
}
/**
* Gets the default charset for use when no explicit charset parameter is provided by the sender.
*
* @return the default charset
*/
public Charset getCharsetDefault() {
return charsetDefault;
}
/**
* Gets the tracker, which is responsible for deleting temporary files.
*
* @return An instance of {@link FileCleaningTracker}, or null (default), if temporary files aren't tracked.
*/
public FileCleaningTracker getFileCleaningTracker() {
return fileCleaningTracker;
}
/**
* Gets 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.
*/
public Path getRepository() {
return repository;
}
/**
* Gets the size threshold beyond which files are written directly to disk. The default value is {@value #DEFAULT_THRESHOLD} bytes.
*
* @return The size threshold in bytes.
*/
public int getThreshold() {
return threshold;
}
}
././@LongLink 0000644 0000000 0000000 00000000205 00000000000 011600 L ustar root root libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/FileItemHeaders.java libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileu0000644 0001750 0001750 00000005252 15142145735 032066 0 ustar zigo zigo /*
* 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
*
* https://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.commons.fileupload2.core;
import java.util.Iterator;
/**
* This class provides support for accessing the headers for a file or form item that was received within a {@code multipart/form-data} POST request.
*/
public interface FileItemHeaders {
/**
* Adds a header.
*
* @param name name
* @param value value.
*/
void addHeader(String name, String value);
/**
* Gets the value of the specified part header as a {@code String}.
*
* If the part did not include a header of the specified name, this method return {@code null}. 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} specifying the header name
* @return a {@code String} containing the value of the requested header, or {@code null} if the item does not have a header of that name
*/
String getHeader(String name);
/**
* Gets an {@code Iterator} of all the header names.
*
* @return an {@code Iterator} 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}
*/
Iterator getHeaderNames();
/**
* Gets all the values of the specified item header as an {@code Iterator} of {@code String} objects.
*
* If the item did not include any headers of the specified name, this method returns an empty {@code Iterator}. The header name is case insensitive.
*
*
* @param name a {@code String} specifying the header name
* @return an {@code Iterator} containing the values of the requested header. If the item does not have any headers of that name, return an empty
* {@code Iterator}
*/
Iterator getHeaders(String name);
}
././@LongLink 0000644 0000000 0000000 00000000206 00000000000 011601 L ustar root root libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/ProgressListener.java libcommons-fileupload2-java-2.0.0~M5/commons-fileupload2-core/src/main/java/org/apache/commons/fileu0000644 0001750 0001750 00000003077 15142145735 032071 0 ustar zigo zigo /*
* 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
*
* https://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.commons.fileupload2.core;
/**
* Receives progress information. May be used to display a progress bar.
*/
@FunctionalInterface
public interface ProgressListener {
/**
* Nop implementation.
*/
ProgressListener NOP = (bytesRead, contentLength, items) -> {
// nop
};
/**
* Updates the listeners status information.
*
* @param bytesRead The total number of bytes, which have been read so far.
* @param contentLength The total number of bytes, which are being read. May be -1, if this number is unknown.
* @param items The number of the field, which is currently being read. (0 = no item so far, 1 = first item is being read, ...)
*/
void update(long bytesRead, long contentLength, int items);
}
libcommons-fileupload2-java-2.0.0~M5/.gitattributes 0000644 0001750 0001750 00000001513 15142145735 021002 0 ustar zigo zigo # 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
#
# https://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.
* text=auto
*.patch -text
libcommons-fileupload2-java-2.0.0~M5/.asf.yaml 0000644 0001750 0001750 00000003066 15142145735 017627 0 ustar zigo zigo # 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
#
# https://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.
github:
description: "Apache Commons FileUpload is a robust, high-performance, file upload capability to your servlets and web applications"
homepage: https://commons.apache.org/fileupload/
labels:
- java
- fileupload
- upload
- apache
notifications:
commits: commits@commons.apache.org
issues: issues@commons.apache.org
pullrequests: issues@commons.apache.org
jira_options: link label
jobs: notifications@commons.apache.org
# commits_bot_dependabot: dependabot@commons.apache.org
issues_bot_dependabot: dependabot@commons.apache.org
pullrequests_bot_dependabot: dependabot@commons.apache.org
issues_bot_codecov-commenter: notifications@commons.apache.org
pullrequests_bot_codecov-commenter: notifications@commons.apache.org
libcommons-fileupload2-java-2.0.0~M5/src/ 0000755 0001750 0001750 00000000000 15142145735 016676 5 ustar zigo zigo libcommons-fileupload2-java-2.0.0~M5/src/media/ 0000755 0001750 0001750 00000000000 15227714536 017762 5 ustar zigo zigo libcommons-fileupload2-java-2.0.0~M5/src/media/logo.xcf 0000644 0001750 0001750 00000061324 15142145735 021425 0 ustar zigo zigo gimp xcf file , d B B !
gimp-comment Created with The GIMP gimp-image-grid (style solid)
(fgcolor (color-rgba 0 0 0 1))
(bgcolor (color-rgba 1 1 1 1))
(xspacing 10)
(yspacing 10)
(spacing-unit inches)
(xoffset 0)
(yoffset 0)
(offset-unit inches)
r A 5 C `
TM ! ? "
T % $ # gimp-text-layer (text "TM")
(font "Sitka Small Bold")
(font-size 12)
(font-size-unit pixels)
(antialias yes)
(language "en-us")
(base-direction ltr)
(color (color-rgb 0 0 0))
(justify left)
(box-mode dynamic)
(box-unit pixels)
(hinting yes)
s
' tv *렬R $ $ P% Ӆ $ Pk{&P $ P{P $ P ,P $] 'f D dp`; ,
Text Layer#2 ! ? "
S 0 % $ # > , Z , v U 8 U" q 8 U 8 U 8 8 q U8 8Uq U8 8 88 8U
U UU 8
U8
q
8 8q UU U UU
q
U 8U
U
U qU
U
U U
U U
U
U
U
U U q
U
U qq U
U U U
U q q ƪ q qq UqƍU UU 8qq8 88 U
U 8 U8 U8 ? U
U U 8 8 8 UU 88 8 U U U U U U
UU8 U UU U U 8U U U U q 8 U U U Uq U U U U U U U U U U U U U U U U q U 8 U U 8 U q U U q
q q qq 8 q
U U U q 8
U U 8U8 U 8UU8 U$ U: U: U: U: U: U: U9 7 UqU83
q 8<