View Javadoc

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