/*
 * $Header: /home/cvs/commons/fileupload-1.0/ja/src/org/apache/commons/fileupload/MultipartStream.java,v 1.4 2004/04/15 11:15:10 hioki Exp $
 * $Revision: 1.4 $
 * $Date: 2004/04/15 11:15:10 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001-2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */


package org.apache.commons.fileupload;


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;


/**
 * <p> ファイルアップロードを処理するための低レベルAPIです。
 * {@primary Low level API for processing file uploads.}
 *
 * <p>このクラスは <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a> に規定されている
 * MIME 'multipart' フォーマットに従うデータストリームを処理するために使用されます。
 * ストリーム内の(任意の)大量のデータを一定のメモリの使用量で処理することができます。
 * {@primary This class can be used to process data streams conforming to MIME
 * 'multipart' format as defined in
 * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>. Arbitrarily
 * large amounts of data in the stream can be processed under constant
 * memory usage.}
 *
 * <p> ストリームは以下のような形式で定義されています:
 * {@primary The format of the stream is defined in the following way:}<br>
 *
 * <code>
 *   multipart-body := preamble 1*encapsulation close-delimiter epilogue<br>
 *   encapsulation := delimiter body CRLF<br>
 *   delimiter := "--" boundary CRLF<br>
 *   close-delimiter := "--" boudary "--"<br>
 *   preamble := &lt;ignore&gt;<br>
 *   epilogue := &lt;ignore&gt;<br>
 *   body := header-part CRLF body-part<br>
 *   header-part := 1*header CRLF<br>
 *   header := header-name ":" header-value<br>
 *   header-name := &lt;printable ascii characters except ":"&gt;<br>
 *   header-value := &lt;any ascii characters except CR & LF&gt;<br>
 *   body-data := &lt;arbitrary data&gt;<br>
 * </code>
 *
 * <p>body-data が他の multipart エンティティを持つことができることに注意して下さい。
 * このような入れ子になったストリームの単独パスでの処理を制限つきでサポートします。
 * 入れ子になったストリームは<strong>必ず</strong>親のストリームと同じ長さのバウンダリトークンを
 * 持っていなくてはなりません({@link #setBoundary(byte[])}を参照)。
 * {@primary Note that body-data can contain another mulipart entity.  There
 * is limited support for single pass processing of such nested
 * streams.  The nested stream is <strong>required</strong> to have a
 * boundary token of the same length as the parent stream (see {@link
 * #setBoundary(byte[])}).}
 *
 * <p>以下にこのクラスに使用例を示します。
 * {@primary Here is an exaple of usage of this class.}<br>
 *
 * <pre>
 *    try {
 *        MultipartStream multipartStream = new MultipartStream(input,
 *                                                              boundary);
 *        boolean nextPart = malitPartStream.skipPreamble();
 *        OutputStream output;
 *        while(nextPart) {
 *            header = chunks.readHeader();
 *            // ヘッダを処理
 *            // 出力ストリームを生成
 *            multipartStream.readBodyPart(output);
 *            nextPart = multipartStream.readBoundary();
 *        }
 *    } catch(MultipartStream.MalformedStreamException e) {
 *          // ストリームが必要とする文法に従っていない
 *    } catch(IOException) {
 *          // 読み込みまたは書き出しにてエラーが発生
 *    }
 *
 * </pre>
 * {@primary <pre>    try {
 *        MultipartStream multipartStream = new MultipartStream(input,
 *                                                              boundary);
 *        boolean nextPart = malitPartStream.skipPreamble();
 *        OutputStream output;
 *        while(nextPart) {
 *            header = chunks.readHeader();
 *            // process headers
 *            // create some output stream
 *            multipartStream.readBodyPart(output);
 *            nextPart = multipartStream.readBoundary();
 *        &#125;
 *    &#125; catch(MultipartStream.MalformedStreamException e) {
 *          // the stream failed to follow required syntax
 *    &#125; catch(IOException) {
 *          // a read or write error occurred
 *    &#125;
 *
 * </pre>}
 *
 * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
 * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
 * @author Sean C. Sullivan
 * @translator 日置 聡
 * @editor 入江 弘憲
 * @status completion
 * @update 2003/04/16
 *
 * @version $Id: MultipartStream.java,v 1.4 2004/04/15 11:15:10 hioki Exp $
 */
public class MultipartStream
{

    // ----------------------------------------------------- Manifest constants


