196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/****************************************************************
296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Licensed to the Apache Software Foundation (ASF) under one   *
396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * or more contributor license agreements.  See the NOTICE file *
496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * distributed with this work for additional information        *
596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * regarding copyright ownership.  The ASF licenses this file   *
696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * to you under the Apache License, Version 2.0 (the            *
796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * "License"); you may not use this file except in compliance   *
896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * with the License.  You may obtain a copy of the License at   *
996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *                                                              *
1096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *   http://www.apache.org/licenses/LICENSE-2.0                 *
1196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *                                                              *
1296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Unless required by applicable law or agreed to in writing,   *
1396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * software distributed under the License is distributed on an  *
1496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
1596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * KIND, either express or implied.  See the License for the    *
1696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * specific language governing permissions and limitations      *
1796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * under the License.                                           *
1896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project ****************************************************************/
1996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
2096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpackage org.apache.james.mime4j;
2196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
2231d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blankimport com.android.emailcommon.utility.LoggingInputStream;
23dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki
24dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onukiimport org.apache.james.mime4j.decoder.Base64InputStream;
25dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onukiimport org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
26dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki
2796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.IOException;
2896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.InputStream;
2996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.util.BitSet;
3096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.util.LinkedList;
3196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
3296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/**
3396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * <p>
348546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy * Parses MIME (or RFC822) message streams of bytes or characters and reports
3596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * parsing events to a <code>ContentHandler</code> instance.
3696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * </p>
3796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * <p>
3896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Typical usage:<br/>
3996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * <pre>
4096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *      ContentHandler handler = new MyHandler();
4196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *      MimeStreamParser parser = new MimeStreamParser();
4296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *      parser.setContentHandler(handler);
4396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *      parser.parse(new BufferedInputStream(new FileInputStream("mime.msg")));
4496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * </pre>
458546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy * <strong>NOTE:</strong> All lines must end with CRLF
468546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy * (<code>\r\n</code>). If you are unsure of the line endings in your stream
4796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * you should wrap it in a {@link org.apache.james.mime4j.EOLConvertingInputStream} instance.
4896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *
498546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy *
5096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * @version $Id: MimeStreamParser.java,v 1.8 2005/02/11 10:12:02 ntherning Exp $
5196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */
5296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpublic class MimeStreamParser {
5396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private static final Log log = LogFactory.getLog(MimeStreamParser.class);
54dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki
55e98fc403ac8b63836e055a2fffd024e495b7bd8eMakoto Onuki    private static final boolean DEBUG_LOG_MESSAGE = false; //DO NOT RELEASE AS 'TRUE'
56dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki
5796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private static BitSet fieldChars = null;
588546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
5996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private RootInputStream rootStream = null;
608546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy    private LinkedList<BodyDescriptor> bodyDescriptors = new LinkedList<BodyDescriptor>();
6196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private ContentHandler handler = null;
6296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private boolean raw = false;
638546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
6496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    static {
6596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        fieldChars = new BitSet();
6696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        for (int i = 0x21; i <= 0x39; i++) {
6796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            fieldChars.set(i);
6896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
6996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        for (int i = 0x3b; i <= 0x7e; i++) {
7096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            fieldChars.set(i);
7196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
7296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
738546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
7496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
7596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Creates a new <code>MimeStreamParser</code> instance.
7696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
7796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public MimeStreamParser() {
7896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
7996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
8096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
8196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Parses a stream of bytes containing a MIME message.
828546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy     *
8396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param is the stream to parse.
8496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @throws IOException on I/O errors.
8596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
8696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void parse(InputStream is) throws IOException {
8731d9acbf0623872f9d4a2b3210b5970854b654c7Marc Blank        if (DEBUG_LOG_MESSAGE) {
88dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki            is = new LoggingInputStream(is, "MIME", true);
89dfeb1184ebf6c59fc6e617149e03edb73b7e0df7Makoto Onuki        }
9096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        rootStream = new RootInputStream(is);
9196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        parseMessage(rootStream);
9296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
938546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
9496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
9596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Determines if this parser is currently in raw mode.
968546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy     *
9796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @return <code>true</code> if in raw mode, <code>false</code>
9896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     *         otherwise.
9996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @see #setRaw(boolean)
10096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
10196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public boolean isRaw() {
10296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return raw;
10396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
1048546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
10596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
1068546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy     * Enables or disables raw mode. In raw mode all future entities
10796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * (messages or body parts) in the stream will be reported to the
10896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * {@link ContentHandler#raw(InputStream)} handler method only.
1098546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy     * The stream will contain the entire unparsed entity contents
11096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * including header fields and whatever is in the body.
1118546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy     *
11296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param raw <code>true</code> enables raw mode, <code>false</code>
11396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     *        disables it.
11496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
11596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void setRaw(boolean raw) {
11696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        this.raw = raw;
11796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
1188546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
11996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
12096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Finishes the parsing and stops reading lines.
12196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * NOTE: No more lines will be parsed but the parser
1228546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy     * will still call
12396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * {@link ContentHandler#endMultipart()},
12496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * {@link ContentHandler#endBodyPart()},
12596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * {@link ContentHandler#endMessage()}, etc to match previous calls
1268546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy     * to
12796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * {@link ContentHandler#startMultipart(BodyDescriptor)},
12896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * {@link ContentHandler#startBodyPart()},
12996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * {@link ContentHandler#startMessage()}, etc.
13096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
13196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void stop() {
13296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        rootStream.truncate();
13396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
1348546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
13596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
13696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Parses an entity which consists of a header followed by a body containing
13796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * arbitrary data, body parts or an embedded message.
1388546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy     *
13996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param is the stream to parse.
14096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @throws IOException on I/O errors.
14196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
14296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private void parseEntity(InputStream is) throws IOException {
14396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        BodyDescriptor bd = parseHeader(is);
1448546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
14596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (bd.isMultipart()) {
14696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            bodyDescriptors.addFirst(bd);
1478546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
14896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            handler.startMultipart(bd);
1498546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
1508546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy            MimeBoundaryInputStream tempIs =
15196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                new MimeBoundaryInputStream(is, bd.getBoundary());
15296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            handler.preamble(new CloseShieldInputStream(tempIs));
15396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            tempIs.consume();
15496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
15596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            while (tempIs.hasMoreParts()) {
15696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                tempIs = new MimeBoundaryInputStream(is, bd.getBoundary());
15796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                parseBodyPart(tempIs);
15896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                tempIs.consume();
15996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (tempIs.parentEOF()) {
160178e2930e7c852b2dafe3ce9b22b730fcd50f8f7Makoto Onuki//                    if (log.isWarnEnabled()) {
161178e2930e7c852b2dafe3ce9b22b730fcd50f8f7Makoto Onuki//                        log.warn("Line " + rootStream.getLineNumber()
162178e2930e7c852b2dafe3ce9b22b730fcd50f8f7Makoto Onuki//                                + ": Body part ended prematurely. "
163178e2930e7c852b2dafe3ce9b22b730fcd50f8f7Makoto Onuki//                                + "Higher level boundary detected or "
164178e2930e7c852b2dafe3ce9b22b730fcd50f8f7Makoto Onuki//                                + "EOF reached.");
165178e2930e7c852b2dafe3ce9b22b730fcd50f8f7Makoto Onuki//                    }
16696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    break;
16796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
16896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
1698546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
17096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            handler.epilogue(new CloseShieldInputStream(is));
1718546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
17296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            handler.endMultipart();
1738546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
17496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            bodyDescriptors.removeFirst();
1758546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
17696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        } else if (bd.isMessage()) {
17796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (bd.isBase64Encoded()) {
17896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                log.warn("base64 encoded message/rfc822 detected");
17996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                is = new EOLConvertingInputStream(
18096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        new Base64InputStream(is));
18196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            } else if (bd.isQuotedPrintableEncoded()) {
18296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                log.warn("quoted-printable encoded message/rfc822 detected");
18396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                is = new EOLConvertingInputStream(
18496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        new QuotedPrintableInputStream(is));
18596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
18696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            bodyDescriptors.addFirst(bd);
18796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            parseMessage(is);
18896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            bodyDescriptors.removeFirst();
18996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        } else {
19096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            handler.body(bd, new CloseShieldInputStream(is));
19196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
1928546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
19396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /*
19496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * Make sure the stream has been consumed.
19596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         */
19696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        while (is.read() != -1) {
19796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
19896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
1998546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
20096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private void parseMessage(InputStream is) throws IOException {
20196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (raw) {
20296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            handler.raw(new CloseShieldInputStream(is));
20396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        } else {
20496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            handler.startMessage();
20596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            parseEntity(is);
20696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            handler.endMessage();
20796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
20896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
2098546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
21096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private void parseBodyPart(InputStream is) throws IOException {
21196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (raw) {
21296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            handler.raw(new CloseShieldInputStream(is));
21396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        } else {
21496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            handler.startBodyPart();
21596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            parseEntity(is);
21696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            handler.endBodyPart();
21796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
21896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
2198546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
22096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
22196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Parses a header.
2228546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy     *
22396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param is the stream to parse.
2248546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy     * @return a <code>BodyDescriptor</code> describing the body following
22596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     *         the header.
22696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
22796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private BodyDescriptor parseHeader(InputStream is) throws IOException {
2288546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy        BodyDescriptor bd = new BodyDescriptor(bodyDescriptors.isEmpty()
22996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        ? null : (BodyDescriptor) bodyDescriptors.getFirst());
2308546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
23196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        handler.startHeader();
2328546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
23396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        int lineNumber = rootStream.getLineNumber();
2348546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
23596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        StringBuffer sb = new StringBuffer();
23696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        int curr = 0;
23796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        int prev = 0;
23896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        while ((curr = is.read()) != -1) {
23996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (curr == '\n' && (prev == '\n' || prev == 0)) {
24096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                /*
24196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 * [\r]\n[\r]\n or an immediate \r\n have been seen.
24296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 */
24396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                sb.deleteCharAt(sb.length() - 1);
24496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                break;
24596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
24696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            sb.append((char) curr);
24796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            prev = curr == '\r' ? prev : curr;
24896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
2498546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
250178e2930e7c852b2dafe3ce9b22b730fcd50f8f7Makoto Onuki//        if (curr == -1 && log.isWarnEnabled()) {
251178e2930e7c852b2dafe3ce9b22b730fcd50f8f7Makoto Onuki//            log.warn("Line " + rootStream.getLineNumber()
252178e2930e7c852b2dafe3ce9b22b730fcd50f8f7Makoto Onuki//                    + ": Unexpected end of headers detected. "
253178e2930e7c852b2dafe3ce9b22b730fcd50f8f7Makoto Onuki//                    + "Boundary detected in header or EOF reached.");
254178e2930e7c852b2dafe3ce9b22b730fcd50f8f7Makoto Onuki//        }
25596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
25696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        int start = 0;
25796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        int pos = 0;
25896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        int startLineNumber = lineNumber;
25996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        while (pos < sb.length()) {
26096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            while (pos < sb.length() && sb.charAt(pos) != '\r') {
26196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                pos++;
26296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
26396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (pos < sb.length() - 1 && sb.charAt(pos + 1) != '\n') {
26496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                pos++;
26596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                continue;
26696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
2678546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
26896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (pos >= sb.length() - 2 || fieldChars.get(sb.charAt(pos + 2))) {
2698546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
27096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                /*
2718546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy                 * field should be the complete field data excluding the
27296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 * trailing \r\n.
27396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 */
27496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                String field = sb.substring(start, pos);
27596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                start = pos + 2;
2768546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
27796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                /*
27896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 * Check for a valid field.
27996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                 */
28096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                int index = field.indexOf(':');
28196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                boolean valid = false;
28296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (index != -1 && fieldChars.get(field.charAt(0))) {
28396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    valid = true;
28496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    String fieldName = field.substring(0, index).trim();
28596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    for (int i = 0; i < fieldName.length(); i++) {
28696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        if (!fieldChars.get(fieldName.charAt(i))) {
28796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                            valid = false;
28896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                            break;
28996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        }
29096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    }
2918546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
29296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    if (valid) {
29396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        handler.field(field);
29496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                        bd.addField(fieldName, field.substring(index + 1));
2958546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy                    }
29696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                }
2978546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
29896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                if (!valid && log.isWarnEnabled()) {
2998546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy                    log.warn("Line " + startLineNumber
30096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                            + ": Ignoring invalid field: '" + field.trim() + "'");
3018546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy                }
3028546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
30396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                startLineNumber = lineNumber;
30496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
3058546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
30696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            pos += 2;
30796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            lineNumber++;
30896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
3098546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
31096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        handler.endHeader();
3118546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
31296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return bd;
31396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
3148546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy
31596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
3168546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy     * Sets the <code>ContentHandler</code> to use when reporting
31796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * parsing events.
3188546e21e1e127845071c595beda16fc23eb0f58eTodd Kennedy     *
31996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param h the <code>ContentHandler</code>.
32096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
32196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void setContentHandler(ContentHandler h) {
32296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        this.handler = h;
32396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
32496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
32596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project}
326