    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.fileupload;
    018
    019import static java.lang.String.format;
    020
    021import java.io.ByteArrayOutputStream;
    022import java.io.IOException;
    023import java.io.InputStream;
    024import java.io.OutputStream;
    025import java.io.UnsupportedEncodingException;
    026
    027import org.apache.commons.fileupload.FileUploadBase.FileUploadIOException;
    028import org.apache.commons.fileupload.util.Closeable;
    029import org.apache.commons.fileupload.util.Streams;
    030
    031/**
    032 * <p> Low level API for processing file uploads.
    033 *
    034 * <p> This class can be used to process data streams conforming to MIME
    035 * 'multipart' format as defined in
    036 * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Arbitrarily
    037 * large amounts of data in the stream can be processed under constant
    038 * memory usage.
    039 *
    040 * <p> The format of the stream is defined in the following way:<br>
    041 *
    042 * <code>
    043 *   multipart-body := preamble 1*encapsulation close-delimiter epilogue<br>
    044 *   encapsulation := delimiter body CRLF<br>
    045 *   delimiter := "--" boundary CRLF<br>
    046 *   close-delimiter := "--" boundary "--"<br>
    047 *   preamble := &lt;ignore&gt;<br>
    048 *   epilogue := &lt;ignore&gt;<br>
    049 *   body := header-part CRLF body-part<br>
    050 *   header-part := 1*header CRLF<br>
    051 *   header := header-name ":" header-value<br>
    052 *   header-name := &lt;printable ascii characters except ":"&gt;<br>
    053 *   header-value := &lt;any ascii characters except CR &amp; LF&gt;<br>
    054 *   body-data := &lt;arbitrary data&gt;<br>
    055 * </code>
    056 *
    057 * <p>Note that body-data can contain another mulipart entity.  There
    058 * is limited support for single pass processing of such nested
    059 * streams.  The nested stream is <strong>required</strong> to have a
    060 * boundary token of the same length as the parent stream (see {@link
    061 * #setBoundary(byte[])}).
    062 *
    063 * <p>Here is an example of usage of this class.<br>
    064 *
    065 * <pre>
    066 *   try {
    067 *     MultipartStream multipartStream = new MultipartStream(input, boundary);
    068 *     boolean nextPart = multipartStream.skipPreamble();
    069 *     OutputStream output;
    070 *     while(nextPart) {
    071 *       String header = multipartStream.readHeaders();
    072 *       // process headers
    073 *       // create some output stream
    074 *       multipartStream.readBodyData(output);
    075 *       nextPart = multipartStream.readBoundary();
    076 *     }
    077 *   } catch(MultipartStream.MalformedStreamException e) {
    078 *     // the stream failed to follow required syntax
    079 *   } catch(IOException e) {
    080 *     // a read or write error occurred
    081 *   }
    082 * </pre>
    083 */
    084public class MultipartStream {
    085
    086    /**
    087     * Internal class, which is used to invoke the
    088     * {@link ProgressListener}.
    089     */
    090    public static class ProgressNotifier {
    091
    092        /**
    093         * The listener to invoke.
    094         */
    095        private final ProgressListener listener;
    096
    097        /**
    098         * Number of expected bytes, if known, or -1.
    099         */
    100        private final long contentLength;
    101
    102        /**
    103         * Number of bytes, which have been read so far.
    104         */
    105        private long bytesRead;
    106
    107        /**
    108         * Number of items, which have been read so far.
    109         */
    110        private int items;
    111
    112        /**
    113         * Creates a new instance with the given listener
    114         * and content length.
    115         *
    116         * @param pListener The listener to invoke.
    117         * @param pContentLength The expected content length.
    118         */
    119        ProgressNotifier(ProgressListener pListener, long pContentLength) {
    120            listener = pListener;
    121            contentLength = pContentLength;
    122        }
    123
    124        /**
    125         * Called to indicate that bytes have been read.
    126         *
    127         * @param pBytes Number of bytes, which have been read.
    128         */
    129        void noteBytesRead(int pBytes) {
    130            /* Indicates, that the given number of bytes have been read from
    131             * the input stream.
    132             */
    133            bytesRead += pBytes;
    134            notifyListener();
    135        }
    136
    137        /**
    138         * Called to indicate, that a new file item has been detected.
    139         */
    140        void noteItem() {
    141            ++items;
    142            notifyListener();
    143        }
    144
    145        /**
    146         * Called for notifying the listener.
    147         */
    148        private void notifyListener() {
    149            if (listener != null) {
    150                listener.update(bytesRead, contentLength, items);
    151            }
    152        }
    153
    154    }
    155
    156    // ----------------------------------------------------- Manifest constants
    157
    158    /**
    159     * The Carriage Return ASCII character value.
    160     */
    161    public static final byte CR = 0x0D;
    162
    163    /**
    164     * The Line Feed ASCII character value.
    165     */
    166    public static final byte LF = 0x0A;
    167
    168    /**
    169     * The dash (-) ASCII character value.
    170     */
    171    public static final byte DASH = 0x2D;
    172
    173    /**
    174     * The maximum length of <code>header-part</code> that will be
    175     * processed (10 kilobytes = 10240 bytes.).
    176     */
    177    public static final int HEADER_PART_SIZE_MAX = 10240;
    178
    179    /**
    180     * The default length of the buffer used for processing a request.
    181     */
    182    protected static final int DEFAULT_BUFSIZE = 4096;
    183
    184    /**
    185     * A byte sequence that marks the end of <code>header-part</code>
    186     * (<code>CRLFCRLF</code>).
    187     */
    188    protected static final byte[] HEADER_SEPARATOR = {CR, LF, CR, LF};
    189
    190    /**
    191     * A byte sequence that that follows a delimiter that will be
    192     * followed by an encapsulation (<code>CRLF</code>).
    193     */
    194    protected static final byte[] FIELD_SEPARATOR = {CR, LF};
    195
    196    /**
    197     * A byte sequence that that follows a delimiter of the last
    198     * encapsulation in the stream (<code>--</code>).
    199     */
    200    protected static final byte[] STREAM_TERMINATOR = {DASH, DASH};
    201
    202    /**
    203     * A byte sequence that precedes a boundary (<code>CRLF--</code>).
    204     */
    205    protected static final byte[] BOUNDARY_PREFIX = {CR, LF, DASH, DASH};
    206
    207    // ----------------------------------------------------------- Data members
    208
    209    /**
    210     * The input stream from which data is read.
    211     */
    212    private final InputStream input;
    213
    214    /**
    215     * The length of the boundary token plus the leading <code>CRLF--</code>.
    216     */
    217    private int boundaryLength;
    218
    219    /**
    220     * The amount of data, in bytes, that must be kept in the buffer in order
    221     * to detect delimiters reliably.
    222     */
    223    private final int keepRegion;
    224
    225    /**
    226     * The byte sequence that partitions the stream.
    227     */
    228    private final byte[] boundary;
    229
    230    /**
    231     * The table for Knuth-Morris-Pratt search algorithm.
    232     */
    233    private final int[] boundaryTable;
    234
    235    /**
    236     * The length of the buffer used for processing the request.
    237     */
    238    private final int bufSize;
    239
    240    /**
    241     * The buffer used for processing the request.
    242     */
    243    private final byte[] buffer;
    244
    245    /**
    246     * The index of first valid character in the buffer.
    247     * <br>
    248     * 0 <= head < bufSize
    249     */
    250    private int head;
    251
    252    /**
    253     * The index of last valid character in the buffer + 1.
    254     * <br>
    255     * 0 <= tail <= bufSize
    256     */
    257    private int tail;
    258
    259    /**
    260     * The content encoding to use when reading headers.
    261     */
    262    private String headerEncoding;
    263
    264    /**
    265     * The progress notifier, if any, or null.
    266     */
    267    private final ProgressNotifier notifier;
    268
    269    // ----------------------------------------------------------- Constructors
    270
    271    /**
    272     * Creates a new instance.
    273     *
    274     * @deprecated 1.2.1 Use {@link #MultipartStream(InputStream, byte[], int,
    275     * ProgressNotifier)}
    276     */
    277    @Deprecated
    278    public MultipartStream() {
    279        this(null, null, null);
    280    }
    281
    282    /**
    283     * <p> Constructs a <code>MultipartStream</code> with a custom size buffer
    284     * and no progress notifier.
    285     *
    286     * <p> Note that the buffer must be at least big enough to contain the
    287     * boundary string, plus 4 characters for CR/LF and double dash, plus at
    288     * least one byte of data.  Too small a buffer size setting will degrade
    289     * performance.
    290     *
    291     * @param input    The <code>InputStream</code> to serve as a data source.
    292     * @param boundary The token used for dividing the stream into
    293     *                 <code>encapsulations</code>.
    294     * @param bufSize  The size of the buffer to be used, in bytes.
    295     *
    296     * @deprecated 1.2.1 Use {@link #MultipartStream(InputStream, byte[], int,
    297     * ProgressNotifier)}.
    298     */
    299    @Deprecated
    300    public MultipartStream(InputStream input, byte[] boundary, int bufSize) {
    301        this(input, boundary, bufSize, null);
    302    }
    303
    304    /**
    305     * <p> Constructs a <code>MultipartStream</code> with a custom size buffer.
    306     *
    307     * <p> Note that the buffer must be at least big enough to contain the
    308     * boundary string, plus 4 characters for CR/LF and double dash, plus at
    309     * least one byte of data.  Too small a buffer size setting will degrade
    310     * performance.
    311     *
    312     * @param input    The <code>InputStream</code> to serve as a data source.
    313     * @param boundary The token used for dividing the stream into
    314     *                 <code>encapsulations</code>.
    315     * @param bufSize  The size of the buffer to be used, in bytes.
    316     * @param pNotifier The notifier, which is used for calling the
    317     *                  progress listener, if any.
    318     *
    319     * @throws IllegalArgumentException If the buffer size is too small
    320     *
    321     * @since 1.3.1
    322     */
    323    public MultipartStream(InputStream input,
    324            byte[] boundary,
    325            int bufSize,
    326            ProgressNotifier pNotifier) {
    327
    328        if (boundary == null) {
    329            throw new IllegalArgumentException("boundary may not be null");
    330        }
    331        // We prepend CR/LF to the boundary to chop trailing CR/LF from
    332        // body-data tokens.
    333        this.boundaryLength = boundary.length + BOUNDARY_PREFIX.length;
    334        if (bufSize < this.boundaryLength + 1) {
    335            throw new IllegalArgumentException(
    336                    "The buffer size specified for the MultipartStream is too small");
    337        }
    338
    339        this.input = input;
    340        this.bufSize = Math.max(bufSize, boundaryLength * 2);
    341        this.buffer = new byte[this.bufSize];
    342        this.notifier = pNotifier;
    343
    344        this.boundary = new byte[this.boundaryLength];
    345        this.boundaryTable = new int[this.boundaryLength + 1];
    346        this.keepRegion = this.boundary.length;
    347
    348        System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0,
    349                BOUNDARY_PREFIX.length);
    350        System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length,
    351                boundary.length);
    352        computeBoundaryTable();
    353
    354        head = 0;
    355        tail = 0;
    356    }
    357
    358    /**
    359     * <p> Constructs a <code>MultipartStream</code> with a default size buffer.
    360     *
    361     * @param input    The <code>InputStream</code> to serve as a data source.
    362     * @param boundary The token used for dividing the stream into
    363     *                 <code>encapsulations</code>.
    364     * @param pNotifier An object for calling the progress listener, if any.
    365     *
    366     *
    367     * @see #MultipartStream(InputStream, byte[], int, ProgressNotifier)
    368     */
    369    MultipartStream(InputStream input,
    370            byte[] boundary,
    371            ProgressNotifier pNotifier) {
    372        this(input, boundary, DEFAULT_BUFSIZE, pNotifier);
    373    }
    374
    375    /**
    376     * <p> Constructs a <code>MultipartStream</code> with a default size buffer.
    377     *
    378     * @param input    The <code>InputStream</code> to serve as a data source.
    379     * @param boundary The token used for dividing the stream into
    380     *                 <code>encapsulations</code>.
    381     *
    382     * @deprecated 1.2.1 Use {@link #MultipartStream(InputStream, byte[], int,
    383     *  ProgressNotifier)}.
    384     */
    385    @Deprecated
    386    public MultipartStream(InputStream input,
    387            byte[] boundary) {
    388        this(input, boundary, DEFAULT_BUFSIZE, null);
    389    }
    390
    391    // --------------------------------------------------------- Public methods
    392
    393    /**
    394     * Retrieves the character encoding used when reading the headers of an
    395     * individual part. When not specified, or <code>null</code>, the platform
    396     * default encoding is used.
    397     *
    398     * @return The encoding used to read part headers.
    399     */
    400    public String getHeaderEncoding() {
    401        return headerEncoding;
    402    }
    403
    404    /**
    405     * Specifies the character encoding to be used when reading the headers of
    406     * individual parts. When not specified, or <code>null</code>, the platform
    407     * default encoding is used.
    408     *
    409     * @param encoding The encoding used to read part headers.
    410     */
    411    public void setHeaderEncoding(String encoding) {
    412        headerEncoding = encoding;
    413    }
    414
    415    /**
    416     * Reads a byte from the <code>buffer</code>, and refills it as
    417     * necessary.
    418     *
    419     * @return The next byte from the input stream.
    420     *
    421     * @throws IOException if there is no more data available.
    422     */
    423    public byte readByte() throws IOException {
    424        // Buffer depleted ?
    425        if (head == tail) {
    426            head = 0;
    427            // Refill.
    428            tail = input.read(buffer, head, bufSize);
    429            if (tail == -1) {
    430                // No more data available.
    431                throw new IOException("No more data is available");
    432            }
    433            if (notifier != null) {
    434                notifier.noteBytesRead(tail);
    435            }
    436        }
    437        return buffer[head++];
    438    }
    439
    440    /**
    441     * Skips a <code>boundary</code> token, and checks whether more
    442     * <code>encapsulations</code> are contained in the stream.
    443     *
    444     * @return <code>true</code> if there are more encapsulations in
    445     *         this stream; <code>false</code> otherwise.
    446     *
    447     * @throws FileUploadIOException if the bytes read from the stream exceeded the size limits
    448     * @throws MalformedStreamException if the stream ends unexpectedly or
    449     *                                  fails to follow required syntax.
    450     */
    451    public boolean readBoundary()
    452            throws FileUploadIOException, MalformedStreamException {
    453        byte[] marker = new byte[2];
    454        boolean nextChunk = false;
    455
    456        head += boundaryLength;
    457        try {
    458            marker[0] = readByte();
    459            if (marker[0] == LF) {
    460                // Work around IE5 Mac bug with input type=image.
    461                // Because the boundary delimiter, not including the trailing
    462                // CRLF, must not appear within any file (RFC 2046, section
    463                // 5.1.1), we know the missing CR is due to a buggy browser
    464                // rather than a file containing something similar to a
    465                // boundary.
    466                return true;
    467            }
    468
    469            marker[1] = readByte();
    470            if (arrayequals(marker, STREAM_TERMINATOR, 2)) {
    471                nextChunk = false;
    472            } else if (arrayequals(marker, FIELD_SEPARATOR, 2)) {
    473                nextChunk = true;
    474            } else {
    475                throw new MalformedStreamException(
    476                "Unexpected characters follow a boundary");
    477            }
    478        } catch (FileUploadIOException e) {
    479            // wraps a SizeException, re-throw as it will be unwrapped later
    480            throw e;
    481        } catch (IOException e) {
    482            throw new MalformedStreamException("Stream ended unexpectedly");
    483        }
    484        return nextChunk;
    485    }
    486
    487    /**
    488     * <p>Changes the boundary token used for partitioning the stream.
    489     *
    490     * <p>This method allows single pass processing of nested multipart
    491     * streams.
    492     *
    493     * <p>The boundary token of the nested stream is <code>required</code>
    494     * to be of the same length as the boundary token in parent stream.
    495     *
    496     * <p>Restoring the parent stream boundary token after processing of a
    497     * nested stream is left to the application.
    498     *
    499     * @param boundary The boundary to be used for parsing of the nested
    500     *                 stream.
    501     *
    502     * @throws IllegalBoundaryException if the <code>boundary</code>
    503     *                                  has a different length than the one
    504     *                                  being currently parsed.
    505     */
    506    public void setBoundary(byte[] boundary)
    507            throws IllegalBoundaryException {
    508        if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) {
    509            throw new IllegalBoundaryException(
    510            "The length of a boundary token cannot be changed");
    511        }
    512        System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length,
    513                boundary.length);
    514        computeBoundaryTable();
    515    }
    516
    517    /**
    518     * Compute the table used for Knuth-Morris-Pratt search algorithm.
    519     */
    520    private void computeBoundaryTable() {
    521        int position = 2;
    522        int candidate = 0;
    523
    524        boundaryTable[0] = -1;
    525        boundaryTable[1] = 0;
    526
    527        while (position <= boundaryLength) {
    528            if (boundary[position - 1] == boundary[candidate]) {
    529                boundaryTable[position] = candidate + 1;
    530                candidate++;
    531                position++;
    532            } else if (candidate > 0) {
    533                candidate = boundaryTable[candidate];
    534            } else {
    535                boundaryTable[position] = 0;
    536                position++;
    537            }
    538        }
    539    }
    540
    541    /**
    542     * <p>Reads the <code>header-part</code> of the current
    543     * <code>encapsulation</code>.
    544     *
    545     * <p>Headers are returned verbatim to the input stream, including the
    546     * trailing <code>CRLF</code> marker. Parsing is left to the
    547     * application.
    548     *
    549     * <p><strong>TODO</strong> allow limiting maximum header size to
    550     * protect against abuse.
    551     *
    552     * @return The <code>header-part</code> of the current encapsulation.
    553     *
    554     * @throws FileUploadIOException if the bytes read from the stream exceeded the size limits.
    555     * @throws MalformedStreamException if the stream ends unexpectedly.
    556     */
    557    public String readHeaders() throws FileUploadIOException, MalformedStreamException {
    558        int i = 0;
    559        byte b;
    560        // to support multi-byte characters
    561        ByteArrayOutputStream baos = new ByteArrayOutputStream();
    562        int size = 0;
    563        while (i < HEADER_SEPARATOR.length) {
    564            try {
    565                b = readByte();
    566            } catch (FileUploadIOException e) {
    567                // wraps a SizeException, re-throw as it will be unwrapped later
    568                throw e;
    569            } catch (IOException e) {
    570                throw new MalformedStreamException("Stream ended unexpectedly");
    571            }
    572            if (++size > HEADER_PART_SIZE_MAX) {
    573                throw new MalformedStreamException(
    574                        format("Header section has more than %s bytes (maybe it is not properly terminated)",
    575                               Integer.valueOf(HEADER_PART_SIZE_MAX)));
    576            }
    577            if (b == HEADER_SEPARATOR[i]) {
    578                i++;
    579            } else {
    580                i = 0;
    581            }
    582            baos.write(b);
    583        }
    584
    585        String headers = null;
    586        if (headerEncoding != null) {
    587            try {
    588                headers = baos.toString(headerEncoding);
    589            } catch (UnsupportedEncodingException e) {
    590                // Fall back to platform default if specified encoding is not
    591                // supported.
    592                headers = baos.toString();
    593            }
    594        } else {
    595            headers = baos.toString();
    596        }
    597
    598        return headers;
    599    }
    600
    601    /**
    602     * <p>Reads <code>body-data</code> from the current
    603     * <code>encapsulation</code> and writes its contents into the
    604     * output <code>Stream</code>.
    605     *
    606     * <p>Arbitrary large amounts of data can be processed by this
    607     * method using a constant size buffer. (see {@link
    608     * #MultipartStream(InputStream,byte[],int,
    609     *   MultipartStream.ProgressNotifier) constructor}).
    610     *
    611     * @param output The <code>Stream</code> to write data into. May
    612     *               be null, in which case this method is equivalent
    613     *               to {@link #discardBodyData()}.
    614     *
    615     * @return the amount of data written.
    616     *
    617     * @throws MalformedStreamException if the stream ends unexpectedly.
    618     * @throws IOException              if an i/o error occurs.
    619     */
    620    public int readBodyData(OutputStream output)
    621            throws MalformedStreamException, IOException {
    622        return (int) Streams.copy(newInputStream(), output, false); // N.B. Streams.copy closes the input stream
    623    }
    624
    625    /**
    626     * Creates a new {@link ItemInputStream}.
    627     * @return A new instance of {@link ItemInputStream}.
    628     */
    629    ItemInputStream newInputStream() {
    630        return new ItemInputStream();
    631    }
    632
    633    /**
    634     * <p> Reads <code>body-data</code> from the current
    635     * <code>encapsulation</code> and discards it.
    636     *
    637     * <p>Use this method to skip encapsulations you don't need or don't
    638     * understand.
    639     *
    640     * @return The amount of data discarded.
    641     *
    642     * @throws MalformedStreamException if the stream ends unexpectedly.
    643     * @throws IOException              if an i/o error occurs.
    644     */
    645    public int discardBodyData() throws MalformedStreamException, IOException {
    646        return readBodyData(null);
    647    }
    648
    649    /**
    650     * Finds the beginning of the first <code>encapsulation</code>.
    651     *
    652     * @return <code>true</code> if an <code>encapsulation</code> was found in
    653     *         the stream.
    654     *
    655     * @throws IOException if an i/o error occurs.
    656     */
    657    public boolean skipPreamble() throws IOException {
    658        // First delimiter may be not preceeded with a CRLF.
    659        System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2);
    660        boundaryLength = boundary.length - 2;
    661        computeBoundaryTable();
    662        try {
    663            // Discard all data up to the delimiter.
    664            discardBodyData();
    665
    666            // Read boundary - if succeeded, the stream contains an
    667            // encapsulation.
    668            return readBoundary();
    669        } catch (MalformedStreamException e) {
    670            return false;
    671        } finally {
    672            // Restore delimiter.
    673            System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2);
    674            boundaryLength = boundary.length;
    675            boundary[0] = CR;
    676            boundary[1] = LF;
    677            computeBoundaryTable();
    678        }
    679    }
    680
    681    /**
    682     * Compares <code>count</code> first bytes in the arrays
    683     * <code>a</code> and <code>b</code>.
    684     *
    685     * @param a     The first array to compare.
    686     * @param b     The second array to compare.
    687     * @param count How many bytes should be compared.
    688     *
    689     * @return <code>true</code> if <code>count</code> first bytes in arrays
    690     *         <code>a</code> and <code>b</code> are equal.
    691     */
    692    public static boolean arrayequals(byte[] a,
    693            byte[] b,
    694            int count) {
    695        for (int i = 0; i < count; i++) {
    696            if (a[i] != b[i]) {
    697                return false;
    698            }
    699        }
    700        return true;
    701    }
    702
    703    /**
    704     * Searches for a byte of specified value in the <code>buffer</code>,
    705     * starting at the specified <code>position</code>.
    706     *
    707     * @param value The value to find.
    708     * @param pos   The starting position for searching.
    709     *
    710     * @return The position of byte found, counting from beginning of the
    711     *         <code>buffer</code>, or <code>-1</code> if not found.
    712     */
    713    protected int findByte(byte value,
    714            int pos) {
    715        for (int i = pos; i < tail; i++) {
    716            if (buffer[i] == value) {
    717                return i;
    718            }
    719        }
    720
    721        return -1;
    722    }
    723
    724    /**
    725     * Searches for the <code>boundary</code> in the <code>buffer</code>
    726     * region delimited by <code>head</code> and <code>tail</code>.
    727     *
    728     * @return The position of the boundary found, counting from the
    729     *         beginning of the <code>buffer</code>, or <code>-1</code> if
    730     *         not found.
    731     */
    732    protected int findSeparator() {
    733
    734        int bufferPos = this.head;
    735        int tablePos = 0;
    736
    737        while (bufferPos < this.tail) {
    738            while (tablePos >= 0 && buffer[bufferPos] != boundary[tablePos]) {
    739                tablePos = boundaryTable[tablePos];
    740            }
    741            bufferPos++;
    742            tablePos++;
    743            if (tablePos == boundaryLength) {
    744                return bufferPos - boundaryLength;
    745            }
    746        }
    747        return -1;
    748    }
    749
    750    /**
    751     * Thrown to indicate that the input stream fails to follow the
    752     * required syntax.
    753     */
    754    public static class MalformedStreamException extends IOException {
    755
    756        /**
    757         * The UID to use when serializing this instance.
    758         */
    759        private static final long serialVersionUID = 6466926458059796677L;
    760
    761        /**
    762         * Constructs a <code>MalformedStreamException</code> with no
    763         * detail message.
    764         */
    765        public MalformedStreamException() {
    766            super();
    767        }
    768
    769        /**
    770         * Constructs an <code>MalformedStreamException</code> with
    771         * the specified detail message.
    772         *
    773         * @param message The detail message.
    774         */
    775        public MalformedStreamException(String message) {
    776            super(message);
    777        }
    778
    779    }
    780
    781    /**
    782     * Thrown upon attempt of setting an invalid boundary token.
    783     */
    784    public static class IllegalBoundaryException extends IOException {
    785
    786        /**
    787         * The UID to use when serializing this instance.
    788         */
    789        private static final long serialVersionUID = -161533165102632918L;
    790
    791        /**
    792         * Constructs an <code>IllegalBoundaryException</code> with no
    793         * detail message.
    794         */
    795        public IllegalBoundaryException() {
    796            super();
    797        }
    798
    799        /**
    800         * Constructs an <code>IllegalBoundaryException</code> with
    801         * the specified detail message.
    802         *
    803         * @param message The detail message.
    804         */
    805        public IllegalBoundaryException(String message) {
    806            super(message);
    807        }
    808
    809    }
    810
    811    /**
    812     * An {@link InputStream} for reading an items contents.
    813     */
    814    public class ItemInputStream extends InputStream implements Closeable {
    815
    816        /**
    817         * The number of bytes, which have been read so far.
    818         */
    819        private long total;
    820
    821        /**
    822         * The number of bytes, which must be hold, because
    823         * they might be a part of the boundary.
    824         */
    825        private int pad;
    826
    827        /**
    828         * The current offset in the buffer.
    829         */
    830        private int pos;
    831
    832        /**
    833         * Whether the stream is already closed.
    834         */
    835        private boolean closed;
    836
    837        /**
    838         * Creates a new instance.
    839         */
    840        ItemInputStream() {
    841            findSeparator();
    842        }
    843
    844        /**
    845         * Called for finding the separator.
    846         */
    847        private void findSeparator() {
    848            pos = MultipartStream.this.findSeparator();
    849            if (pos == -1) {
    850                if (tail - head > keepRegion) {
    851                    pad = keepRegion;
    852                } else {
    853                    pad = tail - head;
    854                }
    855            }
    856        }
    857
    858        /**
    859         * Returns the number of bytes, which have been read
    860         * by the stream.
    861         *
    862         * @return Number of bytes, which have been read so far.
    863         */
    864        public long getBytesRead() {
    865            return total;
    866        }
    867
    868        /**
    869         * Returns the number of bytes, which are currently
    870         * available, without blocking.
    871         *
    872         * @throws IOException An I/O error occurs.
    873         * @return Number of bytes in the buffer.
    874         */
    875        @Override
    876        public int available() throws IOException {
    877            if (pos == -1) {
    878                return tail - head - pad;
    879            }
    880            return pos - head;
    881        }
    882
    883        /**
    884         * Offset when converting negative bytes to integers.
    885         */
    886        private static final int BYTE_POSITIVE_OFFSET = 256;
    887
    888        /**
    889         * Returns the next byte in the stream.
    890         *
    891         * @return The next byte in the stream, as a non-negative
    892         *   integer, or -1 for EOF.
    893         * @throws IOException An I/O error occurred.
    894         */
    895        @Override
    896        public int read() throws IOException {
    897            if (closed) {
    898                throw new FileItemStream.ItemSkippedException();
    899            }
    900            if (available() == 0 && makeAvailable() == 0) {
    901                return -1;
    902            }
    903            ++total;
    904            int b = buffer[head++];
    905            if (b >= 0) {
    906                return b;
    907            }
    908            return b + BYTE_POSITIVE_OFFSET;
    909        }
    910
    911        /**
    912         * Reads bytes into the given buffer.
    913         *
    914         * @param b The destination buffer, where to write to.
    915         * @param off Offset of the first byte in the buffer.
    916         * @param len Maximum number of bytes to read.
    917         * @return Number of bytes, which have been actually read,
    918         *   or -1 for EOF.
    919         * @throws IOException An I/O error occurred.
    920         */
    921        @Override
    922        public int read(byte[] b, int off, int len) throws IOException {
    923            if (closed) {
    924                throw new FileItemStream.ItemSkippedException();
    925            }
    926            if (len == 0) {
    927                return 0;
    928            }
    929            int res = available();
    930            if (res == 0) {
    931                res = makeAvailable();
    932                if (res == 0) {
    933                    return -1;
    934                }
    935            }
    936            res = Math.min(res, len);
    937            System.arraycopy(buffer, head, b, off, res);
    938            head += res;
    939            total += res;
    940            return res;
    941        }
    942
    943        /**
    944         * Closes the input stream.
    945         *
    946         * @throws IOException An I/O error occurred.
    947         */
    948        @Override
    949        public void close() throws IOException {
    950            close(false);
    951        }
    952
    953        /**
    954         * Closes the input stream.
    955         *
    956         * @param pCloseUnderlying Whether to close the underlying stream
    957         *   (hard close)
    958         * @throws IOException An I/O error occurred.
    959         */
    960        public void close(boolean pCloseUnderlying) throws IOException {
    961            if (closed) {
    962                return;
    963            }
    964            if (pCloseUnderlying) {
    965                closed = true;
    966                input.close();
    967            } else {
    968                for (;;) {
    969                    int av = available();
    970                    if (av == 0) {
    971                        av = makeAvailable();
    972                        if (av == 0) {
    973                            break;
    974                        }
    975                    }
    976                    skip(av);
    977                }
    978            }
    979            closed = true;
    980        }
    981
    982        /**
    983         * Skips the given number of bytes.
    984         *
    985         * @param bytes Number of bytes to skip.
    986         * @return The number of bytes, which have actually been
    987         *   skipped.
    988         * @throws IOException An I/O error occurred.
    989         */
    990        @Override
    991        public long skip(long bytes) throws IOException {
    992            if (closed) {
    993                throw new FileItemStream.ItemSkippedException();
    994            }
    995            int av = available();
    996            if (av == 0) {
    997                av = makeAvailable();
    998                if (av == 0) {
    999                    return 0;
    1000                }
    1001            }
    1002            long res = Math.min(av, bytes);
    1003            head += res;
    1004            return res;
    1005        }
    1006
    1007        /**
    1008         * Attempts to read more data.
    1009         *
    1010         * @return Number of available bytes
    1011         * @throws IOException An I/O error occurred.
    1012         */
    1013        private int makeAvailable() throws IOException {
    1014            if (pos != -1) {
    1015                return 0;
    1016            }
    1017
    1018            // Move the data to the beginning of the buffer.
    1019            total += tail - head - pad;
    1020            System.arraycopy(buffer, tail - pad, buffer, 0, pad);
    1021
    1022            // Refill buffer with new data.
    1023            head = 0;
    1024            tail = pad;
    1025
    1026            for (;;) {
    1027                int bytesRead = input.read(buffer, tail, bufSize - tail);
    1028                if (bytesRead == -1) {
    1029                    // The last pad amount is left in the buffer.
    1030                    // Boundary can't be in there so signal an error
    1031                    // condition.
    1032                    final String msg = "Stream ended unexpectedly";
    1033                    throw new MalformedStreamException(msg);
    1034                }
    1035                if (notifier != null) {
    1036                    notifier.noteBytesRead(bytesRead);
    1037                }
    1038                tail += bytesRead;
    1039
    1040                findSeparator();
    1041                int av = available();
    1042
    1043                if (av > 0 || pos != -1) {
    1044                    return av;
    1045                }
    1046            }
    1047        }
    1048
    1049        /**
    1050         * Returns, whether the stream is closed.
    1051         *
    1052         * @return True, if the stream is closed, otherwise false.
    1053         */
    1054        @Override
    1055        public boolean isClosed() {
    1056            return closed;
    1057        }
    1058
    1059    }
    1060
    1061}