    /**
     * 処理される <code>header-part</code> の最大の長さ(10キロバイト = 10240バイト)。
     * {@primary The maximum length of <code>header-part</code> that will be
     * processed (10 kilobytes = 10240 bytes.).}
     */
    public static final int HEADER_PART_SIZE_MAX = 10240;


    /**
     * リクエストを処理する際に使用されるバッファのデフォルトの長さ。
     * {@primary The default length of the buffer used for processing a request.}
     */
    protected static final int DEFAULT_BUFSIZE = 4096;


    /**
     * <code>header-part</code> の終わりを示すバイト列(<code>CRLFCRLF</code>)。
     * {@primary A byte sequence that marks the end of <code>header-part</code>
     * (<code>CRLFCRLF</code>).}
     */
    protected static final byte[] HEADER_SEPARATOR = {0x0D, 0x0A, 0x0D, 0x0A};


    /**
     * encapsulation の前に来る delimiter に続くバイト列(<code>CRLF</code>)。
     * {@primary A byte sequence that that follows a delimiter that will be
     * followed by an encapsulation (<code>CRLF</code>).}
     */
    protected static final byte[] FIELD_SEPARATOR = { 0x0D, 0x0A };


    /**
     * ストリーム内の最後の encapsulation のdelimiter に続くバイト列(<code>--</code>)。
     * {@primary A byte sequence that that follows a delimiter of the last
     * encapsulation in the stream (<code>--</code>).}
     */
    protected static final byte[] STREAM_TERMINATOR = { 0x2D, 0x2D };


    // ----------------------------------------------------------- Data members


    /**
     * データの読み込み対象となる入力ストリーム。
     * {@primary The input stream from which data is read.}
     */
    private InputStream input;


    /**
     * バウンダリトークンと <code>CRLF--</code> を加えた長さ。
     * {@primary The length of the boundary token plus the leading <code>CRLF--</code>.}
     */
    private int boundaryLength;


    /**
     * データの量(バイト単位)。
     * これはデリミタを正しく検出するためにバッファ内に順番に保持されなくてはならない。
     * {@primary The amount of data, in bytes, that must be kept in the buffer in order
     * to detect delimiters reliably.}
     */
    private int keepRegion;


    /**
     * ストリームを仕切るバイト列。
     * {@primary The byte sequence that partitions the stream.}
     */
    private byte[] boundary;


    /**
     * リクエストを処理する際に使用するバッファの長さ。
     * {@primary The length of the buffer used for processing the request.}
     */
    private int bufSize;


    /**
     * リクエストを処理する際に使用するバッファ。
     * {@primary The buffer used for processing the request.}
     */
    private byte[] buffer;


    /**
     * バッファ内の先頭の有効なキャラクタのインデックス。
     * {@primary The index of first valid character in the buffer.}
     * <br>
     * 0 <= head < bufSize
     */
    private int head;


    /**
     * バッファ内の末尾の有効なキャラクタのインデックス + 1。
     * {@primary The index of last valid characer in the buffer + 1.}
     * <br>
     * 0 <= tail <= bufSize
     */
    private int tail;


    /**
     * ヘッダを読み込む際に使用するコンテントエンコーディング。
     * {@primary The content encoding to use when reading headers.}
     */
    private String headerEncoding;


    // ----------------------------------------------------------- Constructors


    /**
     * デフォルトコンストラクタ。
     * {@primary Default constructor.}
     *
     * @see #MultipartStream(InputStream, byte[], int)
     * @see #MultipartStream(InputStream, byte[])
     *
     */
    public MultipartStream()
    {
    }


