Updated Branches: refs/heads/master 7cd01f71c -> 99fcd91fe
http://git-wip-us.apache.org/repos/asf/wicket/blob/99fcd91f/wicket-util/src/main/java/org/apache/wicket/util/upload/MultipartFormInputStream.java ---------------------------------------------------------------------- diff --git a/wicket-util/src/main/java/org/apache/wicket/util/upload/MultipartFormInputStream.java b/wicket-util/src/main/java/org/apache/wicket/util/upload/MultipartFormInputStream.java deleted file mode 100644 index 1602715..0000000 --- a/wicket-util/src/main/java/org/apache/wicket/util/upload/MultipartFormInputStream.java +++ /dev/null @@ -1,1192 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.wicket.util.upload; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; - -import org.apache.wicket.util.io.Streams; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * <p> - * Low level API for processing file uploads. - * - * <p> - * This class can be used to process data streams conforming to MIME 'multipart' format as defined - * in <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Arbitrarily large amounts of data - * in the stream can be processed under constant memory usage. - * - * <p> - * The format of the stream is defined in the following way:<br> - * - * <code> - * multipart-body := preamble 1*encapsulation close-delimiter epilogue<br> - * encapsulation := delimiter body CRLF<br> - * delimiter := "--" boundary CRLF<br> - * close-delimiter := "--" boudary "--"<br> - * preamble := <ignore><br> - * epilogue := <ignore><br> - * body := header-part CRLF body-part<br> - * header-part := 1*header CRLF<br> - * header := header-name ":" header-value<br> - * header-name := <printable ascii characters except ":"><br> - * header-value := <any ascii characters except CR & LF><br> - * body-data := <arbitrary data><br> - * </code> - * - * <p> - * 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 <strong>required</strong> to have a - * boundary token of the same length as the parent stream (see {@link #setBoundary(byte[])}). - * - * <p> - * Here is an example of usage of this class.<br> - * - * <pre> - * try { - * MultipartStream multipartStream = new MultipartStream(input, - * boundary); - * boolean nextPart = multipartStream.skipPreamble(); - * OutputStream output; - * while(nextPart) { - * header = chunks.readHeader(); - * // process headers - * // create some output stream - * multipartStream.readBodyPart(output); - * nextPart = multipartStream.readBoundary(); - * } - * } catch(MultipartStream.MalformedStreamException e) { - * // the stream failed to follow required syntax - * } catch(IOException) { - * // a read or write error occurred - * } - * - * </pre> - * - * @author <a href="mailto:[email protected]">Rafal Krzewski</a> - * @author <a href="mailto:[email protected]">Martin Cooper</a> - * @author Sean C. Sullivan - * - */ -public class MultipartFormInputStream -{ - /** Log. */ - private static final Logger log = LoggerFactory.getLogger(MultipartFormInputStream.class); - - /** - * Internal class, which is used to invoke the {@link ProgressListener}. - */ - static class ProgressNotifier - { - /** - * The listener to invoke. - */ - private final ProgressListener listener; - /** - * 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 pListener - * The listener to invoke. - * @param pContentLength - * The expected content length. - */ - ProgressNotifier(final ProgressListener pListener, final long pContentLength) - { - listener = pListener; - contentLength = pContentLength; - } - - /** - * Called to indicate that bytes have been read. - * - * @param pBytes - * Number of bytes, which have been read. - */ - void noteBytesRead(final int pBytes) - { - /* - * Indicates, that the given number of bytes have been read from the input stream. - */ - bytesRead += pBytes; - notifyListener(); - } - - /** - * Called to indicate, that a new file item has been detected. - */ - void noteItem() - { - ++items; - } - - /** - * Called for notifying the listener. - */ - private void notifyListener() - { - if (listener != null) - { - listener.update(bytesRead, contentLength, items); - } - } - } - - // ----------------------------------------------------- Manifest constants - - - /** - * 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 maximum length of <code>header-part</code> that will be processed (10 kilobytes = 10240 - * bytes.). - */ - public static final int HEADER_PART_SIZE_MAX = 10240; - - - /** - * The default length of the buffer used for processing a request. - */ - protected static final int DEFAULT_BUFSIZE = 4096; - - - /** - * A byte sequence that marks the end of <code>header-part</code> (<code>CRLFCRLF</code>). - */ - protected 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</code>). - */ - protected static final byte[] FIELD_SEPARATOR = { CR, LF }; - - - /** - * A byte sequence that that follows a delimiter of the last encapsulation in the stream ( - * <code>--</code>). - */ - protected static final byte[] STREAM_TERMINATOR = { DASH, DASH }; - - - /** - * A byte sequence that precedes a boundary (<code>CRLF--</code>). - */ - protected static final byte[] BOUNDARY_PREFIX = { CR, LF, DASH, DASH }; - - - // ----------------------------------------------------------- Data members - - - /** - * The input stream from which data is read. - */ - private final InputStream input; - - - /** - * The length of the boundary token plus the leading <code>CRLF--</code>. - */ - 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 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. <br> - * 0 <= head < bufSize - */ - private int head; - - - /** - * The index of last valid characer in the buffer + 1. <br> - * 0 <= tail <= bufSize - */ - private int tail; - - - /** - * The content encoding to use when reading headers. - */ - private String headerEncoding; - - - /** - * The progress notifier, if any, or null. - */ - private final ProgressNotifier notifier; - - // ----------------------------------------------------------- Constructors - - /** - * <p> - * Constructs a <code>MultipartStream</code> with a custom size buffer. - * - * <p> - * 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</code> to serve as a data source. - * @param boundary - * The token used for dividing the stream into <code>encapsulations</code>. - * @param bufSize - * The size of the buffer to be used, in bytes. - * @param pNotifier - * The notifier, which is used for calling the progress listener, if any. - * - * @see #MultipartFormInputStream(InputStream, byte[], - * MultipartFormInputStream.ProgressNotifier) - */ - MultipartFormInputStream(final InputStream input, final byte[] boundary, final int bufSize, - final ProgressNotifier pNotifier) - { - this.input = input; - this.bufSize = bufSize; - buffer = new byte[bufSize]; - notifier = pNotifier; - - // We prepend CR/LF to the boundary to chop trailng CR/LF from - // body-data tokens. - boundaryLength = boundary.length + BOUNDARY_PREFIX.length; - if (bufSize < this.boundaryLength + 1) - { - throw new IllegalArgumentException( - "The buffer size specified for the MultipartFormInputStream is too small"); - } - this.boundary = new byte[this.boundaryLength]; - keepRegion = this.boundary.length; - - System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0, BOUNDARY_PREFIX.length); - System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, boundary.length); - - head = 0; - tail = 0; - } - - - /** - * <p> - * Constructs a <code>MultipartStream</code> with a default size buffer. - * - * @param input - * The <code>InputStream</code> to serve as a data source. - * @param boundary - * The token used for dividing the stream into <code>encapsulations</code>. - * @param pNotifier - * An object for calling the progress listener, if any. - * - * - * @see #MultipartFormInputStream(InputStream, byte[], int, - * MultipartFormInputStream.ProgressNotifier) - * @throws IllegalArgumentException If the buffer size is too small - */ - MultipartFormInputStream(final InputStream input, final byte[] boundary, - final ProgressNotifier pNotifier) - { - this(input, boundary, DEFAULT_BUFSIZE, pNotifier); - } - - // --------------------------------------------------------- Public methods - - - /** - * Retrieves the character encoding used when reading the headers of an individual part. When - * not specified, or <code>null</code>, the platform default encoding is used. - * - * - * @return The encoding used to read part headers. - */ - public String getHeaderEncoding() - { - return headerEncoding; - } - - - /** - * Specifies the character encoding to be used when reading the headers of individual parts. - * When not specified, or <code>null</code>, the platform default encoding is used. - * - * @param encoding - * The encoding used to read part headers. - */ - public void setHeaderEncoding(final String encoding) - { - headerEncoding = encoding; - } - - - /** - * Reads a byte from the <code>buffer</code>, 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++]; - } - - - /** - * Skips a <code>boundary</code> token, and checks whether more <code>encapsulations</code> are - * contained in the stream. - * - * @return <code>true</code> if there are more encapsulations in this stream; <code>false</code> - * otherwise. - * - * @throws MalformedStreamException - * if the stream ends unexpecetedly or fails to follow required syntax. - */ - public boolean readBoundary() throws MalformedStreamException - { - byte[] marker = new byte[2]; - boolean nextChunk = false; - - 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 (IOException e) - { - throw new MalformedStreamException("Stream ended unexpectedly"); - } - return nextChunk; - } - - - /** - * <p> - * Changes the boundary token used for partitioning the stream. - * - * <p> - * This method allows single pass processing of nested multipart streams. - * - * <p> - * The boundary token of the nested stream is <code>required</code> to be of the same length as - * the boundary token in parent stream. - * - * <p> - * 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 IllegalBoundaryException - * if the <code>boundary</code> has a different length than the one being currently - * parsed. - */ - public void setBoundary(final byte[] boundary) throws IllegalBoundaryException - { - if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) - { - throw new IllegalBoundaryException("The length of a boundary token can not be changed"); - } - System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, boundary.length); - } - - - /** - * <p> - * Reads the <code>header-part</code> of the current <code>encapsulation</code>. - * - * <p> - * Headers are returned verbatim to the input stream, including the trailing <code>CRLF</code> - * marker. Parsing is left to the application. - * - * <p> - * <strong>TODO</strong> allow limiting maximum header size to protect against abuse. - * - * @return The <code>header-part</code> of the current encapsulation. - * - * @throws MalformedStreamException - * if the stream ends unexpecetedly. - */ - public String readHeaders() throws MalformedStreamException - { - int i = 0; - byte b; - // to support multi-byte characters - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - int size = 0; - while (i < HEADER_SEPARATOR.length) - { - try - { - b = readByte(); - } - catch (IOException e) - { - throw new MalformedStreamException("Stream ended unexpectedly"); - } - if (++size > HEADER_PART_SIZE_MAX) - { - throw new MalformedStreamException("Header section has more than " + - HEADER_PART_SIZE_MAX + " bytes (maybe it is not properly terminated)"); - } - if (b == HEADER_SEPARATOR[i]) - { - i++; - } - else - { - i = 0; - } - baos.write(b); - } - - String headers = null; - if (headerEncoding != null) - { - try - { - headers = baos.toString(headerEncoding); - } - catch (UnsupportedEncodingException e) - { - // Fall back to platform default if specified encoding is not - // supported. - headers = baos.toString(); - } - } - else - { - headers = baos.toString(); - } - - return headers; - } - - - /** - * <p> - * Reads <code>body-data</code> from the current <code>encapsulation</code> and writes its - * contents into the output <code>Stream</code>. - * - * <p> - * Arbitrary large amounts of data can be processed by this method using a constant size buffer. - * (see - * {@link #MultipartFormInputStream(InputStream,byte[],int, MultipartFormInputStream.ProgressNotifier) - * constructor}). - * - * @param output - * The <code>Stream</code> 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 int readBodyData(final OutputStream output) throws MalformedStreamException, IOException - { - final InputStream istream = newInputStream(); - return Streams.copy(istream, output == null ? new NoopOutputStream() : output); - } - - /** - * Creates a new {@link ItemInputStream}. - * - * @return A new instance of {@link ItemInputStream}. - */ - ItemInputStream newInputStream() - { - return new ItemInputStream(); - } - - /** - * <p> - * Reads <code>body-data</code> from the current <code>encapsulation</code> and discards it. - * - * <p> - * 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 int discardBodyData() throws MalformedStreamException, IOException - { - return readBodyData(null); - } - - - /** - * Finds the beginning of the first <code>encapsulation</code>. - * - * @return <code>true</code> if an <code>encapsulation</code> was found in the stream. - * - * @throws IOException - * if an i/o error occurs. - */ - public boolean skipPreamble() throws IOException - { - // First delimiter may be not preceeded with a CRLF. - System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2); - boundaryLength = boundary.length - 2; - try - { - // Discard all data up to the delimiter. - discardBodyData(); - - // Read boundary - if succeded, the stream contains an - // encapsulation. - return readBoundary(); - } - catch (MalformedStreamException e) - { - log.error("Error while reading servlet request multi-part data: " + e.getMessage() + - ". " + toString()); - return false; - } - finally - { - // Restore delimiter. - System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2); - boundaryLength = boundary.length; - boundary[0] = CR; - boundary[1] = LF; - } - } - - - /** - * Compares <code>count</code> first bytes in the arrays <code>a</code> and <code>b</code>. - * - * @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</code> if <code>count</code> first bytes in arrays <code>a</code> and - * <code>b</code> are equal. - */ - public static boolean arrayequals(final byte[] a, final byte[] b, final int count) - { - for (int i = 0; i < count; i++) - { - if (a[i] != b[i]) - { - return false; - } - } - return true; - } - - - /** - * Searches for a byte of specified value in the <code>buffer</code>, starting at the specified - * <code>position</code>. - * - * @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</code>, or - * <code>-1</code> if not found. - */ - protected int findByte(final byte value, final int pos) - { - for (int i = pos; i < tail; i++) - { - if (buffer[i] == value) - { - return i; - } - } - - return -1; - } - - - /** - * Searches for the <code>boundary</code> in the <code>buffer</code> region delimited by - * <code>head</code> and <code>tail</code>. - * - * @return The position of the boundary found, counting from the beginning of the - * <code>buffer</code>, or <code>-1</code> if not found. - */ - protected int findSeparator() - { - int first; - int match = 0; - int maxpos = tail - boundaryLength; - for (first = head; (first <= maxpos) && (match != boundaryLength); first++) - { - first = findByte(boundary[0], first); - if ((first == -1) || (first > maxpos)) - { - return -1; - } - for (match = 1; match < boundaryLength; match++) - { - if (buffer[first + match] != boundary[match]) - { - break; - } - } - } - if (match == boundaryLength) - { - return first - 1; - } - return -1; - } - - /** - * Returns a string representation of this object. - * - * @return The string representation of this object. - */ - @Override - public String toString() - { - StringBuilder sbTemp = new StringBuilder(); - sbTemp.append("boundary='"); - for (byte b : boundary) - { - if (Character.isDefined(b)) - { - sbTemp.append((char)b); - } - else - { - sbTemp.append("#"); - sbTemp.append(b); - sbTemp.append(";"); - } - } - sbTemp.append("'; bufSize="); - sbTemp.append(bufSize); - return sbTemp.toString(); - } - - - /** - * Thrown to indicate that the input stream fails to follow the required syntax. - */ - public static class MalformedStreamException extends IOException - { - private static final long serialVersionUID = 1L; - - /** - * Constructs a <code>MalformedStreamException</code> with no detail message. - */ - public MalformedStreamException() - { - super(); - } - - /** - * Constructs an <code>MalformedStreamException</code> with the specified detail message. - * - * @param message - * The detail message. - */ - public MalformedStreamException(final String message) - { - super(message); - } - } - - - /** - * Thrown upon attempt of setting an invalid boundary token. - */ - public static class IllegalBoundaryException extends IOException - { - private static final long serialVersionUID = 1L; - - /** - * Constructs an <code>IllegalBoundaryException</code> with no detail message. - */ - public IllegalBoundaryException() - { - super(); - } - - /** - * Constructs an <code>IllegalBoundaryException</code> with the specified detail message. - * - * @param message - * The detail message. - */ - public IllegalBoundaryException(final String message) - { - super(message); - } - } - - /** - * An {@link InputStream} for reading an items contents. - */ - public class ItemInputStream extends InputStream implements Closeable - { - /** - * 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(); - } - - /** - * Called for finding the separator. - */ - private void findSeparator() - { - pos = MultipartFormInputStream.this.findSeparator(); - if (pos == -1) - { - if (tail - head > keepRegion) - { - pad = keepRegion; - } - else - { - pad = tail - head; - } - } - } - - /** - * Returns 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; - } - - /** - * 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; - } - - /** - * Offset when converting negative bytes to integers. - */ - private static final int BYTE_POSITIVE_OFFSET = 256; - - /** - * Returns 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 - { - if (closed) - { - throw new FileItemStream.ItemSkippedException(); - } - if (available() == 0) - { - if (makeAvailable() == 0) - { - return -1; - } - } - ++total; - 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 - { - if (closed) - { - throw new FileItemStream.ItemSkippedException(); - } - if (len == 0) - { - return 0; - } - int 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; - } - - /** - * Closes the input stream. - * - * @throws IOException - * An I/O error occurred. - */ - @Override - public void close() throws IOException - { - close(false); - } - - /** - * Closes the input stream. - * - * @param pCloseUnderlying - * Whether to close the underlying stream (hard close) - * @throws IOException - * An I/O error occurred. - */ - public void close(final boolean pCloseUnderlying) throws IOException - { - if (closed) - { - return; - } - if (pCloseUnderlying) - { - closed = true; - input.close(); - } - else - { - for (;;) - { - int av = available(); - if (av == 0) - { - av = makeAvailable(); - if (av == 0) - { - break; - } - } - skip(av); - } - } - closed = true; - } - - /** - * 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 - { - if (closed) - { - throw new FileItemStream.ItemSkippedException(); - } - int av = available(); - if (av == 0) - { - av = makeAvailable(); - if (av == 0) - { - return 0; - } - } - long res = Math.min(av, bytes); - head += res; - return res; - } - - /** - * 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 (;;) - { - int 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 String msg = "Stream ended unexpectedly"; - throw new MalformedStreamException(msg); - } - if (notifier != null) - { - notifier.noteBytesRead(bytesRead); - } - tail += bytesRead; - - findSeparator(); - int av = available(); - - if ((av > 0) || (pos != -1)) - { - return av; - } - } - } - - /** - * Returns, whether the stream is closed. - * - * @return True, if the stream is closed, otherwise false. - */ - @Override - public boolean isClosed() - { - return closed; - } - } - - private final static class NoopOutputStream extends OutputStream - { - @Override - public void close() - { - } - - @Override - public void flush() - { - } - - @Override - public void write(final byte[] b) - { - } - - @Override - public void write(final byte[] b, final int i, final int l) - { - } - - @Override - public void write(final int b) - { - } - } - - // ------------------------------------------------------ Debugging methods - - - // These are the methods that were used to debug this stuff. - /* - * - * // Dump data. protected void dump() { System.out.println("01234567890"); byte[] temp = new - * byte[buffer.length]; for(int i=0; i<buffer.length; i++) { if (buffer[i] == 0x0D || buffer[i] - * == 0x0A) { temp[i] = 0x21; } else { temp[i] = buffer[i]; } } System.out.println(new - * String(temp)); int i; for (i=0; i<head; i++) System.out.print(" "); System.out.println("h"); - * for (i=0; i<tail; i++) System.out.print(" "); System.out.println("t"); System.out.flush(); } - * - * // Main routine, for testing purposes only. // // @param args A String[] with the command - * line arguments. // @throws Exception, a generic exception. public static void main( String[] - * args ) throws Exception { File boundaryFile = new File("boundary.dat"); int boundarySize = - * (int)boundaryFile.length(); byte[] boundary = new byte[boundarySize]; FileInputStream input = - * new FileInputStream(boundaryFile); input.read(boundary,0,boundarySize); - * - * input = new FileInputStream("multipart.dat"); MultipartStream chunks = new - * MultipartStream(input, boundary); - * - * int i = 0; String header; OutputStream output; boolean nextChunk = chunks.skipPreamble(); - * while (nextChunk) { header = chunks.readHeaders(); System.out.println("!"+header+"!"); - * System.out.println("wrote part"+i+".dat"); output = new - * FileOutputStream("part"+(i++)+".dat"); chunks.readBodyData(output); nextChunk = - * chunks.readBoundary(); } } - */ -} http://git-wip-us.apache.org/repos/asf/wicket/blob/99fcd91f/wicket-util/src/main/java/org/apache/wicket/util/upload/ParameterParser.java ---------------------------------------------------------------------- diff --git a/wicket-util/src/main/java/org/apache/wicket/util/upload/ParameterParser.java b/wicket-util/src/main/java/org/apache/wicket/util/upload/ParameterParser.java deleted file mode 100644 index c3b2163..0000000 --- a/wicket-util/src/main/java/org/apache/wicket/util/upload/ParameterParser.java +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.wicket.util.upload; - -import java.util.HashMap; -import java.util.Map; - -/** - * A simple parser intended to parse sequences of name/value pairs. Parameter values are exptected - * to be enclosed in quotes if they contain unsafe characters, such as '=' characters or separators. - * Parameter values are optional and can be omitted. - * - * <p> - * <code>param1 = value; param2 = "anything goes; really"; param3</code> - * </p> - * - * @author <a href="mailto:[email protected]">Oleg Kalnichevski</a> - */ - -public class ParameterParser -{ - /** - * String to be parsed. - */ - private char[] chars = null; - - /** - * Current position in the string. - */ - private int pos = 0; - - /** - * Maximum position in the string. - */ - private int len = 0; - - /** - * Start of a token. - */ - private int i1 = 0; - - /** - * End of a token. - */ - private int i2 = 0; - - /** - * Whether names stored in the map should be converted to lower case. - */ - private boolean lowerCaseNames = false; - - /** - * Default ParameterParser constructor. - */ - public ParameterParser() - { - super(); - } - - /** - * Are there any characters left to parse? - * - * @return <tt>true</tt> if there are unparsed characters, <tt>false</tt> otherwise. - */ - private boolean hasChar() - { - return pos < len; - } - - /** - * A helper method to process the parsed token. This method removes leading and trailing blanks - * as well as enclosing quotation marks, when necessary. - * - * @param quoted - * <tt>true</tt> if quotation marks are expected, <tt>false</tt> otherwise. - * @return the token - */ - private String getToken(final boolean quoted) - { - // Trim leading white spaces - while ((i1 < i2) && (Character.isWhitespace(chars[i1]))) - { - i1++; - } - // Trim trailing white spaces - while ((i2 > i1) && (Character.isWhitespace(chars[i2 - 1]))) - { - i2--; - } - // Strip away quotation marks if necessary - if (quoted) - { - if (((i2 - i1) >= 2) && (chars[i1] == '"') && (chars[i2 - 1] == '"')) - { - i1++; - i2--; - } - } - String result = null; - if (i2 > i1) - { - result = new String(chars, i1, i2 - i1); - } - return result; - } - - /** - * Tests if the given character is present in the array of characters. - * - * @param ch - * the character to test for presense in the array of characters - * @param charray - * the array of characters to test against - * - * @return <tt>true</tt> if the character is present in the array of characters, <tt>false</tt> - * otherwise. - */ - private boolean isOneOf(final char ch, final char[] charray) - { - for (char c : charray) - { - if (ch == c) - { - return true; - } - } - return false; - } - - /** - * Parses out a token until any of the given terminators is encountered. - * - * @param terminators - * the array of terminating characters. Any of these characters when encountered - * signify the end of the token - * - * @return the token - */ - private String parseToken(final char[] terminators) - { - char ch; - i1 = pos; - i2 = pos; - while (hasChar()) - { - ch = chars[pos]; - if (isOneOf(ch, terminators)) - { - break; - } - i2++; - pos++; - } - return getToken(false); - } - - /** - * Parses out a token until any of the given terminators is encountered outside the quotation - * marks. - * - * @param terminators - * the array of terminating characters. Any of these characters when encountered - * outside the quotation marks signify the end of the token - * - * @return the token - */ - private String parseQuotedToken(final char[] terminators) - { - char ch; - i1 = pos; - i2 = pos; - boolean quoted = false; - boolean charEscaped = false; - while (hasChar()) - { - ch = chars[pos]; - if (!quoted && isOneOf(ch, terminators)) - { - break; - } - if (!charEscaped && (ch == '"')) - { - quoted = !quoted; - } - charEscaped = (!charEscaped && (ch == '\\')); - i2++; - pos++; - - } - return getToken(true); - } - - /** - * Returns <tt>true</tt> if parameter names are to be converted to lower case when name/value - * pairs are parsed. - * - * @return <tt>true</tt> if parameter names are to be converted to lower case when name/value - * pairs are parsed. Otherwise returns <tt>false</tt> - */ - public boolean isLowerCaseNames() - { - return lowerCaseNames; - } - - /** - * Sets the flag if parameter names are to be converted to lower case when name/value pairs are - * parsed. - * - * @param b - * <tt>true</tt> if parameter names are to be converted to lower case when name/value - * pairs are parsed. <tt>false</tt> otherwise. - */ - public void setLowerCaseNames(final boolean b) - { - lowerCaseNames = b; - } - - /** - * Extracts a map of name/value pairs from the given string. Names are expected to be unique. - * Multiple separators may be specified and the earliest found in the input string is used. - * - * @param str - * the string that contains a sequence of name/value pairs - * @param separators - * the name/value pairs separators - * - * @return a map of name/value pairs - */ - public Map<String, String> parse(final String str, final char[] separators) - { - if ((separators == null) || (separators.length == 0)) - { - return new HashMap<>(); - } - char separator = separators[0]; - if (str != null) - { - int idx = str.length(); - for (char sep : separators) - { - int tmp = str.indexOf(sep); - if (tmp != -1) - { - if (tmp < idx) - { - idx = tmp; - separator = sep; - } - } - } - } - return parse(str, separator); - } - - /** - * Extracts a map of name/value pairs from the given string. Names are expected to be unique. - * - * @param str - * the string that contains a sequence of name/value pairs - * @param separator - * the name/value pairs separator - * - * @return a map of name/value pairs - */ - public Map<String, String> parse(final String str, final char separator) - { - if (str == null) - { - return new HashMap<>(); - } - return parse(str.toCharArray(), separator); - } - - /** - * Extracts a map of name/value pairs from the given array of characters. Names are expected to - * be unique. - * - * @param chars - * the array of characters that contains a sequence of name/value pairs - * @param separator - * the name/value pairs separator - * - * @return a map of name/value pairs - */ - public Map<String, String> parse(final char[] chars, final char separator) - { - if (chars == null) - { - return new HashMap<>(); - } - return parse(chars, 0, chars.length, separator); - } - - /** - * Extracts a map of name/value pairs from the given array of characters. Names are expected to - * be unique. - * - * @param chars - * the array of characters that contains a sequence of name/value pairs - * @param offset - * - the initial offset. - * @param length - * - the length. - * @param separator - * the name/value pairs separator - * - * @return a map of name/value pairs - */ - public Map<String, String> parse(final char[] chars, final int offset, final int length, - final char separator) - { - - if (chars == null) - { - return new HashMap<>(); - } - Map<String, String> params = new HashMap<>(); - this.chars = chars; - pos = offset; - len = length; - - String paramName = null; - String paramValue = null; - while (hasChar()) - { - paramName = parseToken(new char[] { '=', separator }); - paramValue = null; - if (hasChar() && (chars[pos] == '=')) - { - pos++; // skip '=' - paramValue = parseQuotedToken(new char[] { separator }); - } - if (hasChar() && (chars[pos] == separator)) - { - pos++; // skip separator - } - if ((paramName != null) && (paramName.length() > 0)) - { - if (lowerCaseNames) - { - paramName = paramName.toLowerCase(); - } - params.put(paramName, paramValue); - } - } - return params; - } -} http://git-wip-us.apache.org/repos/asf/wicket/blob/99fcd91f/wicket-util/src/main/java/org/apache/wicket/util/upload/ProgressListener.java ---------------------------------------------------------------------- diff --git a/wicket-util/src/main/java/org/apache/wicket/util/upload/ProgressListener.java b/wicket-util/src/main/java/org/apache/wicket/util/upload/ProgressListener.java deleted file mode 100644 index eed1ff6..0000000 --- a/wicket-util/src/main/java/org/apache/wicket/util/upload/ProgressListener.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.wicket.util.upload; - - -/** - * The {@link ProgressListener} may be used to display a progress bar or do stuff like that. - */ -public interface ProgressListener -{ - /** - * Updates the listeners status information. - * - * @param pBytesRead - * The total number of bytes, which have been read so far. - * @param pContentLength - * The total number of bytes, which are being read. May be -1, if this number is - * unknown. - * @param pItems - * The number of the field, which is currently being read. (0 = no item so far, 1 = - * first item is being read, ...) - */ - void update(long pBytesRead, long pContentLength, int pItems); -} http://git-wip-us.apache.org/repos/asf/wicket/blob/99fcd91f/wicket-util/src/main/java/org/apache/wicket/util/upload/RequestContext.java ---------------------------------------------------------------------- diff --git a/wicket-util/src/main/java/org/apache/wicket/util/upload/RequestContext.java b/wicket-util/src/main/java/org/apache/wicket/util/upload/RequestContext.java deleted file mode 100644 index f0a8597..0000000 --- a/wicket-util/src/main/java/org/apache/wicket/util/upload/RequestContext.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.wicket.util.upload; - -import java.io.IOException; -import java.io.InputStream; - -/** - * <p> - * Abstracts access to the request information needed for file uploads. This interface should be - * implemented for each type of request that may be handled by FileUpload, such as servlets and - * portlets. - * </p> - * - * @author <a href="mailto:[email protected]">Martin Cooper</a> - */ -public interface RequestContext -{ - - /** - * Retrieve the character encoding for the request. - * - * @return The character encoding for the request. - */ - String getCharacterEncoding(); - - /** - * Retrieve the content type of the request. - * - * @return The content type of the request. - */ - String getContentType(); - - /** - * Retrieve the content length of the request. - * - * @return The content length of the request. - */ - int getContentLength(); - - /** - * Retrieve the input stream for the request. - * - * @return The input stream for the request. - * - * @throws IOException - * if a problem occurs. - */ - InputStream getInputStream() throws IOException; -} http://git-wip-us.apache.org/repos/asf/wicket/blob/99fcd91f/wicket-util/src/main/java/org/apache/wicket/util/upload/ServletFileUpload.java ---------------------------------------------------------------------- diff --git a/wicket-util/src/main/java/org/apache/wicket/util/upload/ServletFileUpload.java b/wicket-util/src/main/java/org/apache/wicket/util/upload/ServletFileUpload.java deleted file mode 100644 index 7e1062c..0000000 --- a/wicket-util/src/main/java/org/apache/wicket/util/upload/ServletFileUpload.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.wicket.util.upload; - -import java.io.IOException; -import java.util.List; - -import javax.servlet.http.HttpServletRequest; - -/** - * <p> - * High level API for processing file uploads. - * </p> - * - * <p> - * This class handles multiple files per single HTML widget, sent using <code>multipart/mixed</code> - * encoding type, as specified by <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Use - * {@link #parseRequest(HttpServletRequest)} to acquire a list of - * {@link org.apache.wicket.util.upload.FileItem}s associated with a given HTML widget. - * </p> - * - * <p> - * How the data for individual parts is stored is determined by the factory used to create them; a - * given part may be in memory, on disk, or somewhere else. - * </p> - * - * @author <a href="mailto:[email protected]">Rafal Krzewski</a> - * @author <a href="mailto:[email protected]">Daniel Rall</a> - * @author <a href="mailto:[email protected]">Jason van Zyl</a> - * @author <a href="mailto:[email protected]">John McNally</a> - * @author <a href="mailto:[email protected]">Martin Cooper</a> - * @author Sean C. Sullivan - */ -public class ServletFileUpload extends FileUpload -{ - - // ---------------------------------------------------------- Class methods - - - /** - * Utility method that determines whether the request contains multipart content. - * - * @param request - * The servlet request to be evaluated. Must be non-null. - * - * @return <code>true</code> if the request is multipart; <code>false</code> otherwise. - */ - public static final boolean isMultipartContent(final HttpServletRequest request) - { - if (!"post".equals(request.getMethod().toLowerCase())) - { - return false; - } - String contentType = request.getContentType(); - if (contentType == null) - { - return false; - } - if (contentType.toLowerCase().startsWith(MULTIPART)) - { - return true; - } - return false; - } - - - // ----------------------------------------------------------- Constructors - - - /** - * Constructs an uninitialised instance of this class. A factory must be configured, using - * <code>setFileItemFactory()</code>, before attempting to parse requests. - * - * @see FileUpload#FileUpload(FileItemFactory) - */ - public ServletFileUpload() - { - super(); - } - - - /** - * Constructs an instance of this class which uses the supplied factory to create - * <code>FileItem</code> instances. - * - * @see FileUpload#FileUpload() - * @param fileItemFactory - * The factory to use for creating file items. - */ - public ServletFileUpload(final FileItemFactory fileItemFactory) - { - super(fileItemFactory); - } - - - // --------------------------------------------------------- Public methods - - - /** - * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant - * <code>multipart/form-data</code> stream. - * - * @param request - * The servlet request to be parsed. - * - * @return A list of <code>FileItem</code> instances parsed from the request, in the order that - * they were transmitted. - * - * @throws FileUploadException - * if there are problems reading/parsing the request or storing files. - */ - public List<FileItem> parseRequest(final HttpServletRequest request) throws FileUploadException - { - return parseRequest(new ServletRequestContext(request)); - } - - - /** - * Processes an <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> compliant - * <code>multipart/form-data</code> stream. - * - * @param request - * The servlet request to be parsed. - * - * @return An iterator to instances of <code>FileItemStream</code> parsed from the request, in - * the order that they were transmitted. - * - * @throws FileUploadException - * if there are problems reading/parsing the request or storing files. - * @throws IOException - * An I/O error occurred. This may be a network error while communicating with the - * client or a problem while storing the uploaded content. - */ - public FileItemIterator getItemIterator(final HttpServletRequest request) - throws FileUploadException, IOException - { - return super.getItemIterator(new ServletRequestContext(request)); - } -} http://git-wip-us.apache.org/repos/asf/wicket/blob/99fcd91f/wicket-util/src/main/java/org/apache/wicket/util/upload/ServletRequestContext.java ---------------------------------------------------------------------- diff --git a/wicket-util/src/main/java/org/apache/wicket/util/upload/ServletRequestContext.java b/wicket-util/src/main/java/org/apache/wicket/util/upload/ServletRequestContext.java deleted file mode 100644 index 10fb6c9..0000000 --- a/wicket-util/src/main/java/org/apache/wicket/util/upload/ServletRequestContext.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.wicket.util.upload; - -import java.io.IOException; -import java.io.InputStream; - -import javax.servlet.http.HttpServletRequest; - -/** - * <p> - * Provides access to the request information needed for a request made to an HTTP servlet. - * </p> - * - * @author <a href="mailto:[email protected]">Martin Cooper</a> - */ -public class ServletRequestContext implements RequestContext -{ - - // ----------------------------------------------------- Instance Variables - - /** - * The request for which the context is being provided. - */ - private final HttpServletRequest request; - - - // ----------------------------------------------------------- Constructors - - /** - * Construct a context for this request. - * - * @param request - * The request to which this context applies. - */ - public ServletRequestContext(final HttpServletRequest request) - { - this.request = request; - } - - - // --------------------------------------------------------- Public Methods - - /** - * Retrieve the character encoding for the request. - * - * @return The character encoding for the request. - */ - @Override - public String getCharacterEncoding() - { - return request.getCharacterEncoding(); - } - - /** - * Retrieve the content type of the request. - * - * @return The content type of the request. - */ - @Override - public String getContentType() - { - return request.getContentType(); - } - - /** - * Retrieve the content length of the request. - * - * @return The content length of the request. - */ - @Override - public int getContentLength() - { - return request.getContentLength(); - } - - /** - * Retrieve the input stream for the request. - * - * @return The input stream for the request. - * - * @throws IOException - * if a problem occurs. - */ - @Override - public InputStream getInputStream() throws IOException - { - return request.getInputStream(); - } - - /** - * @see java.lang.Object#toString() - */ - @Override - public String toString() - { - return "ContentLength=" + getContentLength() + ", ContentType=" + getContentType(); - } -} http://git-wip-us.apache.org/repos/asf/wicket/blob/99fcd91f/wicket-util/src/main/java/org/apache/wicket/util/upload/package.html ---------------------------------------------------------------------- diff --git a/wicket-util/src/main/java/org/apache/wicket/util/upload/package.html b/wicket-util/src/main/java/org/apache/wicket/util/upload/package.html deleted file mode 100644 index 59b6480..0000000 --- a/wicket-util/src/main/java/org/apache/wicket/util/upload/package.html +++ /dev/null @@ -1,30 +0,0 @@ -<!-- - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You under the Apache License, Version 2.0 - (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<!DOCTYPE HTML PUBLIC "-//W3C/DTD HTML 3.2 Final//NL"> -<html> - <head> - <title> - org.apache.wicket.util.upload package - </title> - </head> - <body> - <p> - Protocol independent upload utilities. Copied from the - Apache Jakarta Commons FileUpload project (1.1-dev, HEAD at 21st of august 2005). - </p> - </body> -</html> \ No newline at end of file
