From d1d7b60a093407e02259a2b88b9bfd4084005323 Mon Sep 17 00:00:00 2001 From: cketti Date: Wed, 26 Nov 2014 01:11:26 +0100 Subject: [PATCH] Add helper method to decode message bodies Depending on whether a Body implements RawDataBody (which indicates the class retains the original encoding) the helper method either strips the transfer encoding or simply returns the result of Body.getInputStream(). This should restore the original functionality. So saving messages in the database should work fine again. --- .../k9/mail/internet/BinaryTempFileBody.java | 7 ++- .../fsck/k9/mail/internet/MimeUtility.java | 49 ++++++++++++++++++- .../fsck/k9/mail/internet/RawDataBody.java | 12 +++++ .../fsck/k9/mail/store/local/LocalFolder.java | 2 +- .../mail/internet/MimeMessageParseTest.java | 8 +-- 5 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 src/com/fsck/k9/mail/internet/RawDataBody.java diff --git a/src/com/fsck/k9/mail/internet/BinaryTempFileBody.java b/src/com/fsck/k9/mail/internet/BinaryTempFileBody.java index 677739bbf..17870c140 100644 --- a/src/com/fsck/k9/mail/internet/BinaryTempFileBody.java +++ b/src/com/fsck/k9/mail/internet/BinaryTempFileBody.java @@ -15,7 +15,7 @@ import java.io.*; * and writeTo one time. After writeTo is called, or the InputStream returned from * getInputStream is closed the file is deleted and the Body should be considered disposed of. */ -public class BinaryTempFileBody implements Body { +public class BinaryTempFileBody implements RawDataBody { private static File mTempDirectory; private File mFile; @@ -26,6 +26,11 @@ public class BinaryTempFileBody implements Body { mTempDirectory = tempDirectory; } + @Override + public String getEncoding() { + return mEncoding; + } + public void setEncoding(String encoding) throws MessagingException { if (mEncoding != null && mEncoding.equalsIgnoreCase(encoding)) { return; diff --git a/src/com/fsck/k9/mail/internet/MimeUtility.java b/src/com/fsck/k9/mail/internet/MimeUtility.java index b78f8e378..da3dca5bd 100644 --- a/src/com/fsck/k9/mail/internet/MimeUtility.java +++ b/src/com/fsck/k9/mail/internet/MimeUtility.java @@ -2,6 +2,7 @@ package com.fsck.k9.mail.internet; import android.content.Context; +import android.util.Base64; import android.util.Log; import com.fsck.k9.K9; import com.fsck.k9.R; @@ -10,6 +11,7 @@ import com.fsck.k9.mail.*; import com.fsck.k9.mail.Message.RecipientType; import com.fsck.k9.mail.internet.BinaryTempFileBody.BinaryTempFileBodyInputStream; +import com.fsck.k9.view.MessageHeader; import org.apache.commons.io.IOUtils; import org.apache.james.mime4j.codec.Base64InputStream; import org.apache.james.mime4j.codec.QuotedPrintableInputStream; @@ -1026,7 +1028,7 @@ public class MimeUtility { * determine the charset from HTML message. */ if (mimeType.equalsIgnoreCase("text/html") && charset == null) { - InputStream in = part.getBody().getInputStream(); + InputStream in = MimeUtility.decodeBody(part.getBody()); try { byte[] buf = new byte[256]; in.read(buf, 0, buf.length); @@ -1062,7 +1064,7 @@ public class MimeUtility { * Now we read the part into a buffer for further processing. Because * the stream is now wrapped we'll remove any transfer encoding at this point. */ - InputStream in = part.getBody().getInputStream(); + InputStream in = MimeUtility.decodeBody(part.getBody()); try { String text = readToString(in, charset); @@ -1151,6 +1153,49 @@ public class MimeUtility { return tempBody; } + /** + * Get decoded contents of a body. + *

+ * Right now only some classes retain the original encoding of the body contents. Those classes have to implement + * the {@link RawDataBody} interface in order for this method to decode the data delivered by + * {@link Body#getInputStream()}. + *

+ * The ultimate goal is to get to a point where all classes retain the original data and {@code RawDataBody} can be + * merged into {@link Body}. + */ + public static InputStream decodeBody(Body body) throws MessagingException { + InputStream inputStream; + if (body instanceof RawDataBody) { + RawDataBody rawDataBody = (RawDataBody) body; + String encoding = rawDataBody.getEncoding(); + final InputStream rawInputStream = rawDataBody.getInputStream(); + if (MimeUtil.ENC_7BIT.equalsIgnoreCase(encoding) || MimeUtil.ENC_8BIT.equalsIgnoreCase(encoding)) { + inputStream = rawInputStream; + } else if (MimeUtil.ENC_BASE64.equalsIgnoreCase(encoding)) { + inputStream = new Base64InputStream(rawInputStream, false) { + @Override + public void close() throws IOException { + super.close(); + rawInputStream.close(); + } + }; + } else if (MimeUtil.ENC_QUOTED_PRINTABLE.equalsIgnoreCase(encoding)) { + inputStream = new QuotedPrintableInputStream(rawInputStream) { + @Override + public void close() throws IOException { + super.close(); + rawInputStream.close(); + } + }; + } else { + throw new RuntimeException("Encoding for RawDataBody not supported: " + encoding); + } + } else { + inputStream = body.getInputStream(); + } + + return inputStream; + } /** * Empty base class for the class hierarchy used by diff --git a/src/com/fsck/k9/mail/internet/RawDataBody.java b/src/com/fsck/k9/mail/internet/RawDataBody.java new file mode 100644 index 000000000..e3dee616e --- /dev/null +++ b/src/com/fsck/k9/mail/internet/RawDataBody.java @@ -0,0 +1,12 @@ +package com.fsck.k9.mail.internet; + + +import com.fsck.k9.mail.Body; + + +/** + * See {@link MimeUtility#decodeBody(Body)} + */ +public interface RawDataBody extends Body { + String getEncoding(); +} diff --git a/src/com/fsck/k9/mail/store/local/LocalFolder.java b/src/com/fsck/k9/mail/store/local/LocalFolder.java index e84098183..e264ef877 100644 --- a/src/com/fsck/k9/mail/store/local/LocalFolder.java +++ b/src/com/fsck/k9/mail/store/local/LocalFolder.java @@ -1561,7 +1561,7 @@ public class LocalFolder extends Folder implements Serializable { * If the attachment has a body we're expected to save it into the local store * so we copy the data into a cached attachment file. */ - InputStream in = attachment.getBody().getInputStream(); + InputStream in = MimeUtility.decodeBody(attachment.getBody()); try { tempAttachmentFile = File.createTempFile("att", null, attachmentDirectory); FileOutputStream out = new FileOutputStream(tempAttachmentFile); diff --git a/tests/src/com/fsck/k9/mail/internet/MimeMessageParseTest.java b/tests/src/com/fsck/k9/mail/internet/MimeMessageParseTest.java index 4558d4264..4107a7490 100644 --- a/tests/src/com/fsck/k9/mail/internet/MimeMessageParseTest.java +++ b/tests/src/com/fsck/k9/mail/internet/MimeMessageParseTest.java @@ -63,7 +63,7 @@ public class MimeMessageParseTest extends AndroidTestCase { private static void checkLeafParts(MimeMessage msg, String... expectedParts) throws Exception { List actual = new ArrayList(); for (Body leaf : getLeafParts(msg.getBody())) { - actual.add(streamToString(leaf.getInputStream())); + actual.add(streamToString(MimeUtility.decodeBody(leaf))); } assertEquals(Arrays.asList(expectedParts), actual); } @@ -83,7 +83,7 @@ public class MimeMessageParseTest extends AndroidTestCase { checkAddresses(msg.getRecipients(RecipientType.TO), "eva@example.org"); assertEquals("Testmail", msg.getSubject()); assertEquals("text/plain", msg.getContentType()); - assertEquals("this is some test text.", streamToString(msg.getBody().getInputStream())); + assertEquals("this is some test text.", streamToString(MimeUtility.decodeBody(msg.getBody()))); } public static void testSinglePart8BitRecurse() throws Exception { @@ -101,7 +101,7 @@ public class MimeMessageParseTest extends AndroidTestCase { checkAddresses(msg.getRecipients(RecipientType.TO), "eva@example.org"); assertEquals("Testmail", msg.getSubject()); assertEquals("text/plain; encoding=ISO-8859-1", msg.getContentType()); - assertEquals("gefährliche Umlaute", streamToString(msg.getBody().getInputStream())); + assertEquals("gefährliche Umlaute", streamToString(MimeUtility.decodeBody(msg.getBody()))); } public static void testSinglePartBase64NoRecurse() throws Exception { @@ -119,7 +119,7 @@ public class MimeMessageParseTest extends AndroidTestCase { checkAddresses(msg.getRecipients(RecipientType.TO), "eva@example.org"); assertEquals("Testmail", msg.getSubject()); assertEquals("text/plain", msg.getContentType()); - assertEquals("this is some more test text.", streamToString(msg.getBody().getInputStream())); + assertEquals("this is some more test text.", streamToString(MimeUtility.decodeBody(msg.getBody()))); } public static void testMultipartSingleLayerNoRecurse() throws Exception {