    /**
     * <p> バッファのサイズを指定して <code>MultipartStream</code> を生成します。
     * {@primary Constructs a <code>MultipartStream</code> with a custom size buffer.}
     *
     * <p> バッファはバウンダリ文字列 + "CR/LF--"の4文字 + 少なくとも1バイトのデータを
     * 保持できる大きさがなくてはならないことに注意してください。
     * 小さすぎるバッファサイズの設定はパフォーマンスを低下させます。
     * {@primary Note that the buffer must be at least big enough to contain the
     * boundary string, plus 4 characters for CR/LF and double dash, plus at
     * least one byte of data.  Too small a buffer size setting will degrade
     * performance.}
     *
     * @param input    データソースを出力する <code>InputStream</code> 。
     * {@primary The <code>InputStream</code> to serve as a data source.}
     * @param boundary ストリームを <code>encapsulations</code> に分割するために使用するトークン。
     * {@primary The token used for dividing the stream into
     *           <code>encapsulations</code>.}
     * @param bufSize  使用するバッファのサイズ(バイト単位)。
     * {@primary The size of the buffer to be used, in bytes.}
     *
     *
     * @see #MultipartStream()
     * @see #MultipartStream(InputStream, byte[])
     *
     */
    public MultipartStream(InputStream input,
                           byte[] boundary,
                           int bufSize)
    {
        this.input = input;
        this.bufSize = bufSize;
        this.buffer = new byte[bufSize];

        // We prepend CR/LF to the boundary to chop trailng CR/LF from
        // body-data tokens.
        this.boundary = new byte[boundary.length + 4];
        this.boundaryLength = boundary.length + 4;
        this.keepRegion = boundary.length + 3;
        this.boundary[0] = 0x0D;
        this.boundary[1] = 0x0A;
        this.boundary[2] = 0x2D;
        this.boundary[3] = 0x2D;
        System.arraycopy(boundary, 0, this.boundary, 4, boundary.length);

        head = 0;
        tail = 0;
    }


    /**
     * <p> デフォルトのバッファサイズを使用する <code>MultipartStream</code> を生成します。
     * {@primary Constructs a <code>MultipartStream</code> with a default size buffer.}
     *
     * @param input    データソースを出力する <code>InputStream</code> 。
     * {@primary The <code>InputStream</code> to serve as a data source.}
     * @param boundary ストリームを <code>encapsulations</code> に分割するために使用するトークン。
     * {@primary The token used for dividing the stream into
     *           <code>encapsulations</code>.}
     *
     * @exception IOException エラーが発生した場合。
     * {@primary when an error occurs.}
     *
     * @see #MultipartStream()
     * @see #MultipartStream(InputStream, byte[], int)
     *
     */
    public MultipartStream(InputStream input,
                           byte[] boundary)
        throws IOException
    {
        this(input, boundary, DEFAULT_BUFSIZE);
    }


    // --------------------------------------------------------- Public methods


    /**
     * 個々のパーツのヘッダを読み込む際に使用するキャラクタエンコーディングを返します。
     * 設定されていないまたは <code>null</code> の場合にはプラットフォームのデフォルトエンコーディングを使用します。
     * {@primary Retrieves the character encoding used when reading the headers of an
     * individual part. When not specified, or <code>null</code>, the platform
     * default encoding is used.}
     *
     * @return パーツのヘッダを読み込む際に使用するエンコーディング。
     * {@primary The encoding used to read part headers.}
     */
    public String getHeaderEncoding()
    {
        return headerEncoding;
    }


    /**
     * 個々のパーツのヘッダを読み込む際に使用するキャラクタエンコーディングを設定します。
     * 設定されていないまたは <code>null</code> の場合にはプラットフォームのデフォルトエンコーディングを使用します。
     * {@primary Specifies the character encoding to be used when reading the headers of
     * individual parts. When not specified, or <code>null</code>, the platform
     * default encoding is used.}
     *
     * @param encoding パーツのヘッダを読み込む際に使用するエンコーディング。
     * {@primary The encoding used to read part headers.}
     */
    public void setHeaderEncoding(String encoding)
    {
        headerEncoding = encoding;
    }


    /**
     * バッファからバイトを返し、必要な分だけ補充します。
     * {@primary Reads a byte from the <code>buffer</code>, and refills it as
     * necessary.}
     *
     * @return 入力ストリームから読み出した次のバイト。
     * {@primary The next byte from the input stream.}
     *
     * @exception IOException もうこれ以上データがない場合。
     * {@primary if there is no more data available.}
     */
    public byte readByte()
        throws IOException
    {
        // Buffer depleted ?
        if (head == tail)
        {
            head = 0;
            // Refill.
            tail = input.read(buffer, head, bufSize);
            if (tail == -1)
            {
                // No more data available.
                throw new IOException("No more data is available");
            }
        }
        return buffer[head++];
    }


