From 135c81c796889fbdd51c07a478ca92593714b39a Mon Sep 17 00:00:00 2001 From: Robert Burrell Donkin Date: Wed, 9 Jul 2008 20:13:50 +0000 Subject: [PATCH] MIME4J-30 Transfer-encoding should be transparent, https://issues.apache.org/jira/browse/MIME4J-30. Committed patch contributed by Oleg Kalnichevski. Introduces transparent encoding but makes it optional. git-svn-id: https://svn.eu.apache.org/repos/asf/james/mime4j/trunk@675333 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/james/mime4j/MimeStreamParser.java | 36 ++++++++++++++++--- .../org/apache/james/mime4j/MimeTokenStream.java | 42 ++++++++++++++-------- .../apache/james/mime4j/MimeStreamParserTest.java | 38 ++++++++++++++++++++ 3 files changed, 97 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/apache/james/mime4j/MimeStreamParser.java b/src/main/java/org/apache/james/mime4j/MimeStreamParser.java index 1c956d3..7ff750f 100644 --- a/src/main/java/org/apache/james/mime4j/MimeStreamParser.java +++ b/src/main/java/org/apache/james/mime4j/MimeStreamParser.java @@ -35,17 +35,36 @@ import java.io.InputStream; * parser.setContentHandler(handler); * parser.parse(new FileInputStream("mime.msg")); * - * NOTE: All lines must end with CRLF - * (\r\n). If you are unsure of the line endings in your stream - * you should wrap it in a {@link org.apache.james.mime4j.EOLConvertingInputStream} instance. - * * * @version $Id: MimeStreamParser.java,v 1.8 2005/02/11 10:12:02 ntherning Exp $ */ public class MimeStreamParser { + private ContentHandler handler = null; + private boolean contentDecoding; private final MimeTokenStream mimeTokenStream = new MimeTokenStream(); + public MimeStreamParser() { + super(); + this.contentDecoding = false; + } + + /** + * Determines whether this parser automatically decodes body content + * based on the on the MIME fields with the standard defaults. + */ + public boolean isContentDecoding() { + return contentDecoding; + } + + /** + * Defines whether parser should automatically decode body content + * based on the on the MIME fields with the standard defaults. + */ + public void setContentDecoding(boolean b) { + this.contentDecoding = b; + } + /** * Parses a stream of bytes containing a MIME message. * @@ -59,7 +78,14 @@ public class MimeStreamParser { int state = mimeTokenStream.getState(); switch (state) { case MimeTokenStream.T_BODY: - handler.body(mimeTokenStream.getBodyDescriptor(), mimeTokenStream.getInputStream()); + BodyDescriptor desc = mimeTokenStream.getBodyDescriptor(); + InputStream bodyContent; + if (contentDecoding) { + bodyContent = mimeTokenStream.getDecodedInputStream(); + } else { + bodyContent = mimeTokenStream.getInputStream(); + } + handler.body(desc, bodyContent); break; case MimeTokenStream.T_END_BODYPART: handler.endBodyPart(); diff --git a/src/main/java/org/apache/james/mime4j/MimeTokenStream.java b/src/main/java/org/apache/james/mime4j/MimeTokenStream.java index 0feaacd..03a283b 100644 --- a/src/main/java/org/apache/james/mime4j/MimeTokenStream.java +++ b/src/main/java/org/apache/james/mime4j/MimeTokenStream.java @@ -239,9 +239,11 @@ public class MimeTokenStream implements EntityStates, RecursionMode { } /** + * This method returns the raw entity, preamble, or epilogue contents. + *

