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