    /**
     * <code>boundary</code> トークンをスキップし、
     * ストリーム内にさらに <code>encapsulations</code> があるかどうかチェックします。
     * {@primary Skips a <code>boundary</code> token, and checks whether more
     * <code>encapsulations</code> are contained in the stream.}
     *
     * @return <code>true</code> ストリーム内にさらに encapsulations がある場合;
     *         <code>false</code> それ以外の場合。
     * {@primary <code>true</code> if there are more encapsulations in
     *           this stream; <code>false</code> otherwise.}
     *
     * @exception MalformedStreamException ストリームが不意に終了した場合、
     *                                     または文法の解釈に失敗した場合。
     * {@primary if the stream ends unexpectedly or fails to follow required syntax.}
     */
    public boolean readBoundary()
        throws MalformedStreamException
    {
        byte[] marker = new byte[2];
        boolean nextChunk = false;

        head += boundaryLength;
        try
        {
            marker[0] = readByte();
            marker[1] = readByte();
            if (arrayequals(marker, STREAM_TERMINATOR, 2))
            {
                nextChunk = false;
            }
            else if (arrayequals(marker, FIELD_SEPARATOR, 2))
            {
                nextChunk = true;
            }
            else
            {
                throw new MalformedStreamException(
                        "Unexpected characters follow a boundary");
            }
        }
        catch (IOException e)
        {
            throw new MalformedStreamException("Stream ended unexpectedly");
        }
        return nextChunk;
    }


    /**
     * <p>ストリームを分割する際に使用するバウンダリトークンを変更します。
     * {@primary Changes the boundary token used for partitioning the stream.}
     *
     * <p>このメソッドは入れ子になったストリームの単独パスでの処理を可能にします。
     * {@primary This method allows single pass processing of nested multipart
     * streams.}
     *
     * <p>入れ子になったストリームのバウンダリトークンは <code>必ず</code>
     * 親のストリームのバウンダリトークンと同じ長さでなくてはなりません。
     * {@primary The boundary token of the nested stream is <code>required</code>
     * to be of the same length as the boundary token in parent stream.}
     *
     * <p>入れ子になったストリームのの処理の後、親のストリームのバウンダリに戻す処理は
     * アプリケーションに委ねられます(手動で設定しなおす必要があります)。
     * {@primary Restoring the parent stream boundary token after processing of a
     * nested stream is left to the application.}
     *
     * @param boundary 入れ子になったストリームのパースに使用するバウンダリ。
     * {@primary The boundary to be used for parsing of the nested stream.}
     *
     * @exception IllegalBoundaryException <code>boundary</code> の長さが現在使用されているものと違った場合。
     * {@primary if the <code>boundary</code> has a different length than the one
     *           being currently parsed.}
     */
    public void setBoundary(byte[] boundary)
        throws IllegalBoundaryException
    {
        if (boundary.length != boundaryLength - 4)
        {
            throw new IllegalBoundaryException(
                    "The length of a boundary token can not be changed");
        }
        System.arraycopy(boundary, 0, this.boundary, 4, boundary.length);
    }


    /**
     * <p>現在の <code>encapsulation</code> から
     * <code>header-part</code> を読み込みます。
     * {@primary Reads the <code>header-part</code> of the current
     * <code>encapsulation</code>.}
     *
     * <p>ヘッダは末尾の <code>CRLF</code> マーカーを含む入力ストリームから読み出したままの状態で返されます。
     * パースの処理はアプリケーションに委ねられます。
     * {@primary Headers are returned verbatim to the input stream, including the
     * trailing <code>CRLF</code> marker. Parsing is left to the
     * application.}
     *
     * <p><strong>TODO</strong> 悪意あるアクセスから保護するために、
     * ヘッダサイズには上限を設けられています。
     * {@primary <strong>TODO</strong> allow limiting maximum header size to
     * protect against abuse.}
     *
     * @return 現在の <code>encapsulation</code> の <code>header-part</code>。
     * {@primary The <code>header-part</code> of the current encapsulation.}
     *
     * @exception MalformedStreamException ストリームが不意に終了した場合。
     * {@primary if the stream ends unexpecetedly.}
     */
    public String readHeaders()
        throws MalformedStreamException
    {
        int i = 0;
        byte b[] = new byte[1];
        // to support multi-byte characters
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int sizeMax = HEADER_PART_SIZE_MAX;
        int size = 0;
        while (i < 4)
        {
            try
            {
                b[0] = readByte();
            }
            catch (IOException e)
            {
                throw new MalformedStreamException("Stream ended unexpectedly");
            }
            size++;
            if (b[0] == HEADER_SEPARATOR[i])
            {
                i++;
            }
            else
            {
                i = 0;
            }
            if (size <= sizeMax)
            {
                baos.write(b[0]);
            }
        }

        String headers = null;
        if (headerEncoding != null)
        {
            try
            {
                headers = baos.toString(headerEncoding);
            }
            catch (UnsupportedEncodingException e)
            {
                // Fall back to platform default if specified encoding is not
                // supported.
                headers = baos.toString();
            }
        }
        else
        {
            headers = baos.toString();
        }

        return headers;
    }