* This method is valid, if {@link #getState()} returns either of * {@link #T_RAW_ENTITY}, {@link #T_PREAMBLE}, or {@link #T_EPILOGUE}. - * It returns the raw entity, preamble, or epilogue contents. + * * @return Data stream, depending on the current state. * @throws IllegalStateException {@link #getState()} returns an * invalid value. @@ -249,6 +251,29 @@ public class MimeTokenStream implements EntityStates, RecursionMode { public InputStream getInputStream() { return currentStateMachine.getContentStream(); } + + /** + * This method returns a transfer decoded stream based on the MIME + * fields with the standard defaults. + *

+ * This method is valid, if {@link #getState()} returns either of + * {@link #T_RAW_ENTITY}, {@link #T_PREAMBLE}, or {@link #T_EPILOGUE}. + * + * @return Data stream, depending on the current state. + * @throws IllegalStateException {@link #getState()} returns an + * invalid value. + */ + public InputStream getDecodedInputStream() { + BodyDescriptor bodyDescriptor = getBodyDescriptor(); + String transferEncoding = bodyDescriptor.getTransferEncoding(); + InputStream dataStream = currentStateMachine.getContentStream(); + if (MimeUtil.isBase64Encoding(transferEncoding)) { + dataStream = new Base64InputStream(dataStream); + } else if (MimeUtil.isQuotedPrintableEncoded(transferEncoding)) { + dataStream = new QuotedPrintableInputStream(dataStream); + } + return dataStream; + } /** * Gets a reader configured for the current body or body part. @@ -270,25 +295,14 @@ public class MimeTokenStream implements EntityStates, RecursionMode { public Reader getReader() { final BodyDescriptor bodyDescriptor = getBodyDescriptor(); final String mimeCharset = bodyDescriptor.getCharset(); - final String transferEncoding = bodyDescriptor.getTransferEncoding(); final Charset charset; if (mimeCharset == null || "".equals(mimeCharset)) { charset = Charset.forName("US-ASCII"); } else { charset = Charset.forName(mimeCharset); } - - final InputStream inputStream; - final InputStream transferEncodedStream = getInputStream(); - if (MimeUtil.isBase64Encoding(transferEncoding)) { - inputStream = new Base64InputStream(transferEncodedStream); - } else if (MimeUtil.isQuotedPrintableEncoded(transferEncoding)) { - inputStream = new QuotedPrintableInputStream(transferEncodedStream); - } else { - inputStream = transferEncodedStream; - } - final InputStreamReader result = new InputStreamReader(inputStream, charset); - return result; + final InputStream instream = getDecodedInputStream(); + return new InputStreamReader(instream, charset); } /** diff --git a/src/test/java/org/apache/james/mime4j/MimeStreamParserTest.java b/src/test/java/org/apache/james/mime4j/MimeStreamParserTest.java index 7730a79..18f66ba 100644 --- a/src/test/java/org/apache/james/mime4j/MimeStreamParserTest.java +++ b/src/test/java/org/apache/james/mime4j/MimeStreamParserTest.java @@ -449,7 +449,45 @@ public class MimeStreamParserTest extends TestCase { } } } + + public void testAutomaticContentDecoding() throws Exception { + MimeStreamParser parser = new MimeStreamParser(); + parser.setContentDecoding(true); + TestHandler handler = new TestHandler(); + parser.setContentHandler(handler); + String msg = "Subject: Yada yada\r\n" + + "From: foo@bar.com\r\n" + + "Content-Type: application/octet-stream\r\n" + + "Content-Transfer-Encoding: base64\r\n" + + "\r\n" + + "V2hvIGF0ZSBteSBjYWtlPwo="; + String expected = "\r\n" + + "

\r\n" + + "\r\n" + + "Subject: Yada yada" + + "\r\n" + + "\r\n" + + "From: foo@bar.com" + + "\r\n" + + "\r\n" + + "Content-Type: application/octet-stream" + + "\r\n" + + "\r\n" + + "Content-Transfer-Encoding: base64" + + "\r\n" + + "
\r\n" + + "\r\n" + + "Who ate my cake?\n" + + "\r\n" + + "\r\n"; + + parser.parse(new ByteArrayInputStream(msg.getBytes())); + String result = handler.sb.toString(); + + assertEquals(expected, result); + } + private static class TestHandler implements ContentHandler { private StringBuffer sb = new StringBuffer(); -- 2.11.4.GIT