From 77407eb5b741f6977fb98bc21c7259d0091d5033 Mon Sep 17 00:00:00 2001 From: Joe Steele Date: Sun, 1 Sep 2013 16:25:09 -0400 Subject: [PATCH] Don't base64 encode attachments of type message/rfc822. The problem: Receive a message with an attachment of type message/rfc822 and forward it. When the message is sent, K-9 Mail uses base64 encoding for the attachment. (Alternatively, you could compose a new message and add such an attachment from a file using a filing-picking app, but that is not 100% effective because the app may not choose the correct message/rfc822 MIME type for the attachment.) Such encoding is prohibited per RFC 2046 (5.2.1) and RFC 2045 (6.4). Only 8bit or 7bit encoding is permitted for attachments of type message/rfc822. Thunderbird refuses to decode such attachments. All that is shown is the base64 encoded body. This commit implements LocalAttachmentBody.setEncoding. If an attachment to a newly composed message is itself a message, then setEncoding("8bit") is called, otherwise setEncoding("base64") is called for the attachment. Similar behavior occurs when an attachment is retrieved from LocalStore. The setEncoding method was added to the Body interface, since all implementations of Body now declare the method. The problem here differs from that in the preceding commit: Here, the encoding problem occurs on sending, not on receipt. Here, the entire message (headers and body) is base64 encoded, not just the body. Here, the headers correctly identify the encoding used; it's just that the RFC does not permit such encoding of attached messages. The problem here could in fact occur in combination with the preceding problem. --- src/com/fsck/k9/activity/MessageCompose.java | 13 ++++++---- src/com/fsck/k9/mail/Body.java | 3 +++ src/com/fsck/k9/mail/store/LocalStore.java | 26 +++++++++++++++++--- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java index 5d9d3759a..8a79039a7 100644 --- a/src/com/fsck/k9/activity/MessageCompose.java +++ b/src/com/fsck/k9/activity/MessageCompose.java @@ -78,10 +78,10 @@ import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeMultipart; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.TextBody; -import com.fsck.k9.mail.store.LocalStore; import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBody; import com.fsck.k9.view.MessageWebView; import org.apache.james.mime4j.codec.EncoderUtil; +import org.apache.james.mime4j.util.MimeUtil; import org.htmlcleaner.CleanerProperties; import org.htmlcleaner.HtmlCleaner; import org.htmlcleaner.SimpleHtmlSerializer; @@ -1473,9 +1473,12 @@ public class MessageCompose extends K9Activity implements OnClickListener { private void addAttachmentsToMessage(final MimeMultipart mp) throws MessagingException { for (int i = 0, count = mAttachments.getChildCount(); i < count; i++) { Attachment attachment = (Attachment) mAttachments.getChildAt(i).getTag(); + String contentType = attachment.contentType; + String encoding = (MimeUtil.isMessage(contentType)? "8bit" : "base64"); - MimeBodyPart bp = new MimeBodyPart( - new LocalStore.LocalAttachmentBody(attachment.uri, getApplication())); + LocalAttachmentBody body = new LocalAttachmentBody(attachment.uri, getApplication()); + MimeBodyPart bp = new MimeBodyPart(body); + body.setEncoding(encoding); /* * Correctly encode the filename here. Otherwise the whole @@ -1483,11 +1486,11 @@ public class MessageCompose extends K9Activity implements OnClickListener { * MimeHeader.writeTo(). */ bp.addHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\n name=\"%s\"", - attachment.contentType, + contentType, EncoderUtil.encodeIfNecessary(attachment.name, EncoderUtil.Usage.WORD_ENTITY, 7))); - bp.addHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64"); + bp.addHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding); /* * TODO: Oh the joys of MIME... diff --git a/src/com/fsck/k9/mail/Body.java b/src/com/fsck/k9/mail/Body.java index 7deb2a1eb..bb990b3f2 100644 --- a/src/com/fsck/k9/mail/Body.java +++ b/src/com/fsck/k9/mail/Body.java @@ -5,7 +5,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import com.fsck.k9.mail.store.UnavailableStorageException; + public interface Body { public InputStream getInputStream() throws MessagingException; + public void setEncoding(String encoding) throws UnavailableStorageException; public void writeTo(OutputStream out) throws IOException, MessagingException; } diff --git a/src/com/fsck/k9/mail/store/LocalStore.java b/src/com/fsck/k9/mail/store/LocalStore.java index 5575499a7..b75c37375 100644 --- a/src/com/fsck/k9/mail/store/LocalStore.java +++ b/src/com/fsck/k9/mail/store/LocalStore.java @@ -25,6 +25,7 @@ import java.util.UUID; import java.util.regex.Pattern; import org.apache.commons.io.IOUtils; +import org.apache.james.mime4j.util.MimeUtil; import android.app.Application; import android.content.ContentResolver; @@ -1936,6 +1937,7 @@ public class LocalStore extends Store implements Serializable { String contentUri = cursor.getString(5); String contentId = cursor.getString(6); String contentDisposition = cursor.getString(7); + String encoding = (MimeUtil.isMessage(type)? "8bit" : "base64"); Body body = null; if (contentDisposition == null) { @@ -1944,10 +1946,11 @@ public class LocalStore extends Store implements Serializable { if (contentUri != null) { body = new LocalAttachmentBody(Uri.parse(contentUri), mApplication); + ((LocalAttachmentBody) body).setEncoding(encoding); } MimeBodyPart bp = new LocalAttachmentBodyPart(body, id); - bp.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64"); + bp.setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, encoding); if (name != null) { bp.setHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\n name=\"%s\"", @@ -3988,6 +3991,7 @@ public class LocalStore extends Store implements Serializable { private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; private Application mApplication; private Uri mUri; + private String mEncoding; public LocalAttachmentBody(Uri uri, Application application) { mApplication = application; @@ -4011,11 +4015,21 @@ public class LocalStore extends Store implements Serializable { public void writeTo(OutputStream out) throws IOException, MessagingException { InputStream in = getInputStream(); try { - Base64OutputStream base64Out = new Base64OutputStream(out); + + // TODO: attachments of type rfc822 are sent with 8bit encoding + // without regard to the SMTP server's support of 8BITMIME, whereas + // strict protocol compliance requires that they be converted to + // 7bit in the (unlikely) event that 8BITMIME is not supported. + + if (MimeUtil.isBase64Encoding(mEncoding)) { + out = new Base64OutputStream(out); + } try { - IOUtils.copy(in, base64Out); + IOUtils.copy(in, out); } finally { - base64Out.close(); + if (MimeUtil.isBase64Encoding(mEncoding)) { + out.close(); + } } } finally { in.close(); @@ -4025,6 +4039,10 @@ public class LocalStore extends Store implements Serializable { public Uri getContentUri() { return mUri; } + + public void setEncoding(String encoding) { + mEncoding = encoding; + } } static class ThreadInfo {