    /**
     * <p>現在の <code>encapsulation</code> から
     * <code>body-data</code> を読み込み、その内容を
     * 出力ストリームに書き出します。
     * {@primary Reads <code>body-data</code> from the current
     * <code>encapsulation</code> and writes its contents into the
     * output <code>Stream</code>.}
     *
     * <p>このメソッドによって(任意の)大量のデータを
     * 一定のサイズのバッファを使用して処理を行うことができます
     * ({@link #MultipartStream(InputStream,byte[],int) コンストラクタ} を参照)。
     * {@primary Arbitrary large amounts of data can be processed by this
     * method using a constant size buffer. (see {@link
     * #MultipartStream(InputStream,byte[],int) constructor}).}
     *
     * @param output データを書き出す対象となる <code>Stream</code>。
     * {@primary The <code>Stream</code> to write data into.}
     *
     * @return 書き出したデータの量。
     * {@primary the amount of data written.}
     *
     * @exception MalformedStreamException ストリームが不意に終了した場合。
     * {@primary if the stream ends unexpectedly.}
     * @exception IOException              入出力エラーが発生した場合。
     * {@primary if an i/o error occurs.}
     */
    public int readBodyData(OutputStream output)
        throws MalformedStreamException,
               IOException
    {
        boolean done = false;
        int pad;
        int pos;
        int bytesRead;
        int total = 0;
        while (!done)
        {
            // Is boundary token present somewere in the buffer?
            pos = findSeparator();
            if (pos != -1)
            {
                // Write the rest of the data before the boundary.
                output.write(buffer, head, pos - head);
                total += pos - head;
                head = pos;
                done = true;
            }
            else
            {
                // Determine how much data should be kept in the
                // buffer.
                if (tail - head > keepRegion)
                {
                    pad = keepRegion;
                }
                else
                {
                    pad = tail - head;
                }
                // Write out the data belonging to the body-data.
                output.write(buffer, head, tail - head - pad);

                // Move the data to the beging of the buffer.
                total += tail - head - pad;
                System.arraycopy(buffer, tail - pad, buffer, 0, pad);

                // Refill buffer with new data.
                head = 0;
                bytesRead = input.read(buffer, pad, bufSize - pad);

                // [pprrrrrrr]
                if (bytesRead != -1)
                {
                    tail = pad + bytesRead;
                }
                else
                {
                    // The last pad amount is left in the buffer.
                    // Boundary can't be in there so write out the
                    // data you have and signal an error condition.
                    output.write(buffer, 0, pad);
                    output.flush();
                    total += pad;
                    throw new MalformedStreamException(
                            "Stream ended unexpectedly");
                }
            }
        }
        output.flush();
        return total;
    }


    /**
     * 現在の <code>encapsulation</code> から
     * <code>body-data</code> を読み込み、これを破棄します。
     * {@primary Reads <code>body-data</code> from the current
     * <code>encapsulation</code> and discards it.}
     *
     * <p>このメソッドは必要のない、もしくは解釈できない encapsulations
     * をスキップする際に使用してください。
     * {@primary Use this method to skip encapsulations you don't need or don't
     * understand.}
     *
     * @return 破棄したデータの量。
     * {@primary The amount of data discarded.}
     *
     * @exception MalformedStreamException ストリームが不意に終了した場合。
     * {@primary if the stream ends unexpectedly.}
     * @exception IOException              入出力エラーが発生した場合。
     * {@primary if an i/o error occurs.}
     */
    public int discardBodyData()
        throws MalformedStreamException,
               IOException
    {
        boolean done = false;
        int pad;
        int pos;
        int bytesRead;
        int total = 0;
        while (!done)
        {
            // Is boundary token present somewere in the buffer?
            pos = findSeparator();
            if (pos != -1)
            {
                // Write the rest of the data before the boundary.
                total += pos - head;
                head = pos;
                done = true;
            }
            else
            {
                // Determine how much data should be kept in the
                // buffer.
                if (tail - head > keepRegion)
                {
                    pad = keepRegion;
                }
                else
                {
                    pad = tail - head;
                }
                total += tail - head - pad;

                // Move the data to the beging of the buffer.
                System.arraycopy(buffer, tail - pad, buffer, 0, pad);

                // Refill buffer with new data.
                head = 0;
                bytesRead = input.read(buffer, pad, bufSize - pad);

                // [pprrrrrrr]
                if (bytesRead != -1)
                {
                    tail = pad + bytesRead;
                }
                else
                {
                    // The last pad amount is left in the buffer.
                    // Boundary can't be in there so signal an error
                    // condition.
                    total += pad;
                    throw new MalformedStreamException(
                            "Stream ended unexpectedly");
                }
            }
        }
        return total;
    }


