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