From 5a93b8f7c6e25eee5b3d84330576ebbfd7472fab Mon Sep 17 00:00:00 2001 From: Tony Mantler Date: Wed, 21 May 2014 10:25:08 -0700 Subject: [PATCH] Test encode/decode of attachments b/15089448 Change-Id: Ifd2afcfb36c9820c5d64ebbcc3230603b2a27089 --- src/com/android/email/LegacyConversions.java | 91 ++++++++++++------- .../android/email/LegacyConversionsTest.java | 88 ++++++++++++++++++ 2 files changed, 145 insertions(+), 34 deletions(-) diff --git a/src/com/android/email/LegacyConversions.java b/src/com/android/email/LegacyConversions.java index 76f757634..528358497 100644 --- a/src/com/android/email/LegacyConversions.java +++ b/src/com/android/email/LegacyConversions.java @@ -45,6 +45,7 @@ import com.android.emailcommon.provider.Mailbox; import com.android.emailcommon.utility.AttachmentUtilities; import com.android.mail.providers.UIProvider; import com.android.mail.utils.LogUtils; +import com.google.common.annotations.VisibleForTesting; import org.apache.commons.io.IOUtils; @@ -183,6 +184,59 @@ public class LegacyConversions { } } + /** + * Convert a MIME Part object into an Attachment object. Separated for unit testing. + * + * @param part MIME part object to convert + * @return Populated Account object + * @throws MessagingException + */ + @VisibleForTesting + protected static Attachment mimePartToAttachment(final Part part) throws MessagingException { + // Transfer fields from mime format to provider format + final String contentType = MimeUtility.unfoldAndDecode(part.getContentType()); + + String name = MimeUtility.getHeaderParameter(contentType, "name"); + if (TextUtils.isEmpty(name)) { + final String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition()); + name = MimeUtility.getHeaderParameter(contentDisposition, "filename"); + } + + // Incoming attachment: Try to pull size from disposition (if not downloaded yet) + long size = 0; + final String disposition = part.getDisposition(); + if (!TextUtils.isEmpty(disposition)) { + String s = MimeUtility.getHeaderParameter(disposition, "size"); + if (!TextUtils.isEmpty(s)) { + try { + size = Long.parseLong(s); + } catch (final NumberFormatException e) { + LogUtils.d(LogUtils.TAG, e, "Could not decode size \"%s\" from attachment part", + size); + } + } + } + + // Get partId for unloaded IMAP attachments (if any) + // This is only provided (and used) when we have structure but not the actual attachment + final String[] partIds = part.getHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA); + final String partId = partIds != null ? partIds[0] : null; + + final Attachment localAttachment = new Attachment(); + + // Run the mime type through inferMimeType in case we have something generic and can do + // better using the filename extension + localAttachment.mMimeType = AttachmentUtilities.inferMimeType(name, part.getMimeType()); + localAttachment.mFileName = name; + localAttachment.mSize = size; + localAttachment.mContentId = part.getContentId(); + localAttachment.setContentUri(null); // Will be rewritten by saveAttachmentBody + localAttachment.mLocation = partId; + localAttachment.mEncoding = "B"; // TODO - convert other known encodings + + return localAttachment; + } + /** * Add a single attachment part to the message * @@ -203,40 +257,8 @@ public class LegacyConversions { public static void addOneAttachment(final Context context, final EmailContent.Message localMessage, final Part part) throws MessagingException, IOException { - // Transfer fields from mime format to provider format - final String contentType = MimeUtility.unfoldAndDecode(part.getContentType()); - String name = MimeUtility.getHeaderParameter(contentType, "name"); - if (name == null) { - final String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition()); - name = MimeUtility.getHeaderParameter(contentDisposition, "filename"); - } - - // Incoming attachment: Try to pull size from disposition (if not downloaded yet) - long size = 0; - final String disposition = part.getDisposition(); - if (disposition != null) { - String s = MimeUtility.getHeaderParameter(disposition, "size"); - if (s != null) { - size = Long.parseLong(s); - } - } - - // Get partId for unloaded IMAP attachments (if any) - // This is only provided (and used) when we have structure but not the actual attachment - final String[] partIds = part.getHeader(MimeHeader.HEADER_ANDROID_ATTACHMENT_STORE_DATA); - final String partId = partIds != null ? partIds[0] : null; - - // Run the mime type through inferMimeType in case we have something generic and can do - // better using the filename extension - final Attachment localAttachment = new Attachment(); - localAttachment.mMimeType = AttachmentUtilities.inferMimeType(name, part.getMimeType()); - localAttachment.mFileName = name; - localAttachment.mSize = size; // May be reset below if file handled - localAttachment.mContentId = part.getContentId(); - localAttachment.setContentUri(null); // Will be rewritten by saveAttachmentBody + final Attachment localAttachment = mimePartToAttachment(part); localAttachment.mMessageKey = localMessage.mId; - localAttachment.mLocation = partId; - localAttachment.mEncoding = "B"; // TODO - convert other known encodings localAttachment.mAccountKey = localMessage.mAccountKey; if (DEBUG_ATTACHMENTS) { @@ -469,7 +491,8 @@ public class LegacyConversions { * @param contentId as referenced from cid: uris in the message body (if applicable) * @param content unencoded bytes */ - private static void addAttachmentPart(final Multipart mp, final String contentType, + @VisibleForTesting + protected static void addAttachmentPart(final Multipart mp, final String contentType, final Long contentSize, final String filename, final String contentId, final InputStream content) throws MessagingException { final Base64Body body = new Base64Body(content); diff --git a/tests/src/com/android/email/LegacyConversionsTest.java b/tests/src/com/android/email/LegacyConversionsTest.java index d3e7dcd6a..1656f4b3d 100644 --- a/tests/src/com/android/email/LegacyConversionsTest.java +++ b/tests/src/com/android/email/LegacyConversionsTest.java @@ -16,23 +16,41 @@ package com.android.email; +import android.content.Context; import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import com.android.emailcommon.TempDirectory; import com.android.emailcommon.internet.MimeBodyPart; import com.android.emailcommon.internet.MimeMessage; +import com.android.emailcommon.internet.MimeMultipart; import com.android.emailcommon.mail.Address; import com.android.emailcommon.mail.Message.RecipientType; import com.android.emailcommon.mail.MessagingException; +import com.android.emailcommon.mail.Multipart; import com.android.emailcommon.mail.Part; import com.android.emailcommon.provider.EmailContent; +import com.android.emailcommon.provider.EmailContent.Attachment; import com.android.emailcommon.utility.ConversionUtilities; import com.android.emailcommon.utility.ConversionUtilities.BodyFieldData; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Date; +@SmallTest public class LegacyConversionsTest extends AndroidTestCase { + @Override + protected void setUp() throws Exception { + super.setUp(); + TempDirectory.setTempDirectory(getContext()); + } + /** * Test basic fields conversion from Store message to Provider message. */ @@ -172,4 +190,74 @@ public class LegacyConversionsTest extends AndroidTestCase { final BodyFieldData data = ConversionUtilities.parseBodyFields(viewables); assertNull(data.textContent); } + + /** + * Test adding an attachment to a message, and then parsing it back out. + * @throws MessagingException + */ + public void testAttachmentRoundTrip() throws Exception { + final Context context = getContext(); + final MimeMultipart mp = new MimeMultipart(); + mp.setSubType("mixed"); + + final long size; + + final File tempDir = context.getCacheDir(); + if (!tempDir.isDirectory() && !tempDir.mkdirs()) { + fail("Could not create temporary directory"); + } + + final File tempAttachmentFile = File.createTempFile("testAttachmentRoundTrip", ".txt", + tempDir); + + try { + final OutputStream attOut = new FileOutputStream(tempAttachmentFile); + try { + attOut.write("TestData".getBytes()); + } finally { + attOut.close(); + } + size = tempAttachmentFile.length(); + final InputStream attIn = new FileInputStream(tempAttachmentFile); + LegacyConversions.addAttachmentPart(mp, "text/plain", size, "test.txt", + "testContentId", attIn); + } finally { + if (!tempAttachmentFile.delete()) { + fail("Setup failure: Could not clean up temp file"); + } + } + + final MimeMessage outMessage = new MimeMessage(); + outMessage.setBody(mp); + + final MimeMessage inMessage; + + final File tempBodyFile = File.createTempFile("testAttachmentRoundTrip", ".eml", + context.getCacheDir()); + try { + final OutputStream bodyOut = new FileOutputStream(tempBodyFile); + try { + outMessage.writeTo(bodyOut); + } finally { + bodyOut.close(); + } + final InputStream bodyIn = new FileInputStream(tempBodyFile); + try { + inMessage = new MimeMessage(bodyIn); + } finally { + bodyIn.close(); + } + } finally { + if (!tempBodyFile.delete()) { + fail("Setup failure: Could not clean up temp file"); + } + } + final Multipart inBody = (Multipart) inMessage.getBody(); + final Part attPart = inBody.getBodyPart(0); + final Attachment att = LegacyConversions.mimePartToAttachment(attPart); + assertEquals(att.mFileName, "test.txt"); + assertEquals(att.mMimeType, "text/plain"); + assertEquals(att.mSize, size); + assertEquals(att.mContentId, "testContentId"); + } }