    /**
     * 最初の <code>encapsulation</code> の始まりを捜し出します。
     * {@primary Finds the beginning of the first <code>encapsulation</code>.}
     *
     * @return <code>true</code> ストリーム内に <code>encapsulation</code> が見つかった場合。
     * {@primary <code>true</code> if an <code>encapsulation</code> was found in
     *           the stream.}
     *
     * @exception IOException 入出力エラーが発生した場合。
     * {@primary if an i/o error occurs.}
     */
    public boolean skipPreamble()
        throws IOException
    {
        // First delimiter may be not preceeded with a CRLF.
        System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2);
        boundaryLength = boundary.length - 2;
        try
        {
            // Discard all data up to the delimiter.
            discardBodyData();

            // Read boundary - if succeded, the stream contains an
            // encapsulation.
            return readBoundary();
        }
        catch (MalformedStreamException e)
        {
            return false;
        }
        finally
        {
            // Restore delimiter.
            System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2);
            boundaryLength = boundary.length;
            boundary[0] = 0x0D;
            boundary[1] = 0x0A;
        }
    }


    /**
     * 2つのバイト配列の先頭から指定されたインデックス分を比較します。
     * {@primary Compares <code>count</code> first bytes in the arrays
     * <code>a</code> and <code>b</code>.}
     *
     * @param a     最初の比較対象となる配列。
     *{@primary The first array to compare.}
     * @param b     2番目の比較対象となる配列。
     * {@primary The second array to compare.}
     * @param count 比較対象となる(先頭からの)バイト数。
     * {@primary How many bytes should be compared.}
     *
     * @return <code>true</code> 配列 <code>a</code> と <code>b</code> の先頭から
     *              <code>count</code> 分のバイト配列が等しい場合。
     * {@primary <code>true</code> if <code>count</code> first bytes in arrays
     *           <code>a</code> and <code>b</code> are equal.}
     */
    public static boolean arrayequals(byte[] a,
                                      byte[] b,
                                      int count)
    {
        for (int i = 0; i < count; i++)
        {
            if (a[i] != b[i])
            {
                return false;
            }
        }
        return true;
    }


    /**
     * バッファ内の指定された位置から指定されたバイトの値を検索します。
     * {@primary Searches for a byte of specified value in the <code>buffer</code>,
     * starting at the specified <code>position</code>.}
     *
     * @param value 検索する値。
     * {@primary The value to find.}
     * @param pos   検索開始位置。
     * {@primary The starting position for searching.}
     *
     * @return 見つかったバイトのバッファの先頭からの位置。
     *         見つからなかった場合には <code>-1</code> 。
     * {@primary The position of byte found, counting from beginning of the
     *           <code>buffer</code>, or <code>-1</code> if not found.}
     */
    protected int findByte(byte value,
                           int pos)
    {
        for (int i = pos; i < tail; i++)
        {
            if (buffer[i] == value)
            {
                return i;
            }
        }

        return -1;
    }


    /**
     * バッファ内の <code>head</code> と <code>tail</code> で区切られた範囲内から
     * <code>boundary</code> を検索します。
     * {@primary Searches for the <code>boundary</code> in the <code>buffer</code>
     * region delimited by <code>head</code> and <code>tail</code>.}
     *
     * @return 見つかったバウンダリのバッファの先頭からの位置。
     *         見つからなかった場合には <code>-1</code> 。
     * {@primary The position of the boundary found, counting from the
     *           beginning of the <code>buffer</code>, or <code>-1</code> if
     *           not found.}
     */
    protected int findSeparator()
    {
        int first;
        int match = 0;
        int maxpos = tail - boundaryLength;
        for (first = head;
             (first <= maxpos) && (match != boundaryLength);
             first++)
        {
            first = findByte(boundary[0], first);
            if (first == -1 || (first > maxpos))
            {
                return -1;
            }
            for (match = 1; match < boundaryLength; match++)
            {
                if (buffer[first + match] != boundary[match])
                {
                    break;
                }
            }
        }
        if (match == boundaryLength)
        {
            return first - 1;
        }
        return -1;
    }

    /**
     * このオブジェクトの文字列表現を返します。
     * {@primary Returns a string representation of this object.}
     *
     * @return このオブジェクトの文字列表現。
     * {@primary The string representation of this object.}
     */
    public String toString()
    {
        StringBuffer sbTemp = new StringBuffer();
        sbTemp.append("boundary='");
        sbTemp.append(String.valueOf(boundary));
        sbTemp.append("'\nbufSize=");
        sbTemp.append(bufSize);
        return sbTemp.toString();
    }

    /**
     * 入力ストリームが必要な文法に従っていない場合に投げられます。
     * {@primary Thrown to indicate that the input stream fails to follow the
     * required syntax.}
     */
    public class MalformedStreamException
        extends IOException
    {
        /**
         * 詳細メッセージなしで <code>MalformedStreamException</code> を生成します。
         * {@primary Constructs a <code>MalformedStreamException</code> with no
         * detail message.}
         */
        public MalformedStreamException()
        {
            super();
        }

        /**
         * 詳細メッセージを設定して <code>MalformedStreamException</code> を生成します。
         * {@primary Constructs an <code>MalformedStreamException</code> with
         * the specified detail message.}
         *
         * @param message 詳細メッセージ。
         * {@primary The detail message.}
         */
        public MalformedStreamException(String message)
        {
            super(message);
        }
    }


    /**
     * 不正なバウンダリトークンを設定しようとした場合に投げられます。
     * {@primary Thrown upon attempt of setting an invalid boundary token.}
     */
    public class IllegalBoundaryException
        extends IOException
    {
        /**
         * 詳細メッセージなしで <code>IllegalBoundaryException</code> を生成します。
         * {@primary Constructs an <code>IllegalBoundaryException</code> with no
         * detail message.}
         */
        public IllegalBoundaryException()
        {
            super();
        }

        /**
         * 詳細メッセージを設定して <code>IllegalBoundaryException</code> を生成します。
         * {@primary Constructs an <code>IllegalBoundaryException</code> with
         * the specified detail message.}
         *
         * @param message 詳細メッセージ。
         * {@primary The detail message.}
         */
        public IllegalBoundaryException(String message)
        {
            super(message);
        }
    }


    // ------------------------------------------------------ Debugging methods


    // These are the methods that were used to debug this stuff.
    /*

    // Dump data.
    protected void dump()
    {
        System.out.println("01234567890");
        byte[] temp = new byte[buffer.length];
        for(int i=0; i<buffer.length; i++)
        {
            if (buffer[i] == 0x0D || buffer[i] == 0x0A)
            {
                temp[i] = 0x21;
            }
            else
            {
                temp[i] = buffer[i];
            }
        }
        System.out.println(new String(temp));
        int i;
        for (i=0; i<head; i++)
            System.out.print(" ");
        System.out.println("h");
        for (i=0; i<tail; i++)
            System.out.print(" ");
        System.out.println("t");
        System.out.flush();
    }

    // Main routine, for testing purposes only.
    //
    // @param args A String[] with the command line arguments.
    // @exception Exception, a generic exception.
    public static void main( String[] args )
        throws Exception
    {
        File boundaryFile = new File("boundary.dat");
        int boundarySize = (int)boundaryFile.length();
        byte[] boundary = new byte[boundarySize];
        FileInputStream input = new FileInputStream(boundaryFile);
        input.read(boundary,0,boundarySize);

        input = new FileInputStream("multipart.dat");
        MultipartStream chunks = new MultipartStream(input, boundary);

        int i = 0;
        String header;
        OutputStream output;
        boolean nextChunk = chunks.skipPreamble();
        while (nextChunk)
        {
            header = chunks.readHeaders();
            System.out.println("!"+header+"!");
            System.out.println("wrote part"+i+".dat");
            output = new FileOutputStream("part"+(i++)+".dat");
            chunks.readBodyData(output);
            nextChunk = chunks.readBoundary();
        }
    }

    */
}
