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