Index: src/main/java/org/apache/james/mime4j/message/Message.java =================================================================== --- src/main/java/org/apache/james/mime4j/message/Message.java (revision 764557) +++ src/main/java/org/apache/james/mime4j/message/Message.java (working copy) @@ -44,6 +44,7 @@ import org.apache.james.mime4j.parser.Field; import org.apache.james.mime4j.parser.MimeEntityConfig; import org.apache.james.mime4j.parser.MimeStreamParser; +import org.apache.james.mime4j.parser.MimeTokenStream; import org.apache.james.mime4j.storage.DefaultStorageProvider; import org.apache.james.mime4j.storage.StorageProvider; @@ -97,11 +98,32 @@ * on MIME protocol violations. */ public Message(InputStream is) throws IOException, MimeIOException { - this(is, null, DefaultStorageProvider.getInstance()); + this(is, null, DefaultStorageProvider.getInstance(), null); } /** * Parses the specified MIME message stream into a Message + * instance. If a content type is specified headless parsing ( + * {@link MimeTokenStream#parseHeadless(InputStream, String)}) is performed. + * + * @param is + * the stream to parse. + * @param contentType + * the content type used to parse. If not null a headless parsing + * ({@link MimeTokenStream#parseHeadless(InputStream, String)}) + * is performed. + * @throws IOException + * on I/O errors. + * @throws MimeIOException + * on MIME protocol violations. + */ + public Message(InputStream is, String contentType) throws IOException, + MimeIOException { + this(is, null, DefaultStorageProvider.getInstance(), contentType); + } + + /** + * Parses the specified MIME message stream into a Message * instance using given {@link MimeEntityConfig}. * * @param is @@ -113,7 +135,7 @@ */ public Message(InputStream is, MimeEntityConfig config) throws IOException, MimeIOException { - this(is, config, DefaultStorageProvider.getInstance()); + this(is, config, DefaultStorageProvider.getInstance(), null); } /** @@ -135,10 +157,46 @@ public Message(InputStream is, MimeEntityConfig config, StorageProvider storageProvider) throws IOException, MimeIOException { + this(is, config, storageProvider, null); + } + + /** + * Parses the specified MIME message stream into a Message + * instance using given {@link MimeEntityConfig} and {@link StorageProvider} + * . If a not null content type is specified headless parsing ( + * {@link MimeTokenStream#parseHeadless(InputStream, String)}) is performed. + * + * @param is + * the stream to parse. + * @param config + * {@link MimeEntityConfig} to use. + * @param storageProvider + * {@link StorageProvider} to use for storing text and binary + * message bodies. + * @param contentType + * the content type used to parse. If not null a headless parsing + * ({@link MimeTokenStream#parseHeadless(InputStream, String)}) + * is performed. + * @throws IOException + * on I/O errors. + * @throws MimeIOException + * on MIME protocol violations. + */ + public Message(InputStream is, MimeEntityConfig config, + StorageProvider storageProvider, String contentType) + throws IOException, MimeIOException { try { MimeStreamParser parser = new MimeStreamParser(config); parser.setContentHandler(new MessageBuilder(this, storageProvider)); - parser.parse(is); + if (contentType == null) + parser.parse(is); + else { + parser.parseHeadless(is, contentType); + + Header header = obtainHeader(); + + header.addField(Fields.contentType(contentType)); + } } catch (MimeException e) { throw new MimeIOException(e); } Index: src/main/java/org/apache/james/mime4j/parser/MimeStreamParser.java =================================================================== --- src/main/java/org/apache/james/mime4j/parser/MimeStreamParser.java (revision 764557) +++ src/main/java/org/apache/james/mime4j/parser/MimeStreamParser.java (working copy) @@ -87,6 +87,41 @@ */ public void parse(InputStream is) throws MimeException, IOException { mimeTokenStream.parse(is); + doParse(false); + } + + /** + * Parses a stream of bytes containing a MIME message without the header. + * Calls {@link MimeTokenStream#parseHeadless(InputStream, String)}. + * + * @param is + * the stream to parse. + * @param contentType + * the content tpye of the MIME message, can not be null + * @throws MimeException + * if the message can not be processed + * @throws IOException + * on I/O errors. + */ + public void parseHeadless(InputStream is, String contentType) + throws MimeException, IOException { + mimeTokenStream.parseHeadless(is, contentType); + doParse(true); + } + + /** + * Iterates over the states of the MIME message. + * + * @param headless + * true if doing headless parsing when there is no start header + * available in the underlying stream. + * @throws MimeException + * if the message can not be processed + * @throws IOException + * on I/O errors. + */ + protected void doParse(boolean headless) throws MimeException, IOException { + boolean skipNextHeaderEnd = headless; OUTER: for (;;) { int state = mimeTokenStream.getState(); switch (state) { @@ -104,7 +139,10 @@ handler.endBodyPart(); break; case MimeTokenStream.T_END_HEADER: - handler.endHeader(); + if (!skipNextHeaderEnd) + handler.endHeader(); + else + skipNextHeaderEnd = false; break; case MimeTokenStream.T_END_MESSAGE: handler.endMessage(); Index: src/test/java/org/apache/james/mime4j/message/MessageHeadlessParserTest.java =================================================================== --- src/test/java/org/apache/james/mime4j/message/MessageHeadlessParserTest.java (revision 0) +++ src/test/java/org/apache/james/mime4j/message/MessageHeadlessParserTest.java (revision 0) @@ -0,0 +1,57 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.mime4j.message; + +import java.io.ByteArrayInputStream; + +import junit.framework.TestCase; + +import org.apache.james.mime4j.field.ContentTypeField; +import org.apache.james.mime4j.field.FieldName; + +public class MessageHeadlessParserTest extends TestCase { + + public void testMultipartFormContent() throws Exception { + String contentType = "multipart/form-data; boundary=foo"; + String headlessContent = "\r\n" + + "--foo\r\nContent-Disposition: form-data; name=\"field01\"" + + "\r\n" + + "\r\n" + + "this stuff\r\n" + + "--foo\r\n" + + "Content-Disposition: form-data; name=\"field02\"\r\n" + + "\r\n" + + "that stuff\r\n" + + "--foo\r\n" + + "Content-Disposition: form-data; name=\"field03\"; filename=\"mypic.jpg\"\r\n" + + "Content-Type: image/jpeg\r\n" + "\r\n" + + "all kind of stuff\r\n" + "--foo--\r\n"; + + Message message = new Message(new ByteArrayInputStream(headlessContent + .getBytes("UTF-8")), contentType); + assertEquals(message.getMimeType(), "multipart/form-data"); + assertEquals(message.getHeader().getFields().size(), 1); + ContentTypeField contentTypeField = ((ContentTypeField) message + .getHeader().getField(FieldName.CONTENT_TYPE)); + assertEquals(contentTypeField.getBoundary(), "foo"); + Multipart multipart = (Multipart) message.getBody(); + assertEquals(multipart.getCount(), 3); + } +}